]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Better support for multitable protocols.
authorOndrej Zajicek <santiago@crfreenet.org>
Thu, 15 Mar 2012 10:58:08 +0000 (11:58 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Thu, 15 Mar 2012 11:13:04 +0000 (12:13 +0100)
The nest-protocol interaction is changed to better handle multitable
protocols. Multitable protocols now declare that by 'multitable' field,
which tells nest that a protocol handles things related to proto-rtable
interaction (table locking, announce hook adding, reconfiguration of
filters) itself.

Filters and stats are moved to announce hooks, a protocol could have
different filters and stats to different tables.

The patch is based on one from Alexander V. Chernikov, thanks.

nest/proto-hooks.c
nest/proto.c
nest/protocol.h
nest/route.h
nest/rt-table.c
proto/bgp/bgp.c
proto/pipe/pipe.c
proto/pipe/pipe.h

index 2582c48b6464dfe25529d0ea8e664d4e0733b810..e80f87ea527517b4cab321a891eb2bbe1738ebb6 100644 (file)
@@ -90,7 +90,7 @@ void dump_attrs(rte *e)
  * @p: protocol instance
  *
  * The start() hook is called by the core when it wishes to start
- * the instance.
+ * the instance. Multitable protocols should lock their tables here.
  *
  * Result: new protocol state
  */
@@ -109,6 +109,17 @@ int start(struct proto *p)
 int shutdown(struct proto *p)
 { DUMMY; }
 
+/**
+ * cleanup - request instance cleanup
+ * @p: protocol instance
+ *
+ * The cleanup() hook is called by the core when the protocol became
+ * hungry/down, i.e. all protocol ahooks and routes are flushed.
+ * Multitable protocols should unlock their tables here.
+ */
+void cleanup(struct proto *p)
+{ DUMMY; }
+
 /**
  * get_status - get instance status
  * @p: protocol instance
index 0fc72ce16ed74729a4349938f8ddc9df3d70ec81..9a7f123e62a8ad4b923aa547cec41dd0b2260773 100644 (file)
@@ -112,8 +112,6 @@ proto_new(struct proto_config *c, unsigned size)
   p->disabled = c->disabled;
   p->proto = pr;
   p->table = c->table->table;
-  p->in_filter = c->in_filter;
-  p->out_filter = c->out_filter;
   p->hash_key = random_u32();
   c->proto = p;
   return p;
@@ -126,49 +124,102 @@ proto_init_instance(struct proto *p)
   p->pool = rp_new(proto_pool, p->proto->name);
   p->attn = ev_new(p->pool);
   p->attn->data = p;
-  rt_lock_table(p->table);
+
+  if (! p->proto->multitable)
+    rt_lock_table(p->table);
 }
 
+extern pool *rt_table_pool;
 /**
  * proto_add_announce_hook - connect protocol to a routing table
  * @p: protocol instance
  * @t: routing table to connect to
+ * @in: input filter
+ * @out: output filter
+ * @stats: per-table protocol statistics
  *
  * This function creates a connection between the protocol instance @p
  * and the routing table @t, making the protocol hear all changes in
  * the table.
  *
+ * The announce hook is linked in the protocol ahook list and, if the
+ * protocol accepts routes, also in the table ahook list. Announce
+ * hooks are allocated from the routing table resource pool, they are
+ * unlinked from the table ahook list after the protocol went down,
+ * (in proto_schedule_flush()) and they are automatically freed after the
+ * protocol is flushed (in proto_fell_down()).
+ *
  * Unless you want to listen to multiple routing tables (as the Pipe
  * protocol does), you needn't to worry about this function since the
  * connection to the protocol's primary routing table is initialized
  * automatically by the core code.
  */
 struct announce_hook *
-proto_add_announce_hook(struct proto *p, struct rtable *t)
+proto_add_announce_hook(struct proto *p, struct rtable *t, struct filter *in,
+                       struct filter *out, struct proto_stats *stats)
 {
   struct announce_hook *h;
 
-  if (!p->rt_notify)
-    return NULL;
   DBG("Connecting protocol %s to table %s\n", p->name, t->name);
   PD(p, "Connected to table %s", t->name);
-  h = mb_alloc(p->pool, sizeof(struct announce_hook));
+
+  h = mb_allocz(rt_table_pool, sizeof(struct announce_hook));
   h->table = t;
   h->proto = p;
+  h->in_filter = in;
+  h->out_filter = out;
+  h->stats = stats;
+
   h->next = p->ahooks;
   p->ahooks = h;
-  add_tail(&t->hooks, &h->n);
+
+  if (p->rt_notify)
+    add_tail(&t->hooks, &h->n);
   return h;
 }
 
+/**
+ * proto_find_announce_hook - find announce hooks
+ * @p: protocol instance
+ * @t: routing table
+ *
+ * Returns pointer to announce hook or NULL
+ */
+struct announce_hook *
+proto_find_announce_hook(struct proto *p, struct rtable *t)
+{
+  struct announce_hook *a;
+
+  for (a = p->ahooks; a; a = a->next)
+    if (a->table == t)
+      return a;
+
+  return NULL;
+}
+
 static void
-proto_flush_hooks(struct proto *p)
+proto_unlink_ahooks(struct proto *p)
 {
   struct announce_hook *h;
 
-  for(h=p->ahooks; h; h=h->next)
-    rem_node(&h->n);
+  if (p->rt_notify)
+    for(h=p->ahooks; h; h=h->next)
+      rem_node(&h->n);
+}
+
+static void
+proto_free_ahooks(struct proto *p)
+{
+  struct announce_hook *h, *hn;
+
+  for(h = p->ahooks; h; h = hn)
+  {
+    hn = h->next;
+    mb_free(h);
+  }
+
   p->ahooks = NULL;
+  p->main_ahook = NULL;
 }
 
 /**
@@ -322,6 +373,8 @@ proto_init(struct proto_config *c)
   return q;
 }
 
+int proto_reconfig_type;  /* Hack to propagate type info to pipe reconfigure hook */
+
 static int
 proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config *nc, int type)
 {
@@ -336,23 +389,10 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
       (proto_get_router_id(nc) != proto_get_router_id(oc)))
     return 0;
 
-  int import_changed = (type != RECONFIG_SOFT) && ! filter_same(nc->in_filter, oc->in_filter);
-  int export_changed = (type != RECONFIG_SOFT) && ! filter_same(nc->out_filter, oc->out_filter);
-
-  /* We treat a change in preferences by reimporting routes */
-  if (nc->preference != oc->preference)
-    import_changed = 1;
-
-  /* If the protocol in not UP, it has no routes and we can ignore such changes */
-  if (p->proto_state != PS_UP)
-    import_changed = export_changed = 0;
-
-  /* Without this hook we cannot reload routes and have to restart the protocol */
-  if (import_changed && ! p->reload_routes)
-    return 0;
 
   p->debug = nc->debug;
   p->mrtdump = nc->mrtdump;
+  proto_reconfig_type = type;
 
   /* Execute protocol specific reconfigure hook */
   if (! (p->proto->reconfigure && p->proto->reconfigure(p, nc)))
@@ -362,14 +402,37 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
   PD(p, "Reconfigured");
   p->cf = nc;
   p->name = nc->name;
-  p->in_filter = nc->in_filter;
-  p->out_filter = nc->out_filter;
   p->preference = nc->preference;
 
+
+  /* Multitable protocols handle rest in their reconfigure hooks */
+  if (p->proto->multitable)
+    return 1;
+
+  /* Update filters in the main announce hook */
+  if (p->main_ahook)
+    {
+      p->main_ahook->in_filter = nc->in_filter;
+      p->main_ahook->out_filter = nc->out_filter;
+    }
+
+  /* Update routes when filters changed. If the protocol in not UP,
+     it has no routes and we can ignore such changes */
+  if ((p->proto_state != PS_UP) || (type == RECONFIG_SOFT))
+    return 1;
+
+  int import_changed = ! filter_same(nc->in_filter, oc->in_filter);
+  int export_changed = ! filter_same(nc->out_filter, oc->out_filter);
+
+  /* We treat a change in preferences by reimporting routes */
+  if (nc->preference != oc->preference)
+    import_changed = 1;
+
   if (import_changed || export_changed)
     log(L_INFO "Reloading protocol %s", p->name);
 
-  if (import_changed && ! p->reload_routes(p))
+  /* If import filter changed, call reload hook */
+  if (import_changed && ! (p->reload_routes && p->reload_routes(p)))
     {
       /* Now, the protocol is reconfigured. But route reload failed
         and we have to do regular protocol restart. */
@@ -553,6 +616,7 @@ void
 protos_dump_all(void)
 {
   struct proto *p;
+  struct announce_hook *a;
 
   debug("Protocols:\n");
 
@@ -560,10 +624,14 @@ protos_dump_all(void)
     {
       debug("  protocol %s state %s/%s\n", p->name,
            p_states[p->proto_state], c_states[p->core_state]);
-      if (p->in_filter)
-       debug("\tInput filter: %s\n", filter_name(p->in_filter));
-      if (p->out_filter != FILTER_REJECT)
-       debug("\tOutput filter: %s\n", filter_name(p->out_filter));
+      for (a = p->ahooks; a; a = a->next)
+       {
+         debug("\tTABLE %s\n", a->table->name);
+         if (a->in_filter)
+           debug("\tInput filter: %s\n", filter_name(a->in_filter));
+         if (a->out_filter != FILTER_REJECT)
+           debug("\tOutput filter: %s\n", filter_name(a->out_filter));
+       }
       if (p->disabled)
        debug("\tDISABLED\n");
       else if (p->proto->dump)
@@ -647,7 +715,10 @@ proto_fell_down(struct proto *p)
     log(L_ERR "Protocol %s is down but still has %d routes", p->name, p->stats.imp_routes);
 
   bzero(&p->stats, sizeof(struct proto_stats));
-  rt_unlock_table(p->table);
+  proto_free_ahooks(p);
+
+  if (! p->proto->multitable)
+    rt_unlock_table(p->table);
 
   if (p->proto->cleanup)
     p->proto->cleanup(p);
@@ -686,7 +757,7 @@ proto_feed_initial(void *P)
     return;
 
   DBG("Feeding protocol %s\n", p->name);
-  proto_add_announce_hook(p, p->table);
+
   if_feed_baby(p);
   proto_feed_more(P);
 }
@@ -701,7 +772,7 @@ proto_schedule_flush(struct proto *p)
   DBG("%s: Scheduling flush\n", p->name);
   p->core_state = FS_FLUSHING;
   proto_relink(p);
-  proto_flush_hooks(p);
+  proto_unlink_ahooks(p);
   ev_schedule(proto_flush_event);
 }
 
@@ -716,6 +787,11 @@ proto_schedule_feed(struct proto *p, int initial)
   if (!initial)
     p->stats.exp_routes = 0;
 
+  /* Connect protocol to routing table */
+  if (initial && !p->proto->multitable)
+    p->main_ahook = proto_add_announce_hook(p, p->table,
+      p->cf->in_filter, p->cf->out_filter, &p->stats);
+
   proto_relink(p);
   p->attn->hook = initial ? proto_feed_initial : proto_feed_more;
   ev_schedule(p->attn);
@@ -855,9 +931,8 @@ proto_state_name(struct proto *p)
 }
 
 static void
-proto_do_show_stats(struct proto *p)
+proto_show_stats(struct proto_stats *s)
 {
-  struct proto_stats *s = &p->stats;
   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");
@@ -875,47 +950,17 @@ proto_do_show_stats(struct proto *p)
          s->exp_withdraws_received, s->exp_withdraws_accepted);
 }
 
-#ifdef CONFIG_PIPE
-static void
-proto_do_show_pipe_stats(struct proto *p)
+void
+proto_show_basic_info(struct proto *p)
 {
-  struct proto_stats *s1 = &p->stats;
-  struct proto_stats *s2 = pipe_get_peer_stats(p);
-
-  /*
-   * Pipe stats (as anything related to pipes) are a bit tricky. There
-   * are two sets of stats - s1 for routes going from the primary
-   * routing table to the secondary routing table ('exported' from the
-   * user point of view) and s2 for routes going in the other
-   * direction ('imported' from the user point of view).
-   *
-   * Each route going through a pipe is, technically, first exported
-   * to the pipe and then imported from that pipe and such operations
-   * are counted in one set of stats according to the direction of the
-   * route propagation. Filtering is done just in the first part
-   * (export). Therefore, we compose stats for one directon for one
-   * user direction from both import and export stats, skipping
-   * immediate and irrelevant steps (exp_updates_accepted,
-   * imp_updates_received, imp_updates_filtered, ...)
-   */
+  // cli_msg(-1006, "  Table:          %s", p->table->name);
+  cli_msg(-1006, "  Preference:     %d", p->preference);
+  cli_msg(-1006, "  Input filter:   %s", filter_name(p->cf->in_filter));
+  cli_msg(-1006, "  Output filter:  %s", filter_name(p->cf->out_filter));
 
-  cli_msg(-1006, "  Routes:         %u imported, %u exported", 
-         s2->imp_routes, s1->imp_routes);
-  cli_msg(-1006, "  Route change stats:     received   rejected   filtered    ignored   accepted");
-  cli_msg(-1006, "    Import updates:     %10u %10u %10u %10u %10u",
-         s2->exp_updates_received, s2->exp_updates_rejected + s2->imp_updates_invalid,
-         s2->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
-  cli_msg(-1006, "    Import withdraws:   %10u %10u        --- %10u %10u",
-         s2->exp_withdraws_received, s2->imp_withdraws_invalid,
-         s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
-  cli_msg(-1006, "    Export updates:     %10u %10u %10u %10u %10u",
-         s1->exp_updates_received, s1->exp_updates_rejected + s1->imp_updates_invalid,
-         s1->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
-  cli_msg(-1006, "    Export withdraws:   %10u %10u        --- %10u %10u",
-         s1->exp_withdraws_received, s1->imp_withdraws_invalid,
-         s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
+  if (p->proto_state != PS_DOWN)
+    proto_show_stats(&p->stats);
 }
-#endif
 
 void
 proto_cmd_show(struct proto *p, unsigned int verbose, int cnt)
@@ -943,22 +988,11 @@ proto_cmd_show(struct proto *p, unsigned int verbose, int cnt)
        cli_msg(-1006, "  Description:    %s", p->cf->dsc);
       if (p->cf->router_id)
        cli_msg(-1006, "  Router ID:      %R", p->cf->router_id);
-      cli_msg(-1006, "  Preference:     %d", p->preference);
-      cli_msg(-1006, "  Input filter:   %s", filter_name(p->in_filter));
-      cli_msg(-1006, "  Output filter:  %s", filter_name(p->out_filter));
-
-      if (p->proto_state != PS_DOWN)
-       {
-#ifdef CONFIG_PIPE
-         if (proto_is_pipe(p))
-           proto_do_show_pipe_stats(p);
-         else
-#endif
-           proto_do_show_stats(p);
-       }
 
       if (p->proto->show_proto_info)
        p->proto->show_proto_info(p);
+      else
+       proto_show_basic_info(p);
 
       cli_msg(-1006, "");
     }
index a83c4ffc2003a20b6a413c3505a73b4dd5449a86..187b87121f6975387d0722759ffbd5db2e0a8ada 100644 (file)
@@ -39,6 +39,7 @@ struct protocol {
   char *template;                      /* Template for automatic generation of names */
   int name_counter;                    /* Counter for automatic name generation */
   int attr_class;                      /* Attribute class known to this protocol */
+  int multitable;                      /* Protocol handles all announce hooks itself */
   unsigned preference;                 /* Default protocol preference */
 
   void (*preconfig)(struct protocol *, struct config *);       /* Just before configuring */
@@ -193,8 +194,7 @@ struct proto {
   void (*rte_remove)(struct network *, struct rte *);
 
   struct rtable *table;                        /* Our primary routing table */
-  struct filter *in_filter;            /* Input filter */
-  struct filter *out_filter;           /* Output filter */
+  struct announce_hook *main_ahook;    /* Primary announcement hook */
   struct announce_hook *ahooks;                /* Announcement hooks for this protocol */
 
   struct fib_iterator *feed_iterator;  /* Routing table iterator used during protocol feeding */
@@ -218,6 +218,9 @@ static inline void
 proto_copy_rest(struct proto_config *dest, struct proto_config *src, unsigned size)
 { memcpy(dest + 1, src + 1, size - sizeof(struct proto_config)); }
 
+
+void proto_show_basic_info(struct proto *p);
+
 void proto_cmd_show(struct proto *, unsigned int, int);
 void proto_cmd_disable(struct proto *, unsigned int, int);
 void proto_cmd_enable(struct proto *, unsigned int, int);
@@ -352,18 +355,13 @@ struct announce_hook {
   node n;
   struct rtable *table;
   struct proto *proto;
+  struct filter *in_filter;            /* Input filter */
+  struct filter *out_filter;           /* Output filter */
+  struct proto_stats *stats;           /* Per-table protocol statistics */
   struct announce_hook *next;          /* Next hook for the same protocol */
 };
 
-struct announce_hook *proto_add_announce_hook(struct proto *, struct rtable *);
-
-/*
- *     Some pipe-specific nest hacks
- */
-
-#ifdef CONFIG_PIPE
-#include "proto/pipe/pipe.h"
-#endif
-
+struct announce_hook *proto_add_announce_hook(struct proto *, struct rtable *, struct filter *, struct filter *, struct proto_stats *);
+struct announce_hook *proto_find_announce_hook(struct proto *p, struct rtable *t);
 
 #endif
index e6712c6476605f2443c112a0c32a15b14a72be24..fcb4f2838917e6b71cbeaa1bbd63c800ad8e3cde 100644 (file)
@@ -12,6 +12,7 @@
 #include "lib/lists.h"
 #include "lib/resource.h"
 #include "lib/timer.h"
+#include "nest/protocol.h"
 
 struct protocol;
 struct proto;
@@ -179,7 +180,7 @@ struct hostentry {
 typedef struct rte {
   struct rte *next;
   net *net;                            /* Network this RTE belongs to */
-  struct proto *sender;                        /* Protocol instance that sent the route to the routing table */
+  struct announce_hook *sender;                /* Announce hook used to send the route to the routing table */
   struct rta *attrs;                   /* Attributes of this route */
   byte flags;                          /* Flags (REF_...) */
   byte pflags;                         /* Protocol-specific flags */
@@ -234,7 +235,8 @@ static inline net *net_find(rtable *tab, ip_addr addr, unsigned len) { return (n
 static inline net *net_get(rtable *tab, ip_addr addr, unsigned len) { return (net *) fib_get(&tab->fib, &addr, len); }
 rte *rte_find(net *net, struct proto *p);
 rte *rte_get_temp(struct rta *);
-void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new);
+void rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src);
+static inline void rte_update(rtable *tab, net *net, struct proto *p, struct proto *src, rte *new) { rte_update2(p->main_ahook, net, new, src); }
 void rte_discard(rtable *tab, rte *old);
 void rte_dump(rte *);
 void rte_free(rte *);
index 377687de9b6bbdb4549ef9942d34fe453b42b968..3807ef8d6a44fd1908f9f4327586dc1019a68549 100644 (file)
@@ -184,24 +184,16 @@ rte_trace_out(unsigned int flag, struct proto *p, rte *e, char *msg)
 }
 
 static inline void
-do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
+do_rte_announce(struct announce_hook *ah, int type UNUSED, net *net, rte *new, rte *old, ea_list *tmpa, int refeed)
 {
-  struct proto *p = a->proto;
-  struct filter *filter = p->out_filter;
-  struct proto_stats *stats = &p->stats;
+  struct proto *p = ah->proto;
+  struct filter *filter = ah->out_filter;
+  struct proto_stats *stats = ah->stats;
+
   rte *new0 = new;
   rte *old0 = old;
   int ok;
 
-#ifdef CONFIG_PIPE
-  /* The secondary direction of the pipe */
-  if (proto_is_pipe(p) && (p->table != a->table))
-    {
-      filter = p->in_filter;
-      stats = pipe_get_peer_stats(p);
-    }
-#endif
-
   if (new)
     {
       stats->exp_updates_received++;
@@ -294,18 +286,18 @@ do_rte_announce(struct announce_hook *a, int type UNUSED, net *net, rte *new, rt
        rte_trace_out(D_ROUTES, p, old, "removed");
     }
   if (!new)
-    p->rt_notify(p, a->table, net, NULL, old, NULL);
+    p->rt_notify(p, ah->table, net, NULL, old, NULL);
   else if (tmpa)
     {
       ea_list *t = tmpa;
       while (t->next)
        t = t->next;
       t->next = new->attrs->eattrs;
-      p->rt_notify(p, a->table, net, new, old, tmpa);
+      p->rt_notify(p, ah->table, net, new, old, tmpa);
       t->next = NULL;
     }
   else
-    p->rt_notify(p, a->table, net, new, old, new->attrs->eattrs);
+    p->rt_notify(p, ah->table, net, new, old, new->attrs->eattrs);
   if (new && new != new0)      /* Discard temporary rte's */
     rte_free(new);
   if (old && old != old0)
@@ -375,7 +367,7 @@ rte_validate(rte *e)
   if ((n->n.pxlen > BITS_PER_IP_ADDRESS) || !ip_is_prefix(n->n.prefix,n->n.pxlen))
     {
       log(L_WARN "Ignoring bogus prefix %I/%d received via %s",
-         n->n.prefix, n->n.pxlen, e->sender->name);
+         n->n.prefix, n->n.pxlen, e->sender->proto->name);
       return 0;
     }
 
@@ -383,7 +375,7 @@ rte_validate(rte *e)
   if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK))
     {
       log(L_WARN "Ignoring bogus route %I/%d received via %s",
-         n->n.prefix, n->n.pxlen, e->sender->name);
+         n->n.prefix, n->n.pxlen, e->sender->proto->name);
       return 0;
     }
 
@@ -423,18 +415,15 @@ rte_same(rte *x, rte *y)
 }
 
 static void
-rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte *new, ea_list *tmpa)
+rte_recalculate(struct announce_hook *ah, net *net, rte *new, ea_list *tmpa, struct proto *src)
 {
-  struct proto_stats *stats = &p->stats;
+  struct proto *p = ah->proto;
+  struct rtable *table = ah->table;
+  struct proto_stats *stats = ah->stats;
   rte *old_best = net->routes;
   rte *old = NULL;
   rte **k, *r, *s;
 
-#ifdef CONFIG_PIPE
-  if (proto_is_pipe(p) && (p->table == table))
-    stats = pipe_get_peer_stats(p);
-#endif
-
   k = &net->routes;                    /* Find and remove original route from the same protocol */
   while (old = *k)
     {
@@ -449,7 +438,7 @@ rte_recalculate(rtable *table, net *net, struct proto *p, struct proto *src, rte
           * ignore it completely (there might be 'spurious withdraws',
           * see FIXME in do_rte_announce())
           */
-         if (old->sender != p)
+         if (old->sender->proto != p)
            {
              if (new)
                {
@@ -613,6 +602,7 @@ rte_update_unlock(void)
 /**
  * rte_update - enter a new update to a routing table
  * @table: table to be updated
+ * @ah: pointer to table announce hook
  * @net: network node
  * @p: protocol submitting the update
  * @src: protocol originating the update
@@ -652,28 +642,17 @@ rte_update_unlock(void)
  */
 
 void
-rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new)
+rte_update2(struct announce_hook *ah, net *net, rte *new, struct proto *src)
 {
+  struct proto *p = ah->proto;
+  struct proto_stats *stats = ah->stats;
+  struct filter *filter = ah->in_filter;
   ea_list *tmpa = NULL;
-  struct proto_stats *stats = &p->stats;
-
-#ifdef CONFIG_PIPE
-  if (proto_is_pipe(p) && (p->table == table))
-    stats = pipe_get_peer_stats(p);
-#endif
 
   rte_update_lock();
   if (new)
     {
-      new->sender = p;
-      struct filter *filter = p->in_filter;
-
-      /* Do not filter routes going through the pipe, 
-        they are filtered in the export filter only. */
-#ifdef CONFIG_PIPE
-      if (proto_is_pipe(p))
-       filter = FILTER_ACCEPT;
-#endif
+      new->sender = ah;
 
       stats->imp_updates_received++;
       if (!rte_validate(new))
@@ -710,13 +689,13 @@ rte_update(rtable *table, net *net, struct proto *p, struct proto *src, rte *new
   else
     stats->imp_withdraws_received++;
 
-  rte_recalculate(table, net, p, src, new, tmpa);
+  rte_recalculate(ah, net, new, tmpa, src);
   rte_update_unlock();
   return;
 
 drop:
   rte_free(new);
-  rte_recalculate(table, net, p, src, NULL, NULL);
+  rte_recalculate(ah, net, NULL, NULL, src);
   rte_update_unlock();
 }
 
@@ -739,7 +718,7 @@ void
 rte_discard(rtable *t, rte *old)       /* Non-filtered route deletion, used during garbage collection */
 {
   rte_update_lock();
-  rte_recalculate(t, old->net, old->sender, old->attrs->proto, NULL, NULL);
+  rte_recalculate(old->sender, old->net, NULL, NULL, old->attrs->proto);
   rte_update_unlock();
 }
 
@@ -912,8 +891,8 @@ again:
       ncnt++;
     rescan:
       for (e=n->routes; e; e=e->next, rcnt++)
-       if (e->sender->core_state != FS_HAPPY &&
-           e->sender->core_state != FS_FEEDING)
+       if (e->sender->proto->core_state != FS_HAPPY &&
+           e->sender->proto->core_state != FS_FEEDING)
          {
            rte_discard(tab, e);
            rdel++;
@@ -1026,7 +1005,7 @@ rt_next_hop_update_net(rtable *tab, net *n)
        *k = new;
 
        rte_announce_i(tab, RA_ANY, n, new, e);
-       rte_trace_in(D_ROUTES, new->sender, new, "updated");
+       rte_trace_in(D_ROUTES, new->sender->proto, new, "updated");
 
        /* Call a pre-comparison hook */
        /* Not really an efficient way to compute this */
@@ -1066,7 +1045,7 @@ rt_next_hop_update_net(rtable *tab, net *n)
   if (new != old_best)
     {
       rte_announce_i(tab, RA_OPTIMAL, n, new, old_best);
-      rte_trace_in(D_ROUTES, new->sender, new, "updated [best]");
+      rte_trace_in(D_ROUTES, new->sender->proto, new, "updated [best]");
     }
 
    if (free_old_best)
@@ -1693,6 +1672,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
 {
   rte *e, *ee;
   byte ia[STD_ADDRESS_P_LENGTH+8];
+  struct announce_hook *a;
   int ok;
 
   bsprintf(ia, "%I/%d", n->n.prefix, n->n.pxlen);
@@ -1722,8 +1702,8 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
                 'configure soft' command may change the export filter
                 and do not update routes */
 
-             if ((p1->out_filter == FILTER_REJECT) ||
-                 (p1->out_filter && f_run(p1->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT))
+             if ((a = proto_find_announce_hook(p1, d->table)) && ((a->out_filter == FILTER_REJECT) ||
+                 (a->out_filter && f_run(a->out_filter, &e, &tmpa, rte_update_pool, FF_FORCE_TMPATTR) > F_ACCEPT)))
                ok = 0;
            }
        }
index 4d3c32fba61011de791f8857d0b7bc853cda67a1..4dd4b7be2054a8a671b011102504457112abbe88 100644 (file)
@@ -1125,6 +1125,8 @@ bgp_show_proto_info(struct proto *P)
   struct bgp_proto *p = (struct bgp_proto *) P;
   struct bgp_conn *c = p->conn;
 
+  proto_show_basic_info(P);
+
   cli_msg(-1006, "  BGP state:          %s", bgp_state_dsc(p));
   cli_msg(-1006, "    Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
   cli_msg(-1006, "    Neighbor AS:      %u", p->remote_as);
index fe8618b6781d630b90f09a41e7aa7274e81442e3..36b06d43b240de8f59ea4af999a267ece174200e 100644 (file)
  *
  * To avoid pipe loops, Pipe keeps a `being updated' flag in each routing
  * table.
+ *
+ * A pipe has two announce hooks, the first connected to the main
+ * table, the second connected to the peer table. When a new route is
+ * announced on the main table, it gets checked by an export filter in
+ * ahook 1, and, after that, it is announced to the peer table via
+ * rte_update(), an import filter in ahook 2 is called. When a new
+ * route is announced in the peer table, an export filter in ahook2
+ * and an import filter in ahook 1 are used. Oviously, there is no
+ * need in filtering the same route twice, so both import filters
+ * are set to accept, while user configured 'import' and 'export'
+ * filters are used as export filters in ahooks 2 and 1.
  */
 
 #undef LOCAL_DEBUG
@@ -24,6 +35,7 @@
 #include "nest/iface.h"
 #include "nest/protocol.h"
 #include "nest/route.h"
+#include "nest/cli.h"
 #include "conf/conf.h"
 #include "filter/filter.h"
 #include "lib/string.h"
@@ -34,7 +46,8 @@ static void
 pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, ea_list *attrs)
 {
   struct pipe_proto *p = (struct pipe_proto *) P;
-  rtable *dest = (src_table == P->table) ? p->peer : P->table; /* The other side of the pipe */
+  struct announce_hook *ah = (src_table == P->table) ? p->peer_ahook : P->main_ahook;
+  rtable *dst_table = ah->table;
   struct proto *src;
 
   net *nn;
@@ -44,13 +57,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
   if (!new && !old)
     return;
 
-  if (dest->pipe_busy)
+  if (dst_table->pipe_busy)
     {
       log(L_ERR "Pipe loop detected when sending %I/%d to table %s",
-         n->n.prefix, n->n.pxlen, dest->name);
+         n->n.prefix, n->n.pxlen, dst_table->name);
       return;
     }
-  nn = net_get(dest, n->n.prefix, n->n.pxlen);
+
+  nn = net_get(dst_table, n->n.prefix, n->n.pxlen);
   if (new)
     {
       memcpy(&a, new->attrs, sizeof(rta));
@@ -85,14 +99,14 @@ pipe_rt_notify(struct proto *P, rtable *src_table, net *n, rte *new, rte *old, e
     }
 
   src_table->pipe_busy = 1;
-  rte_update(dest, nn, &p->p, (p->mode == PIPE_OPAQUE) ? &p->p : src, e);
+  rte_update2(ah, nn, e, (p->mode == PIPE_OPAQUE) ? &p->p : src);
   src_table->pipe_busy = 0;
 }
 
 static int
 pipe_import_control(struct proto *P, rte **ee, ea_list **ea UNUSED, struct linpool *p UNUSED)
 {
-  struct proto *pp = (*ee)->sender;
+  struct proto *pp = (*ee)->sender->proto;
 
   if (pp == P)
     return -1; /* Avoid local loops automatically */
@@ -112,20 +126,39 @@ pipe_reload_routes(struct proto *P)
   return 1;
 }
 
+static struct proto *
+pipe_init(struct proto_config *C)
+{
+  struct pipe_config *c = (struct pipe_config *) C;
+  struct proto *P = proto_new(C, sizeof(struct pipe_proto));
+  struct pipe_proto *p = (struct pipe_proto *) P;
+
+  p->mode = c->mode;
+  p->peer_table = c->peer->table;
+  P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY;
+  P->rt_notify = pipe_rt_notify;
+  P->import_control = pipe_import_control;
+  P->reload_routes = pipe_reload_routes;
+
+  return P;
+}
+
 static int
 pipe_start(struct proto *P)
 {
   struct pipe_proto *p = (struct pipe_proto *) P;
-  struct announce_hook *a;
 
-  /* Clean up the secondary stats */
-  bzero(&p->peer_stats, sizeof(struct proto_stats));
+  /* Lock both tables, unlock is handled in pipe_cleanup() */
+  rt_lock_table(P->table);
+  rt_lock_table(p->peer_table);
 
-  /* Lock the peer table, unlock is handled in pipe_cleanup() */
-  rt_lock_table(p->peer);
+  /* Going directly to PS_UP - prepare for feeding,
+     connect the protocol to both routing tables */
 
-  /* Connect the protocol also to the peer routing table. */
-  a = proto_add_announce_hook(P, p->peer);
+  P->main_ahook = proto_add_announce_hook(P, P->table,
+    FILTER_ACCEPT, P->cf->out_filter, &P->stats);
+  p->peer_ahook = proto_add_announce_hook(P, p->peer_table,
+    FILTER_ACCEPT, P->cf->in_filter,  &p->peer_stats);
 
   return PS_UP;
 }
@@ -134,24 +167,15 @@ static void
 pipe_cleanup(struct proto *P)
 {
   struct pipe_proto *p = (struct pipe_proto *) P;
-  rt_unlock_table(p->peer);
-}
 
-static struct proto *
-pipe_init(struct proto_config *C)
-{
-  struct pipe_config *c = (struct pipe_config *) C;
-  struct proto *P = proto_new(C, sizeof(struct pipe_proto));
-  struct pipe_proto *p = (struct pipe_proto *) P;
+  bzero(&P->stats, sizeof(struct proto_stats));
+  bzero(&p->peer_stats, sizeof(struct proto_stats));
 
-  p->peer = c->peer->table;
-  p->mode = c->mode;
-  P->accept_ra_types = (p->mode == PIPE_OPAQUE) ? RA_OPTIMAL : RA_ANY;
-  P->rt_notify = pipe_rt_notify;
-  P->import_control = pipe_import_control;
-  P->reload_routes = pipe_reload_routes;
+  P->main_ahook = NULL;
+  p->peer_ahook = NULL;
 
-  return P;
+  rt_unlock_table(P->table);
+  rt_unlock_table(p->peer_table);
 }
 
 static void
@@ -165,16 +189,34 @@ pipe_postconfig(struct proto_config *C)
     cf_error("Primary table and peer table must be different");
 }
 
+extern int proto_reconfig_type;
+
 static int
 pipe_reconfigure(struct proto *P, struct proto_config *new)
 {
-  // struct pipe_proto *p = (struct pipe_proto *) P;
-  struct pipe_config *o = (struct pipe_config *) P->cf;
-  struct pipe_config *n = (struct pipe_config *) new;
+  struct pipe_proto *p = (struct pipe_proto *)P;
+  struct proto_config *old = P->cf;
+  struct pipe_config *oc = (struct pipe_config *) old;
+  struct pipe_config *nc = (struct pipe_config *) new;
 
-  if ((o->peer->table != n->peer->table) || (o->mode != n->mode))
+  if ((oc->peer->table != nc->peer->table) || (oc->mode != nc->mode))
     return 0;
 
+  /* Update output filters in ahooks */
+  if (P->main_ahook)
+    P->main_ahook->out_filter = new->out_filter;
+
+  if (p->peer_ahook)
+    p->peer_ahook->out_filter = new->in_filter;
+
+  if ((P->proto_state != PS_UP) || (proto_reconfig_type == RECONFIG_SOFT))
+    return 1;
+  
+  if ((new->preference != old->preference)
+      || ! filter_same(new->in_filter, old->in_filter)
+      || ! filter_same(new->out_filter, old->out_filter))
+    proto_request_feeding(P);
+
   return 1;
 }
 
@@ -190,19 +232,80 @@ pipe_get_status(struct proto *P, byte *buf)
 {
   struct pipe_proto *p = (struct pipe_proto *) P;
 
-  bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer->name);
+  bsprintf(buf, "%c> %s", (p->mode == PIPE_OPAQUE) ? '-' : '=', p->peer_table->name);
+}
+
+static void
+pipe_show_stats(struct pipe_proto *p)
+{
+  struct proto_stats *s1 = &p->p.stats;
+  struct proto_stats *s2 = &p->peer_stats;
+
+  /*
+   * Pipe stats (as anything related to pipes) are a bit tricky. There
+   * are two sets of stats - s1 for ahook to the primary routing and
+   * s2 for the ahook to the secondary routing table. The user point
+   * of view is that routes going from the primary routing table to
+   * the secondary routing table are 'exported', while routes going in
+   * the other direction are 'imported'.
+   *
+   * Each route going through a pipe is, technically, first exported
+   * to the pipe and then imported from that pipe and such operations
+   * are counted in one set of stats according to the direction of the
+   * route propagation. Filtering is done just in the first part
+   * (export). Therefore, we compose stats for one directon for one
+   * user direction from both import and export stats, skipping
+   * immediate and irrelevant steps (exp_updates_accepted,
+   * imp_updates_received, imp_updates_filtered, ...).
+   *
+   * Rule of thumb is that stats s1 have the correct 'polarity'
+   * (imp/exp), while stats s2 have switched 'polarity'.
+   */
+
+  cli_msg(-1006, "  Routes:         %u imported, %u exported", 
+         s1->imp_routes, s2->imp_routes);
+  cli_msg(-1006, "  Route change stats:     received   rejected   filtered    ignored   accepted");
+  cli_msg(-1006, "    Import updates:     %10u %10u %10u %10u %10u",
+         s2->exp_updates_received, s2->exp_updates_rejected + s1->imp_updates_invalid,
+         s2->exp_updates_filtered, s1->imp_updates_ignored, s1->imp_updates_accepted);
+  cli_msg(-1006, "    Import withdraws:   %10u %10u        --- %10u %10u",
+         s2->exp_withdraws_received, s1->imp_withdraws_invalid,
+         s1->imp_withdraws_ignored, s1->imp_withdraws_accepted);
+  cli_msg(-1006, "    Export updates:     %10u %10u %10u %10u %10u",
+         s1->exp_updates_received, s1->exp_updates_rejected + s2->imp_updates_invalid,
+         s1->exp_updates_filtered, s2->imp_updates_ignored, s2->imp_updates_accepted);
+  cli_msg(-1006, "    Export withdraws:   %10u %10u        --- %10u %10u",
+         s1->exp_withdraws_received, s2->imp_withdraws_invalid,
+         s2->imp_withdraws_ignored, s2->imp_withdraws_accepted);
+}
+
+static void
+pipe_show_proto_info(struct proto *P)
+{
+  struct pipe_proto *p = (struct pipe_proto *) P;
+
+  // cli_msg(-1006, "  Table:          %s", P->table->name);
+  // cli_msg(-1006, "  Peer table:     %s", p->peer_table->name);
+  cli_msg(-1006, "  Preference:     %d", P->preference);
+  cli_msg(-1006, "  Input filter:   %s", filter_name(P->cf->in_filter));
+  cli_msg(-1006, "  Output filter:  %s", filter_name(P->cf->out_filter));
+
+  if (P->proto_state != PS_DOWN)
+    pipe_show_stats(p);
 }
 
 
 struct protocol proto_pipe = {
-  name:                "Pipe",
-  template:    "pipe%d",
-  preference:  DEF_PREF_PIPE,
-  postconfig:  pipe_postconfig,
-  init:                pipe_init,
-  start:       pipe_start,
-  cleanup:     pipe_cleanup,
-  reconfigure: pipe_reconfigure,
-  copy_config:  pipe_copy_config,
-  get_status:  pipe_get_status,
+  name:                        "Pipe",
+  template:            "pipe%d",
+  multitable:          1,
+  preference:          DEF_PREF_PIPE,
+  postconfig:          pipe_postconfig,
+  init:                        pipe_init,
+  start:               pipe_start,
+  cleanup:             pipe_cleanup,
+  reconfigure:         pipe_reconfigure,
+  copy_config:         pipe_copy_config,
+  get_status:          pipe_get_status,
+  show_proto_info:     pipe_show_proto_info
 };
index fbd2129153c72e21e83a0d4948966a17f6ea512c..50b3169815f4c93a5bab944b2eb89ecd55887a58 100644 (file)
@@ -20,7 +20,8 @@ struct pipe_config {
 
 struct pipe_proto {
   struct proto p;
-  struct rtable *peer;
+  struct rtable *peer_table;
+  struct announce_hook *peer_ahook;    /* Announce hook for direction peer->primary */
   struct proto_stats peer_stats;       /* Statistics for the direction peer->primary */
   int mode;                            /* PIPE_OPAQUE or PIPE_TRANSPARENT */
 };
@@ -31,10 +32,4 @@ extern struct protocol proto_pipe;
 static inline int proto_is_pipe(struct proto *p)
 { return p->proto == &proto_pipe; }
 
-static inline struct rtable * pipe_get_peer_table(struct proto *P)
-{ return ((struct pipe_proto *) P)->peer; }
-
-static inline struct proto_stats * pipe_get_peer_stats(struct proto *P)
-{ return &((struct pipe_proto *) P)->peer_stats; }
-
 #endif