diff options
Diffstat (limited to 'net/openbgpd/files/patch-bgpd_kroute.c')
-rw-r--r-- | net/openbgpd/files/patch-bgpd_kroute.c | 3140 |
1 files changed, 0 insertions, 3140 deletions
diff --git a/net/openbgpd/files/patch-bgpd_kroute.c b/net/openbgpd/files/patch-bgpd_kroute.c deleted file mode 100644 index 0b7f3943ac4a..000000000000 --- a/net/openbgpd/files/patch-bgpd_kroute.c +++ /dev/null @@ -1,3140 +0,0 @@ -Index: bgpd/kroute.c -=================================================================== -RCS file: /home/cvs/private/hrs/openbgpd/bgpd/kroute.c,v -retrieving revision 1.1.1.7 -retrieving revision 1.15 -diff -u -p -r1.1.1.7 -r1.15 ---- bgpd/kroute.c 14 Feb 2010 20:19:57 -0000 1.1.1.7 -+++ bgpd/kroute.c 16 May 2014 00:36:26 -0000 1.15 -@@ -1,4 +1,4 @@ --/* $OpenBSD: kroute.c,v 1.169 2009/06/25 15:54:22 claudio Exp $ */ -+/* $OpenBSD: kroute.c,v 1.190 2012/07/13 16:57:35 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 -+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(struct kroute_label *kl) -+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 (kroute_remove(kr) == -1) -+ 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 (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; - -- RB_FOREACH(kr, kroute_tree, &krt) -+ kt->fib_sync = 1; -+ -+ 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,41 +994,190 @@ 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; - } -- -- send_imsg_session(IMSG_CTL_END, imsg->hdr.pid, NULL, 0); -+ -+ send_imsg_session(IMSG_CTL_END, imsg->hdr.pid, NULL, 0); -+} -+ -+void -+kr_ifinfo(char *ifname) -+{ -+ struct kif_node *kif; -+ -+ RB_FOREACH(kif, kif_tree, &kit) -+ if (!strcmp(ifname, kif->k.ifname)) { -+ send_imsg_session(IMSG_IFINFO, 0, -+ &kif->k, sizeof(kif->k)); -+ return; -+ } -+} -+ -+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); - } - --void --kr_ifinfo(char *ifname) -+struct network * -+kr_net_match6(struct ktable *kt, struct kroute6 *kr6) - { -- struct kif_node *kif; -+ struct network *xn; - -- RB_FOREACH(kif, kif_tree, &kit) -- if (!strcmp(ifname, kif->k.ifname)) { -- send_imsg_session(IMSG_IFINFO, 0, -- &kif->k, sizeof(kif->k)); -- return; -+ 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); - } - --struct redist_node { -- LIST_ENTRY(redist_node) entry; -- struct kroute *kr; -- struct kroute6 *kr6; --}; -+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_net_reload(u_int rtableid, struct network_head *nh) -+{ -+ 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); -+ } -+ } - --LIST_HEAD(, redist_node) redistlist; -+ return (0); -+} - - int --kr_redistribute(int type, struct kroute *kr) -+kr_redistribute(int type, struct ktable *kt, struct kroute *kr) - { -- struct redist_node *rn; -+ 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, &knt) -- knexthop_validate(nh); -+ RB_FOREACH(nh, knexthop_tree, KT2KNT(kt)) -+ knexthop_validate(kt, 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); -@@ -1056,17 +1643,29 @@ kroute6_find(const struct in6_addr *pref - struct kroute6_node * - kroute6_matchgw(struct kroute6_node *kr, struct sockaddr_in6 *sa_in6) - { -- struct in6_addr nexthop; -+ struct sockaddr_in6 nexthop; - - if (sa_in6 == NULL) { - log_warnx("kroute6_matchgw: no nexthop defined"); - return (NULL); - } -- memcpy(&nexthop, &sa_in6->sin6_addr, sizeof(nexthop)); -+ memcpy(&nexthop, sa_in6, sizeof(nexthop)); -+#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER) -+ if (IN6_IS_ADDR_LINKLOCAL(&nexthop.sin6_addr)) { -+ /* Embed scope id and set sin6_scope_id. */ -+ if (nexthop.sin6_scope_id == 0) -+ nexthop.sin6_scope_id = -+ IN6_LINKLOCAL_IFINDEX(nexthop.sin6_addr); -+ else -+ SET_IN6_LINKLOCAL_IFINDEX(nexthop.sin6_addr, -+ nexthop.sin6_scope_id); -+ } -+#endif - - while (kr) { -- if (memcmp(&kr->r.nexthop, &nexthop, sizeof(nexthop)) == NULL) -- return (kr); -+ if (memcmp(&kr->r.nexthop, &nexthop.sin6_addr, -+ sizeof(nexthop.sin6_addr)) == 0) -+ return (kr); - kr = kr->next; - } - -@@ -1074,13 +1673,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 +1687,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 +1704,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 +1724,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 +1752,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 +1772,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 +1822,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 +1859,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 +1868,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 +2079,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 +2191,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 +2205,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 +2214,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 +2228,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 +2266,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 +2276,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 +2290,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 +2327,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 +2365,7 @@ mask2prefixlen6(struct sockaddr_in6 *sa_ - case 0x00: - return (l); - default: -- fatalx("non continguous inet6 netmask"); -+ fatalx("non contiguous inet6 netmask"); - } - } - -@@ -1788,7 +2389,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 +2409,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 +2433,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 +2452,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 +2490,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 +2570,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 +2613,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 +2626,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 +2645,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 +2714,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 +2727,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 +2740,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 +2773,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 +2809,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 +2832,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 +2854,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,11 +2873,25 @@ 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, - sizeof(kr6->r.prefix)); -+#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER) -+ if (IN6_IS_ADDR_LINKLOCAL(&kr6->r.prefix)) { -+ if (((struct sockaddr_in6 *)sa)->sin6_scope_id !=0) -+ SET_IN6_LINKLOCAL_IFINDEX(kr6->r.prefix, -+ ((struct sockaddr_in6 *)sa)->sin6_scope_id); -+ else -+ SET_IN6_LINKLOCAL_IFINDEX(kr6->r.prefix, -+ rtm->rtm_index); -+ } -+#endif - - sa_in6 = (struct sockaddr_in6 *)rti_info[RTAX_NETMASK]; - if (rtm->rtm_flags & RTF_STATIC) -@@ -2257,8 +2906,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; -@@ -2280,6 +2933,13 @@ fetchtable(u_int rtableid, int connected - memcpy(&kr6->r.nexthop, - &((struct sockaddr_in6 *)gw)->sin6_addr, - sizeof(kr6->r.nexthop)); -+#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER) -+ if (IN6_IS_ADDR_LINKLOCAL(&kr6->r.nexthop) && -+ ((struct sockaddr_in6 *)gw)->sin6_scope_id != 0) { -+ SET_IN6_LINKLOCAL_IFINDEX(kr6->r.nexthop, -+ ((struct sockaddr_in6 *)gw)->sin6_scope_id); -+ } -+#endif - break; - case AF_LINK: - if (sa->sa_family == AF_INET) -@@ -2290,23 +2950,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 +2992,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 +3061,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 +3083,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 +3099,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 +3127,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 +3135,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 +3161,54 @@ 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)); -+#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER) -+ if (IN6_IS_ADDR_LINKLOCAL(&prefix.v6) != 0) { -+ if (((struct sockaddr_in6 *)sa)->sin6_scope_id !=0) -+ SET_IN6_LINKLOCAL_IFINDEX(prefix.v6, -+ ((struct sockaddr_in6 *)sa)->sin6_scope_id); -+ else -+ SET_IN6_LINKLOCAL_IFINDEX(prefix.v6, -+ rtm->rtm_index); -+ } -+#endif - 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 +3227,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 +3244,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 +3264,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 +3292,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,50 +3346,62 @@ 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 && - (kr6 = kroute6_matchgw(kr6, sa_in6)) == - NULL) { - log_warnx("dispatch_rtmsg[change] " -- "mpath route not found"); -+ "IPv6 mpath route not found"); - return (-1); - } 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", -@@ -2719,8 +3426,12 @@ add6: - kr6->r.flags = flags; - kr6->r.ifindex = ifindex; - kr6->r.priority = prio; -- -- kroute6_insert(kr6); -+#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER) -+ if (IN6_IS_ADDR_LINKLOCAL(&kr6->r.nexthop)) -+ SET_IN6_LINKLOCAL_IFINDEX(kr6->r.nexthop, -+ ifindex); -+#endif -+ kroute6_insert(kt, kr6); - } - break; - } |