Here is a patch adapted from ospfd patch of "Tue Sep 25 11:25:41 2007
UTC" (the one of version 1.52 of kroute.c):
<<
Last missing piece in the equal cost multipath support for ospfd.
Send all possible nexthops to the parent process and correctly sync
the RIB, FIB and kernel routing table. Based on initial work by pyr@.
OK pyr@ norby@
PS: don't forget that you need to enable multipath support via a sysctl
>>
It seems to solve my problem but not "perfectly".
When starting ospf6d with the best link between 2 hosts down, fib contains
2 other routes comme from 2 other hosts (these 2 routes have equal cost).
When the link came UP, these 2 routes are removed and replaced by the best
route; that's alright.
Next when the link goes down, the 2 alternative routes are well added in fib
but the precedent best route is still in the fib (I see it with route -n -v
show |grep TargetIP). May be an important point: TargetIP is an IPv6 on lo1.
I can't find why; if you have any idea... I have a test network so I can make
test easily...
Manuel
===== The Patch =====
diff -u ospf6d.uptodate/kroute.c ospf6d.patch1/kroute.c
--- ospf6d.uptodate/kroute.c Thu Sep 20 15:25:33 2012
+++ ospf6d.patch1/kroute.c Thu Sep 27 18:01:37 2012
@@ -59,6 +59,8 @@
int kr_redist_eval(struct kroute *, struct rroute *);
void kr_redistribute(struct kroute_node *);
int kroute_compare(struct kroute_node *, struct kroute_node *);
+int kr_change_fib(struct kroute_node *, struct kroute *, int, int);
+int kr_delete_fib(struct kroute_node *);
struct kroute_node *kroute_find(const struct in6_addr *, u_int8_t);
struct kroute_node *kroute_matchgw(struct kroute_node *,
@@ -140,18 +142,102 @@
}
int
-kr_change(struct kroute *kroute)
+kr_change_fib(struct kroute_node *kr, struct kroute *kroute, int krcount,
+ int action)
{
+ int i;
+ struct kroute_node *kn, *nkn;
+
+ if (action == RTM_ADD) {
+ /*
+ * First remove all stale multipath routes.
+ * This step must be skipped when the action is RTM_CHANGE
+ * because it is already a single path route that will be
+ * changed.
+ */
+ for (kn = kr; kn != NULL; kn = nkn) {
+ for (i = 0; i < krcount; i++) {
+ if
(IN6_ARE_ADDR_EQUAL(&kn->r.nexthop,&kroute[i].nexthop))
+ break;
+ }
+ nkn = kn->next;
+ if (i == krcount)
+ /* stale route */
+ if (kr_delete_fib(kn) == -1)
+ log_warnx("kr_delete_fib failed");
+ log_debug("kr_update_fib: before: %s%s",
+ log_in6addr(&kn->r.nexthop),
+ i == krcount ? " (deleted)" : "");
+ }
+ }
+
+ /*
+ * now add or change the route
+ */
+ for (i = 0; i < krcount; i++) {
+ /* nexthop within 127/8 -> ignore silently */
+ if (kr && IN6_IS_ADDR_LOOPBACK(&kr->r.nexthop))
+ continue;
+
+ if (action == RTM_ADD && kr) {
+ for (kn = kr; kn != NULL; kn = kn->next) {
+ if
(IN6_ARE_ADDR_EQUAL(&kn->r.nexthop,&kroute[i].nexthop))
+ break;
+ }
+
+ log_debug("kr_update_fib: after : %s%s",
+ log_in6addr(&kroute[i].nexthop),
+ kn == NULL ? " (added)" : "");
+
+ if (kn != NULL)
+ /* nexthop already present, skip it */
+ continue;
+ } else
+ /* modify first entry */
+ kn = kr;
+
+ /* send update */
+ if (send_rtmsg(kr_state.fd, action, &kroute[i]) == -1)
+ return (-1);
+
+ /* create new entry unless we are changing the first entry */
+ if (action == RTM_ADD)
+ if ((kn = calloc(1, sizeof(*kn))) == NULL)
+ fatal(NULL);
+
+ kn->r.prefix = kroute[i].prefix;
+ kn->r.prefixlen = kroute[i].prefixlen;
+ kn->r.nexthop = kroute[i].nexthop;
+ kn->r.scope = kroute[i].scope;
+ kn->r.flags = kroute[i].flags | F_OSPFD_INSERTED;
+ kn->r.ext_tag = kroute[i].ext_tag;
+ rtlabel_unref(kn->r.rtlabel); /* for RTM_CHANGE */
+ kn->r.rtlabel = kroute[i].rtlabel;
+ if (action == RTM_ADD) {
+ if (kroute_insert(kn) == -1) {
+ log_debug("kr_update_fib: cannot insert %s",
+ log_in6addr(&kn->r.nexthop));
+ free(kn);
+ }
+ }
+ action = RTM_ADD;
+ }
+ return (0);
+}
+
+int
+kr_change(struct kroute *kroute, int krcount)
+{
struct kroute_node *kr;
int action = RTM_ADD;
kroute->rtlabel = rtlabel_tag2id(kroute->ext_tag);
- if ((kr = kroute_find(&kroute->prefix, kroute->prefixlen)) !=
- NULL) {
- if (!(kr->r.flags & F_KERNEL))
- action = RTM_CHANGE;
- else { /* a non-ospf route already exists. not a problem */
+ kr = kroute_find(&kroute->prefix, kroute->prefixlen);
+
+ if (kr != NULL) {
+ if (kr->r.flags & F_KERNEL) {
+ /* a non-ospf route already exists. not a problem */
if (!(kr->r.flags & F_BGPD_INSERTED)) {
do {
kr->r.flags |= F_OSPFD_INSERTED;
@@ -170,79 +256,43 @@
* - zero out ifindex (this is no longer relevant)
*/
action = RTM_CHANGE;
- kr->r.flags = kroute->flags | F_OSPFD_INSERTED;
- kr->r.ifindex = 0;
- rtlabel_unref(kr->r.rtlabel);
- kr->r.ext_tag = kroute->ext_tag;
- kr->r.rtlabel = kroute->rtlabel;
- }
+ } else if (kr->next == NULL) /* single path OSPF route */
+ action = RTM_CHANGE;
}
- /* nexthop within 127/8 -> ignore silently */
- if (kr && IN6_IS_ADDR_LOOPBACK(&kr->r.nexthop))
- return (0);
+ return (kr_change_fib(kr, kroute, krcount, action));
+}
- /*
- * Ingnore updates that did not change the route.
- * Currently only the nexthop can change.
- */
- if (kr && kr->r.scope == kroute->scope &&
- IN6_ARE_ADDR_EQUAL(&kr->r.nexthop, &kroute->nexthop))
+int
+kr_delete_fib(struct kroute_node *kr)
+{
+ if (!(kr->r.flags & F_OSPFD_INSERTED))
return (0);
- if (send_rtmsg(kr_state.fd, action, kroute) == -1)
+ if (send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r) == -1)
return (-1);
- if (action == RTM_ADD) {
- if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
- log_warn("kr_change");
- return (-1);
- }
- kr->r.prefix = kroute->prefix;
- kr->r.prefixlen = kroute->prefixlen;
- kr->r.nexthop = kroute->nexthop;
- kr->r.scope = kroute->scope;
- kr->r.flags = kroute->flags | F_OSPFD_INSERTED;
- kr->r.ext_tag = kroute->ext_tag;
- kr->r.rtlabel = kroute->rtlabel;
+ if (kroute_remove(kr) == -1)
+ return (-1);
- if (kroute_insert(kr) == -1)
- free(kr);
- } else if (kr) {
- kr->r.nexthop = kroute->nexthop;
- kr->r.scope = kroute->scope;
- }
-
return (0);
}
int
kr_delete(struct kroute *kroute)
{
- struct kroute_node *kr;
+ struct kroute_node *kr, *nkr;
if ((kr = kroute_find(&kroute->prefix, kroute->prefixlen)) ==
NULL)
return (0);
- if (!(kr->r.flags & F_OSPFD_INSERTED))
- return (0);
-
- if (kr->r.flags & F_KERNEL) {
- /* remove F_OSPFD_INSERTED flag, route still exists in kernel */
- do {
- kr->r.flags &= ~F_OSPFD_INSERTED;
- kr = kr->next;
- } while (kr);
- return (0);
+ while (kr != NULL) {
+ nkr = kr->next;
+ if (kr_delete_fib(kr) == -1)
+ return (-1);
+ kr = nkr;
}
-
- if (send_rtmsg(kr_state.fd, RTM_DELETE, kroute) == -1)
- return (-1);
-
- if (kroute_remove(kr) == -1)
- return (-1);
-
return (0);
}
@@ -257,6 +307,7 @@
kr_fib_couple(void)
{
struct kroute_node *kr;
+ struct kroute_node *kn;
if (kr_state.fib_sync == 1) /* already coupled */
return;
@@ -265,7 +316,9 @@
RB_FOREACH(kr, kroute_tree, &krt)
if (!(kr->r.flags & F_KERNEL))
- send_rtmsg(kr_state.fd, RTM_ADD, &kr->r);
+ for (kn = kr; kn != NULL; kn = kn->next) {
+ send_rtmsg(kr_state.fd, RTM_ADD, &kn->r);
+ }
log_info("kernel routing table coupled");
}
@@ -274,13 +327,16 @@
kr_fib_decouple(void)
{
struct kroute_node *kr;
+ struct kroute_node *kn;
if (kr_state.fib_sync == 0) /* already decoupled */
return;
RB_FOREACH(kr, kroute_tree, &krt)
if (!(kr->r.flags & F_KERNEL))
- send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
+ for (kn = kr; kn != NULL; kn = kn->next) {
+ send_rtmsg(kr_state.fd, RTM_DELETE, &kn->r);
+ }
kr_state.fib_sync = 0;
@@ -1062,7 +1118,7 @@
bzero(&hdr, sizeof(hdr));
hdr.rtm_version = RTM_VERSION;
hdr.rtm_type = action;
- hdr.rtm_flags = RTF_UP;
+ hdr.rtm_flags = RTF_UP|RTF_MPATH;
hdr.rtm_priority = RTP_OSPF;
if (action == RTM_CHANGE)
hdr.rtm_fmask = RTF_REJECT|RTF_BLACKHOLE;
diff -u ospf6d.uptodate/ospf6d.c ospf6d.patch1/ospf6d.c
--- ospf6d.uptodate/ospf6d.c Thu Sep 20 15:25:33 2012
+++ ospf6d.patch1/ospf6d.c Thu Sep 20 22:27:10 2012
@@ -422,7 +422,7 @@
struct imsgbuf *ibuf = &iev->ibuf;
struct imsg imsg;
ssize_t n;
- int shut = 0;
+ int count, shut = 0;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1)
@@ -444,7 +444,9 @@
switch (imsg.hdr.type) {
case IMSG_KROUTE_CHANGE:
- if (kr_change(imsg.data))
+ count = (imsg.hdr.len - IMSG_HEADER_SIZE) /
+ sizeof(struct kroute);
+ if (kr_change(imsg.data,count))
log_warn("main_dispatch_rde: error changing "
"route");
break;
diff -u ospf6d.uptodate/ospf6d.h ospf6d.patch1/ospf6d.h
--- ospf6d.uptodate/ospf6d.h Thu Sep 20 15:25:33 2012
+++ ospf6d.patch1/ospf6d.h Thu Sep 20 22:27:10 2012
@@ -526,7 +526,7 @@
/* kroute.c */
int kr_init(int);
-int kr_change(struct kroute *);
+int kr_change(struct kroute *, int);
int kr_delete(struct kroute *);
void kr_shutdown(void);
void kr_fib_couple(void);
diff -u ospf6d.uptodate/rde.c ospf6d.patch1/rde.c
--- ospf6d.uptodate/rde.c Thu Sep 20 15:25:33 2012
+++ ospf6d.patch1/rde.c Thu Sep 27 17:51:59 2012
@@ -869,28 +869,36 @@
void
rde_send_change_kroute(struct rt_node *r)
{
+ int krcount = 0;
struct kroute kr;
struct rt_nexthop *rn;
+ struct ibuf *wbuf;
- TAILQ_FOREACH(rn, &r->nexthop, entry) {
- if (!rn->invalid)
- break;
+ if ((wbuf = imsg_create(&iev_main->ibuf, IMSG_KROUTE_CHANGE, 0, 0,
+ sizeof(kr))) == NULL) {
+ return;
}
- if (!rn)
- fatalx("rde_send_change_kroute: no valid nexthop found");
- bzero(&kr, sizeof(kr));
- kr.prefix = r->prefix;
- kr.nexthop = rn->nexthop;
- if (IN6_IS_ADDR_LINKLOCAL(&rn->nexthop) ||
- IN6_IS_ADDR_MC_LINKLOCAL(&rn->nexthop))
- kr.scope = rn->ifindex;
- kr.ifindex = rn->ifindex;
- kr.prefixlen = r->prefixlen;
- kr.ext_tag = r->ext_tag;
+ TAILQ_FOREACH(rn, &r->nexthop, entry) {
+ if (rn->invalid)
+ continue;
+ krcount++;
- imsg_compose_event(iev_main, IMSG_KROUTE_CHANGE, 0, 0, -1,
- &kr, sizeof(kr));
+ bzero(&kr, sizeof(kr));
+ kr.prefix = r->prefix;
+ kr.nexthop = rn->nexthop;
+ if (IN6_IS_ADDR_LINKLOCAL(&rn->nexthop) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&rn->nexthop))
+ kr.scope = rn->ifindex;
+ kr.ifindex = rn->ifindex;
+ kr.prefixlen = r->prefixlen;
+ kr.ext_tag = r->ext_tag;
+ imsg_add(wbuf, &kr, sizeof(kr));
+ }
+ if (krcount == 0)
+ fatalx("rde_send_change_kroute: no valid nexthop found");
+ imsg_close(&iev_main->ibuf, wbuf);
+ imsg_event_add(iev_main);
}
void