]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merged multipath and single-path data structures.
authorJan Moskyto Matejka <mq@ucw.cz>
Fri, 6 May 2016 13:48:35 +0000 (15:48 +0200)
committerJan Moskyto Matejka <mq@ucw.cz>
Thu, 22 Dec 2016 12:01:06 +0000 (13:01 +0100)
Dropped struct mpnh and mpnh_*()
Now struct nexthop exists, nexthop_*(), and also included struct nexthop
into struct rta.

Also converted RTD_DEVICE and RTD_ROUTER to RTD_UNICAST. If it is needed
to distinguish between these two cases, RTD_DEVICE is equivalent to
IPA_ZERO(a->nh.gw), RTD_ROUTER is then IPA_NONZERO(a->nh.gw).

From now on, we also explicitely want C99 compatible compiler. We assume
that this 20-year norm should be known almost everywhere.

21 files changed:
configure.in
filter/filter.c
nest/config.Y
nest/route.h
nest/rt-attr.c
nest/rt-dev.c
nest/rt-table.c
proto/bgp/attrs.c
proto/bgp/packets.c
proto/ospf/ospf.c
proto/ospf/rt.c
proto/ospf/rt.h
proto/ospf/topology.c
proto/ospf/topology.h
proto/rip/rip.c
proto/static/config.Y
proto/static/static.c
proto/static/static.h
sysdep/bsd/krt-sock.c
sysdep/linux/netlink.c
sysdep/unix/krt.c

index 32344d1f959d5f5590e97eadbddce50aa904e720..d5ddb2a5d0f6e177331c77a5658a821ec4dbde07 100644 (file)
@@ -57,6 +57,7 @@ if test "$ac_test_CFLAGS" != set ; then
        bird_cflags_default=yes
 fi
 
+AC_PROG_CC
 AC_PROG_CC_C99
 if test -z "$GCC" ; then
        AC_MSG_ERROR([This program requires the GNU C Compiler.])
index 4ec045541bb609cbf4ec4bc210e2ef78da20e8d7..926316ac53a8451d3fa9c3dda35fdf1b9a4cac46 100644 (file)
@@ -900,15 +900,15 @@ interpret(struct f_inst *what)
       switch (what->a2.i)
       {
       case SA_FROM:    res.val.ip = rta->from; break;
-      case SA_GW:      res.val.ip = rta->gw; break;
+      case SA_GW:      res.val.ip = rta->nh.gw; break;
       case SA_NET:     res.val.net = (*f_rte)->net->n.addr; break;
       case SA_PROTO:   res.val.s = rta->src->proto->name; break;
       case SA_SOURCE:  res.val.i = rta->source; break;
       case SA_SCOPE:   res.val.i = rta->scope; break;
       case SA_CAST:    res.val.i = rta->cast; break;
       case SA_DEST:    res.val.i = rta->dest; break;
-      case SA_IFNAME:  res.val.s = rta->iface ? rta->iface->name : ""; break;
-      case SA_IFINDEX: res.val.i = rta->iface ? rta->iface->index : 0; break;
+      case SA_IFNAME:  res.val.s = rta->nh.iface ? rta->nh.iface->name : ""; break;
+      case SA_IFINDEX: res.val.i = rta->nh.iface ? rta->nh.iface->index : 0; break;
 
       default:
        bug("Invalid static attribute access (%x)", res.type);
@@ -938,10 +938,10 @@ interpret(struct f_inst *what)
          if (!n || (n->scope == SCOPE_HOST))
            runtime( "Invalid gw address" );
 
-         rta->dest = RTD_ROUTER;
-         rta->gw = ip;
-         rta->iface = n->iface;
-         rta->nexthops = NULL;
+         rta->dest = RTD_UNICAST;
+         rta->nh.gw = ip;
+         rta->nh.iface = n->iface;
+         rta->nh.next = NULL;
          rta->hostentry = NULL;
        }
        break;
@@ -956,9 +956,9 @@ interpret(struct f_inst *what)
          runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
 
        rta->dest = i;
-       rta->gw = IPA_NONE;
-       rta->iface = NULL;
-       rta->nexthops = NULL;
+       rta->nh.gw = IPA_NONE;
+       rta->nh.iface = NULL;
+       rta->nh.next = NULL;
        rta->hostentry = NULL;
        break;
 
index 23d6a45281c7404b153a1f125e3dd5f7a0e0244f..95ce59cd5e079316df549f8f8e3e95c2b437dd63 100644 (file)
@@ -79,7 +79,7 @@ CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIREC
        RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
 CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
 CF_ENUM(T_ENUM_RTC, RTC_, UNICAST, BROADCAST, MULTICAST, ANYCAST)
-CF_ENUM(T_ENUM_RTD, RTD_, ROUTER, DEVICE, BLACKHOLE, UNREACHABLE, PROHIBIT, MULTIPATH)
+CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
 CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
 
 %type <i32> idval
index d652ca15adfc87c39eeb030c69cc65c7dba2f460..37c9abfbcf3f4c8a4619f46302232cc182705398 100644 (file)
@@ -195,7 +195,7 @@ struct hostentry {
   unsigned hash_key;                   /* Hash key */
   unsigned uc;                         /* Use count */
   struct rta *src;                     /* Source rta entry */
-  ip_addr gw;                          /* Chosen next hop */
+  struct nexthop *nh;                  /* Chosen next hop */
   byte dest;                           /* Chosen route destination type (RTD_...) */
   u32 igp_metric;                      /* Chosen route IGP metric */
 };
@@ -332,11 +332,11 @@ void rt_show(struct rt_show_data *);
  *     construction of BGP route attribute lists.
  */
 
-/* Multipath next-hop */
-struct mpnh {
+/* Nexthop structure */
+struct nexthop {
   ip_addr gw;                          /* Next hop */
   struct iface *iface;                 /* Outgoing interface */
-  struct mpnh *next;
+  struct nexthop *next;
   byte weight;
 };
 
@@ -353,20 +353,19 @@ typedef struct rta {
   struct rta *next, **pprev;           /* Hash chain */
   u32 uc;                              /* Use count */
   u32 hash_key;                                /* Hash over important fields */
-  struct mpnh *nexthops;               /* Next-hops for multipath routes */
   struct ea_list *eattrs;              /* Extended Attribute chain */
   struct rte_src *src;                 /* Route source that created the route */
   struct hostentry *hostentry;         /* Hostentry for recursive next-hops */
-  struct iface *iface;                 /* Outgoing interface */
-  ip_addr gw;                          /* Next hop */
   ip_addr from;                                /* Advertising router */
   u32 igp_metric;                      /* IGP metric to next hop (for iBGP routes) */
-  byte source;                         /* Route source (RTS_...) */
-  byte scope;                          /* Route scope (SCOPE_... -- see ip.h) */
-  byte cast;                           /* Casting type (RTC_...) */
-  byte dest;                           /* Route destination type (RTD_...) */
-  byte flags;                          /* Route flags (RTF_...), now unused */
-  byte aflags;                         /* Attribute cache flags (RTAF_...) */
+  u32 bf[0];
+  u32 source:6;                                /* Route source (RTS_...) */
+  u32 scope:6;                         /* Route scope (SCOPE_... -- see ip.h) */
+  u32 cast:6;                          /* Casting type (RTC_...) */
+  u32 dest:6;                          /* Route destination type (RTD_...) */
+//  u32 eflags:8;                              /* Flags (RTAF_...) */
+  u32 aflags:8;
+  struct nexthop nh;                   /* Next hop */
 } rta;
 
 #define RTS_DUMMY 0                    /* Dummy route to be removed soon */
@@ -391,12 +390,10 @@ typedef struct rta {
 #define RTC_MULTICAST 2
 #define RTC_ANYCAST 3                  /* IPv6 Anycast */
 
-#define RTD_ROUTER 0                   /* Next hop is neighbor router */
-#define RTD_DEVICE 1                   /* Points to device */
+#define RTD_UNICAST 0                  /* Next hop is neighbor router */
 #define RTD_BLACKHOLE 2                        /* Silently drop packets */
 #define RTD_UNREACHABLE 3              /* Reject as unreachable */
 #define RTD_PROHIBIT 4                 /* Administratively prohibited */
-#define RTD_MULTIPATH 5                        /* Multipath route (nexthops != NULL) */
 #define RTD_NONE 6                     /* Invalid RTD */
 
                                        /* Flags for net->n.flags, used by kernel syncer */
@@ -411,7 +408,7 @@ typedef struct rta {
 
 /* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */
 static inline int rte_is_reachable(rte *r)
-{ uint d = r->attrs->dest; return (d == RTD_ROUTER) || (d == RTD_DEVICE) || (d == RTD_MULTIPATH); }
+{ uint d = r->attrs->dest; return (d == RTD_UNICAST); }
 
 
 /*
@@ -516,12 +513,14 @@ uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */
 ea_list *ea_append(ea_list *to, ea_list *what);
 void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
 
-int mpnh__same(struct mpnh *x, struct mpnh *y); /* Compare multipath nexthops */
-static inline int mpnh_same(struct mpnh *x, struct mpnh *y)
-{ return (x == y) || mpnh__same(x, y); }
-struct mpnh *mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp);
-void mpnh_insert(struct mpnh **n, struct mpnh *y);
-int mpnh_is_sorted(struct mpnh *x);
+int nexthop__same(struct nexthop *x, struct nexthop *y); /* Compare multipath nexthops */
+static inline int nexthop_same(struct nexthop *x, struct nexthop *y)
+{ return (x == y) || nexthop__same(x, y); }
+struct nexthop *nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp);
+static inline void nexthop_link(struct rta *a, struct nexthop *from)
+{ a->nh.gw = from->gw; a->nh.iface = from->iface; a->nh.weight = from->weight; a->nh.next = from->next; }
+void nexthop_insert(struct nexthop *n, struct nexthop *y);
+int nexthop_is_sorted(struct nexthop *x);
 
 void rta_init(void);
 rta *rta_lookup(rta *);                        /* Get rta equivalent to this one, uc++ */
index 94f25de89976bed4d4d549dccf8741e2ddeb4cf0..0eacfe3f70d97c61ca99069654df19a13480bffa 100644 (file)
@@ -61,7 +61,7 @@
 pool *rta_pool;
 
 static slab *rta_slab;
-static slab *mpnh_slab;
+static slab *nexthop_slab;
 static slab *rte_src_slab;
 
 static struct idm src_ids;
@@ -144,7 +144,7 @@ rt_prune_sources(void)
  */
 
 static inline u32
-mpnh_hash(struct mpnh *x)
+nexthop_hash(struct nexthop *x)
 {
   u32 h = 0;
   for (; x; x = x->next)
@@ -154,7 +154,7 @@ mpnh_hash(struct mpnh *x)
 }
 
 int
-mpnh__same(struct mpnh *x, struct mpnh *y)
+nexthop__same(struct nexthop *x, struct nexthop *y)
 {
   for (; x && y; x = x->next, y = y->next)
     if (!ipa_equal(x->gw, y->gw) || (x->iface != y->iface) || (x->weight != y->weight))
@@ -164,7 +164,7 @@ mpnh__same(struct mpnh *x, struct mpnh *y)
 }
 
 static int
-mpnh_compare_node(struct mpnh *x, struct mpnh *y)
+nexthop_compare_node(struct nexthop *x, struct nexthop *y)
 {
   int r;
 
@@ -185,10 +185,10 @@ mpnh_compare_node(struct mpnh *x, struct mpnh *y)
   return ((int) x->iface->index) - ((int) y->iface->index);
 }
 
-static inline struct mpnh *
-mpnh_copy_node(const struct mpnh *src, linpool *lp)
+static inline struct nexthop *
+nexthop_copy_node(const struct nexthop *src, linpool *lp)
 {
-  struct mpnh *n = lp_alloc(lp, sizeof(struct mpnh));
+  struct nexthop *n = lp_alloc(lp, sizeof(struct nexthop));
   n->gw = src->gw;
   n->iface = src->iface;
   n->next = NULL;
@@ -197,7 +197,7 @@ mpnh_copy_node(const struct mpnh *src, linpool *lp)
 }
 
 /**
- * mpnh_merge - merge nexthop lists
+ * nexthop_merge - merge nexthop lists
  * @x: list 1
  * @y: list 2
  * @rx: reusability of list @x
@@ -205,7 +205,7 @@ mpnh_copy_node(const struct mpnh *src, linpool *lp)
  * @max: max number of nexthops
  * @lp: linpool for allocating nexthops
  *
- * The mpnh_merge() function takes two nexthop lists @x and @y and merges them,
+ * The nexthop_merge() function takes two nexthop lists @x and @y and merges them,
  * eliminating possible duplicates. The input lists must be sorted and the
  * result is sorted too. The number of nexthops in result is limited by @max.
  * New nodes are allocated from linpool @lp.
@@ -218,28 +218,28 @@ mpnh_copy_node(const struct mpnh *src, linpool *lp)
  * resulting list is no longer needed. When reusability is not set, the
  * corresponding lists are not modified nor linked from the resulting list.
  */
-struct mpnh *
-mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp)
+struct nexthop *
+nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp)
 {
-  struct mpnh *root = NULL;
-  struct mpnh **n = &root;
+  struct nexthop *root = NULL;
+  struct nexthop **n = &root;
 
   while ((x || y) && max--)
   {
-    int cmp = mpnh_compare_node(x, y);
+    int cmp = nexthop_compare_node(x, y);
     if (cmp < 0)
     {
-      *n = rx ? x : mpnh_copy_node(x, lp);
+      *n = rx ? x : nexthop_copy_node(x, lp);
       x = x->next;
     }
     else if (cmp > 0)
     {
-      *n = ry ? y : mpnh_copy_node(y, lp);
+      *n = ry ? y : nexthop_copy_node(y, lp);
       y = y->next;
     }
     else
     {
-      *n = rx ? x : (ry ? y : mpnh_copy_node(x, lp));
+      *n = rx ? x : (ry ? y : nexthop_copy_node(x, lp));
       x = x->next;
       y = y->next;
     }
@@ -251,43 +251,55 @@ mpnh_merge(struct mpnh *x, struct mpnh *y, int rx, int ry, int max, linpool *lp)
 }
 
 void
-mpnh_insert(struct mpnh **n, struct mpnh *x)
+nexthop_insert(struct nexthop *n, struct nexthop *x)
 {
-  for (; *n; n = &((*n)->next))
+  struct nexthop tmp;
+  memcpy(&tmp, n, sizeof(struct nexthop));
+  if (nexthop_compare_node(n, x) > 0) /* Insert to the included nexthop */
   {
-    int cmp = mpnh_compare_node(*n, x);
+    memcpy(n, x, sizeof(struct nexthop));
+    memcpy(x, &tmp, sizeof(struct nexthop));
+    n->next = x;
+    return;
+  }
+
+  for (struct nexthop **nn = &(n->next); *nn; nn = &((*nn)->next))
+  {
+    int cmp = nexthop_compare_node(*nn, x);
 
     if (cmp < 0)
       continue;
-    else if (cmp > 0)
-      break;
-    else
-      return;
+    
+    if (cmp > 0)
+    {
+      x->next = *nn;
+      *nn = x;
+    }
+    
+    return;
   }
 
-  x->next = *n;
-  *n = x;
 }
 
 int
-mpnh_is_sorted(struct mpnh *x)
+nexthop_is_sorted(struct nexthop *x)
 {
   for (; x && x->next; x = x->next)
-    if (mpnh_compare_node(x, x->next) >= 0)
+    if (nexthop_compare_node(x, x->next) >= 0)
       return 0;
 
   return 1;
 }
 
-static struct mpnh *
-mpnh_copy(struct mpnh *o)
+static struct nexthop *
+nexthop_copy(struct nexthop *o)
 {
-  struct mpnh *first = NULL;
-  struct mpnh **last = &first;
+  struct nexthop *first = NULL;
+  struct nexthop **last = &first;
 
   for (; o; o = o->next)
     {
-      struct mpnh *n = sl_alloc(mpnh_slab);
+      struct nexthop *n = sl_alloc(nexthop_slab);
       n->gw = o->gw;
       n->iface = o->iface;
       n->next = NULL;
@@ -301,14 +313,14 @@ mpnh_copy(struct mpnh *o)
 }
 
 static void
-mpnh_free(struct mpnh *o)
+nexthop_free(struct nexthop *o)
 {
-  struct mpnh *n;
+  struct nexthop *n;
 
   while (o)
     {
       n = o->next;
-      sl_free(mpnh_slab, o);
+      sl_free(nexthop_slab, o);
       o = n;
     }
 }
@@ -994,19 +1006,12 @@ rta_hash(rta *a)
 #define MIX(f) mem_hash_mix(&h, &(a->f), sizeof(a->f));
   MIX(src);
   MIX(hostentry);
-  MIX(iface);
-  MIX(gw);
   MIX(from);
   MIX(igp_metric);
-  MIX(source);
-  MIX(scope);
-  MIX(cast);
-  MIX(dest);
-  MIX(flags);
-  MIX(aflags);
+  mem_hash_mix(&h, a->bf, sizeof(u32));
 #undef MIX
 
-  return mem_hash_value(&h) ^ mpnh_hash(a->nexthops) ^ ea_hash(a->eattrs);
+  return mem_hash_value(&h) ^ nexthop_hash(&(a->nh)) ^ ea_hash(a->eattrs);
 }
 
 static inline int
@@ -1017,13 +1022,12 @@ rta_same(rta *x, rta *y)
          x->scope == y->scope &&
          x->cast == y->cast &&
          x->dest == y->dest &&
-         x->flags == y->flags &&
          x->igp_metric == y->igp_metric &&
-         ipa_equal(x->gw, y->gw) &&
+         ipa_equal(x->nh.gw, y->nh.gw) &&
          ipa_equal(x->from, y->from) &&
-         x->iface == y->iface &&
+         x->nh.iface == y->nh.iface &&
          x->hostentry == y->hostentry &&
-         mpnh_same(x->nexthops, y->nexthops) &&
+         nexthop_same(&(x->nh), &(y->nh)) &&
          ea_same(x->eattrs, y->eattrs));
 }
 
@@ -1034,7 +1038,7 @@ rta_copy(rta *o)
 
   memcpy(r, o, sizeof(rta));
   r->uc = 1;
-  r->nexthops = mpnh_copy(o->nexthops);
+  r->nh.next = nexthop_copy(o->nh.next);
   r->eattrs = ea_list_copy(o->eattrs);
   return r;
 }
@@ -1130,7 +1134,8 @@ rta__free(rta *a)
   a->aflags = 0;               /* Poison the entry */
   rt_unlock_hostentry(a->hostentry);
   rt_unlock_source(a->src);
-  mpnh_free(a->nexthops);
+  if (a->nh.next)
+    nexthop_free(a->nh.next);
   ea_free(a->eattrs);
   sl_free(rta_slab, a);
 }
@@ -1167,10 +1172,12 @@ rta_dump(rta *a)
   if (!(a->aflags & RTAF_CACHED))
     debug(" !CACHED");
   debug(" <-%I", a->from);
-  if (a->dest == RTD_ROUTER)
-    debug(" ->%I", a->gw);
-  if (a->dest == RTD_DEVICE || a->dest == RTD_ROUTER)
-    debug(" [%s]", a->iface ? a->iface->name : "???" );
+  if (a->dest == RTD_UNICAST)
+    for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
+      {
+       if (ipa_nonzero(nh->gw)) debug(" ->%I", nh->gw);
+       debug(" [%s]", nh->iface ? nh->iface->name : "???");
+      }
   if (a->eattrs)
     {
       debug(" EA: ");
@@ -1228,7 +1235,7 @@ rta_init(void)
 {
   rta_pool = rp_new(&root_pool, "Attributes");
   rta_slab = sl_new(rta_pool, sizeof(rta));
-  mpnh_slab = sl_new(rta_pool, sizeof(struct mpnh));
+  nexthop_slab = sl_new(rta_pool, sizeof(struct nexthop));
   rta_alloc_hash();
   rte_src_init();
 }
index d98cd79f39965df9092385e9c4876df768b09ca0..43628af82165c31ca4e479cf1ff0efaa135831bc 100644 (file)
@@ -79,8 +79,10 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
        .source = RTS_DEVICE,
        .scope = SCOPE_UNIVERSE,
        .cast = RTC_UNICAST,
-       .dest = RTD_DEVICE,
-       .iface = ad->iface
+       .dest = RTD_UNICAST,
+       .nh = {
+         .iface = ad->iface
+       }
       };
 
       a = rta_lookup(&a0);
index 8c429874034487822911790830b32a5d435b7b8b..46857d0dddb7c73e703b088a7ffeebb5f12645f4 100644 (file)
@@ -708,19 +708,17 @@ rt_notify_accepted(struct channel *c, net *net, rte *new_changed, rte *old_chang
 }
 
 
-static struct mpnh *
-mpnh_merge_rta(struct mpnh *nhs, rta *a, linpool *pool, int max)
+static struct nexthop *
+nexthop_merge_rta(struct nexthop *nhs, rta *a, linpool *pool, int max)
 {
-  struct mpnh nh = { .gw = a->gw, .iface = a->iface };
-  struct mpnh *nh2 = (a->dest == RTD_MULTIPATH) ? a->nexthops : &nh;
-  return mpnh_merge(nhs, nh2, 1, 0, max, pool);
+  return nexthop_merge(nhs, &(a->nh), 1, 0, max, pool);
 }
 
 rte *
 rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, linpool *pool, int silent)
 {
   // struct proto *p = c->proto;
-  struct mpnh *nhs = NULL;
+  struct nexthop *nhs = NULL;
   rte *best0, *best, *rt0, *rt, *tmp;
 
   best0 = net->routes;
@@ -745,7 +743,7 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
       continue;
 
     if (rte_is_reachable(rt))
-      nhs = mpnh_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
+      nhs = nexthop_merge_rta(nhs, rt->attrs, pool, c->merge_limit);
 
     if (tmp)
       rte_free(tmp);
@@ -753,13 +751,12 @@ rt_export_merged(struct channel *c, net *net, rte **rt_free, ea_list **tmpa, lin
 
   if (nhs)
   {
-    nhs = mpnh_merge_rta(nhs, best->attrs, pool, c->merge_limit);
+    nhs = nexthop_merge_rta(nhs, best->attrs, pool, c->merge_limit);
 
     if (nhs->next)
     {
       best = rte_cow_rta(best, pool);
-      best->attrs->dest = RTD_MULTIPATH;
-      best->attrs->nexthops = nhs;
+      nexthop_link(best->attrs, nhs);
     }
   }
 
@@ -922,7 +919,7 @@ rte_validate(rte *e)
     return 0;
   }
 
-  if ((e->attrs->dest == RTD_MULTIPATH) && !mpnh_is_sorted(e->attrs->nexthops))
+  if ((e->attrs->dest == RTD_UNICAST) && !nexthop_is_sorted(&(e->attrs->nh)))
     {
       log(L_WARN "Ignoring unsorted multipath route %N received via %s",
          n->n.addr, e->sender->proto->name);
@@ -1763,20 +1760,22 @@ rta_next_hop_outdated(rta *a)
   if (!he->src)
     return a->dest != RTD_UNREACHABLE;
 
-  return (a->iface != he->src->iface) || !ipa_equal(a->gw, he->gw) ||
-    (a->dest != he->dest) || (a->igp_metric != he->igp_metric) ||
-    !mpnh_same(a->nexthops, he->src->nexthops);
+  return (a->dest != he->dest) || (a->igp_metric != he->igp_metric) ||
+    !nexthop_same(&(a->nh), he->nh);
 }
 
 static inline void
 rta_apply_hostentry(rta *a, struct hostentry *he)
 {
   a->hostentry = he;
-  a->iface = he->src ? he->src->iface : NULL;
-  a->gw = he->gw;
+
+  a->nh.gw = ipa_nonzero(he->nh->gw) ? he->nh->gw : he->link;
+  a->nh.iface = he->nh->iface;
+  a->nh.weight = he->nh->weight;
+  a->nh.next = he->nh->next;
+  
   a->dest = he->dest;
   a->igp_metric = he->igp_metric;
-  a->nexthops = he->src ? he->src->nexthops : NULL;
 }
 
 static inline rte *
@@ -2310,8 +2309,7 @@ rt_get_igp_metric(rte *rt)
     return rt->u.rip.metric;
 #endif
 
-  /* Device routes */
-  if ((a->dest != RTD_ROUTER) && (a->dest != RTD_MULTIPATH))
+  if (a->source == RTS_DEVICE)
     return 0;
 
   return IGP_METRIC_UNKNOWN;
@@ -2325,7 +2323,6 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
 
   /* Reset the hostentry */
   he->src = NULL;
-  he->gw = IPA_NONE;
   he->dest = RTD_UNREACHABLE;
   he->igp_metric = 0;
 
@@ -2346,24 +2343,24 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
          goto done;
        }
 
-      if (a->dest == RTD_DEVICE)
-       {
-         if (if_local_addr(he->addr, a->iface))
+      if ((a->dest == RTD_UNICAST) && ipa_zero(a->nh.gw) && !a->next)
+       { /* We have singlepath device route */
+         if (if_local_addr(he->addr, a->nh.iface))
            {
              /* The host address is a local address, this is not valid */
              log(L_WARN "Next hop address %I is a local address of iface %s",
-                 he->addr, a->iface->name);
+                 he->addr, a->nh.iface->name);
              goto done;
            }
 
          /* The host is directly reachable, use link as a gateway */
-         he->gw = he->link;
-         he->dest = RTD_ROUTER;
+         he->nh = NULL;
+         he->dest = RTD_UNICAST;
        }
       else
        {
          /* The host is reachable through some route entry */
-         he->gw = a->gw;
+         he->nh = (&a->nh);
          he->dest = a->dest;
        }
 
@@ -2442,16 +2439,21 @@ rt_format_via(rte *e)
   rta *a = e->attrs;
 
   /* Max text length w/o IP addr and interface name is 16 */
-  static byte via[IPA_MAX_TEXT_LENGTH+sizeof(a->iface->name)+16];
+  static byte via[IPA_MAX_TEXT_LENGTH+sizeof(a->nh.iface->name)+16];
 
   switch (a->dest)
     {
-    case RTD_ROUTER:   bsprintf(via, "via %I on %s", a->gw, a->iface->name); break;
-    case RTD_DEVICE:   bsprintf(via, "dev %s", a->iface->name); break;
+    case RTD_UNICAST:  if (a->nh.next)
+                         bsprintf(via, "multipath");
+                       else
+                         {
+                           if (ipa_nonzero(a->nh.gw)) bsprintf(via, "via %I ", a->nh.gw);
+                           bsprintf(via, "dev %s", a->nh.iface->name);
+                         }
+                       break;
     case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); 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, "???");
     }
   return via;
@@ -2466,10 +2468,10 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
   int primary = (e->net->routes == e);
   int sync_error = (e->net->n.flags & KRF_SYNC_ERROR);
   void (*get_route_info)(struct rte *, byte *buf, struct ea_list *attrs);
-  struct mpnh *nh;
+  struct nexthop *nh;
 
   tm_format_datetime(tm, &config->tf_route, e->lastmod);
-  if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->gw))
+  if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
     bsprintf(from, " from %I", a->from);
   else
     from[0] = 0;
@@ -2490,8 +2492,9 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, ea_list *tm
     bsprintf(info, " (%d)", e->pref);
   cli_printf(c, -1007, "%-18s %s [%s %s%s]%s%s", ia, rt_format_via(e), a->src->proto->name,
             tm, from, primary ? (sync_error ? " !" : " *") : "", info);
-  for (nh = a->nexthops; nh; nh = nh->next)
-    cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1);
+  if (a->nh.next)
+    for (nh = &(a->nh); nh; nh = nh->next)
+      cli_printf(c, -1007, "\tvia %I on %s weight %d", nh->gw, nh->iface->name, nh->weight + 1);
   if (d->verbose)
     rta_show(c, a, tmpa);
 }
index 52b56efa429daf1ce7ec8e00d7a907d7332771ee..11666f30d6e5c9ef14855385683987fd2ab78f7a 100644 (file)
@@ -1462,7 +1462,7 @@ static inline int
 rte_resolvable(rte *rt)
 {
   int rd = rt->attrs->dest;
-  return (rd == RTD_ROUTER) || (rd == RTD_DEVICE) || (rd == RTD_MULTIPATH);
+  return (rd == RTD_UNICAST);
 }
 
 int
index 66561ee4a0c5ab3bd79af5331242e8ab6b38f3b5..0baa84c999950d330c3e2050cb2fd7b0c7be85bc 100644 (file)
@@ -699,9 +699,10 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
     if (!nbr || (nbr->scope == SCOPE_HOST))
       WITHDRAW(BAD_NEXT_HOP);
 
-    a->dest = RTD_ROUTER;
-    a->gw = nbr->addr;
-    a->iface = nbr->iface;
+    a->dest = RTD_UNICAST;
+    a->nh.gw = nbr->addr;
+    a->nh.iface = nbr->iface;
+    a->nh.next = NULL;
     a->hostentry = NULL;
     a->igp_metric = 0;
   }
@@ -749,7 +750,7 @@ bgp_use_gateway(struct bgp_export_state *s)
     return 0;
 
   /* We need valid global gateway */
-  if ((ra->dest != RTD_ROUTER) || ipa_zero(ra->gw) || ipa_is_link_local(ra->gw))
+  if ((ra->dest != RTD_UNICAST) || (ra->nh.next) || ipa_zero(ra->nh.gw) || ipa_is_link_local(ra->nh.gw))
     return 0;
 
   /* Use it when exported to internal peers */
@@ -757,7 +758,7 @@ bgp_use_gateway(struct bgp_export_state *s)
     return 1;
 
   /* Use it when forwarded to single-hop BGP peer on on the same iface */
-  return p->neigh && (p->neigh->iface == ra->iface);
+  return p->neigh && (p->neigh->iface == ra->nh.iface);
 }
 
 static void
@@ -767,7 +768,7 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
   {
     if (bgp_use_gateway(s))
     {
-      ip_addr nh[1] = { s->route->attrs->gw };
+      ip_addr nh[1] = { s->route->attrs->nh.gw };
       bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, 16);
     }
     else
index d074600a12c6d821b85987f9e3aafee6f4423543..daf76ff26e3d033b39c781b0a9adc51abbd4990c 100644 (file)
@@ -235,7 +235,7 @@ ospf_start(struct proto *P)
   p->lsab_size = 256;
   p->lsab_used = 0;
   p->lsab = mb_alloc(P->pool, p->lsab_size);
-  p->nhpool = lp_new(P->pool, 12*sizeof(struct mpnh));
+  p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop));
   init_list(&(p->iface_list));
   init_list(&(p->area_list));
   fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6,
index 054841caa58e70e22306b75ad90fbd463dbda7aa..09cc57761b4f26f17aeaf808393a65739f5fdab9 100644 (file)
@@ -22,7 +22,7 @@ static inline void reset_ri(ort *ort)
 }
 
 static inline int
-nh_is_vlink(struct mpnh *nhs)
+nh_is_vlink(struct nexthop *nhs)
 {
   return !nhs->iface;
 }
@@ -33,10 +33,10 @@ unresolved_vlink(ort *ort)
   return ort->n.nhs && nh_is_vlink(ort->n.nhs);
 }
 
-static inline struct mpnh *
+static inline struct nexthop *
 new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight)
 {
-  struct mpnh *nh = lp_alloc(p->nhpool, sizeof(struct mpnh));
+  struct nexthop *nh = lp_alloc(p->nhpool, sizeof(struct nexthop));
   nh->gw = gw;
   nh->iface = iface;
   nh->next = NULL;
@@ -46,7 +46,7 @@ new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight)
 
 /* Returns true if there are device nexthops in n */
 static inline int
-has_device_nexthops(const struct mpnh *n)
+has_device_nexthops(const struct nexthop *n)
 {
   for (; n; n = n->next)
     if (ipa_zero(n->gw))
@@ -56,13 +56,13 @@ has_device_nexthops(const struct mpnh *n)
 }
 
 /* Replace device nexthops with nexthops to gw */
-static struct mpnh *
-fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
+static struct nexthop *
+fix_device_nexthops(struct ospf_proto *p, const struct nexthop *n, ip_addr gw)
 {
-  struct mpnh *root1 = NULL;
-  struct mpnh *root2 = NULL;
-  struct mpnh **nn1 = &root1;
-  struct mpnh **nn2 = &root2;
+  struct nexthop *root1 = NULL;
+  struct nexthop *root2 = NULL;
+  struct nexthop **nn1 = &root1;
+  struct nexthop **nn2 = &root2;
 
   if (!p->ecmp)
     return new_nexthop(p, gw, n->iface, n->weight);
@@ -73,7 +73,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
 
   for (; n; n = n->next)
   {
-    struct mpnh *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight);
+    struct nexthop *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight);
 
     if (ipa_zero(n->gw))
     {
@@ -87,7 +87,7 @@ fix_device_nexthops(struct ospf_proto *p, const struct mpnh *n, ip_addr gw)
     }
   }
 
-  return mpnh_merge(root1, root2, 1, 1, p->ecmp, p->nhpool);
+  return nexthop_merge(root1, root2, 1, 1, p->ecmp, p->nhpool);
 }
 
 
@@ -283,7 +283,7 @@ ort_merge(struct ospf_proto *p, ort *o, const orta *new)
 
   if (old->nhs != new->nhs)
   {
-    old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
+    old->nhs = nexthop_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
                          p->ecmp, p->nhpool);
     old->nhs_reuse = 1;
   }
@@ -299,7 +299,7 @@ ort_merge_ext(struct ospf_proto *p, ort *o, const orta *new)
 
   if (old->nhs != new->nhs)
   {
-    old->nhs = mpnh_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
+    old->nhs = nexthop_merge(old->nhs, new->nhs, old->nhs_reuse, new->nhs_reuse,
                          p->ecmp, p->nhpool);
     old->nhs_reuse = 1;
   }
@@ -1673,18 +1673,18 @@ ospf_rt_spf(struct ospf_proto *p)
 
 
 static inline int
-inherit_nexthops(struct mpnh *pn)
+inherit_nexthops(struct nexthop *pn)
 {
   /* Proper nexthops (with defined GW) or dummy vlink nexthops (without iface) */
   return pn && (ipa_nonzero(pn->gw) || !pn->iface);
 }
 
-static struct mpnh *
+static struct nexthop *
 calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
              struct top_hash_entry *par, int pos)
 {
   struct ospf_proto *p = oa->po;
-  struct mpnh *pn = par->nhs;
+  struct nexthop *pn = par->nhs;
   struct ospf_iface *ifa;
   u32 rid = en->lsa.rt;
 
@@ -1812,7 +1812,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
   if (!link_back(oa, en, par))
     return;
 
-  struct mpnh *nhs = calc_next_hop(oa, en, par, pos);
+  struct nexthop *nhs = calc_next_hop(oa, en, par, pos);
   if (!nhs)
   {
     log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)",
@@ -1850,7 +1850,7 @@ add_cand(list * l, struct top_hash_entry *en, struct top_hash_entry *par,
 
     /* Merge old and new */
     int new_reuse = (par->nhs != nhs);
-    en->nhs = mpnh_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool);
+    en->nhs = nexthop_merge(en->nhs, nhs, en->nhs_reuse, new_reuse, p->ecmp, p->nhpool);
     en->nhs_reuse = 1;
     return;
   }
@@ -1906,8 +1906,8 @@ ort_changed(ort *nf, rta *nr)
     (nf->n.metric1 != nf->old_metric1) || (nf->n.metric2 != nf->old_metric2) ||
     (nf->n.tag != nf->old_tag) || (nf->n.rid != nf->old_rid) ||
     (nr->source != or->source) || (nr->dest != or->dest) ||
-    (nr->iface != or->iface) || !ipa_equal(nr->gw, or->gw) ||
-    !mpnh_same(nr->nexthops, or->nexthops);
+    (nr->nh.iface != or->nh.iface) || !ipa_equal(nr->nh.gw, or->nh.gw) ||
+    !nexthop_same(&(nr->nh), &(or->nh));
 }
 
 static void
@@ -1931,7 +1931,7 @@ again1:
     /* Sanity check of next-hop addresses, failure should not happen */
     if (nf->n.type)
     {
-      struct mpnh *nh;
+      struct nexthop *nh;
       for (nh = nf->n.nhs; nh; nh = nh->next)
        if (ipa_nonzero(nh->gw))
        {
@@ -1954,22 +1954,8 @@ again1:
        .cast = RTC_UNICAST
       };
 
-      if (nf->n.nhs->next)
-      {
-       a0.dest = RTD_MULTIPATH;
-       a0.nexthops = nf->n.nhs;
-      }
-      else if (ipa_nonzero(nf->n.nhs->gw))
-      {
-       a0.dest = RTD_ROUTER;
-       a0.iface = nf->n.nhs->iface;
-       a0.gw = nf->n.nhs->gw;
-      }
-      else
-      {
-       a0.dest = RTD_DEVICE;
-       a0.iface = nf->n.nhs->iface;
-      }
+      nexthop_link(&a0, nf->n.nhs);
+      a0.dest = RTD_UNICAST;
 
       if (reload || ort_changed(nf, &a0))
       {
index 959d12e95382a17c21ecdccc1e1622d59fd75983..842792f0e1829a1be8df84f25b266cb0e5707b4f 100644 (file)
@@ -53,7 +53,7 @@ typedef struct orta
   struct ospf_area *oa;
   struct ospf_area *voa;       /* Used when route is replaced in ospf_rt_sum_tr(),
                                   NULL otherwise */
-  struct mpnh *nhs;            /* Next hops computed during SPF */
+  struct nexthop *nhs;         /* Next hops computed during SPF */
   struct top_hash_entry *en;   /* LSA responsible for this orta */
 }
 orta;
index aaaf2e8e3a687dcc23e576207d093822f3d4eb92..ce77f57a7c94481880bd638d331ab902f7d1f943 100644 (file)
@@ -1288,8 +1288,8 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte
   ip_addr fwd = IPA_NONE;
 
 
-  if ((a->dest == RTD_ROUTER) && use_gw_for_fwaddr(p, a->gw, a->iface))
-    fwd = a->gw;
+  if ((a->dest == RTD_UNICAST) && use_gw_for_fwaddr(p, a->nh.gw, a->nh.iface))
+    fwd = a->nh.gw;
 
   /* NSSA-LSA with P-bit set must have non-zero forwarding address */
   if (oa && ipa_zero(fwd))
index 38447fdfa82fa8c5a5568bf2fa8e2e17b9594d57..d1682c54f783616b7507c838f24406a7f00f246a 100644 (file)
@@ -28,7 +28,7 @@ struct top_hash_entry
   u16 next_lsa_opts;           /* For postponed LSA origination */
   bird_clock_t inst_time;      /* Time of installation into DB */
   struct ort *nf;              /* Reference fibnode for sum and ext LSAs, NULL for otherwise */
-  struct mpnh *nhs;            /* Computed nexthops - valid only in ospf_rt_spf() */
+  struct nexthop *nhs;         /* Computed nexthops - valid only in ospf_rt_spf() */
   ip_addr lb;                  /* In OSPFv2, link back address. In OSPFv3, any global address in the area useful for vlinks */
   u32 lb_id;                   /* Interface ID of link back iface (for bcast or NBMA networks) */
   u32 dist;                    /* Distance from the root */
index d87a078cb390735edcdf663ab51a9652aaa730c2..8b09330cb1ee968d3562704b232e4ad8af25d244 100644 (file)
@@ -158,10 +158,10 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
     while (rt2 && !rip_valid_rte(rt2))
       rt2 = rt2->next;
 
+    a0.dest = RTD_UNICAST;
     if (p->ecmp && rt2)
     {
       /* ECMP route */
-      struct mpnh *nhs = NULL;
       int num = 0;
 
       for (rt = en->routes; rt && (num < p->ecmp); rt = rt->next)
@@ -169,33 +169,34 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
        if (!rip_valid_rte(rt))
            continue;
 
-       struct mpnh *nh = alloca(sizeof(struct mpnh));
+       struct nexthop *nh = (a0.nh.next ? &(a0.nh) : alloca(sizeof(struct nexthop)));
+
        nh->gw = rt->next_hop;
        nh->iface = rt->from->nbr->iface;
        nh->weight = rt->from->ifa->cf->ecmp_weight;
-       mpnh_insert(&nhs, nh);
+
+       if (a0.nh.next)
+         nexthop_insert(&(a0.nh), nh);
+
        num++;
 
        if (rt->tag != rt_tag)
          rt_tag = 0;
       }
-
-      a0.dest = RTD_MULTIPATH;
-      a0.nexthops = nhs;
     }
     else
     {
       /* Unipath route */
-      a0.dest = RTD_ROUTER;
-      a0.gw = rt->next_hop;
-      a0.iface = rt->from->nbr->iface;
+      a0.nh.next = NULL;
+      a0.nh.gw = rt->next_hop;
+      a0.nh.iface = rt->from->nbr->iface;
       a0.from = rt->from->nbr->addr;
     }
 
     rta *a = rta_lookup(&a0);
     rte *e = rte_get_temp(a);
 
-    e->u.rip.from = a0.iface;
+    e->u.rip.from = a0.nh.iface;
     e->u.rip.metric = rt_metric;
     e->u.rip.tag = rt_tag;
 
@@ -345,8 +346,8 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
     en->metric = rt_metric;
     en->tag = rt_tag;
     en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL;
-    en->iface = new->attrs->iface;
-    en->next_hop = new->attrs->gw;
+    en->iface = new->attrs->nh.iface;
+    en->next_hop = new->attrs->nh.gw;
   }
   else
   {
index 86359f0bbd6caa2bbb6ac26e76d5907d235d0af0..8103166d15234f9cda87207fbfa5cc51e688667c 100644 (file)
@@ -13,7 +13,7 @@ CF_HDR
 CF_DEFINES
 
 #define STATIC_CFG ((struct static_config *) this_proto)
-static struct static_route *this_srt, *this_srt_nh, *last_srt_nh;
+static struct static_route *this_srt, *last_srt;
 static struct f_inst **this_srt_last_cmd;
 
 static void
@@ -22,10 +22,9 @@ static_route_finish(void)
   struct static_route *r;
 
   /* Update undefined use_bfd entries in multipath nexthops */
-  if (this_srt->dest == RTD_MULTIPATH)
-    for (r = this_srt->mp_next; r; r = r->mp_next)
-      if (r->use_bfd < 0)
-        r->use_bfd = this_srt->use_bfd;
+  for (r = this_srt->mp_next; r; r = r->mp_next)
+    if (r->use_bfd < 0)
+      r->use_bfd = this_srt->use_bfd;
 }
 
 CF_DECLS
@@ -58,48 +57,50 @@ stat_route0: ROUTE net_any {
      add_tail(&STATIC_CFG->other_routes, &this_srt->n);
      this_srt->net = $2;
      this_srt_last_cmd = &(this_srt->cmds);
+     this_srt->mp_next = NULL;
+     last_srt = NULL;
   }
  ;
 
 stat_multipath1:
    VIA ipa ipa_scope {
-     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->via_if = $3;
-     this_srt_nh->if_name = (void *) this_srt; /* really */
-     this_srt_nh->use_bfd = -1; /* undefined */
+     last_srt = last_srt ? last_srt->mp_next = cfg_allocz(sizeof(struct static_route)) : this_srt;
+
+     last_srt->dest = RTD_UNICAST;
+     last_srt->via = $2;
+     last_srt->via_if = $3;
+     last_srt->if_name = (void *) this_srt; /* really */
+     last_srt->use_bfd = -1; /* undefined */
+     last_srt->mp_next = NULL;
    }
  | stat_multipath1 WEIGHT expr {
-     this_srt_nh->weight = $3 - 1;
+     last_srt->weight = $3 - 1;
      if (($3<1) || ($3>256)) cf_error("Weight must be in range 1-256"); 
    }
  | stat_multipath1 BFD bool {
-     this_srt_nh->use_bfd = $3; cf_check_bfd($3);
+     last_srt->use_bfd = $3; cf_check_bfd($3);
    }
  ;
 
 stat_multipath:
-   stat_multipath1 { this_srt->mp_next = this_srt_nh; }
- | stat_multipath stat_multipath1 { last_srt_nh->mp_next = this_srt_nh; }
+   stat_multipath1
+ | stat_multipath stat_multipath1
  ;
 
 stat_route:
    stat_route0 VIA ipa ipa_scope {
-      this_srt->dest = RTD_ROUTER;
+      this_srt->dest = RTD_UNICAST;
       this_srt->via = $3;
       this_srt->via_if = $4;
    }
  | stat_route0 VIA TEXT {
-      this_srt->dest = RTD_DEVICE;
+      this_srt->dest = RTD_UNICAST;
+      this_srt->via = IPA_NONE;
       this_srt->if_name = $3;
       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 MULTIPATH stat_multipath
  | stat_route0 RECURSIVE ipa {
       this_srt->dest = RTDX_RECURSIVE;
       this_srt->via = $3;
index fb547537584838bd6f85983b5418f3f56093ca4f..f3cfec010ee3aabe7473863079d31da6b2ae2271 100644 (file)
@@ -58,49 +58,53 @@ p_igp_table(struct proto *p)
 }
 
 static void
-static_install(struct proto *p, struct static_route *r, struct iface *ifa)
+static_install(struct proto *p, struct static_route *r)
 {
   rta a;
   rte *e;
 
-  if (r->installed > 0)
+  if (!(r->state & STS_WANT) && r->dest != RTD_UNICAST)
     return;
 
   DBG("Installing static route %N, rtd=%d\n", r->net, r->dest);
   bzero(&a, sizeof(a));
   a.src = p->main_source;
-  a.source = (r->dest == RTD_DEVICE) ? RTS_STATIC_DEVICE : RTS_STATIC;
+  a.source = ((r->dest == RTD_UNICAST) && ipa_zero(r->via)) ? RTS_STATIC_DEVICE : RTS_STATIC;
   a.scope = SCOPE_UNIVERSE;
   a.cast = RTC_UNICAST;
   a.dest = r->dest;
-  a.gw = r->via;
-  a.iface = ifa;
-
-  if (r->dest == RTD_MULTIPATH)
+  if (r->dest == RTD_UNICAST)
     {
       struct static_route *r2;
-      struct mpnh *nhs = NULL;
+      int num = 0;
+
+      for (r2 = r; r2; r2 = r2->mp_next)
+      {
+       if ((r2->state & STS_INSTALLED) && !(r2->state & STS_FORCE))
+         continue;
 
-      for (r2 = r->mp_next; r2; r2 = r2->mp_next)
-       if (r2->installed)
+       if (r2->state & STS_WANT)
          {
-           struct mpnh *nh = alloca(sizeof(struct mpnh));
+           struct nexthop *nh = (a.nh.next) ? alloca(sizeof(struct nexthop)) : &(a.nh);
            nh->gw = r2->via;
            nh->iface = r2->neigh->iface;
            nh->weight = r2->weight;
-           mpnh_insert(&nhs, nh);
+           if (a.nh.next)
+             nexthop_insert(&(a.nh), nh);
+           r2->state |= STS_INSTALLED;
+           num++;
          }
+       else
+         r2->state = 0;
+      }
 
-      /* 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;
+      if (!num) // No nexthop to install
+      {
+       if (r->state & STS_INSTALLED_ANY)
+         rte_update(p, r->net, NULL);
+       
+       return;
+      }
     }
 
   if (r->dest == RTDX_RECURSIVE)
@@ -115,7 +119,6 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
     f_eval_rte(r->cmds, &e, static_lp);
 
   rte_update(p, r->net, e);
-  r->installed = 1;
 
   if (r->cmds)
     lp_flush(static_lp);
@@ -124,12 +127,13 @@ static_install(struct proto *p, struct static_route *r, struct iface *ifa)
 static void
 static_remove(struct proto *p, struct static_route *r)
 {
-  if (!r->installed)
+  if (!(r->state & STS_INSTALLED_ANY))
     return;
 
   DBG("Removing static route %N via %I\n", r->net, r->via);
   rte_update(p, r->net, NULL);
-  r->installed = 0;
+
+  r->state &= ~(STS_INSTALLED | STS_INSTALLED_ANY);
 }
 
 static void
@@ -180,38 +184,12 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
   DBG("static_add(%N,%d)\n", r->net, r->dest);
   switch (r->dest)
     {
-    case RTD_ROUTER:
-      {
-       struct neighbor *n = neigh_find2(p, &r->via, r->via_if, NEF_STICKY);
-       if (n)
-         {
-           r->chain = n->data;
-           n->data = r;
-           r->neigh = n;
-
-           static_update_bfd(p, r);
-           if (static_decide(cf, r))
-             static_install(p, r, n->iface);
-           else
-             static_remove(p, r);
-         }
-       else
-         {
-           log(L_ERR "Static route destination %I is invalid. Ignoring.", r->via);
-           static_remove(p, r);
-         }
-       break;
-      }
-
-    case RTD_DEVICE:
-      break;
-
-    case RTD_MULTIPATH:
+    case RTD_UNICAST:
       {
        int count = 0;
        struct static_route *r2;
 
-       for (r2 = r->mp_next; r2; r2 = r2->mp_next)
+       for (r2 = r; r2; r2 = r2->mp_next)
          {
            struct neighbor *n = neigh_find2(p, &r2->via, r2->via_if, NEF_STICKY);
            if (n)
@@ -221,20 +199,19 @@ static_add(struct proto *p, struct static_config *cf, struct static_route *r)
                r2->neigh = n;
 
                static_update_bfd(p, r2);
-               r2->installed = static_decide(cf, r2);
-               count += r2->installed;
+               r2->state = static_decide(cf,r2) ? STS_WANT : 0;
+               count++;
              }
            else
              {
                log(L_ERR "Static route destination %I is invalid. Ignoring.", r2->via);
-               r2->installed = 0;
+               r2->state = 0;
              }
          }
 
        if (count)
          static_install(p, r, NULL);
-       else
-         static_remove(p, r);
+
        break;
       }
 
@@ -247,20 +224,13 @@ static void
 static_rte_cleanup(struct proto *p UNUSED, struct static_route *r)
 {
   struct static_route *r2;
-
-  if (r->bfd_req)
-  {
-    rfree(r->bfd_req);
-    r->bfd_req = NULL;
-  }
-
-  if (r->dest == RTD_MULTIPATH)
-    for (r2 = r->mp_next; r2; r2 = r2->mp_next)
-      if (r2->bfd_req)
-      {
-       rfree(r2->bfd_req);
-       r2->bfd_req = NULL;
-      }
+  
+  for (r2 = r; r2; r2 = r2->mp_next)
+    if (r2->bfd_req)
+    {
+      rfree(r2->bfd_req);
+      r2->bfd_req = NULL;
+    }
 }
 
 static int
@@ -293,11 +263,11 @@ static_shutdown(struct proto *p)
 
   /* Just reset the flag, the routes will be flushed by the nest */
   WALK_LIST(r, cf->iface_routes)
-    r->installed = 0;
+    r->state = 0;
   WALK_LIST(r, cf->other_routes)
   {
     static_rte_cleanup(p, r);
-    r->installed = 0;
+    r->state = 0;
   }
 
   /* Handle failure during channel reconfigure */
@@ -306,9 +276,9 @@ static_shutdown(struct proto *p)
   if (cf)
   {
     WALK_LIST(r, cf->iface_routes)
-      r->installed = 0;
+      r->state = 0;
     WALK_LIST(r, cf->other_routes)
-      r->installed = 0;
+      r->state = 0;
   }
 
   return PS_DOWN;
@@ -326,40 +296,13 @@ static_cleanup(struct proto *p)
 static void
 static_update_rte(struct proto *p, struct static_route *r)
 {
-  switch (r->dest)
-  {
-  case RTD_ROUTER:
-    if (static_decide((struct static_config *) p->cf, r))
-      static_install(p, r, r->neigh->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);
+  if (r->dest != RTD_UNICAST)
+    return;
 
-    break;
-  }
-  }
+  if (static_decide((struct static_config *) p->cf, r))
+    static_install(p, r, r->neigh->iface);
+  else
+    static_remove(p, r);
 }
 
 static void
@@ -391,18 +334,13 @@ static void
 static_dump_rt(struct static_route *r)
 {
   debug("%-1N: ", r->net);
-  switch (r->dest)
-    {
-    case RTD_ROUTER:
-      debug("via %I\n", r->via);
-      break;
-    case RTD_DEVICE:
+  if (r->dest == RTD_UNICAST)
+    if (ipa_zero(r->via))
       debug("dev %s\n", r->if_name);
-      break;
-    default:
-      debug("rtd %d\n", r->dest);
-      break;
-    }
+    else
+      debug("via %I\n", r->via);
+  else
+    debug("rtd %d\n", r->dest);
 }
 
 static void
@@ -496,22 +434,27 @@ static_same_dest(struct static_route *x, struct static_route *y)
 
   switch (x->dest)
     {
-    case RTD_ROUTER:
-      return ipa_equal(x->via, y->via) && (x->via_if == y->via_if);
-
-    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) ||
-           (x->via_if != y->via_if) ||
-           (x->use_bfd != y->use_bfd) ||
-           (x->weight != y->weight))
-         return 0;
-      return !x && !y;
+    case RTD_UNICAST:
+      {
+       struct static_route *xc, *yc;
+       for (xc = x, yc = y; xc && yc; xc = xc->mp_next, yc = yc->mp_next)
+       {
+         if (ipa_nonzero(xc->via) && ipa_nonzero(yc->via))
+         {
+           if (!ipa_equal(x->via, y->via) ||
+               (x->via_if != y->via_if) ||
+               (x->use_bfd != y->use_bfd) ||
+               (x->weight != y->weight))
+             return 0;
+         }
+         else
+           if (strcmp(x->if_name, y->if_name) ||
+               (x->use_bfd != y->use_bfd) ||
+               (x->weight != y->weight))
+             return 0;
+       }
+       return 1;
+      }
 
     case RTDX_RECURSIVE:
       return ipa_equal(x->via, y->via);
@@ -556,10 +499,10 @@ static_match(struct proto *p, struct static_route *r, struct static_config *n)
 
  found:
   /* If destination is different, force reinstall */
-  if ((r->installed > 0) && !static_same_rte(r, t))
-    t->installed = -1;
+  if (r->state && !static_same_rte(r, t))
+    t->state = r->state | STS_WANT | STS_FORCE;
   else
-    t->installed = r->installed;
+    t->state = r->state;
 }
 
 static inline rtable *
@@ -606,37 +549,24 @@ static_reconfigure(struct proto *p, struct proto_config *CF)
 static void
 static_copy_routes(list *dlst, list *slst)
 {
-  struct static_route *dr, *sr;
+  struct static_route *sr;
 
   init_list(dlst);
   WALK_LIST(sr, *slst)
     {
-      /* copy one route */
-      dr = cfg_alloc(sizeof(struct static_route));
-      memcpy(dr, sr, sizeof(struct static_route));
-
-      /* This fn is supposed to be called on fresh src routes, which have 'live'
-        fields (like .chain, .neigh or .installed) zero, so no need to zero them */
-
-      /* We need to copy multipath chain, because there are backptrs in 'if_name' */
-      if (dr->dest == RTD_MULTIPATH)
-       {
-         struct static_route *md, *ms, **mp_last;
-
-         mp_last = &(dr->mp_next);
-         for (ms = sr->mp_next; ms; ms = ms->mp_next)
-           {
-             md = cfg_alloc(sizeof(struct static_route));
-             memcpy(md, ms, sizeof(struct static_route));
-             md->if_name = (void *) dr; /* really */
-
-             *mp_last = md;
-             mp_last = &(md->mp_next);
-           }
-         *mp_last = NULL;
-       }
+      struct static_route *srr, *drr = NULL;
+      for (srr = sr; srr; srr = srr->mp_next)
+      {
+       /* copy one route */
+       struct static_route *dr = cfg_alloc(sizeof(struct static_route));
+       if (drr)
+         drr->mp_next = dr;
+       else
+         add_tail(dlst, &(dr->n));
 
-      add_tail(dlst, (node *) dr);
+       memcpy(dr, sr, sizeof(struct static_route));
+       drr = dr;
+      }
     }
 }
 
@@ -668,30 +598,39 @@ struct protocol proto_static = {
   .copy_config =       static_copy_config
 };
 
-static void
-static_show_rt(struct static_route *r)
+static byte *
+static_format_via(struct static_route *r)
 {
-  byte via[IPA_MAX_TEXT_LENGTH + 25];
+  static byte via[IPA_MAX_TEXT_LENGTH + 25];
 
   switch (r->dest)
     {
-    case RTD_ROUTER:   bsprintf(via, "via %I%J", r->via, r->via_if); break;
-    case RTD_DEVICE:   bsprintf(via, "dev %s", r->if_name); break;
+    case RTD_UNICAST:  if (ipa_zero(r->via)) bsprintf(via, "dev %s", r->if_name);
+                       else bsprintf(via, "via %I%J", r->via, r->via_if);
+                       break;
     case RTD_BLACKHOLE:        bsprintf(via, "blackhole"); break;
     case RTD_UNREACHABLE: bsprintf(via, "unreachable"); break;
     case RTD_PROHIBIT: bsprintf(via, "prohibited"); break;
-    case RTD_MULTIPATH:        bsprintf(via, "multipath"); break;
     case RTDX_RECURSIVE: bsprintf(via, "recursive %I", r->via); break;
     default:           bsprintf(via, "???");
     }
-  cli_msg(-1009, "%N %s%s%s", r->net, via,
-         r->bfd_req ? " (bfd)" : "", r->installed ? "" : " (dormant)");
+  return via;
+}
 
-  struct static_route *r2;
-  if (r->dest == RTD_MULTIPATH)
-    for (r2 = r->mp_next; r2; r2 = r2->mp_next)
-      cli_msg(-1009, "\tvia %I%J weight %d%s%s", r2->via, r2->via_if, r2->weight + 1,
-             r2->bfd_req ? " (bfd)" : "", r2->installed ? "" : " (dormant)");
+static void
+static_show_rt(struct static_route *r)
+{
+  if (r->mp_next)
+  {
+    cli_msg(-1009, "%N", r->net);
+    struct static_route *r2;
+    for (r2 = r; r2; r2 = r2->mp_next)
+      cli_msg(-1009, "\t%s weight %d%s%s", static_format_via(r2), r2->weight + 1,
+             r2->bfd_req ? " (bfd)" : "", (r2->state & STS_INSTALLED) ? "" : " (dormant)");
+  }
+  else
+    cli_msg(-1009, "%N %s%s%s", r->net, static_format_via(r),
+         r->bfd_req ? " (bfd)" : "", (r->state & STS_INSTALLED) ? "" : " (dormant)");
 }
 
 void
index 51486e836f0feb750408eaab7734ca77156ab418..418b307648a22dfa32819a13a0a2636af4026000 100644 (file)
@@ -32,14 +32,19 @@ struct static_route {
   struct iface *via_if;                        /* Destination iface, for link-local vias */
   struct neighbor *neigh;
   byte *if_name;                       /* Name for RTD_DEVICE routes */
-  struct static_route *mp_next;                /* Nexthops for RTD_MULTIPATH routes */
+  struct static_route *mp_next;                /* Nexthops for multipath routes */
   struct f_inst *cmds;                 /* List of commands for setting attributes */
-  int installed;                       /* Installed in rt table, -1 for reinstall */
+  u32 state;                           /* Current state: STS_* */
   int use_bfd;                         /* Configured to use BFD */
   int weight;                          /* Multipath next hop weight */
   struct bfd_request *bfd_req;         /* BFD request, if BFD is used */
 };
 
+#define STS_INSTALLED          0x1
+#define STS_INSTALLED_ANY      0x2
+#define STS_WANT               0x4
+#define STS_FORCE              0x8
+
 /* Dummy nodes (parts of multipath route) abuses masklen field for weight
    and if_name field for a ptr to the master (RTD_MULTIPATH) node. */
 
index d2372a3d480acf9f19af3150ca52282f69c3aa87..fbaa8e3214cf84266c959b77e5dee6906e2b66e0 100644 (file)
@@ -148,8 +148,7 @@ krt_capable(rte *e)
 
   return
     a->cast == RTC_UNICAST &&
-    (a->dest == RTD_ROUTER
-     || a->dest == RTD_DEVICE
+    ((a->dest == RTD_UNICAST && !a->nh.next) /* No multipath support */
 #ifdef RTF_REJECT
      || a->dest == RTD_UNREACHABLE
 #endif
@@ -190,7 +189,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
   net *net = e->net;
   rta *a = e->attrs;
   static int msg_seq;
-  struct iface *j, *i = a->iface;
+  struct iface *j, *i = a->nh.iface;
   int l;
   struct ks_msg msg;
   char *body = (char *)msg.buf;
@@ -243,7 +242,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
     }
   }
 
-  gw = a->gw;
+  gw = a->nh.gw;
 
   /* Embed interface ID to link-local address */
   if (ipa_is_link_local(gw))
@@ -270,9 +269,28 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
 
   switch (a->dest)
   {
-    case RTD_ROUTER:
-      msg.rtm.rtm_flags |= RTF_GATEWAY;
-      msg.rtm.rtm_addrs |= RTA_GATEWAY;
+    case RTD_UNICAST:
+      if (ipa_zero(gw))
+      {
+       if(i)
+       {
+#ifdef RTF_CLONING
+         if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS)  /* PTP */
+           msg.rtm.rtm_flags |= RTF_CLONING;
+#endif
+
+         if(!i->addr) {
+           log(L_ERR "KRT: interface %s has no IP addess", i->name);
+           return -1;
+         }
+
+         sockaddr_fill(&gate, ipa_is_ip4(i->addr->ip) ? AF_INET : AF_INET6, i->addr->ip, NULL, 0);
+         msg.rtm.rtm_addrs |= RTA_GATEWAY;
+       }
+      } else {
+       msg.rtm.rtm_flags |= RTF_GATEWAY;
+       msg.rtm.rtm_addrs |= RTA_GATEWAY;
+      }
       break;
 
 #ifdef RTF_REJECT
@@ -281,23 +299,6 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
 #ifdef RTF_BLACKHOLE
     case RTD_BLACKHOLE:
 #endif
-    case RTD_DEVICE:
-      if(i)
-      {
-#ifdef RTF_CLONING
-        if (cmd == RTM_ADD && (i->flags & IF_MULTIACCESS) != IF_MULTIACCESS)   /* PTP */
-          msg.rtm.rtm_flags |= RTF_CLONING;
-#endif
-
-        if(!i->addr) {
-          log(L_ERR "KRT: interface %s has no IP addess", i->name);
-          return -1;
-        }
-
-       sockaddr_fill(&gate, ipa_is_ip4(i->addr->ip) ? AF_INET : AF_INET6, i->addr->ip, NULL, 0);
-        msg.rtm.rtm_addrs |= RTA_GATEWAY;
-      }
-      break;
     default:
       bug("krt-sock: unknown flags, but not filtered");
   }
@@ -489,39 +490,40 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
   }
 #endif
 
-  a.iface = if_find_by_index(msg->rtm.rtm_index);
-  if (!a.iface)
+  a.nh.iface = if_find_by_index(msg->rtm.rtm_index);
+  if (!a.nh.iface)
     {
       log(L_ERR "KRT: Received route %N with unknown ifindex %u",
          net->n.addr, msg->rtm.rtm_index);
       return;
     }
 
+  a.dest = RTD_UNICAST;
+  a.nh.next = NULL;
   if (flags & RTF_GATEWAY)
   {
     neighbor *ng;
-    a.dest = RTD_ROUTER;
-    a.gw = igate;
+    a.nh.gw = igate;
 
     /* Clean up embedded interface ID returned in link-local address */
-    if (ipa_is_link_local(a.gw))
-      _I0(a.gw) = 0xfe800000;
+    if (ipa_is_link_local(a.nh.gw))
+      _I0(a.nh.gw) = 0xfe800000;
 
-    ng = neigh_find2(&p->p, &a.gw, a.iface, 0);
+    ng = neigh_find2(&p->p, &a.nh.gw, a.nh.iface, 0);
     if (!ng || (ng->scope == SCOPE_HOST))
       {
        /* Ignore routes with next-hop 127.0.0.1, host routes with such
           next-hop appear on OpenBSD for address aliases. */
-        if (ipa_classify(a.gw) == (IADDR_HOST | SCOPE_HOST))
+        if (ipa_classify(a.nh.gw) == (IADDR_HOST | SCOPE_HOST))
           return;
 
        log(L_ERR "KRT: Received route %N with strange next-hop %I",
-           net->n.addr, a.gw);
+           net->n.addr, a.nh.gw);
        return;
       }
   }
   else
-    a.dest = RTD_DEVICE;
+    a.nh.gw = IPA_NONE;
 
  done:
   e = rte_get_temp(&a);
index ee2cd1257abe207e5d994f712290749b9d9a095a..6e75ee53c4344b6c0c4267ed7701fb00640a9a16 100644 (file)
@@ -20,6 +20,7 @@
 #include "nest/route.h"
 #include "nest/protocol.h"
 #include "nest/iface.h"
+#include "lib/alloca.h"
 #include "sysdep/unix/timer.h"
 #include "sysdep/unix/unix.h"
 #include "sysdep/unix/krt.h"
@@ -303,7 +304,6 @@ static struct nl_want_attrs ifa_attr_want4[BIRD_IFA_MAX] = {
   [IFA_ADDRESS]          = { 1, 1, sizeof(ip4_addr) },
   [IFA_LOCAL]    = { 1, 1, sizeof(ip4_addr) },
   [IFA_BROADCAST] = { 1, 1, sizeof(ip4_addr) },
-  [IFA_FLAGS]    = { 1, 1, sizeof(u32) },
 };
 
 static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = {
@@ -315,7 +315,7 @@ static struct nl_want_attrs ifa_attr_want6[BIRD_IFA_MAX] = {
 
 #define BIRD_RTA_MAX  (RTA_TABLE+1)
 
-static struct nl_want_attrs mpnh_attr_want4[BIRD_RTA_MAX] = {
+static struct nl_want_attrs nexthop_attr_want4[BIRD_RTA_MAX] = {
   [RTA_GATEWAY]          = { 1, 1, sizeof(ip4_addr) },
 };
 
@@ -472,7 +472,7 @@ nl_close_nexthop(struct nlmsghdr *h, struct rtnexthop *nh)
 }
 
 static void
-nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct mpnh *nh)
+nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct nexthop *nh)
 {
   struct rtattr *a = nl_open_attr(h, bufsize, RTA_MULTIPATH);
 
@@ -492,17 +492,17 @@ nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct mpnh *nh)
   nl_close_attr(h, a);
 }
 
-static struct mpnh *
+static struct nexthop *
 nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
 {
   /* Temporary buffer for multicast nexthops */
-  static struct mpnh *nh_buffer;
+  static struct nexthop *nh_buffer;
   static int nh_buf_size;      /* in number of structures */
   static int nh_buf_used;
 
   struct rtattr *a[BIRD_RTA_MAX];
   struct rtnexthop *nh = RTA_DATA(ra);
-  struct mpnh *rv, *first, **last;
+  struct nexthop *rv, *first, **last;
   unsigned len = RTA_PAYLOAD(ra);
 
   first = NULL;
@@ -518,7 +518,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
       if (nh_buf_used == nh_buf_size)
       {
        nh_buf_size = nh_buf_size ? (nh_buf_size * 2) : 4;
-       nh_buffer = xrealloc(nh_buffer, nh_buf_size * sizeof(struct mpnh));
+       nh_buffer = xrealloc(nh_buffer, nh_buf_size * sizeof(struct nexthop));
       }
       *last = rv = nh_buffer + nh_buf_used++;
       rv->next = NULL;
@@ -531,7 +531,7 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
 
       /* Nonexistent RTNH_PAYLOAD ?? */
       nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0);
-      nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a));
+      nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want4, a, sizeof(a));
       if (a[RTA_GATEWAY])
        {
          rv->gw = rta_get_ipa(a[RTA_GATEWAY]);
@@ -957,14 +957,14 @@ krt_capable(rte *e)
 
   switch (a->dest)
     {
-    case RTD_ROUTER:
-    case RTD_DEVICE:
-      if (a->iface == NULL)
-       return 0;
+    case RTD_UNICAST:
+      for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
+       if (nh->iface)
+         return 1;
+      return 0;
     case RTD_BLACKHOLE:
     case RTD_UNREACHABLE:
     case RTD_PROHIBIT:
-    case RTD_MULTIPATH:
       break;
     default:
       return 0;
@@ -973,7 +973,7 @@ krt_capable(rte *e)
 }
 
 static inline int
-nh_bufsize(struct mpnh *nh)
+nh_bufsize(struct nexthop *nh)
 {
   int rv = 0;
   for (; nh != NULL; nh = nh->next)
@@ -982,12 +982,12 @@ nh_bufsize(struct mpnh *nh)
 }
 
 static int
-nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int dest, ip_addr gw, struct iface *iface)
+nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int dest, struct nexthop *nh)
 {
   eattr *ea;
   net *net = e->net;
   rta *a = e->attrs;
-  int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(a->nexthops);
+  int bufsize = 128 + KRT_METRICS_MAX*8 + nh_bufsize(&(a->nh));
   u32 priority = 0;
 
   struct {
@@ -1043,7 +1043,7 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int d
   if (ea = ea_find(eattrs, EA_KRT_SCOPE))
     r->r.rtm_scope = ea->u.data;
   else
-    r->r.rtm_scope = (dest == RTD_DEVICE) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
+    r->r.rtm_scope = (dest == RTD_UNICAST && ipa_zero(nh->gw)) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
 
   if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
     nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
@@ -1071,14 +1071,17 @@ dest:
   /* a->iface != NULL checked in krt_capable() for router and device routes */
   switch (dest)
     {
-    case RTD_ROUTER:
-      r->r.rtm_type = RTN_UNICAST;
-      nl_add_attr_u32(&r->h, rsize, RTA_OIF, iface->index);
-      nl_add_attr_ipa(&r->h, rsize, RTA_GATEWAY, gw);
-      break;
-    case RTD_DEVICE:
+    case RTD_UNICAST:
       r->r.rtm_type = RTN_UNICAST;
-      nl_add_attr_u32(&r->h, rsize, RTA_OIF, iface->index);
+      if (nh->next && !krt_ecmp6(p))
+       nl_add_multipath(&r->h, rsize, nh);
+      else
+      {
+       nl_add_attr_u32(&r->h, rsize, RTA_OIF, nh->iface->index);
+
+       if (ipa_nonzero(nh->gw))
+         nl_add_attr_ipa(&r->h, rsize, RTA_GATEWAY, nh->gw);
+      }
       break;
     case RTD_BLACKHOLE:
       r->r.rtm_type = RTN_BLACKHOLE;
@@ -1089,10 +1092,6 @@ dest:
     case RTD_PROHIBIT:
       r->r.rtm_type = RTN_PROHIBIT;
       break;
-    case RTD_MULTIPATH:
-      r->r.rtm_type = RTN_UNICAST;
-      nl_add_multipath(&r->h, rsize, a->nexthops);
-      break;
     case RTD_NONE:
       break;
     default:
@@ -1109,21 +1108,21 @@ nl_add_rte(struct krt_proto *p, rte *e, struct ea_list *eattrs)
   rta *a = e->attrs;
   int err = 0;
 
-  if (krt_ecmp6(p) && (a->dest == RTD_MULTIPATH))
+  if (krt_ecmp6(p) && a->nh.next)
   {
-    struct mpnh *nh = a->nexthops;
+    struct nexthop *nh = &(a->nh);
 
-    err = nl_send_route(p, e, eattrs, NL_OP_ADD, RTD_ROUTER, nh->gw, nh->iface);
+    err = nl_send_route(p, e, eattrs, NL_OP_ADD, RTD_UNICAST, nh);
     if (err < 0)
       return err;
 
     for (nh = nh->next; nh; nh = nh->next)
-      err += nl_send_route(p, e, eattrs, NL_OP_APPEND, RTD_ROUTER, nh->gw, nh->iface);
+      err += nl_send_route(p, e, eattrs, NL_OP_APPEND, RTD_UNICAST, nh);
 
     return err;
   }
 
-  return nl_send_route(p, e, eattrs, NL_OP_ADD, a->dest, a->gw, a->iface);
+  return nl_send_route(p, e, eattrs, NL_OP_ADD, a->dest, &(a->nh));
 }
 
 static inline int
@@ -1133,7 +1132,7 @@ nl_delete_rte(struct krt_proto *p, rte *e, struct ea_list *eattrs)
 
   /* For IPv6, we just repeatedly request DELETE until we get error */
   do
-    err = nl_send_route(p, e, eattrs, NL_OP_DELETE, RTD_NONE, IPA_NONE, NULL);
+    err = nl_send_route(p, e, eattrs, NL_OP_DELETE, RTD_NONE, NULL);
   while (krt_ecmp6(p) && !err);
 
   return err;
@@ -1168,10 +1167,10 @@ krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old, struct ea_list
 }
 
 
-static inline struct mpnh *
-nl_alloc_mpnh(struct nl_parse_state *s, ip_addr gw, struct iface *iface, byte weight)
+static inline struct nexthop *
+nl_alloc_nexthop(struct nl_parse_state *s, ip_addr gw, struct iface *iface, byte weight)
 {
-  struct mpnh *nh = lp_alloc(s->pool, sizeof(struct mpnh));
+  struct nexthop *nh = lp_alloc(s->pool, sizeof(struct nexthop));
 
   nh->gw = gw;
   nh->iface = iface;
@@ -1342,7 +1341,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
   if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type))
     nl_announce_route(s);
 
-  rta *ra = lp_allocz(s->pool, sizeof(rta));
+  rta *ra = lp_allocz(s->pool, sizeof(rta)); // TODO: fix alloc
   ra->src = p->p.main_source;
   ra->source = RTS_INHERIT;
   ra->scope = SCOPE_UNIVERSE;
@@ -1354,19 +1353,19 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
 
       if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET))
        {
-         ra->dest = RTD_MULTIPATH;
-         ra->nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH]);
-         if (!ra->nexthops)
+         struct nexthop *nh = nl_parse_multipath(p, a[RTA_MULTIPATH]);
+         if (!nh)
            {
              log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
              return;
            }
 
+         nexthop_link(ra, nh);
          break;
        }
 
-      ra->iface = if_find_by_index(oif);
-      if (!ra->iface)
+      ra->nh.iface = if_find_by_index(oif);
+      if (!ra->nh.iface)
        {
          log(L_ERR "KRT: Received route %N with unknown ifindex %u", net->n.addr, oif);
          return;
@@ -1374,28 +1373,23 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
 
       if (a[RTA_GATEWAY])
        {
-         ra->dest = RTD_ROUTER;
-         ra->gw = rta_get_ipa(a[RTA_GATEWAY]);
+         ra->nh.gw = rta_get_ipa(a[RTA_GATEWAY]);
 
          /* Silently skip strange 6to4 routes */
          const net_addr_ip6 sit = NET_ADDR_IP6(IP6_NONE, 96);
-         if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->gw, (net_addr *) &sit))
+         if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->nh.gw, (net_addr *) &sit))
            return;
 
          neighbor *nbr;
-         nbr = neigh_find2(&p->p, &ra->gw, ra->iface,
+         nbr = neigh_find2(&p->p, &ra->nh.gw, ra->nh.iface,
                            (i->rtm_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
          if (!nbr || (nbr->scope == SCOPE_HOST))
            {
-             log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr, ra->gw);
+             log(L_ERR "KRT: Received route %N with strange next-hop %I", net->n.addr,
+                  ra->nh.gw);
              return;
            }
        }
-      else
-       {
-         ra->dest = RTD_DEVICE;
-         def_scope = RT_SCOPE_LINK;
-       }
 
       break;
     case RTN_BLACKHOLE:
@@ -1510,13 +1504,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
     /* Merge next hops with the stored route */
     rta *a = s->attrs;
 
-    if (a->dest != RTD_MULTIPATH)
-    {
-      a->dest = RTD_MULTIPATH;
-      a->nexthops = nl_alloc_mpnh(s, a->gw, a->iface, 0);
-    }
-
-    mpnh_insert(&a->nexthops, nl_alloc_mpnh(s, ra->gw, ra->iface, 0));
+    nexthop_insert(&a->nh, &ra->nh);
   }
 }
 
index e899671df0d8c5bc3194c609d7b0a72303786b99..9f66d2f4c5fcd8681c3a0a46d34fa1d7205aeebc 100644 (file)
@@ -645,17 +645,11 @@ krt_same_dest(rte *k, rte *e)
 
   if (ka->dest != ea->dest)
     return 0;
-  switch (ka->dest)
-    {
-    case RTD_ROUTER:
-      return ipa_equal(ka->gw, ea->gw);
-    case RTD_DEVICE:
-      return !strcmp(ka->iface->name, ea->iface->name);
-    case RTD_MULTIPATH:
-      return mpnh_same(ka->nexthops, ea->nexthops);
-    default:
-      return 1;
-    }
+
+  if (ka->dest == RTD_UNICAST)
+    return nexthop_same(&(ka->nh), &(ea->nh));
+
+  return 1;
 }
 
 /*
@@ -1011,10 +1005,16 @@ krt_import_control(struct proto *P, rte **new, ea_list **attrs UNUSED, struct li
     return -1;
   }
 
-  if (!KRT_CF->devroutes &&
-      (e->attrs->dest == RTD_DEVICE) &&
-      (e->attrs->source != RTS_STATIC_DEVICE))
-    return -1;
+  if (!KRT_CF->devroutes && (e->attrs->source != RTS_STATIC_DEVICE))
+  {
+    struct nexthop *nh = &(e->attrs->nh);
+    for (; nh; nh = nh->next)
+      if (ipa_nonzero(nh->gw))
+       break;
+
+    if (!nh) /* Gone through all the nexthops and no explicit GW found */
+      return -1;
+  }
 
   if (!krt_capable(e))
     return -1;