X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=nest%2Fproto.c;h=696616509613933809cd9ddd1f04e4df51e6a6a2;hb=18f70a6229f586d5e4f387075be42d7a1ef5d269;hp=3d764df07c252419f6dab02c00b57310e2e050ec;hpb=fd1f355b7b24f354f7d57f127911b4fd98354b34;p=thirdparty%2Fbird.git diff --git a/nest/proto.c b/nest/proto.c index 3d764df07..696616509 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -13,6 +13,7 @@ #include "lib/resource.h" #include "lib/lists.h" #include "lib/event.h" +#include "lib/timer.h" #include "lib/string.h" #include "conf/conf.h" #include "nest/route.h" @@ -24,6 +25,7 @@ pool *proto_pool; list proto_list; static list protocol_list; +struct protocol *class_to_protocol[PROTOCOL__MAX]; #define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0) @@ -43,11 +45,11 @@ static char *c_states[] = { "DOWN", "START", "UP", "FLUSHING" }; extern struct protocol proto_unix_iface; -static void proto_shutdown_loop(struct timer *); +static void proto_shutdown_loop(timer *); static void proto_rethink_goal(struct proto *p); static char *proto_state_name(struct proto *p); static void channel_verify_limits(struct channel *c); -static void channel_reset_limit(struct channel_limit *l); +static inline void channel_reset_limit(struct channel_limit *l); static inline int proto_is_done(struct proto *p) @@ -162,7 +164,8 @@ proto_add_channel(struct proto *p, struct channel_config *cf) c->channel_state = CS_DOWN; c->export_state = ES_DOWN; - c->last_state_change = now; + c->last_state_change = current_time(); + c->last_tx_filter_change = current_time(); c->reloadable = 1; CALL(c->channel->init, c, cf); @@ -281,6 +284,54 @@ channel_stop_export(struct channel *c) c->stats.exp_routes = 0; } + +/* Called by protocol for reload from in_table */ +void +channel_schedule_reload(struct channel *c) +{ + ASSERT(c->channel_state == CS_UP); + + rt_reload_channel_abort(c); + ev_schedule(c->reload_event); +} + +static void +channel_reload_loop(void *ptr) +{ + struct channel *c = ptr; + + if (!rt_reload_channel(c)) + { + ev_schedule(c->reload_event); + return; + } +} + +static void +channel_reset_import(struct channel *c) +{ + /* Need to abort feeding */ + ev_postpone(c->reload_event); + rt_reload_channel_abort(c); + + rt_prune_sync(c->in_table, 1); +} + +/* Called by protocol to activate in_table */ +void +channel_setup_in_table(struct channel *c) +{ + struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config)); + cf->name = "import"; + cf->addr_type = c->net_type; + + c->in_table = mb_allocz(c->proto->pool, sizeof(struct rtable)); + rt_setup(c->proto->pool, c->in_table, cf); + + c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c); +} + + static void channel_do_start(struct channel *c) { @@ -288,9 +339,7 @@ channel_do_start(struct channel *c) add_tail(&c->table->channels, &c->table_node); c->proto->active_channels++; - c->feed_event = ev_new(c->proto->pool); - c->feed_event->data = c; - c->feed_event->hook = channel_feed_loop; + c->feed_event = ev_new_init(c->proto->pool, channel_feed_loop, c); channel_reset_limit(&c->rx_limit); channel_reset_limit(&c->in_limit); @@ -314,6 +363,8 @@ channel_do_flush(struct channel *c) static void channel_do_down(struct channel *c) { + ASSERT(!c->feed_active && !c->reload_active); + rem_node(&c->table_node); rt_unlock_table(c->table); c->proto->active_channels--; @@ -323,6 +374,9 @@ channel_do_down(struct channel *c) memset(&c->stats, 0, sizeof(struct proto_stats)); + c->in_table = NULL; + c->reload_event = NULL; + CALL(c->channel->cleanup, c); /* Schedule protocol shutddown */ @@ -341,7 +395,7 @@ channel_set_state(struct channel *c, uint state) return; c->channel_state = state; - c->last_state_change = now; + c->last_state_change = current_time(); switch (state) { @@ -354,6 +408,9 @@ channel_set_state(struct channel *c, uint state) if (es != ES_DOWN) channel_stop_export(c); + if (c->in_table && (cs == CS_UP)) + channel_reset_import(c); + break; case CS_UP: @@ -373,6 +430,9 @@ channel_set_state(struct channel *c, uint state) if (es != ES_DOWN) channel_stop_export(c); + if (c->in_table && (cs == CS_UP)) + channel_reset_import(c); + channel_do_flush(c); break; @@ -436,7 +496,7 @@ static void channel_request_reload(struct channel *c) { ASSERT(c->channel_state == CS_UP); - // ASSERT(channel_reloadable(c)); + ASSERT(channel_reloadable(c)); c->proto->reload_routes(c); @@ -454,11 +514,10 @@ const struct channel_class channel_basic = { }; void * -channel_config_new(const struct channel_class *cc, uint net_type, struct proto_config *proto) +channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto) { struct channel_config *cf = NULL; struct rtable_config *tab = NULL; - const char *name = NULL; if (net_type) { @@ -469,7 +528,6 @@ channel_config_new(const struct channel_class *cc, uint net_type, struct proto_c cf_error("Different channel type"); tab = new_config->def_tables[net_type]; - name = net_label[net_type]; } if (!cc) @@ -478,6 +536,7 @@ channel_config_new(const struct channel_class *cc, uint net_type, struct proto_c cf = cfg_allocz(cc->config_size); cf->name = name; cf->channel = cc; + cf->parent = proto; cf->table = tab; cf->out_filter = FILTER_REJECT; @@ -490,6 +549,26 @@ channel_config_new(const struct channel_class *cc, uint net_type, struct proto_c return cf; } +void * +channel_config_get(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto) +{ + struct channel_config *cf; + + /* We are using name as token, so no strcmp() */ + WALK_LIST(cf, proto->channels) + if (cf->name == name) + { + /* Allow to redefine channel only if inherited from template */ + if (cf->parent == proto) + cf_error("Multiple %s channels", name); + + cf->parent = proto; + return cf; + } + + return channel_config_new(cc, name, net_type, proto); +} + struct channel_config * channel_copy_config(struct channel_config *src, struct proto_config *proto) { @@ -512,8 +591,9 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode))) return 0; - int import_changed = !filter_same(c->in_filter, cf->in_filter); - int export_changed = !filter_same(c->out_filter, cf->out_filter); + /* Note that filter_same() requires arguments in (new, old) order */ + int import_changed = !filter_same(cf->in_filter, c->in_filter); + int export_changed = !filter_same(cf->out_filter, c->out_filter); if (c->preference != cf->preference) import_changed = 1; @@ -535,6 +615,9 @@ channel_reconfigure(struct channel *c, struct channel_config *cf) channel_verify_limits(c); + if (export_changed) + c->last_tx_filter_change = current_time(); + /* Execute channel-specific reconfigure hook */ if (c->channel->reconfigure && !c->channel->reconfigure(c, cf)) return 0; @@ -578,6 +661,14 @@ proto_configure_channel(struct proto *p, struct channel **pc, struct channel_con if (!c && cf) { + /* We could add the channel, but currently it would just stay in down state + until protocol is restarted, so it is better to force restart anyways. */ + if (p->proto_state != PS_DOWN) + { + log(L_INFO "Cannot add channel %s.%s", p->name, cf->name); + return 0; + } + *pc = proto_add_channel(p, cf); } else if (c && !cf) @@ -672,12 +763,12 @@ proto_init(struct proto_config *c, node *n) struct proto *p = pr->init(c); p->proto_state = PS_DOWN; - p->last_state_change = now; + p->last_state_change = current_time(); + p->vrf = c->vrf; + p->vrf_set = c->vrf_set; insert_node(&p->n, n); - p->event = ev_new(proto_pool); - p->event->hook = proto_event; - p->event->data = p; + p->event = ev_new_init(proto_pool, proto_event, p); PD(p, "Initializing%s", p->disabled ? " [disabled]" : ""); @@ -784,6 +875,28 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src) dest->protocol->copy_config(dest, src); } +void +proto_clone_config(struct symbol *sym, struct proto_config *parent) +{ + struct proto_config *cf = proto_config_new(parent->protocol, SYM_PROTO); + proto_copy_config(cf, parent); + cf->name = sym->name; + cf->proto = NULL; + cf->parent = parent; + + sym->class = cf->class; + sym->def = cf; +} + +static void +proto_undef_clone(struct symbol *sym, struct proto_config *cf) +{ + rem_node(&cf->n); + + sym->class = SYM_VOID; + sym->def = NULL; +} + /** * protos_preconfig - pre-configuration processing * @c: new configuration @@ -819,7 +932,9 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config /* If there is a too big change in core attributes, ... */ if ((nc->protocol != oc->protocol) || (nc->net_type != oc->net_type) || - (nc->disabled != p->disabled)) + (nc->disabled != p->disabled) || + (nc->vrf != oc->vrf) || + (nc->vrf_set != oc->vrf_set)) return 0; p->name = nc->name; @@ -882,6 +997,24 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty { p = oc->proto; sym = cf_find_symbol(new, oc->name); + + /* Handle dynamic protocols */ + if (!sym && oc->parent && !new->shutdown) + { + struct symbol *parsym = cf_find_symbol(new, oc->parent->name); + if (parsym && parsym->class == SYM_PROTO) + { + /* This is hack, we would like to share config, but we need to copy it now */ + new_config = new; + cfg_mem = new->mem; + conf_this_scope = new->root_scope; + sym = cf_get_symbol(oc->name); + proto_clone_config(sym, parsym->def); + new_config = NULL; + cfg_mem = NULL; + } + } + if (sym && sym->class == SYM_PROTO && !new->shutdown) { /* Found match, let's check if we can smoothly switch to new configuration */ @@ -893,6 +1026,12 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty if (! force_reconfig && proto_reconfigure(p, oc, nc, type)) continue; + if (nc->parent) + { + proto_undef_clone(sym, nc); + goto remove; + } + /* Unsuccessful, we will restart it */ if (!p->disabled && !nc->disabled) log(L_INFO "Restarting protocol %s", p->name); @@ -906,10 +1045,16 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty } else if (!new->shutdown) { + remove: log(L_INFO "Removing protocol %s", p->name); p->down_code = PDC_CF_REMOVE; p->cf_new = NULL; } + else if (new->gr_down) + { + p->down_code = PDC_CMD_GR_DOWN; + p->cf_new = NULL; + } else /* global shutdown */ { p->down_code = PDC_CMD_SHUTDOWN; @@ -977,6 +1122,7 @@ proto_rethink_goal(struct proto *p) proto_remove_channels(p); rem_node(&p->n); rfree(p->event); + mb_free(p->message); mb_free(p); if (!nc) return; @@ -1013,6 +1159,15 @@ proto_rethink_goal(struct proto *p) } } +struct proto * +proto_spawn(struct proto_config *cf, uint disabled) +{ + struct proto *p = proto_init(cf, TAIL(proto_list)); + p->disabled = disabled; + proto_rethink_goal(p); + return p; +} + /** * DOC: Graceful restart recovery @@ -1046,7 +1201,7 @@ proto_rethink_goal(struct proto *p) * */ -static void graceful_restart_done(struct timer *t); +static void graceful_restart_done(timer *t); /** * graceful_restart_recovery - request initial graceful restart recovery @@ -1083,9 +1238,8 @@ graceful_restart_init(void) } graceful_restart_state = GRS_ACTIVE; - gr_wait_timer = tm_new(proto_pool); - gr_wait_timer->hook = graceful_restart_done; - tm_start(gr_wait_timer, config->gr_wait); + gr_wait_timer = tm_new_init(proto_pool, graceful_restart_done, NULL, 0, 0); + tm_start(gr_wait_timer, config->gr_wait S); } /** @@ -1099,7 +1253,7 @@ graceful_restart_init(void) * restart wait timer fires (but there are still some locks). */ static void -graceful_restart_done(struct timer *t UNUSED) +graceful_restart_done(timer *t UNUSED) { log(L_INFO "Graceful restart done"); graceful_restart_state = GRS_DONE; @@ -1136,7 +1290,7 @@ graceful_restart_show_status(void) cli_msg(-24, "Graceful restart recovery in progress"); cli_msg(-24, " Waiting for %d channels to recover", graceful_restart_locks); - cli_msg(-24, " Wait timer is %d/%d", tm_remains(gr_wait_timer), config->gr_wait); + cli_msg(-24, " Wait timer is %t/%u", tm_remains(gr_wait_timer), config->gr_wait); } /** @@ -1233,11 +1387,9 @@ void proto_build(struct protocol *p) { add_tail(&protocol_list, &p->n); - if (p->attr_class) - { - ASSERT(!attr_class_to_protocol[p->attr_class]); - attr_class_to_protocol[p->attr_class] = p; - } + ASSERT(p->class); + ASSERT(!class_to_protocol[p->class]); + class_to_protocol[p->class] = p; } /* FIXME: convert this call to some protocol hook */ @@ -1268,6 +1420,9 @@ protos_build(void) #ifdef CONFIG_STATIC proto_build(&proto_static); #endif +#ifdef CONFIG_MRT + proto_build(&proto_mrt); +#endif #ifdef CONFIG_OSPF proto_build(&proto_ospf); #endif @@ -1287,6 +1442,9 @@ protos_build(void) #ifdef CONFIG_RPKI proto_build(&proto_rpki); #endif +#ifdef CONFIG_PERF + proto_build(&proto_perf); +#endif proto_pool = rp_new(&root_pool, "Protocols"); proto_shutdown_timer = tm_new(proto_pool); @@ -1298,7 +1456,7 @@ protos_build(void) int proto_restart; static void -proto_shutdown_loop(struct timer *t UNUSED) +proto_shutdown_loop(timer *t UNUSED) { struct proto *p, *p_next; @@ -1329,7 +1487,40 @@ proto_schedule_down(struct proto *p, byte restart, byte code) p->down_sched = restart ? PDS_RESTART : PDS_DISABLE; p->down_code = code; - tm_start_max(proto_shutdown_timer, restart ? 2 : 0); + tm_start_max(proto_shutdown_timer, restart ? 250 MS : 0); +} + +/** + * proto_set_message - set administrative message to protocol + * @p: protocol + * @msg: message + * @len: message length (-1 for NULL-terminated string) + * + * The function sets administrative message (string) related to protocol state + * change. It is called by the nest code for manual enable/disable/restart + * commands all routes to the protocol, and by protocol-specific code when the + * protocol state change is initiated by the protocol. Using NULL message clears + * the last message. The message string may be either NULL-terminated or with an + * explicit length. + */ +void +proto_set_message(struct proto *p, char *msg, int len) +{ + mb_free(p->message); + p->message = NULL; + + if (!msg || !len) + return; + + if (len < 0) + len = strlen(msg); + + if (!len) + return; + + p->message = mb_alloc(proto_pool, len + 1); + memcpy(p->message, msg, len); + p->message[len] = 0; } @@ -1500,7 +1691,7 @@ proto_notify_state(struct proto *p, uint state) return; p->proto_state = state; - p->last_state_change = now; + p->last_state_change = current_time(); switch (state) { @@ -1565,11 +1756,11 @@ channel_show_stats(struct channel *c) struct proto_stats *s = &c->stats; if (c->in_keep_filtered) - cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported", - s->imp_routes, s->filt_routes, s->exp_routes); + cli_msg(-1006, " Routes: %u imported, %u filtered, %u exported, %u preferred", + s->imp_routes, s->filt_routes, s->exp_routes, s->pref_routes); else - cli_msg(-1006, " Routes: %u imported, %u exported", - s->imp_routes, s->exp_routes); + cli_msg(-1006, " Routes: %u imported, %u exported, %u preferred", + s->imp_routes, s->exp_routes, s->pref_routes); cli_msg(-1006, " Route change stats: received rejected filtered ignored accepted"); cli_msg(-1006, " Import updates: %10u %10u %10u %10u %10u", @@ -1620,19 +1811,20 @@ channel_show_info(struct channel *c) } void -proto_cmd_show(struct proto *p, uint verbose, int cnt) +proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt) { byte buf[256], tbuf[TM_DATETIME_BUFFER_SIZE]; /* First protocol - show header */ if (!cnt) - cli_msg(-2002, "name proto table state since info"); + cli_msg(-2002, "%-10s %-10s %-10s %-6s %-12s %s", + "Name", "Proto", "Table", "State", "Since", "Info"); buf[0] = 0; if (p->proto->get_status) p->proto->get_status(p, buf); - tm_format_datetime(tbuf, &config->tf_proto, p->last_state_change); - cli_msg(-1002, "%-8s %-8s %-8s %-5s %-10s %s", + tm_format_time(tbuf, &config->tf_proto, p->last_state_change); + cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s", p->name, p->proto->name, p->main_channel ? p->main_channel->table->name : "---", @@ -1644,8 +1836,12 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt) { if (p->cf->dsc) cli_msg(-1006, " Description: %s", p->cf->dsc); + if (p->message) + cli_msg(-1006, " Message: %s", p->message); if (p->cf->router_id) cli_msg(-1006, " Router ID: %R", p->cf->router_id); + if (p->vrf_set) + cli_msg(-1006, " VRF: %s", p->vrf ? p->vrf->name : "default"); if (p->proto->show_proto_info) p->proto->show_proto_info(p); @@ -1661,7 +1857,7 @@ proto_cmd_show(struct proto *p, uint verbose, int cnt) } void -proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED) +proto_cmd_disable(struct proto *p, uintptr_t arg, int cnt UNUSED) { if (p->disabled) { @@ -1672,12 +1868,13 @@ proto_cmd_disable(struct proto *p, uint arg UNUSED, int cnt UNUSED) log(L_INFO "Disabling protocol %s", p->name); p->disabled = 1; p->down_code = PDC_CMD_DISABLE; + proto_set_message(p, (char *) arg, -1); proto_rethink_goal(p); cli_msg(-9, "%s: disabled", p->name); } void -proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED) +proto_cmd_enable(struct proto *p, uintptr_t arg, int cnt UNUSED) { if (!p->disabled) { @@ -1687,12 +1884,13 @@ proto_cmd_enable(struct proto *p, uint arg UNUSED, int cnt UNUSED) log(L_INFO "Enabling protocol %s", p->name); p->disabled = 0; + proto_set_message(p, (char *) arg, -1); proto_rethink_goal(p); cli_msg(-11, "%s: enabled", p->name); } void -proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED) +proto_cmd_restart(struct proto *p, uintptr_t arg, int cnt UNUSED) { if (p->disabled) { @@ -1703,6 +1901,7 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED) log(L_INFO "Restarting protocol %s", p->name); p->disabled = 1; p->down_code = PDC_CMD_RESTART; + proto_set_message(p, (char *) arg, -1); proto_rethink_goal(p); p->disabled = 0; proto_rethink_goal(p); @@ -1710,7 +1909,7 @@ proto_cmd_restart(struct proto *p, uint arg UNUSED, int cnt UNUSED) } void -proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED) +proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED) { struct channel *c; @@ -1749,19 +1948,19 @@ proto_cmd_reload(struct proto *p, uint dir, int cnt UNUSED) } void -proto_cmd_debug(struct proto *p, uint mask, int cnt UNUSED) +proto_cmd_debug(struct proto *p, uintptr_t mask, int cnt UNUSED) { p->debug = mask; } void -proto_cmd_mrtdump(struct proto *p, uint mask, int cnt UNUSED) +proto_cmd_mrtdump(struct proto *p, uintptr_t mask, int cnt UNUSED) { p->mrtdump = mask; } static void -proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int), uint arg) +proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg) { if (s->class != SYM_PROTO) { @@ -1774,7 +1973,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uint, int) } static void -proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint arg) +proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg) { struct proto *p; int cnt = 0; @@ -1790,8 +1989,8 @@ proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uint, int), uint a } void -proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uint, int), - int restricted, uint arg) +proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), + int restricted, uintptr_t arg) { if (restricted && cli_access_restricted()) return;