mirror of
https://git.freebsd.org/ports.git
synced 2025-06-28 08:00:31 -04:00
1178 lines
34 KiB
C
1178 lines
34 KiB
C
Index: bgpd/kroute.c
|
|
===================================================================
|
|
RCS file: /home/cvs/private/hrs/openbgpd/bgpd/kroute.c,v
|
|
retrieving revision 1.1.1.1
|
|
retrieving revision 1.5
|
|
diff -u -p -r1.1.1.1 -r1.5
|
|
--- bgpd/kroute.c 30 Jun 2009 05:46:15 -0000 1.1.1.1
|
|
+++ bgpd/kroute.c 10 Aug 2009 21:20:05 -0000 1.5
|
|
@@ -1,4 +1,4 @@
|
|
-/* $OpenBSD: kroute.c,v 1.160 2008/05/09 12:45:25 henning Exp $ */
|
|
+/* $OpenBSD: kroute.c,v 1.172 2009/07/23 14:53:20 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
|
|
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);
|
|
@@ -118,6 +125,7 @@ int kif_validate(struct kif *);
|
|
int kroute_validate(struct kroute *);
|
|
int kroute6_validate(struct kroute6 *);
|
|
void knexthop_validate(struct knexthop_node *);
|
|
+void knexthop_track(void *);
|
|
struct kroute_node *kroute_match(in_addr_t, int);
|
|
struct kroute6_node *kroute6_match(struct in6_addr *, int);
|
|
void kroute_detach_nexthop(struct knexthop_node *);
|
|
@@ -164,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");
|
|
@@ -219,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)) ==
|
|
@@ -252,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)
|
|
@@ -277,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))
|
|
@@ -307,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))
|
|
@@ -340,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)
|
|
@@ -366,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))
|
|
@@ -509,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;
|
|
@@ -530,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 +
|
|
@@ -573,11 +587,17 @@ kr_show_route(struct imsg *imsg)
|
|
case AF_INET:
|
|
kr = h->kroute;
|
|
snh.valid = kroute_validate(&kr->r);
|
|
+ snh.krvalid = 1;
|
|
+ memcpy(&snh.kr.kr4, &kr->r,
|
|
+ sizeof(snh.kr.kr4));
|
|
ifindex = kr->r.ifindex;
|
|
break;
|
|
case AF_INET6:
|
|
kr6 = h->kroute;
|
|
snh.valid = kroute6_validate(&kr6->r);
|
|
+ snh.krvalid = 1;
|
|
+ memcpy(&snh.kr.kr6, &kr6->r,
|
|
+ sizeof(snh.kr.kr6));
|
|
ifindex = kr6->r.ifindex;
|
|
break;
|
|
}
|
|
@@ -791,6 +811,14 @@ kroute_compare(struct kroute_node *a, st
|
|
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);
|
|
}
|
|
|
|
@@ -810,32 +838,42 @@ kroute6_compare(struct kroute6_node *a,
|
|
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);
|
|
}
|
|
|
|
int
|
|
knexthop_compare(struct knexthop_node *a, struct knexthop_node *b)
|
|
{
|
|
- u_int32_t r;
|
|
+ int i;
|
|
|
|
if (a->nexthop.af != b->nexthop.af)
|
|
return (b->nexthop.af - a->nexthop.af);
|
|
|
|
switch (a->nexthop.af) {
|
|
case AF_INET:
|
|
- if ((r = b->nexthop.addr32[0] - a->nexthop.addr32[0]) != 0)
|
|
- return (r);
|
|
+ if (ntohl(a->nexthop.v4.s_addr) < ntohl(b->nexthop.v4.s_addr))
|
|
+ return (-1);
|
|
+ if (ntohl(a->nexthop.v4.s_addr) > ntohl(b->nexthop.v4.s_addr))
|
|
+ return (1);
|
|
break;
|
|
case AF_INET6:
|
|
- if ((r = b->nexthop.addr32[3] - a->nexthop.addr32[3]) != 0)
|
|
- return (r);
|
|
- if ((r = b->nexthop.addr32[2] - a->nexthop.addr32[2]) != 0)
|
|
- return (r);
|
|
- if ((r = b->nexthop.addr32[1] - a->nexthop.addr32[1]) != 0)
|
|
- return (r);
|
|
- if ((r = b->nexthop.addr32[0] - a->nexthop.addr32[0]) != 0)
|
|
- return (r);
|
|
+ for (i = 0; i < 16; i++) {
|
|
+ if (a->nexthop.v6.s6_addr[i] < b->nexthop.v6.s6_addr[i])
|
|
+ return (-1);
|
|
+ if (a->nexthop.v6.s6_addr[i] > b->nexthop.v6.s6_addr[i])
|
|
+ return (1);
|
|
+ }
|
|
break;
|
|
+ default:
|
|
+ fatalx("knexthop_compare: unknown AF");
|
|
}
|
|
|
|
return (0);
|
|
@@ -853,27 +891,62 @@ kif_compare(struct kif_node *a, struct k
|
|
*/
|
|
|
|
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;
|
|
|
|
- return (RB_FIND(kroute_tree, &krt, &s));
|
|
+ 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;
|
|
+
|
|
+ if (sa_in == NULL) {
|
|
+ log_warnx("kroute_matchgw: no nexthop defined");
|
|
+ return (NULL);
|
|
+ }
|
|
+ nexthop = sa_in->sin_addr.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)
|
|
{
|
|
+ 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) {
|
|
@@ -888,29 +961,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)
|
|
@@ -933,27 +1038,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;
|
|
|
|
- return (RB_FIND(kroute6_tree, &krt6, &s));
|
|
+ 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));
|
|
+
|
|
+ while (kr) {
|
|
+ if (memcmp(&kr->r.nexthop, &nexthop, sizeof(nexthop)) == 0)
|
|
+ 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) {
|
|
@@ -970,7 +1110,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);
|
|
@@ -979,21 +1121,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)
|
|
@@ -1020,6 +1192,7 @@ knexthop_find(struct bgpd_addr *addr)
|
|
{
|
|
struct knexthop_node s;
|
|
|
|
+ bzero(&s, sizeof(s));
|
|
memcpy(&s.nexthop, addr, sizeof(s.nexthop));
|
|
|
|
return (RB_FIND(knexthop_tree, &knt, &s));
|
|
@@ -1374,6 +1547,46 @@ knexthop_validate(struct knexthop_node *
|
|
}
|
|
}
|
|
|
|
+void
|
|
+knexthop_track(void *krn)
|
|
+{
|
|
+ struct knexthop_node *kn;
|
|
+ struct kroute_node *kr;
|
|
+ struct kroute6_node *kr6;
|
|
+ struct kroute_nexthop n;
|
|
+
|
|
+ RB_FOREACH(kn, knexthop_tree, &knt)
|
|
+ if (kn->kroute == krn) {
|
|
+ bzero(&n, sizeof(n));
|
|
+ memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
|
|
+
|
|
+ switch (kn->nexthop.af) {
|
|
+ case AF_INET:
|
|
+ kr = krn;
|
|
+ n.valid = 1;
|
|
+ n.connected = kr->r.flags & F_CONNECTED;
|
|
+ if ((n.gateway.v4.s_addr =
|
|
+ kr->r.nexthop.s_addr) != 0)
|
|
+ n.gateway.af = AF_INET;
|
|
+ memcpy(&n.kr.kr4, &kr->r, sizeof(n.kr.kr4));
|
|
+ break;
|
|
+ case AF_INET6:
|
|
+ kr6 = krn;
|
|
+ n.valid = 1;
|
|
+ n.connected = kr6->r.flags & F_CONNECTED;
|
|
+ if (memcmp(&kr6->r.nexthop, &in6addr_any,
|
|
+ sizeof(struct in6_addr)) != 0) {
|
|
+ n.gateway.af = AF_INET6;
|
|
+ memcpy(&n.gateway.v6, &kr6->r.nexthop,
|
|
+ sizeof(struct in6_addr));
|
|
+ }
|
|
+ memcpy(&n.kr.kr6, &kr6->r, sizeof(n.kr.kr6));
|
|
+ break;
|
|
+ }
|
|
+ send_nexthop_update(&n);
|
|
+ }
|
|
+}
|
|
+
|
|
struct kroute_node *
|
|
kroute_match(in_addr_t key, int matchall)
|
|
{
|
|
@@ -1385,13 +1598,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);
|
|
|
|
@@ -1408,13 +1621,13 @@ kroute6_match(struct in6_addr *key, int
|
|
/* 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);
|
|
|
|
@@ -1456,7 +1669,6 @@ kroute_detach_nexthop(struct knexthop_no
|
|
kn->kroute = NULL;
|
|
}
|
|
|
|
-
|
|
/*
|
|
* misc helpers
|
|
*/
|
|
@@ -1568,15 +1780,6 @@ mask2prefixlen6(struct sockaddr_in6 *sa_
|
|
return (l);
|
|
}
|
|
|
|
-in_addr_t
|
|
-prefixlen2mask(u_int8_t prefixlen)
|
|
-{
|
|
- if (prefixlen == 0)
|
|
- return (0);
|
|
-
|
|
- return (0xffffffff << (32 - prefixlen));
|
|
-}
|
|
-
|
|
struct in6_addr *
|
|
prefixlen2mask6(u_int8_t prefixlen)
|
|
{
|
|
@@ -1593,25 +1796,8 @@ prefixlen2mask6(u_int8_t prefixlen)
|
|
return (&mask);
|
|
}
|
|
|
|
-void
|
|
-inet6applymask(struct in6_addr *dest, const struct in6_addr *src, int prefixlen)
|
|
-{
|
|
- struct in6_addr mask;
|
|
- int i;
|
|
-
|
|
- bzero(&mask, sizeof(mask));
|
|
- for (i = 0; i < prefixlen / 8; i++)
|
|
- mask.s6_addr[i] = 0xff;
|
|
- i = prefixlen % 8;
|
|
- if (i)
|
|
- mask.s6_addr[prefixlen / 8] = 0xff00 >> i;
|
|
-
|
|
- for (i = 0; i < 16; i++)
|
|
- dest->s6_addr[i] = src->s6_addr[i] & mask.s6_addr[i];
|
|
-}
|
|
-
|
|
-#define ROUNDUP(a, size) \
|
|
- (((a) & ((size) - 1)) ? (1 + ((a) | ((size) - 1))) : (a))
|
|
+#define ROUNDUP(a) \
|
|
+ (((a) & ((sizeof(long)) - 1)) ? (1 + ((a) | ((sizeof(long)) - 1))) : (a))
|
|
|
|
void
|
|
get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
|
|
@@ -1622,7 +1808,7 @@ get_rtaddrs(int addrs, struct sockaddr *
|
|
if (addrs & (1 << i)) {
|
|
rti_info[i] = sa;
|
|
sa = (struct sockaddr *)((char *)(sa) +
|
|
- ROUNDUP(sa->sa_len, sizeof(long)));
|
|
+ ROUNDUP(sa->sa_len));
|
|
} else
|
|
rti_info[i] = NULL;
|
|
}
|
|
@@ -1747,7 +1933,9 @@ send_rtmsg(int fd, int action, struct kr
|
|
struct sockaddr_in prefix;
|
|
struct sockaddr_in nexthop;
|
|
struct sockaddr_in mask;
|
|
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
|
|
struct sockaddr_rtlabel label;
|
|
+#endif /* !defined(__FreeBSD__) */
|
|
int iovcnt = 0;
|
|
|
|
if (kr_state.fib_sync == 0)
|
|
@@ -1757,9 +1945,13 @@ send_rtmsg(int fd, int action, struct kr
|
|
bzero(&hdr, sizeof(hdr));
|
|
hdr.rtm_version = RTM_VERSION;
|
|
hdr.rtm_type = action;
|
|
+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no multiple routing tables */
|
|
hdr.rtm_tableid = kr_state.rtableid;
|
|
+#endif /* !defined(__FreeBSD__) */
|
|
hdr.rtm_flags = RTF_PROTO1;
|
|
+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no rtm_priority */
|
|
hdr.rtm_priority = RTP_BGP;
|
|
+#endif /* !defined(__FreeBSD__) */
|
|
if (kroute->flags & F_BLACKHOLE)
|
|
hdr.rtm_flags |= RTF_BLACKHOLE;
|
|
if (kroute->flags & F_REJECT)
|
|
@@ -1808,6 +2000,7 @@ send_rtmsg(int fd, int action, struct kr
|
|
iov[iovcnt].iov_base = &mask;
|
|
iov[iovcnt++].iov_len = sizeof(mask);
|
|
|
|
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
|
|
if (kroute->labelid) {
|
|
bzero(&label, sizeof(label));
|
|
label.sr_len = sizeof(label);
|
|
@@ -1820,6 +2013,7 @@ send_rtmsg(int fd, int action, struct kr
|
|
iov[iovcnt].iov_base = &label;
|
|
iov[iovcnt++].iov_len = sizeof(label);
|
|
}
|
|
+#endif /* !defined(__FreeBSD__) */
|
|
|
|
retry:
|
|
if (writev(fd, iov, iovcnt) == -1) {
|
|
@@ -1857,10 +2051,13 @@ send_rt6msg(int fd, int action, struct k
|
|
{
|
|
struct iovec iov[5];
|
|
struct rt_msghdr hdr;
|
|
- struct sockaddr_in6 prefix;
|
|
- struct sockaddr_in6 nexthop;
|
|
- struct sockaddr_in6 mask;
|
|
+ struct pad {
|
|
+ struct sockaddr_in6 addr;
|
|
+ char pad[sizeof(long)];
|
|
+ } prefix, nexthop, mask;
|
|
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
|
|
struct sockaddr_rtlabel label;
|
|
+#endif /* !defined(__FreeBSD__) */
|
|
int iovcnt = 0;
|
|
|
|
if (kr_state.fib_sync == 0)
|
|
@@ -1870,7 +2067,9 @@ send_rt6msg(int fd, int action, struct k
|
|
bzero(&hdr, sizeof(hdr));
|
|
hdr.rtm_version = RTM_VERSION;
|
|
hdr.rtm_type = action;
|
|
+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no multiple routing tables */
|
|
hdr.rtm_tableid = kr_state.rtableid;
|
|
+#endif /* !defined(__FreeBSD__) */
|
|
hdr.rtm_flags = RTF_PROTO1;
|
|
if (kroute->flags & F_BLACKHOLE)
|
|
hdr.rtm_flags |= RTF_BLACKHOLE;
|
|
@@ -1885,44 +2084,46 @@ send_rt6msg(int fd, int action, struct k
|
|
iov[iovcnt++].iov_len = sizeof(hdr);
|
|
|
|
bzero(&prefix, sizeof(prefix));
|
|
- prefix.sin6_len = sizeof(prefix);
|
|
- prefix.sin6_family = AF_INET6;
|
|
- memcpy(&prefix.sin6_addr, &kroute->prefix, sizeof(struct in6_addr));
|
|
+ prefix.addr.sin6_len = sizeof(struct sockaddr_in6);
|
|
+ prefix.addr.sin6_family = AF_INET6;
|
|
+ memcpy(&prefix.addr.sin6_addr, &kroute->prefix,
|
|
+ sizeof(struct in6_addr));
|
|
/* XXX scope does not matter or? */
|
|
/* adjust header */
|
|
hdr.rtm_addrs |= RTA_DST;
|
|
- hdr.rtm_msglen += sizeof(prefix);
|
|
+ hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
|
|
/* adjust iovec */
|
|
iov[iovcnt].iov_base = &prefix;
|
|
- iov[iovcnt++].iov_len = sizeof(prefix);
|
|
+ iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
|
|
|
|
if (memcmp(&kroute->nexthop, &in6addr_any, sizeof(struct in6_addr))) {
|
|
bzero(&nexthop, sizeof(nexthop));
|
|
- nexthop.sin6_len = sizeof(nexthop);
|
|
- nexthop.sin6_family = AF_INET6;
|
|
- memcpy(&nexthop.sin6_addr, &kroute->nexthop,
|
|
+ nexthop.addr.sin6_len = sizeof(struct sockaddr_in6);
|
|
+ nexthop.addr.sin6_family = AF_INET6;
|
|
+ memcpy(&nexthop.addr.sin6_addr, &kroute->nexthop,
|
|
sizeof(struct in6_addr));
|
|
/* adjust header */
|
|
hdr.rtm_flags |= RTF_GATEWAY;
|
|
hdr.rtm_addrs |= RTA_GATEWAY;
|
|
- hdr.rtm_msglen += sizeof(nexthop);
|
|
+ hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
|
|
/* adjust iovec */
|
|
iov[iovcnt].iov_base = &nexthop;
|
|
- iov[iovcnt++].iov_len = sizeof(nexthop);
|
|
+ iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
|
|
}
|
|
|
|
bzero(&mask, sizeof(mask));
|
|
- mask.sin6_len = sizeof(mask);
|
|
- mask.sin6_family = AF_INET6;
|
|
- memcpy(&mask.sin6_addr, prefixlen2mask6(kroute->prefixlen),
|
|
+ mask.addr.sin6_len = sizeof(struct sockaddr_in6);
|
|
+ mask.addr.sin6_family = AF_INET6;
|
|
+ memcpy(&mask.addr.sin6_addr, prefixlen2mask6(kroute->prefixlen),
|
|
sizeof(struct in6_addr));
|
|
/* adjust header */
|
|
hdr.rtm_addrs |= RTA_NETMASK;
|
|
- hdr.rtm_msglen += sizeof(mask);
|
|
+ hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_in6));
|
|
/* adjust iovec */
|
|
iov[iovcnt].iov_base = &mask;
|
|
- iov[iovcnt++].iov_len = sizeof(mask);
|
|
+ iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
|
|
|
|
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
|
|
if (kroute->labelid) {
|
|
bzero(&label, sizeof(label));
|
|
label.sr_len = sizeof(label);
|
|
@@ -1935,6 +2136,7 @@ send_rt6msg(int fd, int action, struct k
|
|
iov[iovcnt].iov_base = &label;
|
|
iov[iovcnt++].iov_len = sizeof(label);
|
|
}
|
|
+#endif /* !defined(__FreeBSD__) */
|
|
|
|
retry:
|
|
if (writev(fd, iov, iovcnt) == -1) {
|
|
@@ -1949,7 +2151,7 @@ retry:
|
|
kroute->prefixlen);
|
|
return (0);
|
|
} else {
|
|
- log_warnx("send_rtmsg: action %u, "
|
|
+ log_warnx("send_rt6msg: action %u, "
|
|
"prefix %s/%u: %s", hdr.rtm_type,
|
|
log_in6addr(&kroute->prefix),
|
|
kroute->prefixlen, strerror(errno));
|
|
@@ -1957,7 +2159,7 @@ retry:
|
|
}
|
|
break;
|
|
default:
|
|
- log_warnx("send_rtmsg: action %u, prefix %s/%u: %s",
|
|
+ log_warnx("send_rt6msg: action %u, prefix %s/%u: %s",
|
|
hdr.rtm_type, log_in6addr(&kroute->prefix),
|
|
kroute->prefixlen, strerror(errno));
|
|
return (0);
|
|
@@ -1970,8 +2172,8 @@ retry:
|
|
int
|
|
fetchtable(u_int rtableid, int connected_only)
|
|
{
|
|
- size_t len;
|
|
- int mib[7];
|
|
+ size_t len;
|
|
+ int mib[6];
|
|
char *buf, *next, *lim;
|
|
struct rt_msghdr *rtm;
|
|
struct sockaddr *sa, *gw, *rti_info[RTAX_MAX];
|
|
@@ -1986,9 +2188,8 @@ fetchtable(u_int rtableid, int connected
|
|
mib[3] = 0;
|
|
mib[4] = NET_RT_DUMP;
|
|
mib[5] = 0;
|
|
- mib[6] = rtableid;
|
|
|
|
- if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
|
|
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
|
|
if (rtableid != 0 && errno == EINVAL) /* table nonexistent */
|
|
return (0);
|
|
log_warn("sysctl");
|
|
@@ -1998,7 +2199,7 @@ fetchtable(u_int rtableid, int connected
|
|
log_warn("fetchtable");
|
|
return (-1);
|
|
}
|
|
- if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
|
|
+ if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
|
|
log_warn("sysctl");
|
|
free(buf);
|
|
return (-1);
|
|
@@ -2007,7 +2208,13 @@ fetchtable(u_int rtableid, int connected
|
|
lim = buf + len;
|
|
for (next = buf; next < lim; next += rtm->rtm_msglen) {
|
|
rtm = (struct rt_msghdr *)next;
|
|
- sa = (struct sockaddr *)(rtm + 1);
|
|
+ if (rtm->rtm_version != RTM_VERSION)
|
|
+ continue;
|
|
+#if !defined(__FreeBSD__) /* no rtm_hdrlen on FreeBSD */
|
|
+ sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
|
|
+#else
|
|
+ sa = (struct sockaddr *)(next + sizeof(struct rt_msghdr));
|
|
+#endif
|
|
get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
|
|
|
|
if ((sa = rti_info[RTAX_DST]) == NULL)
|
|
@@ -2016,10 +2223,6 @@ fetchtable(u_int rtableid, int connected
|
|
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))) ==
|
|
@@ -2030,6 +2233,11 @@ fetchtable(u_int rtableid, int connected
|
|
}
|
|
|
|
kr->r.flags = F_KERNEL;
|
|
+#if defined(__FreeBSD__) /* no rtm_priority on FreeBSD */
|
|
+ kr->r.priority = RTP_BGP;
|
|
+#else
|
|
+ kr->r.priority = rtm->rtm_priority;
|
|
+#endif
|
|
kr->r.ifindex = rtm->rtm_index;
|
|
kr->r.prefix.s_addr =
|
|
((struct sockaddr_in *)sa)->sin_addr.s_addr;
|
|
@@ -2062,6 +2270,11 @@ fetchtable(u_int rtableid, int connected
|
|
}
|
|
|
|
kr6->r.flags = F_KERNEL;
|
|
+#if defined(__FreeBSD__) /* no rtm_priority on FreeBSD */
|
|
+ kr6->r.priority = RTP_BGP;
|
|
+#else
|
|
+ kr6->r.priority = rtm->rtm_priority;
|
|
+#endif
|
|
kr6->r.ifindex = rtm->rtm_index;
|
|
memcpy(&kr6->r.prefix,
|
|
&((struct sockaddr_in6 *)sa)->sin6_addr,
|
|
@@ -2113,7 +2326,12 @@ fetchtable(u_int rtableid, int connected
|
|
}
|
|
|
|
if (sa->sa_family == AF_INET) {
|
|
- if (rtm->rtm_flags & RTF_PROTO1) {
|
|
+#if !defined(__FreeBSD__) /* no rtm_priority on FreeBSD */
|
|
+ if (rtm->rtm_priority == RTP_BGP) {
|
|
+#else
|
|
+ /* never delete route */
|
|
+ if (0) {
|
|
+#endif
|
|
send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
|
|
free(kr);
|
|
} else if (connected_only &&
|
|
@@ -2122,7 +2340,12 @@ fetchtable(u_int rtableid, int connected
|
|
else
|
|
kroute_insert(kr);
|
|
} else if (sa->sa_family == AF_INET6) {
|
|
- if (rtm->rtm_flags & RTF_PROTO1) {
|
|
+#if !defined(__FreeBSD__) /* no rtm_priority on FreeBSD */
|
|
+ if (rtm->rtm_priority == RTP_BGP) {
|
|
+#else
|
|
+ /* never delete route */
|
|
+ if (0) {
|
|
+#endif
|
|
send_rt6msg(kr_state.fd, RTM_DELETE, &kr6->r);
|
|
free(kr6);
|
|
} else if (connected_only &&
|
|
@@ -2234,12 +2457,18 @@ dispatch_rtmsg(void)
|
|
lim = buf + n;
|
|
for (next = buf; next < lim; next += rtm->rtm_msglen) {
|
|
rtm = (struct rt_msghdr *)next;
|
|
+ if (rtm->rtm_version != RTM_VERSION)
|
|
+ continue;
|
|
|
|
switch (rtm->rtm_type) {
|
|
case RTM_ADD:
|
|
case RTM_CHANGE:
|
|
case RTM_DELETE:
|
|
- sa = (struct sockaddr *)(rtm + 1);
|
|
+#if !defined(__FreeBSD__) /* no rtm_hdrlen on FreeBSD */
|
|
+ sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
|
|
+#else
|
|
+ sa = (struct sockaddr *)(next + sizeof(struct rt_msghdr));
|
|
+#endif
|
|
get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
|
|
|
|
if (rtm->rtm_pid == kr_state.pid) /* cause by us */
|
|
@@ -2252,12 +2481,14 @@ dispatch_rtmsg(void)
|
|
continue;
|
|
|
|
connected_only = 0;
|
|
+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no multiple routing tables */
|
|
if (rtm->rtm_tableid != kr_state.rtableid) {
|
|
if (rtm->rtm_tableid == 0)
|
|
connected_only = 1;
|
|
else
|
|
continue;
|
|
}
|
|
+#endif /* !defined(__FreeBSD__) */
|
|
|
|
if (dispatch_rtmsg_addr(rtm, rti_info,
|
|
connected_only) == -1)
|
|
@@ -2289,9 +2520,10 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
|
|
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;
|
|
@@ -2309,7 +2541,16 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
|
|
flags |= F_REJECT;
|
|
if (rtm->rtm_flags & RTF_DYNAMIC)
|
|
flags |= F_DYNAMIC;
|
|
+#ifdef RTF_MPATH
|
|
+ if (rtm->rtm_flags & RTF_MPATH)
|
|
+ mpath = 1;
|
|
+#endif
|
|
|
|
+#if !defined(__FreeBSD__) /* no rtm_priority on FreeBSD */
|
|
+ prio = rtm->rtm_priority;
|
|
+#else
|
|
+ prio = RTP_BGP;
|
|
+#endif
|
|
prefix.af = sa->sa_family;
|
|
switch (prefix.af) {
|
|
case AF_INET:
|
|
@@ -2341,22 +2582,54 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
|
|
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 (0);
|
|
+ }
|
|
+
|
|
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] "
|
|
+ "IPv6 mpath route not found");
|
|
+ return (0);
|
|
+ }
|
|
+
|
|
if (kroute6_remove(kr6) == -1)
|
|
return (-1);
|
|
break;
|
|
@@ -2364,15 +2637,6 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
|
|
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);
|
|
|
|
@@ -2385,8 +2649,18 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
|
|
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;
|
|
@@ -2409,12 +2683,15 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
|
|
kr_redistribute(IMSG_NETWORK_ADD,
|
|
&kr->r);
|
|
}
|
|
+ if (kr->r.flags & F_NEXTHOP)
|
|
+ knexthop_track(kr);
|
|
}
|
|
} else if (rtm->rtm_type == RTM_CHANGE) {
|
|
log_warnx("change req for %s/%u: not in table",
|
|
log_addr(&prefix), prefixlen);
|
|
return (0);
|
|
} else {
|
|
+add4:
|
|
if ((kr = calloc(1,
|
|
sizeof(struct kroute_node))) == NULL) {
|
|
log_warn("dispatch_rtmsg");
|
|
@@ -2428,14 +2705,25 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
|
|
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,
|
|
@@ -2461,12 +2749,15 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
|
|
kr_redistribute6(IMSG_NETWORK_ADD,
|
|
&kr6->r);
|
|
}
|
|
+ if (kr6->r.flags & F_NEXTHOP)
|
|
+ knexthop_track(kr6);
|
|
}
|
|
} else if (rtm->rtm_type == RTM_CHANGE) {
|
|
log_warnx("change req for %s/%u: not in table",
|
|
log_addr(&prefix), prefixlen);
|
|
return (0);
|
|
} else {
|
|
+add6:
|
|
if ((kr6 = calloc(1,
|
|
sizeof(struct kroute6_node))) == NULL) {
|
|
log_warn("dispatch_rtmsg");
|
|
@@ -2483,6 +2774,7 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
|
|
sizeof(struct in6_addr));
|
|
kr6->r.flags = flags;
|
|
kr6->r.ifindex = ifindex;
|
|
+ kr6->r.priority = prio;
|
|
|
|
kroute6_insert(kr6);
|
|
}
|