Hi,

I noticed that the multipath support in OSPF seems to be fairly limited. Essentially I was only able to make it do multipath if I had two interfaces connecting to the same router. At my company, we need true multipath between multiple routers using a single interface. (If I needed the other, I could use LACP)

I am aware of the implications the default multipath implementation in Linux which operates on a per-packet basis, which is why we've patched our kernels to do it per-flow instead.

Anyway, I seemed to have managed to make multipath work as expected - at least in our setup. (Patch attached)

Essentially, I've hooked my multipath code into ri_install_ext() and ri_install_net(), where I add the equal routes if the routes share the same type, metrics and OSPF area. I realize that my add_nexthops() is /very/ similar to merge_nexthops() in functionality, but it seemed that the top_hash_entry() could be null, so I wrote a new method which did not rely on that - at the cost of more calls to copy_nexthop(), I guess.

Any thoughts?

Best regard
Peter Christensen
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index f509b89..b49abd5 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -12,6 +12,8 @@ static void add_cand(list * l, struct top_hash_entry *en,
 		     struct top_hash_entry *par, u32 dist,
 		     struct ospf_area *oa, int i);
 static void rt_sync(struct proto_ospf *po);
+static int
+cmp_nhs(struct mpnh *s1, struct mpnh *s2);
 
 /* In ospf_area->rtr we store paths to routers, but we use RID (and not IP address)
    as index, so we need to encapsulate RID to IP address */
@@ -64,6 +66,71 @@ copy_nexthop(struct proto_ospf *po, struct mpnh *src)
   return nh;
 }
 
+static struct mpnh *
+add_nexthops(struct proto_ospf *po, struct mpnh *old, struct mpnh *new)
+{
+  struct mpnh *ret = old;
+  struct mpnh **pnh = &ret;
+
+  if (old == NULL || new == NULL)
+    return old;
+
+  int count = po->ecmp;
+  struct mpnh *nh;
+  while (new != NULL && count--)
+  {
+    nh = *pnh;
+    if (nh == NULL)
+    {
+      /* Add next hop to end of list */
+      *pnh = nh = copy_nexthop(po, new);
+
+      pnh = &nh->next;
+      new = new->next;
+    }
+    else
+    {
+      int cmp = cmp_nhs(nh, new);
+      if (cmp < 0)
+      {
+	/* Continue to next nexthop */
+	pnh = &nh->next;
+      }
+      else if (cmp > 0)
+      {
+	/* Add nexthop before current nexthop */
+	struct mpnh *next;
+
+	*pnh = next = copy_nexthop(po, new);
+	next->next = nh;
+
+	pnh = &next->next;
+	new = new->next;
+      }
+      else /* if (cmp == 0) */
+      {
+	/* Do not add identical route */
+	pnh = &nh->next;
+	new = new->next;
+      }
+    }
+  }
+
+  *pnh = NULL;
+
+  return ret;
+}
+
+/* If new is equal in cost to old return 1 */
+static int
+ri_equal_cost(const orta *old, const orta *new)
+{
+  /* 16.8. - Each one of the multiple routes will be of the same type, cost, and will have the same associated area */
+  if (old->type == new->type && old->metric1 == new->metric1 && old->metric2 == new->metric2 && old->oa == new->oa)
+    return 1;
+  else
+    return 0;
+}
 
 /* If new is better return 1 */
 static int
@@ -212,6 +279,14 @@ ri_install_net(struct proto_ospf *po, ip_addr prefix, int pxlen, orta *new)
   ort *old = (ort *) fib_get(&po->rtf, &prefix, pxlen);
   if (ri_better(po, new, &old->n))
     memcpy(&old->n, new, sizeof(orta));
+  else if (po->ecmp && ri_equal_cost(&old->n, new) && old->n.nhs && new->nhs)
+  {
+    DBG("Adding gateway %R to existing route %R/%i\n",
+	new->nhs->gw,
+	prefix,
+	pxlen);
+    old->n.nhs = add_nexthops(po, old->n.nhs, new->nhs);
+  }
 }
 
 static inline void
@@ -237,6 +312,14 @@ ri_install_ext(struct proto_ospf *po, ip_addr prefix, int pxlen, orta *new)
   ort *old = (ort *) fib_get(&po->rtf, &prefix, pxlen);
   if (ri_better_ext(po, new, &old->n))
     memcpy(&old->n, new, sizeof(orta));
+  else if (po->ecmp && ri_equal_cost(&old->n, new) && old->n.nhs && new->nhs)
+  {
+    DBG("Adding gateway %R to existing external route %R/%i\n",
+	new->nhs->gw,
+	prefix,
+	pxlen);
+    old->n.nhs = add_nexthops(po, old->n.nhs, new->nhs);
+  }
 }
 
 static inline struct ospf_iface *

Reply via email to