diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2007-03-27 09:43:21 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2007-03-27 09:43:21 +0000 |
commit | 59623f9705b56e0d932234598ecd816984f345ad (patch) | |
tree | 7d60cbf1120bc9f6b0b9eb994fde69813fa8bea8 /usr.sbin/ospfd | |
parent | 325a5f79df48df0b7532f3514d05103b5b1ebd89 (diff) |
First step to make ospfd equal cost multipath aware.
Change kroute.c code so that it correctly tracks multipath routes.
Route redistribution is way more complex in a mulitpath environment.
It is not possible to redistribute multiple pathes and only a part of the
multipath routes may be redistributable. So a route is redistributed as soon
as one of the multipath routes is redistributable.
Tested and OK norby@ and pyr@ -- at least we see no regression
Diffstat (limited to 'usr.sbin/ospfd')
-rw-r--r-- | usr.sbin/ospfd/kroute.c | 285 |
1 files changed, 220 insertions, 65 deletions
diff --git a/usr.sbin/ospfd/kroute.c b/usr.sbin/ospfd/kroute.c index 40d93115f80..62ad54dde86 100644 --- a/usr.sbin/ospfd/kroute.c +++ b/usr.sbin/ospfd/kroute.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kroute.c,v 1.44 2007/03/13 17:01:15 claudio Exp $ */ +/* $OpenBSD: kroute.c,v 1.45 2007/03/27 09:43:20 claudio Exp $ */ /* * Copyright (c) 2004 Esben Norby <norby@openbsd.org> @@ -50,6 +50,7 @@ struct { struct kroute_node { RB_ENTRY(kroute_node) entry; struct kroute r; + struct kroute_node *next; }; struct kif_node { @@ -58,11 +59,14 @@ struct kif_node { struct kif k; }; -void kr_redistribute(int, struct kroute *); +void kr_redist_remove(struct kroute_node *, struct kroute_node *); +int kr_redist_eval(struct kroute *, struct rroute *); +void kr_redistribute(struct kroute_node *); int kroute_compare(struct kroute_node *, struct kroute_node *); int kif_compare(struct kif_node *, struct kif_node *); struct kroute_node *kroute_find(in_addr_t, u_int8_t); +struct kroute_node *kroute_matchgw(struct kroute_node *, struct in_addr); int kroute_insert(struct kroute_node *); int kroute_remove(struct kroute_node *); void kroute_clear(void); @@ -166,10 +170,17 @@ kr_change(struct kroute *kroute) action = RTM_CHANGE; else { /* a non-ospf route already exists. not a problem */ if (!(kr->r.flags & F_BGPD_INSERTED)) { - kr->r.flags |= F_OSPFD_INSERTED; + do { + kr->r.flags |= F_OSPFD_INSERTED; + kr = kr->next; + } while (kr); return (0); } /* + * XXX as long as there is no multipath support in + * bgpd this is safe else we end up in a bad situation. + */ + /* * ospf route has higher pref * - reset flags to the ospf ones * - use RTM_CHANGE @@ -223,7 +234,10 @@ kr_delete(struct kroute *kroute) if (kr->r.flags & F_KERNEL) { /* remove F_OSPFD_INSERTED flag, route still exists in kernel */ - kr->r.flags &= ~F_OSPFD_INSERTED; + do { + kr->r.flags &= ~F_OSPFD_INSERTED; + kr = kr->next; + } while (kr); return (0); } @@ -341,24 +355,33 @@ kr_ifinfo(char *ifname, pid_t pid) } void -kr_redistribute(int type, struct kroute *kr) +kr_redist_remove(struct kroute_node *kh, struct kroute_node *kn) { - u_int32_t a, metric = 0; - struct rroute rr; + struct rroute rr; - if (type == IMSG_NETWORK_DEL) { -dont_redistribute: - /* was the route redistributed? */ - if (kr->flags & F_REDISTRIBUTED) { - /* remove redistributed flag and inform the RDE */ - kr->flags &= ~F_REDISTRIBUTED; - rr.kr = *kr; - rr.metric = 0; - main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &rr, - sizeof(struct rroute)); - } + /* was the route redistributed? */ + if ((kn->r.flags & F_REDISTRIBUTED) == 0) return; - } + + /* remove redistributed flag */ + kn->r.flags &= ~F_REDISTRIBUTED; + rr.kr = kn->r; + rr.metric = 0; + + /* probably inform the RDE (check if no other path is redistributed) */ + for (kn = kh; kn; kn = kn->next) + if (kn->r.flags & F_REDISTRIBUTED) + break; + + if (kn == NULL) + main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &rr, + sizeof(struct rroute)); +} + +int +kr_redist_eval(struct kroute *kr, struct rroute *rr) +{ + u_int32_t a, metric = 0; /* Only non-ospfd routes are considered for redistribution. */ if (!(kr->flags & F_KERNEL)) @@ -390,28 +413,75 @@ dont_redistribute: if (!ospf_redistribute(kr, &metric)) goto dont_redistribute; - /* Does not matter if we resend the kr, the RDE will cope. */ + /* prefix should be redistributed */ kr->flags |= F_REDISTRIBUTED; + /* + * only on of all multipath routes can be redistributed so + * redistribute the best one. + */ + if (rr->metric > metric) { + rr->kr = *kr; + rr->metric = metric; + } + return (1); - rr.kr = *kr; - rr.metric = metric; - main_imsg_compose_rde(type, 0, &rr, sizeof(struct rroute)); +dont_redistribute: + /* was the route redistributed? */ + if ((kr->flags & F_REDISTRIBUTED) == 0) + return (0); + + kr->flags &= ~F_REDISTRIBUTED; + return (1); +} + +void +kr_redistribute(struct kroute_node *kh) +{ + struct kroute_node *kn; + struct rroute rr; + int redistribute = 0; + + bzero(&rr, sizeof(rr)); + for (kn = kh; kn; kn = kn->next) + if (kr_redist_eval(&kn->r, &rr)) + redistribute = 1; + + if (!redistribute) + return; + + if (rr.kr.flags & F_REDISTRIBUTED) { + main_imsg_compose_rde(IMSG_NETWORK_ADD, 0, &rr, + sizeof(struct rroute)); + } else { + rr.kr = kh->r; + main_imsg_compose_rde(IMSG_NETWORK_DEL, 0, &rr, + sizeof(struct rroute)); + } } void kr_reload(void) { - struct kroute_node *kr; + struct kroute_node *kr, *kn; u_int32_t dummy; int r; RB_FOREACH(kr, kroute_tree, &krt) { - r = ospf_redistribute(&kr->r, &dummy); - if (kr->r.flags & F_REDISTRIBUTED && !r) { - kr_redistribute(IMSG_NETWORK_DEL, &kr->r); - } else if (r) { - /* RIB will cope with duplicates */ - kr_redistribute(IMSG_NETWORK_ADD, &kr->r); + for (kn = kr; kn; kn = kn->next) { + r = ospf_redistribute(&kn->r, &dummy); + /* + * if it is redistributed, redistribute again metric + * may have changed. + */ + if ((kn->r.flags & F_REDISTRIBUTED && !r) || r) + break; + } + if (kn) { + /* + * kr_redistribute copes with removes and RDE with + * duplicates + */ + kr_redistribute(kr); } } } @@ -449,15 +519,40 @@ kroute_find(in_addr_t prefix, u_int8_t prefixlen) return (RB_FIND(kroute_tree, &krt, &s)); } +struct kroute_node * +kroute_matchgw(struct kroute_node *kr, struct in_addr nh) +{ + in_addr_t nexthop; + + nexthop = nh.s_addr; + + while (kr) { + if (kr->r.nexthop.s_addr == nexthop) + return (kr); + kr = kr->next; + } + + return (NULL); +} + + int kroute_insert(struct kroute_node *kr) { - if (RB_INSERT(kroute_tree, &krt, kr) != NULL) { - log_warnx("kroute_tree insert failed for %s/%u", - inet_ntoa(kr->r.prefix), kr->r.prefixlen); - free(kr); - return (-1); - } + struct kroute_node *krm; + + if ((krm = RB_INSERT(kroute_tree, &krt, kr)) != NULL) { + /* + * Multipath route, add at end of list and clone the + * ospfd inserted flag. + */ + kr->r.flags |= krm->r.flags & F_OSPFD_INSERTED; + while (krm->next != NULL) + krm = krm->next; + krm->next = kr; + kr->next = NULL; /* to be sure */ + } else + krm = kr; if (!(kr->r.flags & F_KERNEL)) { /* don't validate or redistribute ospf route */ @@ -470,21 +565,49 @@ kroute_insert(struct kroute_node *kr) else kr->r.flags |= F_DOWN; - kr_redistribute(IMSG_NETWORK_ADD, &kr->r); - + kr_redistribute(krm); return (0); } int kroute_remove(struct kroute_node *kr) { - if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) { - log_warnx("kroute_remove failed for %s/%u", + struct kroute_node *krm; + + if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) { + log_warnx("kroute_remove failed to find %s/%u", inet_ntoa(kr->r.prefix), kr->r.prefixlen); return (-1); } - kr_redistribute(IMSG_NETWORK_DEL, &kr->r); + if (krm == kr) { + /* head element */ + if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) { + log_warnx("kroute_remove failed for %s/%u", + inet_ntoa(kr->r.prefix), kr->r.prefixlen); + return (-1); + } + if (kr->next != NULL) { + if (RB_INSERT(kroute_tree, &krt, kr->next) != NULL) { + log_warnx("kroute_remove failed to add %s/%u", + inet_ntoa(kr->r.prefix), kr->r.prefixlen); + return (-1); + } + } + } else { + /* somewhere in the list */ + while (krm->next != kr && krm->next != NULL) + krm = krm->next; + if (krm->next == NULL) { + log_warnx("kroute_remove multipath list corrupted " + "for %s/%u", inet_ntoa(kr->r.prefix), + kr->r.prefixlen); + return (-1); + } + krm->next = kr->next; + } + + kr_redist_remove(krm, kr); rtlabel_unref(kr->r.rtlabel); free(kr); @@ -682,8 +805,7 @@ void if_change(u_short ifindex, int flags, struct if_data *ifd) { struct kif_node *kif; - struct kroute_node *kr; - int type; + struct kroute_node *kr, *tkr; u_int8_t reachable; if ((kif = kif_find(ifindex)) == NULL) { @@ -703,21 +825,24 @@ if_change(u_short ifindex, int flags, struct if_data *ifd) return; /* nothing changed wrt nexthop validity */ kif->k.nh_reachable = reachable; - type = reachable ? IMSG_NETWORK_ADD : IMSG_NETWORK_DEL; /* notify ospfe about interface link state */ main_imsg_compose_ospfe(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k)); /* update redistribute list */ - RB_FOREACH(kr, kroute_tree, &krt) - if (kr->r.ifindex == ifindex) { - if (reachable) - kr->r.flags &= ~F_DOWN; - else - kr->r.flags |= F_DOWN; + RB_FOREACH(kr, kroute_tree, &krt) { + for (tkr = kr; tkr != NULL; tkr = tkr->next) { + if (tkr->r.ifindex == ifindex) { + if (reachable) + tkr->r.flags &= ~F_DOWN; + else + tkr->r.flags |= F_DOWN; - kr_redistribute(type, &kr->r); + } } + kr_redistribute(kr); + } + } void @@ -861,11 +986,6 @@ fetchtable(void) if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ continue; -#ifdef RTF_MPATH - if (rtm->rtm_flags & RTF_MPATH) /* multipath */ - continue; -#endif - if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { log_warn("fetchtable"); free(buf); @@ -1064,10 +1184,10 @@ dispatch_rtmsg(void) struct sockaddr *sa, *rti_info[RTAX_MAX]; struct sockaddr_in *sa_in; struct sockaddr_rtlabel *label; - struct kroute_node *kr; + struct kroute_node *kr, *okr; struct in_addr prefix, nexthop; u_int8_t prefixlen; - int flags; + int flags, mpath; u_short ifindex = 0; if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) { @@ -1088,6 +1208,7 @@ dispatch_rtmsg(void) prefixlen = 0; flags = F_KERNEL; nexthop.s_addr = 0; + mpath = 0; if (rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE || rtm->rtm_type == RTM_DELETE) { @@ -1097,15 +1218,19 @@ dispatch_rtmsg(void) if (rtm->rtm_tableid != 0) continue; - if (rtm->rtm_pid == kr_state.pid) /* cause by us */ + if (rtm->rtm_pid == kr_state.pid) /* caused by us */ continue; - if (rtm->rtm_errno) /* failed attempts... */ + if (rtm->rtm_errno) /* failed attempts... */ continue; if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */ continue; +#ifdef RTF_MPATH + if (rtm->rtm_flags & RTF_MPATH) + mpath = 1; +#endif switch (sa->sa_family) { case AF_INET: prefix.s_addr = @@ -1155,10 +1280,26 @@ dispatch_rtmsg(void) continue; } - if ((kr = kroute_find(prefix.s_addr, prefixlen)) != + if ((okr = kroute_find(prefix.s_addr, prefixlen)) != NULL) { - /* ospf route overridden by kernel */ - /* pref is not checked because this is forced */ + /* just add new multipath routes */ + if (mpath && rtm->rtm_type == RTM_ADD) + goto add; + /* get the correct route */ + kr = okr; + if (mpath && (kr = kroute_matchgw(okr, + nexthop)) == NULL) { + log_warnx("dispatch_rtmsg mpath route" + " not found"); + /* add routes we missed out earlier */ + goto add; + } + + /* + * ospf route overridden by kernel. Preference + * of the route is not checked because this is + * forced -- most probably by a user. + */ if (kr->r.flags & F_OSPFD_INSERTED) flags |= F_OSPFD_INSERTED; if (kr->r.flags & F_REDISTRIBUTED) @@ -1180,8 +1321,9 @@ dispatch_rtmsg(void) kr->r.flags |= F_DOWN; /* just readd, the RDE will care */ - kr_redistribute(IMSG_NETWORK_ADD, &kr->r); + kr_redistribute(okr); } else { +add: if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) { log_warn("dispatch_rtmsg"); @@ -1207,7 +1349,20 @@ dispatch_rtmsg(void) continue; if (!(kr->r.flags & F_KERNEL)) continue; - if (kr->r.flags & F_OSPFD_INSERTED) + /* get the correct route */ + okr = kr; + if (mpath && + (kr = kroute_matchgw(kr, nexthop)) == NULL) { + log_warnx("dispatch_rtmsg mpath route" + " not found"); + return (-1); + } + /* + * last route is getting removed request the + * ospf route from the RDE to insert instead + */ + if (okr == kr && kr->next == NULL && + kr->r.flags & F_OSPFD_INSERTED) main_imsg_compose_rde(IMSG_KROUTE_GET, 0, &kr->r, sizeof(struct kroute)); if (kroute_remove(kr) == -1) |