]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Multipath support for static protocol.
authorOndrej Zajicek <santiago@crfreenet.org>
Tue, 7 Dec 2010 22:34:36 +0000 (23:34 +0100)
committerOndrej Zajicek <santiago@crfreenet.org>
Tue, 7 Dec 2010 22:34:36 +0000 (23:34 +0100)
proto/static/config.Y
proto/static/static.c
proto/static/static.h

index 2849015b3c1ad614e610da9c0e33017b8b2bffc1..46debbc3d05f69c824442f883febb92b255d1ea8 100644 (file)
@@ -13,11 +13,13 @@ CF_HDR
 CF_DEFINES
 
 #define STATIC_CFG ((struct static_config *) this_proto)
-static struct static_route *this_srt;
+static struct static_route *this_srt, *this_srt_nh, *last_srt_nh;
 
 CF_DECLS
 
 CF_KEYWORDS(STATIC, ROUTE, VIA, DROP, REJECT, PROHIBIT, PREFERENCE, CHECK, LINK)
+CF_KEYWORDS(MULTIPATH, WEIGHT)
+
 
 CF_GRAMMAR
 
@@ -44,6 +46,25 @@ stat_route0: ROUTE prefix {
   }
  ;
 
+stat_multipath1:
+   VIA ipa {
+     last_srt_nh = this_srt_nh;
+     this_srt_nh = cfg_allocz(sizeof(struct static_route));
+     this_srt_nh->dest = RTD_NONE;
+     this_srt_nh->via = $2;
+     this_srt_nh->if_name = (void *) this_srt; /* really */
+   }
+ | stat_multipath1 WEIGHT expr {
+     this_srt_nh->masklen = $3 - 1; /* really */
+     if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256"); 
+   }
+ ;
+
+stat_multipath:
+   stat_multipath1 { this_srt->mp_next = this_srt_nh; }
+ | stat_multipath stat_multipath1 { last_srt_nh->mp_next = this_srt_nh; }
+ ;
+
 stat_route:
    stat_route0 VIA ipa {
       this_srt->dest = RTD_ROUTER;
@@ -55,6 +76,9 @@ stat_route:
       rem_node(&this_srt->n);
       add_tail(&STATIC_CFG->iface_routes, &this_srt->n);
    }
+ | stat_route0 MULTIPATH stat_multipath {
+      this_srt->dest = RTD_MULTIPATH;
+   }
  | stat_route0 DROP { this_srt->dest = RTD_BLACKHOLE; }
  | stat_route0 REJECT { this_srt->dest = RTD_UNREACHABLE; }
  | stat_route0 PROHIBIT { this_srt->dest = RTD_PROHIBIT; }
index 4ee2cbdb45d06102b23cfbfbadd9cb47039c9e67..448a560ad69b566532fbbae46da75188178e2dfe 100644 (file)
  * to be notified about gaining or losing the neighbor. Special
  * routes like black holes or rejects are inserted all the time.
  *
+ * Multipath routes are tricky. Because these routes depends on
+ * several neighbors we need to integrate that to the neighbor
+ * notification handling, we use dummy static_route nodes, one for
+ * each nexthop. Therefore, a multipath route consists of a master
+ * static_route node (of dest RTD_MULTIPATH), which specifies prefix
+ * and is used in most circumstances, and a list of dummy static_route
+ * nodes (of dest RTD_NONE), which stores info about nexthops and are
+ * connected to neighbor entries and neighbor notifications. Dummy
+ * nodes are chained using mp_next, they aren't in other_routes list,
+ * and abuse some fields (masklen, if_name) for other purposes.
+ *
  * The only other thing worth mentioning is that when asked for reconfiguration,
  * Static not only compares the two configurations, but it also calculates
  * difference between the lists of static routes and it just inserts the
@@ -32,6 +43,7 @@
 #include "nest/cli.h"
 #include "conf/conf.h"
 #include "lib/string.h"
+#include "lib/alloca.h"
 
 #include "static.h"
 
@@ -54,8 +66,38 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
   a.dest = r->dest;
   a.gw = r->via;
   a.iface = ifa;
-  aa = rta_lookup(&a);
 
+  if (r->dest == RTD_MULTIPATH)
+    {
+      struct static_route *r2;
+      struct mpnh *nhs = NULL;
+      struct mpnh **nhp = &nhs;
+
+      for (r2 = r->mp_next; r2; r2 = r2->mp_next)
+       if (r2->installed)
+         {
+           struct mpnh *nh = alloca(sizeof(struct mpnh));
+           nh->gw = r2->via;
+           nh->iface = r2->neigh->iface;
+           nh->weight = r2->masklen; /* really */
+           nh->next = NULL;
+           *nhp = nh;
+           nhp = &(nh->next);
+         }
+
+      /* There is at least one nexthop */
+      if (!nhs->next)
+       {
+         /* Fallback to unipath route for exactly one nexthop */
+         a.dest = RTD_ROUTER;
+         a.gw = nhs->gw;
+         a.iface = nhs->iface;
+       }
+      else
+       a.nexthops = nhs;
+    }
+
+  aa = rta_lookup(&a);
   n = net_get(p->table, r->net, r->masklen);
   e = rte_get_temp(aa);
   e->net = n;
@@ -64,20 +106,6 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
   r->installed = 1;
 }
 
-static int
-static_decide(struct static_config *cf, struct static_route *r)
-{
-  struct iface *ifa = r->neigh->iface;
-
-  if (!ifa)
-    return 0;
-
-  if (cf->check_link && !(ifa->flags & IF_LINK_UP))
-    return 0;
-
-  return 1;
-}
-
 static void
 static_remove(struct proto *p, struct static_route *r)
 {
@@ -93,6 +121,24 @@ static_remove(struct proto *p, struct static_route *r)
   r->installed = 0;
 }
 
+static int
+static_decide(struct static_config *cf, struct static_route *r)
+{
+  /* r->dest != RTD_MULTIPATH, but may be RTD_NONE (part of multipath route)
+     the route also have to be valid (r->neigh != NULL) */
+
+  struct iface *ifa = r->neigh->iface;
+
+  if (!ifa)
+    return 0;
+
+  if (cf->check_link && !(ifa->flags & IF_LINK_UP))
+    return 0;
+
+  return 1;
+}
+
+
 static void
 static_add(struct proto *p, struct static_config *cf, struct static_route *r)
 {
@@ -113,11 +159,46 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
              static_remove(p, r);
          }
        else
-         log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
+         {
+           log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
+           static_remove(p, r);
+         }
        break;
       }
+
     case RTD_DEVICE:
       break;
+
+    case RTD_MULTIPATH:
+      {
+       int count = 0;
+       struct static_route *r2;
+
+       for (r2 = r->mp_next; r2; r2 = r2->mp_next)
+         {
+           struct neighbor *n = neigh_find(p, &r2->via, NEF_STICKY);
+           if (n)
+             {
+               r2->chain = n->data;
+               n->data = r2;
+               r2->neigh = n;
+               r2->installed = static_decide(cf, r2);
+               count += r2->installed;
+             }
+           else
+             {
+               log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
+               r2->installed = 0;
+             }
+         }
+
+       if (count)
+         static_install(p, r, NULL);
+       else
+         static_remove(p, r);
+       break;
+      }
+
     default:
       static_install(p, r, NULL);
     }
@@ -156,12 +237,42 @@ static_neigh_notify(struct neighbor *n)
   struct proto *p = n->proto;
   struct static_route *r;
 
-  DBG("Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
+  log(L_WARN "Static: neighbor notify for %I: iface %p\n", n->addr, n->iface);
   for(r=n->data; r; r=r->chain)
-    if (static_decide((struct static_config *) p->cf, r))
-      static_install(p, r, n->iface);
-    else
-      static_remove(p, r);
+    switch (r->dest)
+      {
+      case RTD_ROUTER:
+       if (static_decide((struct static_config *) p->cf, r))
+         static_install(p, r, n->iface);
+       else
+         static_remove(p, r);
+       break;
+
+      case RTD_NONE: /* a part of multipath route */
+       {
+         int decision = static_decide((struct static_config *) p->cf, r);
+         if (decision == r->installed)
+           break; /* no change */
+         r->installed = decision;
+
+         struct static_route *r1, *r2;
+         int count = 0;
+         r1 = (void *) r->if_name; /* really */
+         for (r2 = r1->mp_next; r2; r2 = r2->mp_next)
+           count += r2->installed;
+
+         if (count)
+           {
+             /* Set of nexthops changed - force reinstall */
+             r1->installed = 0;
+             static_install(p, r1, NULL);
+           }
+         else
+           static_remove(p, r1);
+
+         break;
+       }
+      }
 }
 
 static void
@@ -243,9 +354,28 @@ static_same_net(struct static_route *x, struct static_route *y)
 static inline int
 static_same_dest(struct static_route *x, struct static_route *y)
 {
-  return (x->dest == y->dest)
-    && (x->dest != RTD_ROUTER || ipa_equal(x->via, y->via))
-    && (x->dest != RTD_DEVICE || !strcmp(x->if_name, y->if_name));
+  if (x->dest != y->dest)
+    return 0;
+
+  switch (x->dest)
+    {
+    case RTD_ROUTER:
+      return ipa_equal(x->via, y->via);
+
+    case RTD_DEVICE:
+      return !strcmp(x->if_name, y->if_name);
+
+    case RTD_MULTIPATH:
+      for (x = x->mp_next, y = y->mp_next;
+          x && y;
+          x = x->mp_next, y = y->mp_next)
+       if (!ipa_equal(x->via, y->via))
+         return 0;
+      return !x && !y;
+
+    default:
+      return 1;
+    }
 }
 
 static void
@@ -323,11 +453,18 @@ static_show_rt(struct static_route *r)
     case RTD_ROUTER:   bsprintf(via, "via %I", r->via); break;
     case RTD_DEVICE:   bsprintf(via, "dev %s", r->if_name); break;
     case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); break;
-    case RTD_UNREACHABLE:      bsprintf(via, "unreachable"); break;
+    case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
     case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
+    case RTD_MULTIPATH:        bsprintf(via, "multipath"); break;
     default:           bsprintf(via, "???");
     }
   cli_msg(-1009, "%I/%d %s%s", r->net, r->masklen, via, r->installed ? "" : " (dormant)");
+
+  struct static_route *r2;
+  if (r->dest == RTD_MULTIPATH)
+    for (r2 = r->mp_next; r2; r2 = r2->mp_next)
+      cli_msg(-1009, "\tvia %I weight %d%s", r2->via, r2->masklen + 1, /* really */
+             r2->installed ? "" : " (dormant)");
 }
 
 void
index 5c31e0091d6f667ff1da99b03465a544157ab0eb..c91b9ceffbe7549e44d8f40676db4d15f65e2b0b 100644 (file)
@@ -28,9 +28,13 @@ struct static_route {
   ip_addr via;                         /* Destination router */
   struct neighbor *neigh;
   byte *if_name;                       /* Name for RTD_DEVICE routes */
+  struct static_route *mp_next;                /* Nexthops for RTD_MULTIPATH routes */
   int installed;                       /* Installed in master table */
 };
 
+/* Dummy nodes (parts of multipath route) abuses masklen field for weight
+   and if_name field for a ptr to the master (RTD_MULTIPATH) node. */
+
 void static_show(struct proto *);
 
 #endif