summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.sbin/bgpd/bgpd.h4
-rw-r--r--usr.sbin/bgpd/kroute.c373
2 files changed, 297 insertions, 80 deletions
diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h
index 39f4d781bbc..fe7b77287b4 100644
--- a/usr.sbin/bgpd/bgpd.h
+++ b/usr.sbin/bgpd/bgpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: bgpd.h,v 1.222 2008/01/23 08:11:32 claudio Exp $ */
+/* $OpenBSD: bgpd.h,v 1.223 2008/12/12 23:15:12 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -424,6 +424,7 @@ struct kroute {
u_int16_t labelid;
u_short ifindex;
u_int8_t prefixlen;
+ u_int8_t priority;
};
struct kroute6 {
@@ -433,6 +434,7 @@ struct kroute6 {
u_int16_t labelid;
u_short ifindex;
u_int8_t prefixlen;
+ u_int8_t priority;
};
struct kroute_nexthop {
diff --git a/usr.sbin/bgpd/kroute.c b/usr.sbin/bgpd/kroute.c
index 22f544f91dc..40b5d60ea44 100644
--- a/usr.sbin/bgpd/kroute.c
+++ b/usr.sbin/bgpd/kroute.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kroute.c,v 1.161 2008/11/21 17:41:22 claudio Exp $ */
+/* $OpenBSD: kroute.c,v 1.162 2008/12/12 23:15:12 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -48,11 +48,13 @@ struct {
struct kroute_node {
RB_ENTRY(kroute_node) entry;
struct kroute r;
+ struct kroute_node *next;
};
struct kroute6_node {
RB_ENTRY(kroute6_node) entry;
struct kroute6 r;
+ struct kroute6_node *next;
};
struct knexthop_node {
@@ -88,12 +90,17 @@ int kroute6_compare(struct kroute6_node *, struct kroute6_node *);
int knexthop_compare(struct knexthop_node *, struct knexthop_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_find(in_addr_t, u_int8_t, u_int8_t);
+struct kroute_node *kroute_matchgw(struct kroute_node *,
+ struct sockaddr_in *);
int kroute_insert(struct kroute_node *);
int kroute_remove(struct kroute_node *);
void kroute_clear(void);
-struct kroute6_node *kroute6_find(const struct in6_addr *, u_int8_t);
+struct kroute6_node *kroute6_find(const struct in6_addr *, u_int8_t,
+ u_int8_t);
+struct kroute6_node *kroute6_matchgw(struct kroute6_node *,
+ struct sockaddr_in6 *);
int kroute6_insert(struct kroute6_node *);
int kroute6_remove(struct kroute6_node *);
void kroute6_clear(void);
@@ -165,8 +172,8 @@ kr_init(int fs, u_int rtableid)
int opt = 0, rcvbuf, default_rcvbuf;
socklen_t optlen;
- kr_state.fib_sync = fs;
kr_state.rtableid = rtableid;
+ kr_state.fib_sync = fs;
if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
log_warn("kr_init: socket");
@@ -220,13 +227,9 @@ kr_change(struct kroute_label *kl)
struct kroute_node *kr;
int action = RTM_ADD;
- if ((kr = kroute_find(kl->kr.prefix.s_addr, kl->kr.prefixlen)) !=
- NULL) {
- if (kr->r.flags & F_BGPD_INSERTED)
- action = RTM_CHANGE;
- else /* a non-bgp route already exists. not a problem */
- return (0);
- }
+ if ((kr = kroute_find(kl->kr.prefix.s_addr, kl->kr.prefixlen, RTP_BGP))
+ != NULL)
+ action = RTM_CHANGE;
/* nexthop within 127/8 -> ignore silently */
if ((kl->kr.nexthop.s_addr & htonl(IN_CLASSA_NET)) ==
@@ -253,6 +256,7 @@ kr_change(struct kroute_label *kl)
kr->r.prefixlen = kl->kr.prefixlen;
kr->r.nexthop.s_addr = kl->kr.nexthop.s_addr;
kr->r.flags = kl->kr.flags | F_BGPD_INSERTED;
+ kr->r.priority = RTP_BGP;
kr->r.labelid = kl->kr.labelid;
if (kroute_insert(kr) == -1)
@@ -278,8 +282,8 @@ kr_delete(struct kroute_label *kl)
{
struct kroute_node *kr;
- if ((kr = kroute_find(kl->kr.prefix.s_addr, kl->kr.prefixlen)) ==
- NULL)
+ if ((kr = kroute_find(kl->kr.prefix.s_addr, kl->kr.prefixlen, RTP_BGP))
+ == NULL)
return (0);
if (!(kr->r.flags & F_BGPD_INSERTED))
@@ -308,12 +312,9 @@ kr6_change(struct kroute6_label *kl)
int action = RTM_ADD;
struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT;
- if ((kr6 = kroute6_find(&kl->kr.prefix, kl->kr.prefixlen)) != NULL) {
- if (kr6->r.flags & F_BGPD_INSERTED)
- action = RTM_CHANGE;
- else /* a non-bgp route already exists. not a problem */
- return (0);
- }
+ if ((kr6 = kroute6_find(&kl->kr.prefix, kl->kr.prefixlen, RTP_BGP))
+ != NULL)
+ action = RTM_CHANGE;
/* nexthop to loopback -> ignore silently */
if (IN6_IS_ADDR_LOOPBACK(&kl->kr.nexthop))
@@ -341,6 +342,7 @@ kr6_change(struct kroute6_label *kl)
memcpy(&kr6->r.nexthop, &kl->kr.nexthop,
sizeof(struct in6_addr));
kr6->r.flags = kl->kr.flags | F_BGPD_INSERTED;
+ kr6->r.priority = RTP_BGP;
kr6->r.labelid = kl->kr.labelid;
if (kroute6_insert(kr6) == -1)
@@ -367,7 +369,8 @@ kr6_delete(struct kroute6_label *kl)
{
struct kroute6_node *kr6;
- if ((kr6 = kroute6_find(&kl->kr.prefix, kl->kr.prefixlen)) == NULL)
+ if ((kr6 = kroute6_find(&kl->kr.prefix, kl->kr.prefixlen, RTP_BGP))
+ == NULL)
return (0);
if (!(kr6->r.flags & F_BGPD_INSERTED))
@@ -510,8 +513,8 @@ kr_nexthop_delete(struct bgpd_addr *addr)
void
kr_show_route(struct imsg *imsg)
{
- struct kroute_node *kr;
- struct kroute6_node *kr6;
+ struct kroute_node *kr, *kn;
+ struct kroute6_node *kr6, *kn6;
struct bgpd_addr *addr;
int flags;
sa_family_t af;
@@ -531,16 +534,26 @@ kr_show_route(struct imsg *imsg)
memcpy(&af, (char *)imsg->data + sizeof(flags), sizeof(af));
if (!af || af == AF_INET)
RB_FOREACH(kr, kroute_tree, &krt)
- if (!flags || kr->r.flags & flags)
- send_imsg_session(IMSG_CTL_KROUTE,
- imsg->hdr.pid, &kr->r,
- sizeof(kr->r));
+ if (!flags || kr->r.flags & flags) {
+ kn = kr;
+ do {
+ send_imsg_session(
+ IMSG_CTL_KROUTE,
+ imsg->hdr.pid, &kn->r,
+ sizeof(kn->r));
+ } while ((kn = kn->next) != NULL);
+ }
if (!af || af == AF_INET6)
RB_FOREACH(kr6, kroute6_tree, &krt6)
- if (!flags || kr6->r.flags & flags)
- send_imsg_session(IMSG_CTL_KROUTE6,
- imsg->hdr.pid, &kr6->r,
- sizeof(kr6->r));
+ if (!flags || kr6->r.flags & flags) {
+ kn6 = kr6;
+ do {
+ send_imsg_session(
+ IMSG_CTL_KROUTE6,
+ imsg->hdr.pid, &kn6->r,
+ sizeof(kn6->r));
+ } while ((kn6 = kn6->next) != NULL);
+ }
break;
case IMSG_CTL_KROUTE_ADDR:
if (imsg->hdr.len != IMSG_HEADER_SIZE +
@@ -792,6 +805,14 @@ kroute_compare(struct kroute_node *a, struct kroute_node *b)
return (-1);
if (a->r.prefixlen > b->r.prefixlen)
return (1);
+
+ /* if the priority is RTP_ANY finish on the first address hit */
+ if (a->r.priority == RTP_ANY || b->r.priority == RTP_ANY)
+ return (0);
+ if (a->r.priority < b->r.priority)
+ return (-1);
+ if (a->r.priority > b->r.priority)
+ return (1);
return (0);
}
@@ -811,6 +832,14 @@ kroute6_compare(struct kroute6_node *a, struct kroute6_node *b)
return (-1);
if (a->r.prefixlen > b->r.prefixlen)
return (1);
+
+ /* if the priority is RTP_ANY finish on the first address hit */
+ if (a->r.priority == RTP_ANY || b->r.priority == RTP_ANY)
+ return (0);
+ if (a->r.priority < b->r.priority)
+ return (-1);
+ if (a->r.priority > b->r.priority)
+ return (1);
return (0);
}
@@ -854,27 +883,65 @@ kif_compare(struct kif_node *a, struct kif_node *b)
*/
struct kroute_node *
-kroute_find(in_addr_t prefix, u_int8_t prefixlen)
+kroute_find(in_addr_t prefix, u_int8_t prefixlen, u_int8_t prio)
{
struct kroute_node s;
+ struct kroute_node *kn, *tmp;
s.r.prefix.s_addr = prefix;
s.r.prefixlen = prefixlen;
+ s.r.priority = prio;
+
+ kn = RB_FIND(kroute_tree, &krt, &s);
+ if (kn && prio == RTP_ANY) {
+ tmp = RB_PREV(kroute_tree, &krt, kn);
+ while (tmp) {
+ if (kroute_compare(&s, tmp) == 0)
+ kn = tmp;
+ else
+ break;
+ tmp = RB_PREV(kroute_tree, &krt, kn);
+ }
+ }
+ return (kn);
+}
+
+struct kroute_node *
+kroute_matchgw(struct kroute_node *kr, struct sockaddr_in *sa_in)
+{
+ in_addr_t nexthop;
- return (RB_FIND(kroute_tree, &krt, &s));
+ if (sa_in == NULL) {
+ log_warnx("kroute_matchgw: no nexthop defined");
+ return (NULL);
+ }
+ nexthop = sa_in->sin_addr.s_addr;
+log_debug("kroute_matchgw: prefix %s/%u", inet_ntoa(kr->r.prefix), kr->r.prefixlen);
+log_debug("kroute_matchgw: nexthop = %s", inet_ntoa(sa_in->sin_addr));
+
+ while (kr) {
+log_debug("kroute_matchgw: kr->r.nexthop = %s", inet_ntoa(kr->r.nexthop));
+ if (kr->r.nexthop.s_addr == nexthop)
+ return (kr);
+ kr = kr->next;
+ }
+
+ return (NULL);
}
int
kroute_insert(struct kroute_node *kr)
{
+ struct kroute_node *krm;
struct knexthop_node *h;
in_addr_t mask, ina;
- 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);
+ if ((krm = RB_INSERT(kroute_tree, &krt, kr)) != NULL) {
+ /* multipath route, add at end of list */
+ while (krm->next != NULL)
+ krm = krm->next;
+ krm->next = kr;
+ kr->next = NULL; /* to be sure */
}
if (kr->r.flags & F_KERNEL) {
@@ -889,29 +956,61 @@ kroute_insert(struct kroute_node *kr)
if (kif_kr_insert(kr) == -1)
return (-1);
- kr_redistribute(IMSG_NETWORK_ADD, &kr->r);
+ if (krm == NULL)
+ /* redistribute multipath routes only once */
+ kr_redistribute(IMSG_NETWORK_ADD, &kr->r);
}
return (0);
}
+
int
kroute_remove(struct kroute_node *kr)
{
+ struct kroute_node *krm;
struct knexthop_node *s;
- if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) {
- log_warnx("kroute_remove failed for %s/%u",
+ 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);
}
+ 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;
+ }
+
/* check whether a nexthop depends on this kroute */
if ((kr->r.flags & F_KERNEL) && (kr->r.flags & F_NEXTHOP))
RB_FOREACH(s, knexthop_tree, &knt)
if (s->kroute == kr)
knexthop_validate(s);
- if (kr->r.flags & F_KERNEL)
+ if (kr->r.flags & F_KERNEL && kr == krm && kr->next == NULL)
+ /* again remove only once */
kr_redistribute(IMSG_NETWORK_REMOVE, &kr->r);
if (kr->r.flags & F_CONNECTED)
@@ -934,27 +1033,62 @@ kroute_clear(void)
}
struct kroute6_node *
-kroute6_find(const struct in6_addr *prefix, u_int8_t prefixlen)
+kroute6_find(const struct in6_addr *prefix, u_int8_t prefixlen, u_int8_t prio)
{
struct kroute6_node s;
+ struct kroute6_node *kn6, *tmp;
memcpy(&s.r.prefix, prefix, sizeof(struct in6_addr));
s.r.prefixlen = prefixlen;
+ s.r.priority = prio;
+
+ kn6 = RB_FIND(kroute6_tree, &krt6, &s);
+ if (kn6 && prio == RTP_ANY) {
+ tmp = RB_PREV(kroute6_tree, &krt6, kn6);
+ while (tmp) {
+ if (kroute6_compare(&s, tmp) == 0)
+ kn6 = tmp;
+ else
+ break;
+ tmp = RB_PREV(kroute6_tree, &krt6, kn6);
+ }
+ }
+ return (kn6);
+}
+
+struct kroute6_node *
+kroute6_matchgw(struct kroute6_node *kr, struct sockaddr_in6 *sa_in6)
+{
+ struct in6_addr nexthop;
+
+ if (sa_in6 == NULL) {
+ log_warnx("kroute6_matchgw: no nexthop defined");
+ return (NULL);
+ }
+ memcpy(&nexthop, &sa_in6->sin6_addr, sizeof(nexthop));
- return (RB_FIND(kroute6_tree, &krt6, &s));
+ while (kr) {
+ if (memcmp(&kr->r.nexthop, &nexthop, sizeof(nexthop)) == NULL)
+ return (kr);
+ kr = kr->next;
+ }
+
+ return (NULL);
}
int
kroute6_insert(struct kroute6_node *kr)
{
+ struct kroute6_node *krm;
struct knexthop_node *h;
struct in6_addr ina, inb;
- if (RB_INSERT(kroute6_tree, &krt6, kr) != NULL) {
- log_warnx("kroute_tree insert failed for %s/%u",
- log_in6addr(&kr->r.prefix), kr->r.prefixlen);
- free(kr);
- return (-1);
+ if ((krm = RB_INSERT(kroute6_tree, &krt6, kr)) != NULL) {
+ /* multipath route, add at end of list */
+ while (krm->next != NULL)
+ krm = krm->next;
+ krm->next = kr;
+ kr->next = NULL; /* to be sure */
}
if (kr->r.flags & F_KERNEL) {
@@ -971,7 +1105,9 @@ kroute6_insert(struct kroute6_node *kr)
if (kif_kr6_insert(kr) == -1)
return (-1);
- kr_redistribute6(IMSG_NETWORK_ADD, &kr->r);
+ if (krm == NULL)
+ /* redistribute multipath routes only once */
+ kr_redistribute6(IMSG_NETWORK_ADD, &kr->r);
}
return (0);
@@ -980,21 +1116,51 @@ kroute6_insert(struct kroute6_node *kr)
int
kroute6_remove(struct kroute6_node *kr)
{
+ struct kroute6_node *krm;
struct knexthop_node *s;
- if (RB_REMOVE(kroute6_tree, &krt6, kr) == NULL) {
- log_warnx("kroute_remove failed for %s/%u",
+ if ((krm = RB_FIND(kroute6_tree, &krt6, kr)) == NULL) {
+ log_warnx("kroute6_remove failed for %s/%u",
log_in6addr(&kr->r.prefix), kr->r.prefixlen);
return (-1);
}
+ if (krm == kr) {
+ /* head element */
+ if (RB_REMOVE(kroute6_tree, &krt6, kr) == NULL) {
+ log_warnx("kroute6_remove failed for %s/%u",
+ log_in6addr(&kr->r.prefix), kr->r.prefixlen);
+ return (-1);
+ }
+ if (kr->next != NULL) {
+ if (RB_INSERT(kroute6_tree, &krt6, kr->next) != NULL) {
+ log_warnx("kroute6_remove failed to add %s/%u",
+ log_in6addr(&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("kroute6_remove multipath list corrupted "
+ "for %s/%u", log_in6addr(&kr->r.prefix),
+ kr->r.prefixlen);
+ return (-1);
+ }
+ krm->next = kr->next;
+ }
+
/* check whether a nexthop depends on this kroute */
if ((kr->r.flags & F_KERNEL) && (kr->r.flags & F_NEXTHOP))
RB_FOREACH(s, knexthop_tree, &knt)
if (s->kroute == kr)
knexthop_validate(s);
- if (kr->r.flags & F_KERNEL)
+ if (kr->r.flags & F_KERNEL && kr == krm && kr->next == NULL)
+ /* again remove only once */
kr_redistribute6(IMSG_NETWORK_REMOVE, &kr->r);
if (kr->r.flags & F_CONNECTED)
@@ -1426,13 +1592,13 @@ kroute_match(in_addr_t key, int matchall)
/* we will never match the default route */
for (i = 32; i > 0; i--)
- if ((kr =
- kroute_find(htonl(ina & prefixlen2mask(i)), i)) != NULL)
+ if ((kr = kroute_find(htonl(ina & prefixlen2mask(i)), i,
+ RTP_ANY)) != NULL)
if (matchall || bgpd_filternexthop(&kr->r, NULL) == 0)
return (kr);
/* if we don't have a match yet, try to find a default route */
- if ((kr = kroute_find(0, 0)) != NULL)
+ if ((kr = kroute_find(0, 0, RTP_ANY)) != NULL)
if (matchall || bgpd_filternexthop(&kr->r, NULL) == 0)
return (kr);
@@ -1449,13 +1615,13 @@ kroute6_match(struct in6_addr *key, int matchall)
/* we will never match the default route */
for (i = 128; i > 0; i--) {
inet6applymask(&ina, key, i);
- if ((kr6 = kroute6_find(&ina, i)) != NULL)
+ if ((kr6 = kroute6_find(&ina, i, RTP_ANY)) != NULL)
if (matchall || bgpd_filternexthop(NULL, &kr6->r) == 0)
return (kr6);
}
/* if we don't have a match yet, try to find a default route */
- if ((kr6 = kroute6_find(&in6addr_any, 0)) != NULL)
+ if ((kr6 = kroute6_find(&in6addr_any, 0, RTP_ANY)) != NULL)
if (matchall || bgpd_filternexthop(NULL, &kr6->r) == 0)
return (kr6);
@@ -1798,7 +1964,6 @@ send_rtmsg(int fd, int action, struct kroute *kroute)
hdr.rtm_version = RTM_VERSION;
hdr.rtm_type = action;
hdr.rtm_tableid = kr_state.rtableid;
- hdr.rtm_flags = RTF_PROTO1;
hdr.rtm_priority = RTP_BGP;
if (kroute->flags & F_BLACKHOLE)
hdr.rtm_flags |= RTF_BLACKHOLE;
@@ -1911,7 +2076,6 @@ send_rt6msg(int fd, int action, struct kroute6 *kroute)
hdr.rtm_version = RTM_VERSION;
hdr.rtm_type = action;
hdr.rtm_tableid = kr_state.rtableid;
- hdr.rtm_flags = RTF_PROTO1;
if (kroute->flags & F_BLACKHOLE)
hdr.rtm_flags |= RTF_BLACKHOLE;
if (kroute->flags & F_REJECT)
@@ -2056,10 +2220,6 @@ fetchtable(u_int rtableid, int connected_only)
if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
continue;
-#ifdef RTF_MPATH
- if (rtm->rtm_flags & RTF_MPATH) /* multipath */
- continue;
-#endif
switch (sa->sa_family) {
case AF_INET:
if ((kr = calloc(1, sizeof(struct kroute_node))) ==
@@ -2070,6 +2230,7 @@ fetchtable(u_int rtableid, int connected_only)
}
kr->r.flags = F_KERNEL;
+ kr->r.priority = rtm->rtm_priority;
kr->r.ifindex = rtm->rtm_index;
kr->r.prefix.s_addr =
((struct sockaddr_in *)sa)->sin_addr.s_addr;
@@ -2102,6 +2263,7 @@ fetchtable(u_int rtableid, int connected_only)
}
kr6->r.flags = F_KERNEL;
+ kr6->r.priority = rtm->rtm_priority;
kr6->r.ifindex = rtm->rtm_index;
memcpy(&kr6->r.prefix,
&((struct sockaddr_in6 *)sa)->sin6_addr,
@@ -2153,7 +2315,7 @@ fetchtable(u_int rtableid, int connected_only)
}
if (sa->sa_family == AF_INET) {
- if (rtm->rtm_flags & RTF_PROTO1) {
+ if (rtm->rtm_priority == RTP_BGP) {
send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
free(kr);
} else if (connected_only &&
@@ -2162,7 +2324,7 @@ fetchtable(u_int rtableid, int connected_only)
else
kroute_insert(kr);
} else if (sa->sa_family == AF_INET6) {
- if (rtm->rtm_flags & RTF_PROTO1) {
+ if (rtm->rtm_priority == RTP_BGP) {
send_rt6msg(kr_state.fd, RTM_DELETE, &kr6->r);
free(kr6);
} else if (connected_only &&
@@ -2329,9 +2491,10 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
struct kroute_node *kr;
struct kroute6_node *kr6;
struct bgpd_addr prefix;
- int flags, oflags;
+ int flags, oflags, mpath = 0;
u_int16_t ifindex;
u_int8_t prefixlen;
+ u_int8_t prio;
flags = F_KERNEL;
ifindex = 0;
@@ -2349,7 +2512,12 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
flags |= F_REJECT;
if (rtm->rtm_flags & RTF_DYNAMIC)
flags |= F_DYNAMIC;
+#ifdef RTF_MPATH
+ if (rtm->rtm_flags & RTF_MPATH)
+ mpath = 1;
+#endif
+ prio = rtm->rtm_priority;
prefix.af = sa->sa_family;
switch (prefix.af) {
case AF_INET:
@@ -2381,22 +2549,54 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
return (0);
}
+ if ((sa = rti_info[RTAX_GATEWAY]) != NULL)
+ switch (sa->sa_family) {
+ case AF_LINK:
+ flags |= F_CONNECTED;
+ ifindex = rtm->rtm_index;
+ sa = NULL;
+ mpath = 0; /* link local stuff can't be mpath */
+ break;
+ }
+
if (rtm->rtm_type == RTM_DELETE) {
switch (prefix.af) {
case AF_INET:
+ sa_in = (struct sockaddr_in *)sa;
if ((kr = kroute_find(prefix.v4.s_addr,
- prefixlen)) == NULL)
+ prefixlen, prio)) == NULL)
return (0);
if (!(kr->r.flags & F_KERNEL))
return (0);
+
+ if (mpath)
+ /* get the correct route */
+ if ((kr = kroute_matchgw(kr, sa_in)) == NULL) {
+ log_warnx("dispatch_rtmsg[delete] "
+ "mpath route not found");
+ return (-1);
+ }
+
if (kroute_remove(kr) == -1)
return (-1);
break;
case AF_INET6:
- if ((kr6 = kroute6_find(&prefix.v6, prefixlen)) == NULL)
+ sa_in6 = (struct sockaddr_in6 *)sa;
+ if ((kr6 = kroute6_find(&prefix.v6, prefixlen,
+ prio)) == NULL)
return (0);
if (!(kr6->r.flags & F_KERNEL))
return (0);
+
+ if (mpath)
+ /* get the correct route */
+ if ((kr6 = kroute6_matchgw(kr6, sa_in6)) ==
+ NULL) {
+ log_warnx("dispatch_rtmsg[delete] "
+ "mpath route not found");
+ return (-1);
+ }
+
if (kroute6_remove(kr6) == -1)
return (-1);
break;
@@ -2404,15 +2604,6 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
return (0);
}
- if ((sa = rti_info[RTAX_GATEWAY]) != NULL)
- switch (sa->sa_family) {
- case AF_LINK:
- flags |= F_CONNECTED;
- ifindex = rtm->rtm_index;
- sa = NULL;
- break;
- }
-
if (connected_only && !(flags & F_CONNECTED))
return (0);
@@ -2425,8 +2616,18 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
switch (prefix.af) {
case AF_INET:
sa_in = (struct sockaddr_in *)sa;
- if ((kr = kroute_find(prefix.v4.s_addr, prefixlen)) != NULL) {
+ if ((kr = kroute_find(prefix.v4.s_addr, prefixlen,
+ prio)) != NULL) {
if (kr->r.flags & F_KERNEL) {
+ /* get the correct route */
+ if (mpath && rtm->rtm_type == RTM_CHANGE &&
+ (kr = kroute_matchgw(kr, sa_in)) == NULL) {
+ log_warnx("dispatch_rtmsg[change] "
+ "mpath route not found");
+ return (-1);
+ } else if (mpath && rtm->rtm_type == RTM_ADD)
+ goto add4;
+
if (sa_in != NULL)
kr->r.nexthop.s_addr =
sa_in->sin_addr.s_addr;
@@ -2457,6 +2658,7 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
log_addr(&prefix), prefixlen);
return (0);
} else {
+add4:
if ((kr = calloc(1,
sizeof(struct kroute_node))) == NULL) {
log_warn("dispatch_rtmsg");
@@ -2470,14 +2672,25 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
kr->r.nexthop.s_addr = 0;
kr->r.flags = flags;
kr->r.ifindex = ifindex;
+ kr->r.priority = prio;
kroute_insert(kr);
}
break;
case AF_INET6:
sa_in6 = (struct sockaddr_in6 *)sa;
- if ((kr6 = kroute6_find(&prefix.v6, prefixlen)) != NULL) {
+ if ((kr6 = kroute6_find(&prefix.v6, prefixlen, prio)) != NULL) {
if (kr6->r.flags & F_KERNEL) {
+ /* get the correct route */
+ if (mpath && rtm->rtm_type == RTM_CHANGE &&
+ (kr6 = kroute6_matchgw(kr6, sa_in6)) ==
+ NULL) {
+ log_warnx("dispatch_rtmsg[change] "
+ "mpath route not found");
+ return (-1);
+ } else if (mpath && rtm->rtm_type == RTM_ADD)
+ goto add6;
+
if (sa_in6 != NULL)
memcpy(&kr6->r.nexthop,
&sa_in6->sin6_addr,
@@ -2511,6 +2724,7 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
log_addr(&prefix), prefixlen);
return (0);
} else {
+add6:
if ((kr6 = calloc(1,
sizeof(struct kroute6_node))) == NULL) {
log_warn("dispatch_rtmsg");
@@ -2527,6 +2741,7 @@ dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
sizeof(struct in6_addr));
kr6->r.flags = flags;
kr6->r.ifindex = ifindex;
+ kr6->r.priority = prio;
kroute6_insert(kr6);
}