freebsd-ports/net/openbgpd/files/patch-bgpd_kroute.c
Hiroki Sato 256f3a3dba Update to 5.2.20121014.
Feature safe:	yes
2012-10-13 19:40:42 +00:00

3043 lines
82 KiB
C

Index: bgpd/kroute.c
===================================================================
RCS file: /home/cvs/private/hrs/openbgpd/bgpd/kroute.c,v
retrieving revision 1.1.1.7
retrieving revision 1.13
diff -u -p -r1.1.1.7 -r1.13
--- bgpd/kroute.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
+++ bgpd/kroute.c 13 Oct 2012 18:36:00 -0000 1.13
@@ -1,4 +1,4 @@
-/* $OpenBSD: kroute.c,v 1.169 2009/06/25 15:54:22 claudio Exp $ */
+/* $OpenBSD: kroute.c,v 1.189 2012/05/27 18:52:07 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -27,6 +27,9 @@
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
+#if !defined(__FreeBSD__) /* FreeBSD has no mpls support. */
+#include <netmpls/mpls.h>
+#endif
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@@ -37,11 +40,12 @@
#include "bgpd.h"
+struct ktable **krt;
+u_int krt_size;
+
struct {
u_int32_t rtseq;
pid_t pid;
- u_int rtableid;
- int fib_sync;
int fd;
} kr_state;
@@ -83,32 +87,52 @@ struct kif_node {
struct kif_kr6_head kroute6_l;
};
-int kr_redistribute(int, struct kroute *);
-int kr_redistribute6(int, struct kroute6 *);
+int ktable_new(u_int, u_int, char *, char *, int);
+void ktable_free(u_int);
+void ktable_destroy(struct ktable *);
+struct ktable *ktable_get(u_int);
+
+int kr4_change(struct ktable *, struct kroute_full *);
+int kr6_change(struct ktable *, struct kroute_full *);
+int krVPN4_change(struct ktable *, struct kroute_full *);
+int kr4_delete(struct ktable *, struct kroute_full *);
+int kr6_delete(struct ktable *, struct kroute_full *);
+int krVPN4_delete(struct ktable *, struct kroute_full *);
+void kr_net_delete(struct network *);
+struct network *kr_net_match(struct ktable *, struct kroute *);
+struct network *kr_net_match6(struct ktable *, struct kroute6 *);
+struct network *kr_net_find(struct ktable *, struct network *);
+int kr_redistribute(int, struct ktable *, struct kroute *);
+int kr_redistribute6(int, struct ktable *, struct kroute6 *);
+struct kroute_full *kr_tofull(struct kroute *);
+struct kroute_full *kr6_tofull(struct kroute6 *);
int kroute_compare(struct kroute_node *, struct kroute_node *);
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, u_int8_t);
+struct kroute_node *kroute_find(struct ktable *, 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);
+int kroute_insert(struct ktable *, struct kroute_node *);
+int kroute_remove(struct ktable *, struct kroute_node *);
+void kroute_clear(struct ktable *);
-struct kroute6_node *kroute6_find(const struct in6_addr *, u_int8_t,
- u_int8_t);
+struct kroute6_node *kroute6_find(struct ktable *, 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);
-
-struct knexthop_node *knexthop_find(struct bgpd_addr *);
-int knexthop_insert(struct knexthop_node *);
-int knexthop_remove(struct knexthop_node *);
-void knexthop_clear(void);
+int kroute6_insert(struct ktable *, struct kroute6_node *);
+int kroute6_remove(struct ktable *, struct kroute6_node *);
+void kroute6_clear(struct ktable *);
+
+struct knexthop_node *knexthop_find(struct ktable *, struct bgpd_addr *);
+int knexthop_insert(struct ktable *,
+ struct knexthop_node *);
+int knexthop_remove(struct ktable *,
+ struct knexthop_node *);
+void knexthop_clear(struct ktable *);
struct kif_node *kif_find(int);
int kif_insert(struct kif_node *);
@@ -124,13 +148,16 @@ int kif_kr6_remove(struct kroute6_nod
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 *);
+void knexthop_validate(struct ktable *,
+ struct knexthop_node *);
+void knexthop_track(struct ktable *, void *);
+void knexthop_send_update(struct knexthop_node *);
+struct kroute_node *kroute_match(struct ktable *, in_addr_t, int);
+struct kroute6_node *kroute6_match(struct ktable *, struct in6_addr *, int);
+void kroute_detach_nexthop(struct ktable *,
+ struct knexthop_node *);
-int protect_lo(void);
+int protect_lo(struct ktable *);
u_int8_t prefixlen_classful(in_addr_t);
u_int8_t mask2prefixlen(in_addr_t);
u_int8_t mask2prefixlen6(struct sockaddr_in6 *);
@@ -138,23 +165,20 @@ void get_rtaddrs(int, struct sockaddr *
void if_change(u_short, int, struct if_data *);
void if_announce(void *);
-int send_rtmsg(int, int, struct kroute *);
-int send_rt6msg(int, int, struct kroute6 *);
+int send_rtmsg(int, int, struct ktable *, struct kroute *);
+int send_rt6msg(int, int, struct ktable *, struct kroute6 *);
int dispatch_rtmsg(void);
-int fetchtable(u_int, int);
+int fetchtable(struct ktable *);
int fetchifs(int);
int dispatch_rtmsg_addr(struct rt_msghdr *,
- struct sockaddr *[RTAX_MAX], int);
+ struct sockaddr *[RTAX_MAX], struct ktable *);
-RB_HEAD(kroute_tree, kroute_node) krt;
RB_PROTOTYPE(kroute_tree, kroute_node, entry, kroute_compare)
RB_GENERATE(kroute_tree, kroute_node, entry, kroute_compare)
-RB_HEAD(kroute6_tree, kroute6_node) krt6;
RB_PROTOTYPE(kroute6_tree, kroute6_node, entry, kroute6_compare)
RB_GENERATE(kroute6_tree, kroute6_node, entry, kroute6_compare)
-RB_HEAD(knexthop_tree, knexthop_node) knt;
RB_PROTOTYPE(knexthop_tree, knexthop_node, entry, knexthop_compare)
RB_GENERATE(knexthop_tree, knexthop_node, entry, knexthop_compare)
@@ -162,19 +186,21 @@ RB_HEAD(kif_tree, kif_node) kit;
RB_PROTOTYPE(kif_tree, kif_node, entry, kif_compare)
RB_GENERATE(kif_tree, kif_node, entry, kif_compare)
+#define KT2KNT(x) (&(ktable_get((x)->nhtableid)->knt))
+
/*
* exported functions
*/
int
-kr_init(int fs, u_int rtableid)
+kr_init(void)
{
int opt = 0, rcvbuf, default_rcvbuf;
+#if !defined(__FreeBSD__) /* FreeBSD does not have ROUTE_TABLEFILTER. */
+ unsigned int tid = RTABLE_ANY;
+#endif
socklen_t optlen;
- 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");
return (-1);
@@ -198,194 +224,533 @@ kr_init(int fs, u_int rtableid)
rcvbuf /= 2)
; /* nothing */
+#if !defined(__FreeBSD__) /* FreeBSD does not have ROUTE_TABLEFILTER. */
+ if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_TABLEFILTER, &tid,
+ sizeof(tid)) == -1) {
+ log_warn("kr_init: setsockopt AF_ROUTE ROUTE_TABLEFILTER");
+ return (-1);
+ }
+#endif
+
kr_state.pid = getpid();
kr_state.rtseq = 1;
- RB_INIT(&krt);
- RB_INIT(&krt6);
- RB_INIT(&knt);
RB_INIT(&kit);
if (fetchifs(0) == -1)
return (-1);
- if (fetchtable(kr_state.rtableid, 0) == -1)
- return (-1);
- if (kr_state.rtableid != 0)
- if (fetchtable(0, 1) == -1)
+ return (kr_state.fd);
+}
+
+int
+ktable_new(u_int rtableid, u_int rdomid, char *name, char *ifname, int fs)
+{
+ struct ktable **xkrt;
+ struct ktable *kt;
+ size_t newsize, oldsize;
+
+ /* resize index table if needed */
+ if (rtableid >= krt_size) {
+ oldsize = sizeof(struct ktable *) * krt_size;
+ newsize = sizeof(struct ktable *) * (rtableid + 1);
+ if ((xkrt = realloc(krt, newsize)) == NULL) {
+ log_warn("ktable_new");
return (-1);
+ }
+ krt = xkrt;
+ krt_size = rtableid + 1;
+ bzero((char *)krt + oldsize, newsize - oldsize);
+ }
+
+ if (krt[rtableid])
+ fatalx("ktable_new: table already exists.");
- if (protect_lo() == -1)
+ /* allocate new element */
+ kt = krt[rtableid] = calloc(1, sizeof(struct ktable));
+ if (kt == NULL) {
+ log_warn("ktable_new");
return (-1);
+ }
- return (kr_state.fd);
+ /* initialize structure ... */
+ strlcpy(kt->descr, name, sizeof(kt->descr));
+ RB_INIT(&kt->krt);
+ RB_INIT(&kt->krt6);
+ RB_INIT(&kt->knt);
+ TAILQ_INIT(&kt->krn);
+ kt->fib_conf = kt->fib_sync = fs;
+ kt->rtableid = rtableid;
+ kt->nhtableid = rdomid;
+ /* bump refcount of rdomain table for the nexthop lookups */
+ ktable_get(kt->nhtableid)->nhrefcnt++;
+ if (ifname) {
+ strlcpy(kt->ifmpe, ifname, IFNAMSIZ);
+ kt->ifindex = if_nametoindex(ifname);
+ }
+
+ /* ... and load it */
+ if (fetchtable(kt) == -1)
+ return (-1);
+ if (protect_lo(kt) == -1)
+ return (-1);
+
+ /* everything is up and running */
+ kt->state = RECONF_REINIT;
+ log_debug("new ktable %s for rtableid %d", name, rtableid);
+ return (0);
+}
+
+void
+ktable_free(u_int rtableid)
+{
+ struct ktable *kt, *nkt;
+
+ if ((kt = ktable_get(rtableid)) == NULL)
+ return;
+
+ /* decouple from kernel, no new routes will be entered from here */
+ kr_fib_decouple(kt->rtableid);
+
+ /* first unhook from the nexthop table */
+ nkt = ktable_get(kt->nhtableid);
+ nkt->nhrefcnt--;
+
+ /*
+ * Evil little details:
+ * If kt->nhrefcnt > 0 then kt == nkt and nothing needs to be done.
+ * If kt != nkt then kt->nhrefcnt must be 0 and kt must be killed.
+ * If nkt is no longer referenced it must be killed (possible double
+ * free so check that kt != nkt).
+ */
+ if (kt != nkt && nkt->nhrefcnt <= 0)
+ ktable_destroy(nkt);
+ if (kt->nhrefcnt <= 0)
+ ktable_destroy(kt);
+}
+
+void
+ktable_destroy(struct ktable *kt)
+{
+ /* decouple just to be sure, does not hurt */
+ kr_fib_decouple(kt->rtableid);
+
+ log_debug("freeing ktable %s rtableid %u", kt->descr, kt->rtableid);
+ knexthop_clear(kt);
+ kroute_clear(kt);
+ kroute6_clear(kt);
+
+ krt[kt->rtableid] = NULL;
+ free(kt);
+}
+
+struct ktable *
+ktable_get(u_int rtableid)
+{
+ if (rtableid >= krt_size)
+ return (NULL);
+ return (krt[rtableid]);
+}
+
+int
+ktable_update(u_int rtableid, char *name, char *ifname, int flags)
+{
+ struct ktable *kt, *rkt;
+ u_int rdomid;
+
+ if (!ktable_exists(rtableid, &rdomid))
+ fatalx("King Bula lost a table"); /* may not happen */
+
+ if (rdomid != rtableid || flags & F_RIB_NOFIB) {
+ rkt = ktable_get(rdomid);
+ if (rkt == NULL) {
+ char buf[32];
+ snprintf(buf, sizeof(buf), "rdomain_%d", rdomid);
+ if (ktable_new(rdomid, rdomid, buf, NULL, 0))
+ return (-1);
+ } else {
+ /* there is no need for full fib synchronisation if
+ * the table is only used for nexthop lookups.
+ */
+ if (rkt->state == RECONF_DELETE) {
+ rkt->fib_conf = 0;
+ rkt->state = RECONF_KEEP;
+ }
+ }
+ }
+
+ if (flags & (F_RIB_NOEVALUATE | F_RIB_NOFIB))
+ /* only rdomain table must exist */
+ return (0);
+
+ kt = ktable_get(rtableid);
+ if (kt == NULL) {
+ if (ktable_new(rtableid, rdomid, name, ifname,
+ !(flags & F_RIB_NOFIBSYNC)))
+ return (-1);
+ } else {
+ /* fib sync has higher preference then no sync */
+ if (kt->state == RECONF_DELETE) {
+ kt->fib_conf = !(flags & F_RIB_NOFIBSYNC);
+ kt->state = RECONF_KEEP;
+ } else if (!kt->fib_conf)
+ kt->fib_conf = !(flags & F_RIB_NOFIBSYNC);
+
+ strlcpy(kt->descr, name, sizeof(kt->descr));
+ }
+ return (0);
+}
+
+void
+ktable_preload(void)
+{
+ struct ktable *kt;
+ u_int i;
+
+ for (i = 0; i < krt_size; i++) {
+ if ((kt = ktable_get(i)) == NULL)
+ continue;
+ kt->state = RECONF_DELETE;
+ }
+}
+
+void
+ktable_postload(void)
+{
+ struct ktable *kt;
+ u_int i;
+
+ for (i = krt_size; i > 0; i--) {
+ if ((kt = ktable_get(i - 1)) == NULL)
+ continue;
+ if (kt->state == RECONF_DELETE)
+ ktable_free(i - 1);
+ else if (kt->state == RECONF_REINIT)
+ kt->fib_sync = kt->fib_conf;
+ }
}
int
-kr_change(struct kroute_label *kl)
+ktable_exists(u_int rtableid, u_int *rdomid)
+{
+#if !defined(__FreeBSD__) /* FreeBSD does not have NET_RT_TABLE. */
+ size_t len;
+ struct rt_tableinfo info;
+ int mib[6];
+
+ mib[0] = CTL_NET;
+ mib[1] = AF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_TABLE;
+ mib[5] = rtableid;
+
+ len = sizeof(info);
+ if (sysctl(mib, 6, &info, &len, NULL, 0) == -1) {
+ if (errno == ENOENT)
+ /* table nonexistent */
+ return (0);
+ log_warn("sysctl");
+ /* must return 0 so that the table is considered non-existent */
+ return (0);
+ }
+ if (rdomid)
+ *rdomid = info.rti_domainid;
+#else
+ *rdomid = 0;
+#endif
+ return (1);
+}
+
+int
+kr_change(u_int rtableid, struct kroute_full *kl)
+{
+ struct ktable *kt;
+
+ if ((kt = ktable_get(rtableid)) == NULL)
+ /* too noisy during reloads, just ignore */
+ return (0);
+ switch (kl->prefix.aid) {
+ case AID_INET:
+ return (kr4_change(kt, kl));
+ case AID_INET6:
+ return (kr6_change(kt, kl));
+ case AID_VPN_IPv4:
+ return (krVPN4_change(kt, kl));
+ }
+ log_warnx("kr_change: not handled AID");
+ return (-1);
+}
+
+int
+kr4_change(struct ktable *kt, struct kroute_full *kl)
{
struct kroute_node *kr;
int action = RTM_ADD;
+ u_int16_t labelid;
- if ((kr = kroute_find(kl->kr.prefix.s_addr, kl->kr.prefixlen, RTP_BGP))
- != NULL)
+ if ((kr = kroute_find(kt, kl->prefix.v4.s_addr, kl->prefixlen,
+ RTP_BGP)) != NULL)
action = RTM_CHANGE;
/* nexthop within 127/8 -> ignore silently */
- if ((kl->kr.nexthop.s_addr & htonl(IN_CLASSA_NET)) ==
+ if ((kl->nexthop.v4.s_addr & htonl(IN_CLASSA_NET)) ==
htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
return (0);
- if (kr)
- rtlabel_unref(kr->r.labelid);
- kl->kr.labelid = rtlabel_name2id(kl->label);
+ labelid = rtlabel_name2id(kl->label);
/* for blackhole and reject routes nexthop needs to be 127.0.0.1 */
- if (kl->kr.flags & (F_BLACKHOLE|F_REJECT))
- kl->kr.nexthop.s_addr = htonl(INADDR_LOOPBACK);
-
- if (send_rtmsg(kr_state.fd, action, &kl->kr) == -1)
- return (-1);
+ if (kl->flags & (F_BLACKHOLE|F_REJECT))
+ kl->nexthop.v4.s_addr = htonl(INADDR_LOOPBACK);
if (action == RTM_ADD) {
if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
log_warn("kr_change");
return (-1);
}
- kr->r.prefix.s_addr = kl->kr.prefix.s_addr;
- 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.prefix.s_addr = kl->prefix.v4.s_addr;
+ kr->r.prefixlen = kl->prefixlen;
+ kr->r.nexthop.s_addr = kl->nexthop.v4.s_addr;
+ kr->r.flags = kl->flags | F_BGPD_INSERTED;
kr->r.priority = RTP_BGP;
- kr->r.labelid = kl->kr.labelid;
+ kr->r.labelid = labelid;
- if (kroute_insert(kr) == -1)
+ if (kroute_insert(kt, kr) == -1)
free(kr);
} else {
- kr->r.nexthop.s_addr = kl->kr.nexthop.s_addr;
- kr->r.labelid = kl->kr.labelid;
- if (kl->kr.flags & F_BLACKHOLE)
+ kr->r.nexthop.s_addr = kl->nexthop.v4.s_addr;
+ rtlabel_unref(kr->r.labelid);
+ kr->r.labelid = labelid;
+ if (kl->flags & F_BLACKHOLE)
kr->r.flags |= F_BLACKHOLE;
else
kr->r.flags &= ~F_BLACKHOLE;
- if (kl->kr.flags & F_REJECT)
+ if (kl->flags & F_REJECT)
kr->r.flags |= F_REJECT;
else
kr->r.flags &= ~F_REJECT;
}
+ if (send_rtmsg(kr_state.fd, action, kt, &kr->r) == -1)
+ return (-1);
+
return (0);
}
int
-kr_delete(struct kroute_label *kl)
+kr6_change(struct ktable *kt, struct kroute_full *kl)
{
- struct kroute_node *kr;
+ struct kroute6_node *kr6;
+ struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT;
+ int action = RTM_ADD;
+ u_int16_t labelid;
- if ((kr = kroute_find(kl->kr.prefix.s_addr, kl->kr.prefixlen, RTP_BGP))
- == NULL)
- return (0);
+ if ((kr6 = kroute6_find(kt, &kl->prefix.v6, kl->prefixlen, RTP_BGP)) !=
+ NULL)
+ action = RTM_CHANGE;
- if (!(kr->r.flags & F_BGPD_INSERTED))
+ /* nexthop to loopback -> ignore silently */
+ if (IN6_IS_ADDR_LOOPBACK(&kl->nexthop.v6))
return (0);
- /* nexthop within 127/8 -> ignore silently */
- if ((kl->kr.nexthop.s_addr & htonl(IN_CLASSA_NET)) ==
- htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
- return (0);
+ labelid = rtlabel_name2id(kl->label);
- if (send_rtmsg(kr_state.fd, RTM_DELETE, &kl->kr) == -1)
- return (-1);
+ /* for blackhole and reject routes nexthop needs to be ::1 */
+ if (kl->flags & (F_BLACKHOLE|F_REJECT))
+ bcopy(&lo6, &kl->nexthop.v6, sizeof(kl->nexthop.v6));
- rtlabel_unref(kl->kr.labelid);
+ if (action == RTM_ADD) {
+ if ((kr6 = calloc(1, sizeof(struct kroute6_node))) == NULL) {
+ log_warn("kr_change");
+ return (-1);
+ }
+ memcpy(&kr6->r.prefix, &kl->prefix.v6, sizeof(struct in6_addr));
+ kr6->r.prefixlen = kl->prefixlen;
+ memcpy(&kr6->r.nexthop, &kl->nexthop.v6,
+ sizeof(struct in6_addr));
+ kr6->r.flags = kl->flags | F_BGPD_INSERTED;
+ kr6->r.priority = RTP_BGP;
+ kr6->r.labelid = labelid;
+
+ if (kroute6_insert(kt, kr6) == -1)
+ free(kr6);
+ } else {
+ memcpy(&kr6->r.nexthop, &kl->nexthop.v6,
+ sizeof(struct in6_addr));
+ rtlabel_unref(kr6->r.labelid);
+ kr6->r.labelid = labelid;
+ if (kl->flags & F_BLACKHOLE)
+ kr6->r.flags |= F_BLACKHOLE;
+ else
+ kr6->r.flags &= ~F_BLACKHOLE;
+ if (kl->flags & F_REJECT)
+ kr6->r.flags |= F_REJECT;
+ else
+ kr6->r.flags &= ~F_REJECT;
+ }
- if (kroute_remove(kr) == -1)
+ if (send_rt6msg(kr_state.fd, action, kt, &kr6->r) == -1)
return (-1);
return (0);
}
int
-kr6_change(struct kroute6_label *kl)
+krVPN4_change(struct ktable *kt, struct kroute_full *kl)
{
- struct kroute6_node *kr6;
+ struct kroute_node *kr;
int action = RTM_ADD;
- struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT;
+ u_int32_t mplslabel = 0;
+ u_int16_t labelid;
- if ((kr6 = kroute6_find(&kl->kr.prefix, kl->kr.prefixlen, RTP_BGP))
- != NULL)
+ if ((kr = kroute_find(kt, kl->prefix.vpn4.addr.s_addr, kl->prefixlen,
+ RTP_BGP)) != NULL)
action = RTM_CHANGE;
- /* nexthop to loopback -> ignore silently */
- if (IN6_IS_ADDR_LOOPBACK(&kl->kr.nexthop))
+ /* nexthop within 127/8 -> ignore silently */
+ if ((kl->nexthop.v4.s_addr & htonl(IN_CLASSA_NET)) ==
+ htonl(INADDR_LOOPBACK & IN_CLASSA_NET))
return (0);
- if (kr6)
- rtlabel_unref(kr6->r.labelid);
- kl->kr.labelid = rtlabel_name2id(kl->label);
+ /* only single MPLS label are supported for now */
+ if (kl->prefix.vpn4.labellen != 3) {
+ log_warnx("krVPN4_change: %s/%u has not a single label",
+ log_addr(&kl->prefix), kl->prefixlen);
+ return (0);
+ }
+ mplslabel = (kl->prefix.vpn4.labelstack[0] << 24) |
+ (kl->prefix.vpn4.labelstack[1] << 16) |
+ (kl->prefix.vpn4.labelstack[2] << 8);
+ mplslabel = htonl(mplslabel);
- /* for blackhole and reject routes nexthop needs to be ::1 */
- if (kl->kr.flags & (F_BLACKHOLE|F_REJECT))
- bcopy(&lo6, &kl->kr.nexthop, sizeof(kl->kr.nexthop));
+ labelid = rtlabel_name2id(kl->label);
- if (send_rt6msg(kr_state.fd, action, &kl->kr) == -1)
- return (-1);
+ /* for blackhole and reject routes nexthop needs to be 127.0.0.1 */
+ if (kl->flags & (F_BLACKHOLE|F_REJECT))
+ kl->nexthop.v4.s_addr = htonl(INADDR_LOOPBACK);
if (action == RTM_ADD) {
- if ((kr6 = calloc(1, sizeof(struct kroute6_node))) == NULL) {
+ if ((kr = calloc(1, sizeof(struct kroute_node))) == NULL) {
log_warn("kr_change");
return (-1);
}
- memcpy(&kr6->r.prefix, &kl->kr.prefix,
- sizeof(struct in6_addr));
- kr6->r.prefixlen = kl->kr.prefixlen;
- 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;
+ kr->r.prefix.s_addr = kl->prefix.vpn4.addr.s_addr;
+ kr->r.prefixlen = kl->prefixlen;
+ kr->r.nexthop.s_addr = kl->nexthop.v4.s_addr;
+ kr->r.flags = kl->flags | F_BGPD_INSERTED | F_MPLS;
+ kr->r.priority = RTP_BGP;
+ kr->r.labelid = labelid;
+ kr->r.mplslabel = mplslabel;
- if (kroute6_insert(kr6) == -1)
- free(kr6);
+ if (kroute_insert(kt, kr) == -1)
+ free(kr);
} else {
- memcpy(&kr6->r.nexthop, &kl->kr.nexthop,
- sizeof(struct in6_addr));
- kr6->r.labelid = kl->kr.labelid;
- if (kl->kr.flags & F_BLACKHOLE)
- kr6->r.flags |= F_BLACKHOLE;
+ kr->r.mplslabel = mplslabel;
+ kr->r.nexthop.s_addr = kl->nexthop.v4.s_addr;
+ rtlabel_unref(kr->r.labelid);
+ kr->r.labelid = labelid;
+ if (kl->flags & F_BLACKHOLE)
+ kr->r.flags |= F_BLACKHOLE;
else
- kr6->r.flags &= ~F_BLACKHOLE;
- if (kl->kr.flags & F_REJECT)
- kr6->r.flags |= F_REJECT;
+ kr->r.flags &= ~F_BLACKHOLE;
+ if (kl->flags & F_REJECT)
+ kr->r.flags |= F_REJECT;
else
- kr6->r.flags &= ~F_REJECT;
+ kr->r.flags &= ~F_REJECT;
}
+ if (send_rtmsg(kr_state.fd, action, kt, &kr->r) == -1)
+ return (-1);
+
return (0);
}
int
-kr6_delete(struct kroute6_label *kl)
+kr_delete(u_int rtableid, struct kroute_full *kl)
+{
+ struct ktable *kt;
+
+ if ((kt = ktable_get(rtableid)) == NULL)
+ /* too noisy during reloads, just ignore */
+ return (0);
+
+ switch (kl->prefix.aid) {
+ case AID_INET:
+ return (kr4_delete(kt, kl));
+ case AID_INET6:
+ return (kr6_delete(kt, kl));
+ case AID_VPN_IPv4:
+ return (krVPN4_delete(kt, kl));
+ }
+ log_warnx("kr_change: not handled AID");
+ return (-1);
+}
+
+int
+kr4_delete(struct ktable *kt, struct kroute_full *kl)
+{
+ struct kroute_node *kr;
+
+ if ((kr = kroute_find(kt, kl->prefix.v4.s_addr, kl->prefixlen,
+ RTP_BGP)) == NULL)
+ return (0);
+
+ if (!(kr->r.flags & F_BGPD_INSERTED))
+ return (0);
+
+ if (send_rtmsg(kr_state.fd, RTM_DELETE, kt, &kr->r) == -1)
+ return (-1);
+
+ rtlabel_unref(kr->r.labelid);
+
+ if (kroute_remove(kt, kr) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+kr6_delete(struct ktable *kt, struct kroute_full *kl)
{
struct kroute6_node *kr6;
- if ((kr6 = kroute6_find(&kl->kr.prefix, kl->kr.prefixlen, RTP_BGP))
- == NULL)
+ if ((kr6 = kroute6_find(kt, &kl->prefix.v6, kl->prefixlen, RTP_BGP)) ==
+ NULL)
return (0);
if (!(kr6->r.flags & F_BGPD_INSERTED))
return (0);
- /* nexthop to loopback -> ignore silently */
- if (IN6_IS_ADDR_LOOPBACK(&kl->kr.nexthop))
+ if (send_rt6msg(kr_state.fd, RTM_DELETE, kt, &kr6->r) == -1)
+ return (-1);
+
+ rtlabel_unref(kr6->r.labelid);
+
+ if (kroute6_remove(kt, kr6) == -1)
+ return (-1);
+
+ return (0);
+}
+
+int
+krVPN4_delete(struct ktable *kt, struct kroute_full *kl)
+{
+ struct kroute_node *kr;
+
+ if ((kr = kroute_find(kt, kl->prefix.vpn4.addr.s_addr, kl->prefixlen,
+ RTP_BGP)) == NULL)
return (0);
- if (send_rt6msg(kr_state.fd, RTM_DELETE, &kl->kr) == -1)
+ if (!(kr->r.flags & F_BGPD_INSERTED))
+ return (0);
+
+ if (send_rtmsg(kr_state.fd, RTM_DELETE, kt, &kr->r) == -1)
return (-1);
- rtlabel_unref(kl->kr.labelid);
+ rtlabel_unref(kr->r.labelid);
- if (kroute6_remove(kr6) == -1)
+ if (kroute_remove(kt, kr) == -1)
return (-1);
return (0);
@@ -394,53 +759,63 @@ kr6_delete(struct kroute6_label *kl)
void
kr_shutdown(void)
{
- kr_fib_decouple();
- knexthop_clear();
- kroute_clear();
- kroute6_clear();
+ u_int i;
+
+ for (i = krt_size; i > 0; i--)
+ ktable_free(i - 1);
kif_clear();
}
void
-kr_fib_couple(void)
+kr_fib_couple(u_int rtableid)
{
+ struct ktable *kt;
struct kroute_node *kr;
struct kroute6_node *kr6;
- if (kr_state.fib_sync == 1) /* already coupled */
+ if ((kt = ktable_get(rtableid)) == NULL) /* table does not exist */
return;
- kr_state.fib_sync = 1;
+ if (kt->fib_sync) /* already coupled */
+ return;
+
+ kt->fib_sync = 1;
- RB_FOREACH(kr, kroute_tree, &krt)
+ RB_FOREACH(kr, kroute_tree, &kt->krt)
if ((kr->r.flags & F_BGPD_INSERTED))
- send_rtmsg(kr_state.fd, RTM_ADD, &kr->r);
- RB_FOREACH(kr6, kroute6_tree, &krt6)
+ send_rtmsg(kr_state.fd, RTM_ADD, kt, &kr->r);
+ RB_FOREACH(kr6, kroute6_tree, &kt->krt6)
if ((kr6->r.flags & F_BGPD_INSERTED))
- send_rt6msg(kr_state.fd, RTM_ADD, &kr6->r);
+ send_rt6msg(kr_state.fd, RTM_ADD, kt, &kr6->r);
- log_info("kernel routing table coupled");
+ log_info("kernel routing table %u (%s) coupled", kt->rtableid,
+ kt->descr);
}
void
-kr_fib_decouple(void)
+kr_fib_decouple(u_int rtableid)
{
+ struct ktable *kt;
struct kroute_node *kr;
struct kroute6_node *kr6;
- if (kr_state.fib_sync == 0) /* already decoupled */
+ if ((kt = ktable_get(rtableid)) == NULL) /* table does not exist */
+ return;
+
+ if (!kt->fib_sync) /* already decoupled */
return;
- RB_FOREACH(kr, kroute_tree, &krt)
+ RB_FOREACH(kr, kroute_tree, &kt->krt)
if ((kr->r.flags & F_BGPD_INSERTED))
- send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
- RB_FOREACH(kr6, kroute6_tree, &krt6)
+ send_rtmsg(kr_state.fd, RTM_DELETE, kt, &kr->r);
+ RB_FOREACH(kr6, kroute6_tree, &kt->krt6)
if ((kr6->r.flags & F_BGPD_INSERTED))
- send_rt6msg(kr_state.fd, RTM_DELETE, &kr6->r);
+ send_rt6msg(kr_state.fd, RTM_DELETE, kt, &kr6->r);
- kr_state.fib_sync = 0;
+ kt->fib_sync = 0;
- log_info("kernel routing table decoupled");
+ log_info("kernel routing table %u (%s) decoupled", kt->rtableid,
+ kt->descr);
}
int
@@ -450,41 +825,18 @@ kr_dispatch_msg(void)
}
int
-kr_nexthop_add(struct bgpd_addr *addr)
+kr_nexthop_add(u_int rtableid, struct bgpd_addr *addr)
{
+ struct ktable *kt;
struct knexthop_node *h;
- if ((h = knexthop_find(addr)) != NULL) {
+ if ((kt = ktable_get(rtableid)) == NULL) {
+ log_warnx("kr_nexthop_add: non-existent rtableid %d", rtableid);
+ return (0);
+ }
+ if ((h = knexthop_find(kt, addr)) != NULL) {
/* should not happen... this is actually an error path */
- struct kroute_nexthop nh;
- struct kroute_node *k;
- struct kroute6_node *k6;
-
- bzero(&nh, sizeof(nh));
- memcpy(&nh.nexthop, addr, sizeof(nh.nexthop));
- nh.valid = 1;
- if (h->kroute != NULL && addr->af == AF_INET) {
- k = h->kroute;
- nh.connected = k->r.flags & F_CONNECTED;
- if (k->r.nexthop.s_addr != 0) {
- nh.gateway.af = AF_INET;
- nh.gateway.v4.s_addr =
- k->r.nexthop.s_addr;
- }
- memcpy(&nh.kr.kr4, &k->r, sizeof(nh.kr.kr4));
- } else if (h->kroute != NULL && addr->af == AF_INET6) {
- k6 = h->kroute;
- nh.connected = k6->r.flags & F_CONNECTED;
- if (memcmp(&k6->r.nexthop, &in6addr_any,
- sizeof(struct in6_addr)) != 0) {
- nh.gateway.af = AF_INET6;
- memcpy(&nh.gateway.v6, &k6->r.nexthop,
- sizeof(struct in6_addr));
- }
- memcpy(&nh.kr.kr6, &k6->r, sizeof(nh.kr.kr6));
- }
-
- send_nexthop_update(&nh);
+ knexthop_send_update(h);
} else {
if ((h = calloc(1, sizeof(struct knexthop_node))) == NULL) {
log_warn("kr_nexthop_add");
@@ -492,7 +844,7 @@ kr_nexthop_add(struct bgpd_addr *addr)
}
memcpy(&h->nexthop, addr, sizeof(h->nexthop));
- if (knexthop_insert(h) == -1)
+ if (knexthop_insert(kt, h) == -1)
return (-1);
}
@@ -500,19 +852,26 @@ kr_nexthop_add(struct bgpd_addr *addr)
}
void
-kr_nexthop_delete(struct bgpd_addr *addr)
+kr_nexthop_delete(u_int rtableid, struct bgpd_addr *addr)
{
+ struct ktable *kt;
struct knexthop_node *kn;
- if ((kn = knexthop_find(addr)) == NULL)
+ if ((kt = ktable_get(rtableid)) == NULL) {
+ log_warnx("kr_nexthop_delete: non-existent rtableid %d",
+ rtableid);
+ return;
+ }
+ if ((kn = knexthop_find(kt, addr)) == NULL)
return;
- knexthop_remove(kn);
+ knexthop_remove(kt, kn);
}
void
kr_show_route(struct imsg *imsg)
{
+ struct ktable *kt;
struct kroute_node *kr, *kn;
struct kroute6_node *kr6, *kn6;
struct bgpd_addr *addr;
@@ -521,6 +880,7 @@ kr_show_route(struct imsg *imsg)
struct ctl_show_nexthop snh;
struct knexthop_node *h;
struct kif_node *kif;
+ u_int i;
u_short ifindex = 0;
switch (imsg->hdr.type) {
@@ -528,70 +888,96 @@ kr_show_route(struct imsg *imsg)
if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(flags) +
sizeof(af)) {
log_warnx("kr_show_route: wrong imsg len");
- return;
+ break;
+ }
+ kt = ktable_get(imsg->hdr.peerid);
+ if (kt == NULL) {
+ log_warnx("kr_show_route: table %u does not exist",
+ imsg->hdr.peerid);
+ break;
}
memcpy(&flags, imsg->data, sizeof(flags));
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) {
- kn = kr;
- do {
- send_imsg_session(
- IMSG_CTL_KROUTE,
- imsg->hdr.pid, &kn->r,
- sizeof(kn->r));
- } while ((kn = kn->next) != NULL);
- }
+ RB_FOREACH(kr, kroute_tree, &kt->krt) {
+ if (flags && (kr->r.flags & flags) == 0)
+ continue;
+ kn = kr;
+ do {
+ send_imsg_session(IMSG_CTL_KROUTE,
+ imsg->hdr.pid, kr_tofull(&kn->r),
+ sizeof(struct kroute_full));
+ } while ((kn = kn->next) != NULL);
+ }
if (!af || af == AF_INET6)
- RB_FOREACH(kr6, kroute6_tree, &krt6)
- 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);
- }
+ RB_FOREACH(kr6, kroute6_tree, &kt->krt6) {
+ if (flags && (kr6->r.flags & flags) == 0)
+ continue;
+ kn6 = kr6;
+ do {
+ send_imsg_session(IMSG_CTL_KROUTE,
+ imsg->hdr.pid, kr6_tofull(&kn6->r),
+ sizeof(struct kroute_full));
+ } while ((kn6 = kn6->next) != NULL);
+ }
break;
case IMSG_CTL_KROUTE_ADDR:
if (imsg->hdr.len != IMSG_HEADER_SIZE +
sizeof(struct bgpd_addr)) {
log_warnx("kr_show_route: wrong imsg len");
- return;
+ break;
+ }
+ kt = ktable_get(imsg->hdr.peerid);
+ if (kt == NULL) {
+ log_warnx("kr_show_route: table %u does not exist",
+ imsg->hdr.peerid);
+ break;
}
addr = imsg->data;
kr = NULL;
- switch (addr->af) {
- case AF_INET:
- kr = kroute_match(addr->v4.s_addr, 1);
+ switch (addr->aid) {
+ case AID_INET:
+ kr = kroute_match(kt, addr->v4.s_addr, 1);
if (kr != NULL)
send_imsg_session(IMSG_CTL_KROUTE,
- imsg->hdr.pid, &kr->r, sizeof(kr->r));
+ imsg->hdr.pid, kr_tofull(&kr->r),
+ sizeof(struct kroute_full));
break;
- case AF_INET6:
- kr6 = kroute6_match(&addr->v6, 1);
+ case AID_INET6:
+ kr6 = kroute6_match(kt, &addr->v6, 1);
if (kr6 != NULL)
- send_imsg_session(IMSG_CTL_KROUTE6,
- imsg->hdr.pid, &kr6->r, sizeof(kr6->r));
+ send_imsg_session(IMSG_CTL_KROUTE,
+ imsg->hdr.pid, kr6_tofull(&kr6->r),
+ sizeof(struct kroute_full));
break;
}
break;
case IMSG_CTL_SHOW_NEXTHOP:
- RB_FOREACH(h, knexthop_tree, &knt) {
+ kt = ktable_get(imsg->hdr.peerid);
+ if (kt == NULL) {
+ log_warnx("kr_show_route: table %u does not exist",
+ imsg->hdr.peerid);
+ break;
+ }
+ RB_FOREACH(h, knexthop_tree, KT2KNT(kt)) {
bzero(&snh, sizeof(snh));
memcpy(&snh.addr, &h->nexthop, sizeof(snh.addr));
if (h->kroute != NULL) {
- switch (h->nexthop.af) {
- case AF_INET:
+ switch (h->nexthop.aid) {
+ case AID_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:
+ case AID_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;
}
@@ -608,6 +994,24 @@ kr_show_route(struct imsg *imsg)
send_imsg_session(IMSG_CTL_SHOW_INTERFACE,
imsg->hdr.pid, &kif->k, sizeof(kif->k));
break;
+ case IMSG_CTL_SHOW_FIB_TABLES:
+ for (i = 0; i < krt_size; i++) {
+ struct ktable ktab;
+
+ if ((kt = ktable_get(i)) == NULL)
+ continue;
+
+ ktab = *kt;
+ /* do not leak internal information */
+ RB_INIT(&ktab.krt);
+ RB_INIT(&ktab.krt6);
+ RB_INIT(&ktab.knt);
+ TAILQ_INIT(&ktab.krn);
+
+ send_imsg_session(IMSG_CTL_SHOW_FIB_TABLES,
+ imsg->hdr.pid, &ktab, sizeof(ktab));
+ }
+ break;
default: /* nada */
break;
}
@@ -628,21 +1032,152 @@ kr_ifinfo(char *ifname)
}
}
-struct redist_node {
- LIST_ENTRY(redist_node) entry;
- struct kroute *kr;
- struct kroute6 *kr6;
-};
+void
+kr_net_delete(struct network *n)
+{
+ filterset_free(&n->net.attrset);
+ free(n);
+}
+
+struct network *
+kr_net_match(struct ktable *kt, struct kroute *kr)
+{
+ struct network *xn;
+
+ TAILQ_FOREACH(xn, &kt->krn, entry) {
+ if (xn->net.prefix.aid != AID_INET)
+ continue;
+ switch (xn->net.type) {
+ case NETWORK_DEFAULT:
+ if (xn->net.prefixlen == kr->prefixlen &&
+ xn->net.prefix.v4.s_addr == kr->prefix.s_addr)
+ /* static match already redistributed */
+ return (NULL);
+ break;
+ case NETWORK_STATIC:
+ if (kr->flags & F_STATIC)
+ return (xn);
+ break;
+ case NETWORK_CONNECTED:
+ if (kr->flags & F_CONNECTED)
+ return (xn);
+ break;
+ case NETWORK_MRTCLONE:
+ /* can not happen */
+ break;
+ }
+ }
+ return (NULL);
+}
+struct network *
+kr_net_match6(struct ktable *kt, struct kroute6 *kr6)
+{
+ struct network *xn;
+
+ TAILQ_FOREACH(xn, &kt->krn, entry) {
+ if (xn->net.prefix.aid != AID_INET6)
+ continue;
+ switch (xn->net.type) {
+ case NETWORK_DEFAULT:
+ if (xn->net.prefixlen == kr6->prefixlen &&
+ memcmp(&xn->net.prefix.v6, &kr6->prefix,
+ sizeof(struct in6_addr)) == 0)
+ /* static match already redistributed */
+ return (NULL);
+ break;
+ case NETWORK_STATIC:
+ if (kr6->flags & F_STATIC)
+ return (xn);
+ break;
+ case NETWORK_CONNECTED:
+ if (kr6->flags & F_CONNECTED)
+ return (xn);
+ break;
+ case NETWORK_MRTCLONE:
+ /* can not happen */
+ break;
+ }
+ }
+ return (NULL);
+}
-LIST_HEAD(, redist_node) redistlist;
+struct network *
+kr_net_find(struct ktable *kt, struct network *n)
+{
+ struct network *xn;
+
+ TAILQ_FOREACH(xn, &kt->krn, entry) {
+ if (n->net.type != xn->net.type ||
+ n->net.prefixlen != xn->net.prefixlen ||
+ n->net.rtableid != xn->net.rtableid)
+ continue;
+ if (memcmp(&n->net.prefix, &xn->net.prefix,
+ sizeof(n->net.prefix)) == 0)
+ return (xn);
+ }
+ return (NULL);
+}
int
-kr_redistribute(int type, struct kroute *kr)
+kr_net_reload(u_int rtableid, struct network_head *nh)
{
- struct redist_node *rn;
+ struct network *n, *xn;
+ struct ktable *kt;
+
+ if ((kt = ktable_get(rtableid)) == NULL) {
+ log_warnx("kr_net_reload: non-existent rtableid %d", rtableid);
+ return (-1);
+ }
+
+ TAILQ_FOREACH(n, &kt->krn, entry)
+ n->net.old = 1;
+
+ while ((n = TAILQ_FIRST(nh)) != NULL) {
+ TAILQ_REMOVE(nh, n, entry);
+ n->net.old = 0;
+ n->net.rtableid = rtableid;
+ xn = kr_net_find(kt, n);
+ if (xn) {
+ xn->net.old = 0;
+ filterset_free(&xn->net.attrset);
+ filterset_move(&n->net.attrset, &xn->net.attrset);
+ kr_net_delete(n);
+ } else
+ TAILQ_INSERT_TAIL(&kt->krn, n, entry);
+ }
+
+ for (n = TAILQ_FIRST(&kt->krn); n != NULL; n = xn) {
+ xn = TAILQ_NEXT(n, entry);
+ if (n->net.old) {
+ if (n->net.type == NETWORK_DEFAULT)
+ if (send_network(IMSG_NETWORK_REMOVE, &n->net,
+ NULL))
+ return (-1);
+ TAILQ_REMOVE(&kt->krn, n, entry);
+ kr_net_delete(n);
+ }
+ }
+
+ return (0);
+}
+
+int
+kr_redistribute(int type, struct ktable *kt, struct kroute *kr)
+{
+ struct network *match;
+ struct network_config net;
u_int32_t a;
+ /* shortcut for removals */
+ if (type == IMSG_NETWORK_REMOVE) {
+ if (!(kr->flags & F_REDISTRIBUTED))
+ return (0); /* no match, don't redistribute */
+ kr->flags &= ~F_REDISTRIBUTED;
+ match = NULL;
+ goto sendit;
+ }
+
if (!(kr->flags & F_KERNEL))
return (0);
@@ -670,41 +1205,40 @@ kr_redistribute(int type, struct kroute
if (kr->prefix.s_addr == INADDR_ANY && kr->prefixlen == 0)
return (0);
- /* Add or delete kr from list ... */
- LIST_FOREACH(rn, &redistlist, entry)
- if (rn->kr == kr)
- break;
-
- switch (type) {
- case IMSG_NETWORK_ADD:
- if (rn == NULL) {
- if ((rn = calloc(1, sizeof(struct redist_node))) ==
- NULL) {
- log_warn("kr_redistribute");
- return (-1);
- }
- rn->kr = kr;
- LIST_INSERT_HEAD(&redistlist, rn, entry);
- }
- break;
- case IMSG_NETWORK_REMOVE:
- if (rn != NULL) {
- LIST_REMOVE(rn, entry);
- free(rn);
- }
- break;
- default:
- errno = EINVAL;
- return (-1);
- }
-
- return (bgpd_redistribute(type, kr, NULL));
+ match = kr_net_match(kt, kr);
+ if (match == NULL) {
+ if (!(kr->flags & F_REDISTRIBUTED))
+ return (0); /* no match, don't redistribute */
+ /* route no longer matches but is redistributed, so remove */
+ kr->flags &= ~F_REDISTRIBUTED;
+ type = IMSG_NETWORK_REMOVE;
+ } else
+ kr->flags |= F_REDISTRIBUTED;
+
+sendit:
+ bzero(&net, sizeof(net));
+ net.prefix.aid = AID_INET;
+ net.prefix.v4.s_addr = kr->prefix.s_addr;
+ net.prefixlen = kr->prefixlen;
+ net.rtableid = kt->rtableid;
+
+ return (send_network(type, &net, match ? &match->net.attrset : NULL));
}
int
-kr_redistribute6(int type, struct kroute6 *kr6)
-{
- struct redist_node *rn;
+kr_redistribute6(int type, struct ktable *kt, struct kroute6 *kr6)
+{
+ struct network *match;
+ struct network_config net;
+
+ /* shortcut for removals */
+ if (type == IMSG_NETWORK_REMOVE) {
+ if (!(kr6->flags & F_REDISTRIBUTED))
+ return (0); /* no match, don't redistribute */
+ kr6->flags &= ~F_REDISTRIBUTED;
+ match = NULL;
+ goto sendit;
+ }
if (!(kr6->flags & F_KERNEL))
return (0);
@@ -736,60 +1270,107 @@ kr_redistribute6(int type, struct kroute
* never allow ::/0 the default route can only be redistributed
* with announce default.
*/
- if (memcmp(&kr6->prefix, &in6addr_any, sizeof(struct in6_addr)) == 0 &&
- kr6->prefixlen == 0)
+ if (kr6->prefixlen == 0 &&
+ memcmp(&kr6->prefix, &in6addr_any, sizeof(struct in6_addr)) == 0)
return (0);
- /* Add or delete kr from list ...
- * using a linear list to store the redistributed networks will hurt
- * as soon as redistribute ospf comes but until then keep it simple.
- */
- LIST_FOREACH(rn, &redistlist, entry)
- if (rn->kr6 == kr6)
- break;
-
- switch (type) {
- case IMSG_NETWORK_ADD:
- if (rn == NULL) {
- if ((rn = calloc(1, sizeof(struct redist_node))) ==
- NULL) {
- log_warn("kr_redistribute");
- return (-1);
- }
- rn->kr6 = kr6;
- LIST_INSERT_HEAD(&redistlist, rn, entry);
- }
- break;
- case IMSG_NETWORK_REMOVE:
- if (rn != NULL) {
- LIST_REMOVE(rn, entry);
- free(rn);
- }
- break;
- default:
- errno = EINVAL;
- return (-1);
- }
+ match = kr_net_match6(kt, kr6);
+ if (match == NULL) {
+ if (!(kr6->flags & F_REDISTRIBUTED))
+ return (0); /* no match, don't redistribute */
+ /* route no longer matches but is redistributed, so remove */
+ kr6->flags &= ~F_REDISTRIBUTED;
+ type = IMSG_NETWORK_REMOVE;
+ } else
+ kr6->flags |= F_REDISTRIBUTED;
+sendit:
+ bzero(&net, sizeof(net));
+ net.prefix.aid = AID_INET6;
+ memcpy(&net.prefix.v6, &kr6->prefix, sizeof(struct in6_addr));
+ net.prefixlen = kr6->prefixlen;
+ net.rtableid = kt->rtableid;
- return (bgpd_redistribute(type, NULL, kr6));
+ return (send_network(type, &net, match ? &match->net.attrset : NULL));
}
int
kr_reload(void)
{
- struct redist_node *rn;
+ struct ktable *kt;
+ struct kroute_node *kr;
+ struct kroute6_node *kr6;
struct knexthop_node *nh;
+ struct network *n;
+ u_int rid;
+ int hasdyn = 0;
- LIST_FOREACH(rn, &redistlist, entry)
- if (bgpd_redistribute(IMSG_NETWORK_ADD, rn->kr, rn->kr6) == -1)
- return (-1);
+ for (rid = 0; rid < krt_size; rid++) {
+ if ((kt = ktable_get(rid)) == NULL)
+ continue;
+
+ RB_FOREACH(nh, knexthop_tree, KT2KNT(kt))
+ knexthop_validate(kt, nh);
- RB_FOREACH(nh, knexthop_tree, &knt)
- knexthop_validate(nh);
+ TAILQ_FOREACH(n, &kt->krn, entry)
+ if (n->net.type == NETWORK_DEFAULT) {
+ if (send_network(IMSG_NETWORK_ADD, &n->net,
+ &n->net.attrset))
+ return (-1);
+ } else
+ hasdyn = 1;
+
+ if (hasdyn) {
+ /* only evaluate the full tree if we need */
+ RB_FOREACH(kr, kroute_tree, &kt->krt)
+ kr_redistribute(IMSG_NETWORK_ADD, kt, &kr->r);
+ RB_FOREACH(kr6, kroute6_tree, &kt->krt6)
+ kr_redistribute6(IMSG_NETWORK_ADD, kt, &kr6->r);
+ }
+ }
return (0);
}
+struct kroute_full *
+kr_tofull(struct kroute *kr)
+{
+ static struct kroute_full kf;
+
+ bzero(&kf, sizeof(kf));
+
+ kf.prefix.aid = AID_INET;
+ kf.prefix.v4.s_addr = kr->prefix.s_addr;
+ kf.nexthop.aid = AID_INET;
+ kf.nexthop.v4.s_addr = kr->nexthop.s_addr;
+ strlcpy(kf.label, rtlabel_id2name(kr->labelid), sizeof(kf.label));
+ kf.flags = kr->flags;
+ kf.ifindex = kr->ifindex;
+ kf.prefixlen = kr->prefixlen;
+ kf.priority = kr->priority;
+
+ return (&kf);
+}
+
+struct kroute_full *
+kr6_tofull(struct kroute6 *kr6)
+{
+ static struct kroute_full kf;
+
+ bzero(&kf, sizeof(kf));
+
+ kf.prefix.aid = AID_INET6;
+ memcpy(&kf.prefix.v6, &kr6->prefix, sizeof(struct in6_addr));
+ kf.nexthop.aid = AID_INET6;
+ memcpy(&kf.nexthop.v6, &kr6->nexthop, sizeof(struct in6_addr));
+ strlcpy(kf.label, rtlabel_id2name(kr6->labelid), sizeof(kf.label));
+ kf.flags = kr6->flags;
+ kf.ifindex = kr6->ifindex;
+ kf.prefixlen = kr6->prefixlen;
+ kf.priority = kr6->priority;
+
+ return (&kf);
+}
+
/*
* RB-tree compare functions
*/
@@ -846,26 +1427,28 @@ kroute6_compare(struct kroute6_node *a,
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);
+ if (a->nexthop.aid != b->nexthop.aid)
+ return (b->nexthop.aid - a->nexthop.aid);
- switch (a->nexthop.af) {
- case AF_INET:
- if ((r = b->nexthop.addr32[0] - a->nexthop.addr32[0]) != 0)
- return (r);
+ switch (a->nexthop.aid) {
+ case AID_INET:
+ 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);
+ case AID_INET6:
+ 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);
@@ -883,7 +1466,8 @@ kif_compare(struct kif_node *a, struct k
*/
struct kroute_node *
-kroute_find(in_addr_t prefix, u_int8_t prefixlen, u_int8_t prio)
+kroute_find(struct ktable *kt, in_addr_t prefix, u_int8_t prefixlen,
+ u_int8_t prio)
{
struct kroute_node s;
struct kroute_node *kn, *tmp;
@@ -892,15 +1476,15 @@ kroute_find(in_addr_t prefix, u_int8_t p
s.r.prefixlen = prefixlen;
s.r.priority = prio;
- kn = RB_FIND(kroute_tree, &krt, &s);
+ kn = RB_FIND(kroute_tree, &kt->krt, &s);
if (kn && prio == RTP_ANY) {
- tmp = RB_PREV(kroute_tree, &krt, kn);
+ tmp = RB_PREV(kroute_tree, &kt->krt, kn);
while (tmp) {
if (kroute_compare(&s, tmp) == 0)
kn = tmp;
else
break;
- tmp = RB_PREV(kroute_tree, &krt, kn);
+ tmp = RB_PREV(kroute_tree, &kt->krt, kn);
}
}
return (kn);
@@ -927,13 +1511,13 @@ kroute_matchgw(struct kroute_node *kr, s
}
int
-kroute_insert(struct kroute_node *kr)
+kroute_insert(struct ktable *kt, struct kroute_node *kr)
{
struct kroute_node *krm;
struct knexthop_node *h;
in_addr_t mask, ina;
- if ((krm = RB_INSERT(kroute_tree, &krt, kr)) != NULL) {
+ if ((krm = RB_INSERT(kroute_tree, &kt->krt, kr)) != NULL) {
/* multipath route, add at end of list */
while (krm->next != NULL)
krm = krm->next;
@@ -941,13 +1525,14 @@ kroute_insert(struct kroute_node *kr)
kr->next = NULL; /* to be sure */
}
+ /* XXX this is wrong for nexthop validated via BGP */
if (kr->r.flags & F_KERNEL) {
mask = prefixlen2mask(kr->r.prefixlen);
ina = ntohl(kr->r.prefix.s_addr);
- RB_FOREACH(h, knexthop_tree, &knt)
- if (h->nexthop.af == AF_INET &&
+ RB_FOREACH(h, knexthop_tree, KT2KNT(kt))
+ if (h->nexthop.aid == AID_INET &&
(ntohl(h->nexthop.v4.s_addr) & mask) == ina)
- knexthop_validate(h);
+ knexthop_validate(kt, h);
if (kr->r.flags & F_CONNECTED)
if (kif_kr_insert(kr) == -1)
@@ -955,19 +1540,19 @@ kroute_insert(struct kroute_node *kr)
if (krm == NULL)
/* redistribute multipath routes only once */
- kr_redistribute(IMSG_NETWORK_ADD, &kr->r);
+ kr_redistribute(IMSG_NETWORK_ADD, kt, &kr->r);
}
return (0);
}
int
-kroute_remove(struct kroute_node *kr)
+kroute_remove(struct ktable *kt, struct kroute_node *kr)
{
struct kroute_node *krm;
struct knexthop_node *s;
- if ((krm = RB_FIND(kroute_tree, &krt, kr)) == NULL) {
+ if ((krm = RB_FIND(kroute_tree, &kt->krt, kr)) == NULL) {
log_warnx("kroute_remove failed to find %s/%u",
inet_ntoa(kr->r.prefix), kr->r.prefixlen);
return (-1);
@@ -975,13 +1560,14 @@ kroute_remove(struct kroute_node *kr)
if (krm == kr) {
/* head element */
- if (RB_REMOVE(kroute_tree, &krt, kr) == NULL) {
+ if (RB_REMOVE(kroute_tree, &kt->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) {
+ if (RB_INSERT(kroute_tree, &kt->krt, kr->next) !=
+ NULL) {
log_warnx("kroute_remove failed to add %s/%u",
inet_ntoa(kr->r.prefix), kr->r.prefixlen);
return (-1);
@@ -1001,14 +1587,14 @@ kroute_remove(struct kroute_node *kr)
}
/* 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 (kr->r.flags & F_NEXTHOP)
+ RB_FOREACH(s, knexthop_tree, KT2KNT(kt))
if (s->kroute == kr)
- knexthop_validate(s);
+ knexthop_validate(kt, s);
if (kr->r.flags & F_KERNEL && kr == krm && kr->next == NULL)
/* again remove only once */
- kr_redistribute(IMSG_NETWORK_REMOVE, &kr->r);
+ kr_redistribute(IMSG_NETWORK_REMOVE, kt, &kr->r);
if (kr->r.flags & F_CONNECTED)
if (kif_kr_remove(kr) == -1) {
@@ -1021,16 +1607,17 @@ kroute_remove(struct kroute_node *kr)
}
void
-kroute_clear(void)
+kroute_clear(struct ktable *kt)
{
struct kroute_node *kr;
- while ((kr = RB_MIN(kroute_tree, &krt)) != NULL)
- kroute_remove(kr);
+ while ((kr = RB_MIN(kroute_tree, &kt->krt)) != NULL)
+ kroute_remove(kt, kr);
}
struct kroute6_node *
-kroute6_find(const struct in6_addr *prefix, u_int8_t prefixlen, u_int8_t prio)
+kroute6_find(struct ktable *kt, const struct in6_addr *prefix,
+ u_int8_t prefixlen, u_int8_t prio)
{
struct kroute6_node s;
struct kroute6_node *kn6, *tmp;
@@ -1039,15 +1626,15 @@ kroute6_find(const struct in6_addr *pref
s.r.prefixlen = prefixlen;
s.r.priority = prio;
- kn6 = RB_FIND(kroute6_tree, &krt6, &s);
+ kn6 = RB_FIND(kroute6_tree, &kt->krt6, &s);
if (kn6 && prio == RTP_ANY) {
- tmp = RB_PREV(kroute6_tree, &krt6, kn6);
+ tmp = RB_PREV(kroute6_tree, &kt->krt6, kn6);
while (tmp) {
if (kroute6_compare(&s, tmp) == 0)
kn6 = tmp;
- else
+ else
break;
- tmp = RB_PREV(kroute6_tree, &krt6, kn6);
+ tmp = RB_PREV(kroute6_tree, &kt->krt6, kn6);
}
}
return (kn6);
@@ -1065,7 +1652,7 @@ kroute6_matchgw(struct kroute6_node *kr,
memcpy(&nexthop, &sa_in6->sin6_addr, sizeof(nexthop));
while (kr) {
- if (memcmp(&kr->r.nexthop, &nexthop, sizeof(nexthop)) == NULL)
+ if (memcmp(&kr->r.nexthop, &nexthop, sizeof(nexthop)) == 0)
return (kr);
kr = kr->next;
}
@@ -1074,13 +1661,13 @@ kroute6_matchgw(struct kroute6_node *kr,
}
int
-kroute6_insert(struct kroute6_node *kr)
+kroute6_insert(struct ktable *kt, struct kroute6_node *kr)
{
struct kroute6_node *krm;
struct knexthop_node *h;
struct in6_addr ina, inb;
- if ((krm = RB_INSERT(kroute6_tree, &krt6, kr)) != NULL) {
+ if ((krm = RB_INSERT(kroute6_tree, &kt->krt6, kr)) != NULL) {
/* multipath route, add at end of list */
while (krm->next != NULL)
krm = krm->next;
@@ -1088,14 +1675,15 @@ kroute6_insert(struct kroute6_node *kr)
kr->next = NULL; /* to be sure */
}
+ /* XXX this is wrong for nexthop validated via BGP */
if (kr->r.flags & F_KERNEL) {
inet6applymask(&ina, &kr->r.prefix, kr->r.prefixlen);
- RB_FOREACH(h, knexthop_tree, &knt)
- if (h->nexthop.af == AF_INET6) {
+ RB_FOREACH(h, knexthop_tree, KT2KNT(kt))
+ if (h->nexthop.aid == AID_INET6) {
inet6applymask(&inb, &h->nexthop.v6,
kr->r.prefixlen);
if (memcmp(&ina, &inb, sizeof(ina)) == 0)
- knexthop_validate(h);
+ knexthop_validate(kt, h);
}
if (kr->r.flags & F_CONNECTED)
@@ -1104,19 +1692,19 @@ kroute6_insert(struct kroute6_node *kr)
if (krm == NULL)
/* redistribute multipath routes only once */
- kr_redistribute6(IMSG_NETWORK_ADD, &kr->r);
+ kr_redistribute6(IMSG_NETWORK_ADD, kt, &kr->r);
}
return (0);
}
int
-kroute6_remove(struct kroute6_node *kr)
+kroute6_remove(struct ktable *kt, struct kroute6_node *kr)
{
struct kroute6_node *krm;
struct knexthop_node *s;
- if ((krm = RB_FIND(kroute6_tree, &krt6, kr)) == NULL) {
+ if ((krm = RB_FIND(kroute6_tree, &kt->krt6, kr)) == NULL) {
log_warnx("kroute6_remove failed for %s/%u",
log_in6addr(&kr->r.prefix), kr->r.prefixlen);
return (-1);
@@ -1124,13 +1712,14 @@ kroute6_remove(struct kroute6_node *kr)
if (krm == kr) {
/* head element */
- if (RB_REMOVE(kroute6_tree, &krt6, kr) == NULL) {
+ if (RB_REMOVE(kroute6_tree, &kt->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) {
+ if (RB_INSERT(kroute6_tree, &kt->krt6, kr->next) !=
+ NULL) {
log_warnx("kroute6_remove failed to add %s/%u",
log_in6addr(&kr->r.prefix),
kr->r.prefixlen);
@@ -1151,14 +1740,14 @@ kroute6_remove(struct kroute6_node *kr)
}
/* 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 (kr->r.flags & F_NEXTHOP)
+ RB_FOREACH(s, knexthop_tree, KT2KNT(kt))
if (s->kroute == kr)
- knexthop_validate(s);
+ knexthop_validate(kt, s);
if (kr->r.flags & F_KERNEL && kr == krm && kr->next == NULL)
/* again remove only once */
- kr_redistribute6(IMSG_NETWORK_REMOVE, &kr->r);
+ kr_redistribute6(IMSG_NETWORK_REMOVE, kt, &kr->r);
if (kr->r.flags & F_CONNECTED)
if (kif_kr6_remove(kr) == -1) {
@@ -1171,45 +1760,46 @@ kroute6_remove(struct kroute6_node *kr)
}
void
-kroute6_clear(void)
+kroute6_clear(struct ktable *kt)
{
struct kroute6_node *kr;
- while ((kr = RB_MIN(kroute6_tree, &krt6)) != NULL)
- kroute6_remove(kr);
+ while ((kr = RB_MIN(kroute6_tree, &kt->krt6)) != NULL)
+ kroute6_remove(kt, kr);
}
struct knexthop_node *
-knexthop_find(struct bgpd_addr *addr)
+knexthop_find(struct ktable *kt, 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));
+ return (RB_FIND(knexthop_tree, KT2KNT(kt), &s));
}
int
-knexthop_insert(struct knexthop_node *kn)
+knexthop_insert(struct ktable *kt, struct knexthop_node *kn)
{
- if (RB_INSERT(knexthop_tree, &knt, kn) != NULL) {
+ if (RB_INSERT(knexthop_tree, KT2KNT(kt), kn) != NULL) {
log_warnx("knexthop_tree insert failed for %s",
log_addr(&kn->nexthop));
free(kn);
return (-1);
}
- knexthop_validate(kn);
+ knexthop_validate(kt, kn);
return (0);
}
int
-knexthop_remove(struct knexthop_node *kn)
+knexthop_remove(struct ktable *kt, struct knexthop_node *kn)
{
- kroute_detach_nexthop(kn);
+ kroute_detach_nexthop(kt, kn);
- if (RB_REMOVE(knexthop_tree, &knt, kn) == NULL) {
+ if (RB_REMOVE(knexthop_tree, KT2KNT(kt), kn) == NULL) {
log_warnx("knexthop_remove failed for %s",
log_addr(&kn->nexthop));
return (-1);
@@ -1220,12 +1810,12 @@ knexthop_remove(struct knexthop_node *kn
}
void
-knexthop_clear(void)
+knexthop_clear(struct ktable *kt)
{
struct knexthop_node *kn;
- while ((kn = RB_MIN(knexthop_tree, &knt)) != NULL)
- knexthop_remove(kn);
+ while ((kn = RB_MIN(knexthop_tree, KT2KNT(kt))) != NULL)
+ knexthop_remove(kt, kn);
}
struct kif_node *
@@ -1257,6 +1847,7 @@ kif_insert(struct kif_node *kif)
int
kif_remove(struct kif_node *kif)
{
+ struct ktable *kt;
struct kif_kr *kkr;
struct kif_kr6 *kkr6;
@@ -1265,20 +1856,23 @@ kif_remove(struct kif_node *kif)
return (-1);
}
+ if ((kt = ktable_get(/* XXX */ 0)) == NULL)
+ goto done;
+
while ((kkr = LIST_FIRST(&kif->kroute_l)) != NULL) {
LIST_REMOVE(kkr, entry);
kkr->kr->r.flags &= ~F_NEXTHOP;
- kroute_remove(kkr->kr);
+ kroute_remove(kt, kkr->kr);
free(kkr);
}
while ((kkr6 = LIST_FIRST(&kif->kroute6_l)) != NULL) {
LIST_REMOVE(kkr6, entry);
kkr6->kr->r.flags &= ~F_NEXTHOP;
- kroute6_remove(kkr6->kr);
+ kroute6_remove(kt, kkr6->kr);
free(kkr6);
}
-
+done:
free(kif);
return (0);
}
@@ -1473,113 +2067,109 @@ kroute6_validate(struct kroute6 *kr)
}
void
-knexthop_validate(struct knexthop_node *kn)
+knexthop_validate(struct ktable *kt, struct knexthop_node *kn)
{
+ void *oldk;
struct kroute_node *kr;
struct kroute6_node *kr6;
- struct kroute_nexthop n;
- int was_valid = 0;
- if (kn->nexthop.af == AF_INET && (kr = kn->kroute) != NULL)
- was_valid = kroute_validate(&kr->r);
- if (kn->nexthop.af == AF_INET6 && (kr6 = kn->kroute) != NULL)
- was_valid = kroute6_validate(&kr6->r);
+ oldk = kn->kroute;
+ kroute_detach_nexthop(kt, kn);
- bzero(&n, sizeof(n));
- memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
- kroute_detach_nexthop(kn);
-
- switch (kn->nexthop.af) {
- case AF_INET:
- if ((kr = kroute_match(kn->nexthop.v4.s_addr, 0)) == NULL) {
- if (was_valid)
- send_nexthop_update(&n);
- } else { /* match */
- if (kroute_validate(&kr->r)) { /* valid */
- 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));
- send_nexthop_update(&n);
- } else /* down */
- if (was_valid)
- send_nexthop_update(&n);
+ switch (kn->nexthop.aid) {
+ case AID_INET:
+ kr = kroute_match(kt, kn->nexthop.v4.s_addr, 0);
+ if (kr) {
kn->kroute = kr;
kr->r.flags |= F_NEXTHOP;
}
+
+ /*
+ * Send update if nexthop route changed under us if
+ * the route remains the same then the NH state has not
+ * changed. State changes are tracked by knexthop_track().
+ */
+ if (kr != oldk)
+ knexthop_send_update(kn);
break;
- case AF_INET6:
- if ((kr6 = kroute6_match(&kn->nexthop.v6, 0)) == NULL) {
- if (was_valid)
- send_nexthop_update(&n);
- } else { /* match */
- if (kroute6_validate(&kr6->r)) { /* valid */
- 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));
- send_nexthop_update(&n);
- } else /* down */
- if (was_valid)
- send_nexthop_update(&n);
+ case AID_INET6:
+ kr6 = kroute6_match(kt, &kn->nexthop.v6, 0);
+ if (kr6) {
kn->kroute = kr6;
kr6->r.flags |= F_NEXTHOP;
}
+
+ if (kr6 != oldk)
+ knexthop_send_update(kn);
break;
}
}
void
-knexthop_track(void *krn)
+knexthop_track(struct ktable *kt, void *krp)
{
struct knexthop_node *kn;
+
+ RB_FOREACH(kn, knexthop_tree, KT2KNT(kt))
+ if (kn->kroute == krp)
+ knexthop_send_update(kn);
+}
+
+void
+knexthop_send_update(struct knexthop_node *kn)
+{
+ struct kroute_nexthop n;
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));
+ 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);
+ if (kn->kroute == NULL) {
+ n.valid = 0; /* NH is not valid */
+ send_nexthop_update(&n);
+ return;
+ }
+
+ switch (kn->nexthop.aid) {
+ case AID_INET:
+ kr = kn->kroute;
+ n.valid = kroute_validate(&kr->r);
+ n.connected = kr->r.flags & F_CONNECTED;
+ if ((n.gateway.v4.s_addr =
+ kr->r.nexthop.s_addr) != 0)
+ n.gateway.aid = AID_INET;
+ if (n.connected) {
+ n.net.aid = AID_INET;
+ n.net.v4.s_addr = kr->r.prefix.s_addr;
+ n.netlen = kr->r.prefixlen;
+ }
+ break;
+ case AID_INET6:
+ kr6 = kn->kroute;
+ n.valid = kroute6_validate(&kr6->r);
+ n.connected = kr6->r.flags & F_CONNECTED;
+ if (memcmp(&kr6->r.nexthop, &in6addr_any,
+ sizeof(struct in6_addr)) != 0) {
+ n.gateway.aid = AID_INET6;
+ memcpy(&n.gateway.v6, &kr6->r.nexthop,
+ sizeof(struct in6_addr));
+ }
+ if (n.connected) {
+ n.net.aid = AID_INET6;
+ memcpy(&n.net.v6, &kr6->r.nexthop,
+ sizeof(struct in6_addr));
+ n.netlen = kr6->r.prefixlen;
}
+ break;
+ }
+ send_nexthop_update(&n);
}
struct kroute_node *
-kroute_match(in_addr_t key, int matchall)
+kroute_match(struct ktable *kt, in_addr_t key, int matchall)
{
int i;
struct kroute_node *kr;
@@ -1589,13 +2179,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,
+ if ((kr = kroute_find(kt, 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, RTP_ANY)) != NULL)
+ if ((kr = kroute_find(kt, 0, 0, RTP_ANY)) != NULL)
if (matchall || bgpd_filternexthop(&kr->r, NULL) == 0)
return (kr);
@@ -1603,7 +2193,7 @@ kroute_match(in_addr_t key, int matchall
}
struct kroute6_node *
-kroute6_match(struct in6_addr *key, int matchall)
+kroute6_match(struct ktable *kt, struct in6_addr *key, int matchall)
{
int i;
struct kroute6_node *kr6;
@@ -1612,13 +2202,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, RTP_ANY)) != NULL)
+ if ((kr6 = kroute6_find(kt, &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, RTP_ANY)) != NULL)
+ if ((kr6 = kroute6_find(kt, &in6addr_any, 0, RTP_ANY)) != NULL)
if (matchall || bgpd_filternexthop(NULL, &kr6->r) == 0)
return (kr6);
@@ -1626,31 +2216,30 @@ kroute6_match(struct in6_addr *key, int
}
void
-kroute_detach_nexthop(struct knexthop_node *kn)
+kroute_detach_nexthop(struct ktable *kt, struct knexthop_node *kn)
{
struct knexthop_node *s;
struct kroute_node *k;
struct kroute6_node *k6;
+ if (kn->kroute == NULL)
+ return;
+
/*
* check whether there's another nexthop depending on this kroute
* if not remove the flag
*/
-
- if (kn->kroute == NULL)
- return;
-
- for (s = RB_MIN(knexthop_tree, &knt); s != NULL &&
- s->kroute != kn->kroute; s = RB_NEXT(knexthop_tree, &knt, s))
- ; /* nothing */
+ RB_FOREACH(s, knexthop_tree, KT2KNT(kt))
+ if (s->kroute == kn->kroute && s != kn)
+ break;
if (s == NULL) {
- switch (kn->nexthop.af) {
- case AF_INET:
+ switch (kn->nexthop.aid) {
+ case AID_INET:
k = kn->kroute;
k->r.flags &= ~F_NEXTHOP;
break;
- case AF_INET6:
+ case AID_INET6:
k6 = kn->kroute;
k6->r.flags &= ~F_NEXTHOP;
break;
@@ -1665,7 +2254,7 @@ kroute_detach_nexthop(struct knexthop_no
*/
int
-protect_lo(void)
+protect_lo(struct ktable *kt)
{
struct kroute_node *kr;
struct kroute6_node *kr6;
@@ -1675,11 +2264,11 @@ protect_lo(void)
log_warn("protect_lo");
return (-1);
}
- kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK);
+ kr->r.prefix.s_addr = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
kr->r.prefixlen = 8;
kr->r.flags = F_KERNEL|F_CONNECTED;
- if (RB_INSERT(kroute_tree, &krt, kr) != NULL)
+ if (RB_INSERT(kroute_tree, &kt->krt, kr) != NULL)
free(kr); /* kernel route already there, no problem */
/* special protection for loopback */
@@ -1689,9 +2278,9 @@ protect_lo(void)
}
memcpy(&kr6->r.prefix, &in6addr_loopback, sizeof(kr6->r.prefix));
kr6->r.prefixlen = 128;
- kr->r.flags = F_KERNEL|F_CONNECTED;
+ kr6->r.flags = F_KERNEL|F_CONNECTED;
- if (RB_INSERT(kroute6_tree, &krt6, kr6) != NULL)
+ if (RB_INSERT(kroute6_tree, &kt->krt6, kr6) != NULL)
free(kr6); /* kernel route already there, no problem */
return (0);
@@ -1726,17 +2315,17 @@ mask2prefixlen(in_addr_t ina)
u_int8_t
mask2prefixlen6(struct sockaddr_in6 *sa_in6)
{
- u_int8_t l = 0, i, len;
+ u_int8_t l = 0, *ap, *ep;
/*
* sin6_len is the size of the sockaddr so substract the offset of
* the possibly truncated sin6_addr struct.
*/
- len = sa_in6->sin6_len -
- (u_int8_t)(&((struct sockaddr_in6 *)NULL)->sin6_addr);
- for (i = 0; i < len; i++) {
+ ap = (u_int8_t *)&sa_in6->sin6_addr;
+ ep = (u_int8_t *)sa_in6 + sa_in6->sin6_len;
+ for (; ap < ep; ap++) {
/* this "beauty" is adopted from sbin/route/show.c ... */
- switch (sa_in6->sin6_addr.s6_addr[i]) {
+ switch (*ap) {
case 0xff:
l += 8;
break;
@@ -1764,7 +2353,7 @@ mask2prefixlen6(struct sockaddr_in6 *sa_
case 0x00:
return (l);
default:
- fatalx("non continguous inet6 netmask");
+ fatalx("non contiguous inet6 netmask");
}
}
@@ -1788,7 +2377,7 @@ prefixlen2mask6(u_int8_t prefixlen)
}
#define ROUNDUP(a) \
- (((a) & ((sizeof(long)) - 1)) ? (1 + ((a) | ((sizeof(long)) - 1))) : (a))
+ (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
void
get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
@@ -1808,11 +2397,10 @@ get_rtaddrs(int addrs, struct sockaddr *
void
if_change(u_short ifindex, int flags, struct if_data *ifd)
{
+ struct ktable *kt;
struct kif_node *kif;
struct kif_kr *kkr;
struct kif_kr6 *kkr6;
- struct kroute_nexthop nh;
- struct knexthop_node *n;
u_int8_t reachable;
if ((kif = kif_find(ifindex)) == NULL) {
@@ -1833,28 +2421,18 @@ if_change(u_short ifindex, int flags, st
kif->k.nh_reachable = reachable;
+ kt = ktable_get(/* XXX */ 0);
+
LIST_FOREACH(kkr, &kif->kroute_l, entry) {
if (reachable)
kkr->kr->r.flags &= ~F_DOWN;
else
kkr->kr->r.flags |= F_DOWN;
- RB_FOREACH(n, knexthop_tree, &knt)
- if (n->kroute == kkr->kr) {
- bzero(&nh, sizeof(nh));
- memcpy(&nh.nexthop, &n->nexthop,
- sizeof(nh.nexthop));
- if (kroute_validate(&kkr->kr->r)) {
- nh.valid = 1;
- nh.connected = 1;
- if ((nh.gateway.v4.s_addr =
- kkr->kr->r.nexthop.s_addr) != 0)
- nh.gateway.af = AF_INET;
- }
- memcpy(&nh.kr.kr4, &kkr->kr->r,
- sizeof(nh.kr.kr4));
- send_nexthop_update(&nh);
- }
+ if (kt == NULL)
+ continue;
+
+ knexthop_track(kt, kkr->kr);
}
LIST_FOREACH(kkr6, &kif->kroute6_l, entry) {
if (reachable)
@@ -1862,27 +2440,10 @@ if_change(u_short ifindex, int flags, st
else
kkr6->kr->r.flags |= F_DOWN;
- RB_FOREACH(n, knexthop_tree, &knt)
- if (n->kroute == kkr6->kr) {
- bzero(&nh, sizeof(nh));
- memcpy(&nh.nexthop, &n->nexthop,
- sizeof(nh.nexthop));
- if (kroute6_validate(&kkr6->kr->r)) {
- nh.valid = 1;
- nh.connected = 1;
- if (memcmp(&kkr6->kr->r.nexthop,
- &in6addr_any, sizeof(struct
- in6_addr))) {
- nh.gateway.af = AF_INET6;
- memcpy(&nh.gateway.v6,
- &kkr6->kr->r.nexthop,
- sizeof(struct in6_addr));
- }
- }
- memcpy(&nh.kr.kr6, &kkr6->kr->r,
- sizeof(nh.kr.kr6));
- send_nexthop_update(&nh);
- }
+ if (kt == NULL)
+ continue;
+
+ knexthop_track(kt, kkr6->kr);
}
}
@@ -1917,25 +2478,38 @@ if_announce(void *msg)
*/
int
-send_rtmsg(int fd, int action, struct kroute *kroute)
+send_rtmsg(int fd, int action, struct ktable *kt, struct kroute *kroute)
{
- struct iovec iov[5];
+ struct iovec iov[7];
struct rt_msghdr hdr;
struct sockaddr_in prefix;
struct sockaddr_in nexthop;
struct sockaddr_in mask;
+ struct {
+ struct sockaddr_dl dl;
+ char pad[sizeof(long)];
+ } ifp;
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
+ struct sockaddr_mpls mpls;
struct sockaddr_rtlabel label;
+#endif /* !defined(__FreeBSD__) */
int iovcnt = 0;
- if (kr_state.fib_sync == 0)
+ if (!kt->fib_sync)
return (0);
/* initialize header */
bzero(&hdr, sizeof(hdr));
hdr.rtm_version = RTM_VERSION;
hdr.rtm_type = action;
- hdr.rtm_tableid = kr_state.rtableid;
+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no multiple routing tables */
+ hdr.rtm_tableid = kt->rtableid;
+#endif /* !defined(__FreeBSD__) */
+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no rtm_priority */
hdr.rtm_priority = RTP_BGP;
+#else
+ hdr.rtm_flags = RTF_PROTO1;
+#endif /* !defined(__FreeBSD__) */
if (kroute->flags & F_BLACKHOLE)
hdr.rtm_flags |= RTF_BLACKHOLE;
if (kroute->flags & F_REJECT)
@@ -1984,6 +2558,37 @@ send_rtmsg(int fd, int action, struct kr
iov[iovcnt].iov_base = &mask;
iov[iovcnt++].iov_len = sizeof(mask);
+ if (kt->ifindex) {
+ bzero(&ifp, sizeof(ifp));
+ ifp.dl.sdl_len = sizeof(struct sockaddr_dl);
+ ifp.dl.sdl_family = AF_LINK;
+ ifp.dl.sdl_index = kt->ifindex;
+ /* adjust header */
+ hdr.rtm_addrs |= RTA_IFP;
+ hdr.rtm_msglen += ROUNDUP(sizeof(struct sockaddr_dl));
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &ifp;
+ iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_dl));
+ }
+
+#if !defined(__FreeBSD__) /* FreeBSD has no mpls support. */
+ if (kroute->flags & F_MPLS) {
+ bzero(&mpls, sizeof(mpls));
+ mpls.smpls_len = sizeof(mpls);
+ mpls.smpls_family = AF_MPLS;
+ mpls.smpls_label = kroute->mplslabel;
+ /* adjust header */
+ hdr.rtm_flags |= RTF_MPLS;
+ hdr.rtm_mpls = MPLS_OP_PUSH;
+ hdr.rtm_addrs |= RTA_SRC;
+ hdr.rtm_msglen += sizeof(mpls);
+ /* adjust iovec */
+ iov[iovcnt].iov_base = &mpls;
+ iov[iovcnt++].iov_len = sizeof(mpls);
+ }
+#endif
+
+#if !defined(__FreeBSD__) /* FreeBSD has no route labeling. */
if (kroute->labelid) {
bzero(&label, sizeof(label));
label.sr_len = sizeof(label);
@@ -1996,11 +2601,11 @@ 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) {
- switch (errno) {
- case ESRCH:
+ if (errno == ESRCH) {
if (hdr.rtm_type == RTM_CHANGE) {
hdr.rtm_type = RTM_ADD;
goto retry;
@@ -2009,27 +2614,18 @@ retry:
inet_ntoa(kroute->prefix),
kroute->prefixlen);
return (0);
- } else {
- log_warnx("send_rtmsg: action %u, "
- "prefix %s/%u: %s", hdr.rtm_type,
- inet_ntoa(kroute->prefix),
- kroute->prefixlen, strerror(errno));
- return (0);
}
- break;
- default:
- log_warnx("send_rtmsg: action %u, prefix %s/%u: %s",
- hdr.rtm_type, inet_ntoa(kroute->prefix),
- kroute->prefixlen, strerror(errno));
- return (0);
}
+ log_warn("send_rtmsg: action %u, prefix %s/%u", hdr.rtm_type,
+ inet_ntoa(kroute->prefix), kroute->prefixlen);
+ return (0);
}
return (0);
}
int
-send_rt6msg(int fd, int action, struct kroute6 *kroute)
+send_rt6msg(int fd, int action, struct ktable *kt, struct kroute6 *kroute)
{
struct iovec iov[5];
struct rt_msghdr hdr;
@@ -2037,17 +2633,23 @@ send_rt6msg(int fd, int action, struct k
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)
+ if (!kt->fib_sync)
return (0);
/* initialize header */
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;
+#else
+ hdr.rtm_flags = RTF_PROTO1;
+#endif /* !defined(__FreeBSD__) */
if (kroute->flags & F_BLACKHOLE)
hdr.rtm_flags |= RTF_BLACKHOLE;
if (kroute->flags & F_REJECT)
@@ -2100,6 +2702,7 @@ send_rt6msg(int fd, int action, struct k
iov[iovcnt].iov_base = &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);
@@ -2112,11 +2715,11 @@ 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) {
- switch (errno) {
- case ESRCH:
+ if (errno == ESRCH) {
if (hdr.rtm_type == RTM_CHANGE) {
hdr.rtm_type = RTM_ADD;
goto retry;
@@ -2125,31 +2728,26 @@ retry:
log_in6addr(&kroute->prefix),
kroute->prefixlen);
return (0);
- } else {
- log_warnx("send_rt6msg: action %u, "
- "prefix %s/%u: %s", hdr.rtm_type,
- log_in6addr(&kroute->prefix),
- kroute->prefixlen, strerror(errno));
- return (0);
}
- break;
- default:
- log_warnx("send_rt6msg: action %u, prefix %s/%u: %s",
- hdr.rtm_type, log_in6addr(&kroute->prefix),
- kroute->prefixlen, strerror(errno));
- return (0);
}
+ log_warn("send_rt6msg: action %u, prefix %s/%u", hdr.rtm_type,
+ log_in6addr(&kroute->prefix), kroute->prefixlen);
+ return (0);
}
return (0);
}
int
-fetchtable(u_int rtableid, int connected_only)
+fetchtable(struct ktable *kt)
{
size_t len;
+#if !defined(__FreeBSD__) /* FreeBSD has no table id. */
int mib[7];
- char *buf, *next, *lim;
+#else
+ int mib[6];
+#endif
+ char *buf = NULL, *next, *lim;
struct rt_msghdr *rtm;
struct sockaddr *sa, *gw, *rti_info[RTAX_MAX];
struct sockaddr_in *sa_in;
@@ -2163,22 +2761,35 @@ fetchtable(u_int rtableid, int connected
mib[3] = 0;
mib[4] = NET_RT_DUMP;
mib[5] = 0;
- mib[6] = rtableid;
+#if !defined(__FreeBSD__) /* FreeBSD has no table id. */
+ mib[6] = kt->rtableid;
+#endif
+#if !defined(__FreeBSD__) /* FreeBSD has no table id. */
if (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
- if (rtableid != 0 && errno == EINVAL) /* table nonexistent */
+#else
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) == -1) {
+#endif
+ if (kt->rtableid != 0 && errno == EINVAL)
+ /* table nonexistent */
return (0);
log_warn("sysctl");
return (-1);
}
- if ((buf = malloc(len)) == NULL) {
- log_warn("fetchtable");
- return (-1);
- }
- if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
- log_warn("sysctl");
- free(buf);
- return (-1);
+ if (len > 0) {
+ if ((buf = malloc(len)) == NULL) {
+ log_warn("fetchtable");
+ return (-1);
+ }
+#if !defined(__FreeBSD__) /* FreeBSD has no table id. */
+ if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
+#else
+ if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
+#endif
+ log_warn("sysctl2");
+ free(buf);
+ return (-1);
+ }
}
lim = buf + len;
@@ -2186,7 +2797,11 @@ fetchtable(u_int rtableid, int connected
rtm = (struct rt_msghdr *)next;
if (rtm->rtm_version != RTM_VERSION)
continue;
+#if !defined(__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)
@@ -2205,7 +2820,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;
@@ -2223,8 +2842,12 @@ fetchtable(u_int rtableid, int connected
break;
kr->r.prefixlen =
mask2prefixlen(sa_in->sin_addr.s_addr);
- } else if (rtm->rtm_flags & RTF_HOST)
+ } else if (rtm->rtm_flags & RTF_HOST) {
kr->r.prefixlen = 32;
+#if defined(__FreeBSD__) /* RTF_HOST means connected route */
+ kr->r.flags |= F_CONNECTED;
+#endif
+ }
else
kr->r.prefixlen =
prefixlen_classful(kr->r.prefix.s_addr);
@@ -2238,7 +2861,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,
@@ -2257,8 +2884,12 @@ fetchtable(u_int rtableid, int connected
if (sa_in6->sin6_len == 0)
break;
kr6->r.prefixlen = mask2prefixlen6(sa_in6);
- } else if (rtm->rtm_flags & RTF_HOST)
+ } else if (rtm->rtm_flags & RTF_HOST) {
kr6->r.prefixlen = 128;
+#if defined(__FreeBSD__) /* RTF_HOST means connected route */
+ kr6->r.flags |= F_CONNECTED;
+#endif
+ }
else
fatalx("INET6 route without netmask");
break;
@@ -2290,23 +2921,28 @@ fetchtable(u_int rtableid, int connected
}
if (sa->sa_family == AF_INET) {
+#if !defined(__FreeBSD__) /* no rtm_priority on FreeBSD */
if (rtm->rtm_priority == RTP_BGP) {
- send_rtmsg(kr_state.fd, RTM_DELETE, &kr->r);
- free(kr);
- } else if (connected_only &&
- !(kr->r.flags & F_CONNECTED))
+#else
+ /* never delete route */
+ if (0) {
+#endif
+ send_rtmsg(kr_state.fd, RTM_DELETE, kt, &kr->r);
free(kr);
- else
- kroute_insert(kr);
+ } else
+ kroute_insert(kt, kr);
} else if (sa->sa_family == AF_INET6) {
+#if !defined(__FreeBSD__) /* no rtm_priority on FreeBSD */
if (rtm->rtm_priority == RTP_BGP) {
- send_rt6msg(kr_state.fd, RTM_DELETE, &kr6->r);
- free(kr6);
- } else if (connected_only &&
- !(kr6->r.flags & F_CONNECTED))
+#else
+ /* never delete route */
+ if (0) {
+#endif
+ send_rt6msg(kr_state.fd, RTM_DELETE, kt,
+ &kr6->r);
free(kr6);
- else
- kroute6_insert(kr6);
+ } else
+ kroute6_insert(kt, kr6);
}
}
free(buf);
@@ -2327,7 +2963,7 @@ fetchifs(int ifindex)
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
- mib[3] = AF_INET;
+ mib[3] = AF_INET; /* AF does not matter but AF_INET is shorter */
mib[4] = NET_RT_IFLIST;
mib[5] = ifindex;
@@ -2396,7 +3032,7 @@ dispatch_rtmsg(void)
struct rt_msghdr *rtm;
struct if_msghdr ifm;
struct sockaddr *sa, *rti_info[RTAX_MAX];
- int connected_only;
+ struct ktable *kt;
if ((n = read(kr_state.fd, &buf, sizeof(buf))) == -1) {
log_warn("dispatch_rtmsg: read error");
@@ -2418,7 +3054,11 @@ dispatch_rtmsg(void)
case RTM_ADD:
case RTM_CHANGE:
case RTM_DELETE:
+#if !defined(__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 */
@@ -2430,16 +3070,14 @@ dispatch_rtmsg(void)
if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
continue;
- connected_only = 0;
- if (rtm->rtm_tableid != kr_state.rtableid) {
- if (rtm->rtm_tableid == 0)
- connected_only = 1;
- else
- continue;
- }
+#if !defined(__FreeBSD__) /* FreeBSD has no rtm_tableid. */
+ if ((kt = ktable_get(rtm->rtm_tableid)) == NULL)
+#else
+ if ((kt = ktable_get(0)) == NULL)
+#endif
+ continue;
- if (dispatch_rtmsg_addr(rtm, rti_info,
- connected_only) == -1)
+ if (dispatch_rtmsg_addr(rtm, rti_info, kt) == -1)
return (-1);
break;
case RTM_IFINFO:
@@ -2460,7 +3098,7 @@ dispatch_rtmsg(void)
int
dispatch_rtmsg_addr(struct rt_msghdr *rtm, struct sockaddr *rti_info[RTAX_MAX],
- int connected_only)
+ struct ktable *kt)
{
struct sockaddr *sa;
struct sockaddr_in *sa_in;
@@ -2468,7 +3106,7 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
struct kroute_node *kr;
struct kroute6_node *kr6;
struct bgpd_addr prefix;
- int flags, oflags, mpath = 0;
+ int flags, oflags, mpath = 0, changed = 0;
u_int16_t ifindex;
u_int8_t prefixlen;
u_int8_t prio;
@@ -2494,31 +3132,44 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
mpath = 1;
#endif
+#if !defined(__FreeBSD__) /* no rtm_priority on FreeBSD */
prio = rtm->rtm_priority;
- prefix.af = sa->sa_family;
- switch (prefix.af) {
+#else
+ prio = RTP_BGP;
+#endif
+ switch (sa->sa_family) {
case AF_INET:
+ prefix.aid = AID_INET;
prefix.v4.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
sa_in = (struct sockaddr_in *)rti_info[RTAX_NETMASK];
if (sa_in != NULL) {
if (sa_in->sin_len != 0)
prefixlen = mask2prefixlen(
sa_in->sin_addr.s_addr);
- } else if (rtm->rtm_flags & RTF_HOST)
+ } else if (rtm->rtm_flags & RTF_HOST) {
prefixlen = 32;
+#if defined(__FreeBSD__) /* RTF_HOST means connected route */
+ flags |= F_CONNECTED;
+#endif
+ }
else
prefixlen =
prefixlen_classful(prefix.v4.s_addr);
break;
case AF_INET6:
+ prefix.aid = AID_INET6;
memcpy(&prefix.v6, &((struct sockaddr_in6 *)sa)->sin6_addr,
sizeof(struct in6_addr));
sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK];
if (sa_in6 != NULL) {
if (sa_in6->sin6_len != 0)
prefixlen = mask2prefixlen6(sa_in6);
- } else if (rtm->rtm_flags & RTF_HOST)
+ } else if (rtm->rtm_flags & RTF_HOST) {
prefixlen = 128;
+#if defined(__FreeBSD__) /* RTF_HOST means connected route */
+ flags |= F_CONNECTED;
+#endif
+ }
else
fatalx("in6 net addr without netmask");
break;
@@ -2537,10 +3188,10 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
}
if (rtm->rtm_type == RTM_DELETE) {
- switch (prefix.af) {
- case AF_INET:
+ switch (prefix.aid) {
+ case AID_INET:
sa_in = (struct sockaddr_in *)sa;
- if ((kr = kroute_find(prefix.v4.s_addr,
+ if ((kr = kroute_find(kt, prefix.v4.s_addr,
prefixlen, prio)) == NULL)
return (0);
if (!(kr->r.flags & F_KERNEL))
@@ -2554,12 +3205,12 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
return (0);
}
- if (kroute_remove(kr) == -1)
+ if (kroute_remove(kt, kr) == -1)
return (-1);
break;
- case AF_INET6:
+ case AID_INET6:
sa_in6 = (struct sockaddr_in6 *)sa;
- if ((kr6 = kroute6_find(&prefix.v6, prefixlen,
+ if ((kr6 = kroute6_find(kt, &prefix.v6, prefixlen,
prio)) == NULL)
return (0);
if (!(kr6->r.flags & F_KERNEL))
@@ -2574,26 +3225,23 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
return (0);
}
- if (kroute6_remove(kr6) == -1)
+ if (kroute6_remove(kt, kr6) == -1)
return (-1);
break;
}
return (0);
}
- if (connected_only && !(flags & F_CONNECTED))
- return (0);
-
if (sa == NULL && !(flags & F_CONNECTED)) {
log_warnx("dispatch_rtmsg no nexthop for %s/%u",
log_addr(&prefix), prefixlen);
return (0);
}
- switch (prefix.af) {
- case AF_INET:
+ switch (prefix.aid) {
+ case AID_INET:
sa_in = (struct sockaddr_in *)sa;
- if ((kr = kroute_find(prefix.v4.s_addr, prefixlen,
+ if ((kr = kroute_find(kt, prefix.v4.s_addr, prefixlen,
prio)) != NULL) {
if (kr->r.flags & F_KERNEL) {
/* get the correct route */
@@ -2605,30 +3253,38 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
} else if (mpath && rtm->rtm_type == RTM_ADD)
goto add4;
- if (sa_in != NULL)
+ if (sa_in != NULL) {
+ if (kr->r.nexthop.s_addr !=
+ sa_in->sin_addr.s_addr)
+ changed = 1;
kr->r.nexthop.s_addr =
sa_in->sin_addr.s_addr;
- else
+ } else {
+ if (kr->r.nexthop.s_addr != 0)
+ changed = 1;
kr->r.nexthop.s_addr = 0;
+ }
if (kr->r.flags & F_NEXTHOP)
flags |= F_NEXTHOP;
oflags = kr->r.flags;
+ if (flags != oflags)
+ changed = 1;
kr->r.flags = flags;
if ((oflags & F_CONNECTED) &&
!(flags & F_CONNECTED)) {
kif_kr_remove(kr);
kr_redistribute(IMSG_NETWORK_REMOVE,
- &kr->r);
+ kt, &kr->r);
}
if ((flags & F_CONNECTED) &&
!(oflags & F_CONNECTED)) {
kif_kr_insert(kr);
kr_redistribute(IMSG_NETWORK_ADD,
- &kr->r);
+ kt, &kr->r);
}
- if (kr->r.flags & F_NEXTHOP)
- knexthop_track(kr);
+ if (kr->r.flags & F_NEXTHOP && changed)
+ knexthop_track(kt, kr);
}
} else if (rtm->rtm_type == RTM_CHANGE) {
log_warnx("change req for %s/%u: not in table",
@@ -2651,12 +3307,13 @@ add4:
kr->r.ifindex = ifindex;
kr->r.priority = prio;
- kroute_insert(kr);
+ kroute_insert(kt, kr);
}
break;
- case AF_INET6:
+ case AID_INET6:
sa_in6 = (struct sockaddr_in6 *)sa;
- if ((kr6 = kroute6_find(&prefix.v6, prefixlen, prio)) != NULL) {
+ if ((kr6 = kroute6_find(kt, &prefix.v6, prefixlen, prio)) !=
+ NULL) {
if (kr6->r.flags & F_KERNEL) {
/* get the correct route */
if (mpath && rtm->rtm_type == RTM_CHANGE &&
@@ -2668,33 +3325,44 @@ add4:
} else if (mpath && rtm->rtm_type == RTM_ADD)
goto add6;
- if (sa_in6 != NULL)
+ if (sa_in6 != NULL) {
+ if (memcmp(&kr6->r.nexthop,
+ &sa_in6->sin6_addr,
+ sizeof(struct in6_addr)))
+ changed = 1;
memcpy(&kr6->r.nexthop,
&sa_in6->sin6_addr,
sizeof(struct in6_addr));
- else
+ } else {
+ if (memcmp(&kr6->r.nexthop,
+ &in6addr_any,
+ sizeof(struct in6_addr)))
+ changed = 1;
memcpy(&kr6->r.nexthop,
&in6addr_any,
sizeof(struct in6_addr));
+ }
if (kr6->r.flags & F_NEXTHOP)
flags |= F_NEXTHOP;
oflags = kr6->r.flags;
+ if (flags != oflags)
+ changed = 1;
kr6->r.flags = flags;
if ((oflags & F_CONNECTED) &&
!(flags & F_CONNECTED)) {
kif_kr6_remove(kr6);
kr_redistribute6(IMSG_NETWORK_REMOVE,
- &kr6->r);
+ kt, &kr6->r);
}
if ((flags & F_CONNECTED) &&
!(oflags & F_CONNECTED)) {
kif_kr6_insert(kr6);
kr_redistribute6(IMSG_NETWORK_ADD,
- &kr6->r);
+ kt, &kr6->r);
}
- if (kr6->r.flags & F_NEXTHOP)
- knexthop_track(kr6);
+ if (kr6->r.flags & F_NEXTHOP && changed)
+ knexthop_track(kt, kr6);
}
} else if (rtm->rtm_type == RTM_CHANGE) {
log_warnx("change req for %s/%u: not in table",
@@ -2720,7 +3388,7 @@ add6:
kr6->r.ifindex = ifindex;
kr6->r.priority = prio;
- kroute6_insert(kr6);
+ kroute6_insert(kt, kr6);
}
break;
}