]> git.ipfire.org Git - thirdparty/bird.git/blobdiff - nest/proto.c
Nest: VRF of protocol can be explicitly specified as 'default'
[thirdparty/bird.git] / nest / proto.c
index ecc3b0fe3b31380338132ccd519bdcb057cbcfdf..696616509613933809cd9ddd1f04e4df51e6a6a2 100644 (file)
@@ -25,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)
 
@@ -164,6 +165,7 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
   c->channel_state = CS_DOWN;
   c->export_state = ES_DOWN;
   c->last_state_change = current_time();
+  c->last_tx_filter_change = current_time();
   c->reloadable = 1;
 
   CALL(c->channel->init, c, cf);
@@ -282,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)
 {
@@ -289,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);
@@ -315,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--;
@@ -324,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 */
@@ -355,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:
@@ -374,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;
 
@@ -437,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);
 
@@ -455,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)
   {
@@ -470,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)
@@ -479,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;
 
@@ -491,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)
 {
@@ -513,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;
@@ -536,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;
@@ -579,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)
@@ -675,11 +765,10 @@ proto_init(struct proto_config *c, node *n)
   p->proto_state = PS_DOWN;
   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]" : "");
 
@@ -786,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
@@ -822,7 +933,8 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
   if ((nc->protocol != oc->protocol) ||
       (nc->net_type != oc->net_type) ||
       (nc->disabled != p->disabled) ||
-      (nc->vrf != oc->vrf))
+      (nc->vrf != oc->vrf) ||
+      (nc->vrf_set != oc->vrf_set))
     return 0;
 
   p->name = nc->name;
@@ -885,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 */
@@ -896,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);
@@ -909,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;
@@ -980,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;
@@ -1016,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
@@ -1235,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 */
@@ -1270,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
@@ -1289,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);
@@ -1334,6 +1490,39 @@ proto_schedule_down(struct proto *p, byte restart, byte code)
   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;
+}
+
 
 static const char *
 channel_limit_name(struct channel_limit *l)
@@ -1567,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",
@@ -1622,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_time(tbuf, &config->tf_proto, p->last_state_change);
-  cli_msg(-1002, "%-8s %-8s %-8s %-5s  %-10s  %s",
+  cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s  %s",
          p->name,
          p->proto->name,
          p->main_channel ? p->main_channel->table->name : "---",
@@ -1646,10 +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)
-      cli_msg(-1006, "  VRF:            %s", p->vrf->name);
+    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);
@@ -1665,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)
   {
@@ -1676,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)
   {
@@ -1691,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)
   {
@@ -1707,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);
@@ -1714,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;
 
@@ -1753,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)
   {
@@ -1778,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;
@@ -1794,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;