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 *