#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"
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)
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)
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);
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)
{
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);
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--;
memset(&c->stats, 0, sizeof(struct proto_stats));
+ c->in_table = NULL;
+ c->reload_event = NULL;
+
CALL(c->channel->cleanup, c);
/* Schedule protocol shutddown */
return;
c->channel_state = state;
- c->last_state_change = now;
+ c->last_state_change = current_time();
switch (state)
{
if (es != ES_DOWN)
channel_stop_export(c);
+ if (c->in_table && (cs == CS_UP))
+ channel_reset_import(c);
+
break;
case CS_UP:
if (es != ES_DOWN)
channel_stop_export(c);
+ if (c->in_table && (cs == CS_UP))
+ channel_reset_import(c);
+
channel_do_flush(c);
break;
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);
};
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)
{
cf_error("Different channel type");
tab = new_config->def_tables[net_type];
- name = net_label[net_type];
}
if (!cc)
cf = cfg_allocz(cc->config_size);
cf->name = name;
cf->channel = cc;
+ cf->parent = proto;
cf->table = tab;
cf->out_filter = FILTER_REJECT;
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)
{
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;
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;
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)
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]" : "");
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
/* 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;
{
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 */
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);
}
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;
proto_remove_channels(p);
rem_node(&p->n);
rfree(p->event);
+ mb_free(p->message);
mb_free(p);
if (!nc)
return;
}
}
+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
*
*/
-static void graceful_restart_done(struct timer *t);
+static void graceful_restart_done(timer *t);
/**
* graceful_restart_recovery - request initial graceful restart recovery
}
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);
}
/**
* 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;
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);
}
/**
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 */
#ifdef CONFIG_STATIC
proto_build(&proto_static);
#endif
+#ifdef CONFIG_MRT
+ proto_build(&proto_mrt);
+#endif
#ifdef CONFIG_OSPF
proto_build(&proto_ospf);
#endif
#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);
int proto_restart;
static void
-proto_shutdown_loop(struct timer *t UNUSED)
+proto_shutdown_loop(timer *t UNUSED)
{
struct proto *p, *p_next;
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;
}
return;
p->proto_state = state;
- p->last_state_change = now;
+ p->last_state_change = current_time();
switch (state)
{
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",
}
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 : "---",
{
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);
}
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)
{
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)
{
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)
{
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);
}
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;
}
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)
{
}
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;
}
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;