aboutsummaryrefslogtreecommitdiff
path: root/net/openbgpd/files/patch-bgpd_kroute.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/openbgpd/files/patch-bgpd_kroute.c')
-rw-r--r--net/openbgpd/files/patch-bgpd_kroute.c2442
1 files changed, 2338 insertions, 104 deletions
diff --git a/net/openbgpd/files/patch-bgpd_kroute.c b/net/openbgpd/files/patch-bgpd_kroute.c
index 08bb5eacbf50..6f93029d60cd 100644
--- a/net/openbgpd/files/patch-bgpd_kroute.c
+++ b/net/openbgpd/files/patch-bgpd_kroute.c
@@ -2,17 +2,912 @@ Index: bgpd/kroute.c
===================================================================
RCS file: /home/cvs/private/hrs/openbgpd/bgpd/kroute.c,v
retrieving revision 1.1.1.7
-retrieving revision 1.9
-diff -u -p -r1.1.1.7 -r1.9
+retrieving revision 1.12
+diff -u -p -r1.1.1.7 -r1.12
--- bgpd/kroute.c 14 Feb 2010 20:19:57 -0000 1.1.1.7
-+++ bgpd/kroute.c 10 Apr 2010 12:16:23 -0000 1.9
++++ bgpd/kroute.c 3 Jul 2011 04:46:36 -0000 1.12
@@ -1,4 +1,4 @@
-/* $OpenBSD: kroute.c,v 1.169 2009/06/25 15:54:22 claudio Exp $ */
+/* $OpenBSD: kroute.c,v 1.176 2010/04/06 13:25:08 claudio Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
-@@ -463,25 +463,30 @@ kr_nexthop_add(struct bgpd_addr *addr)
+@@ -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,15 @@ 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 *);
++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 +164,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 +185,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 +223,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));
++
++ 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;
+
+- rtlabel_unref(kl->kr.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
++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 kroute6_label *kl)
++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 +758,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,11 +824,16 @@ 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;
+@@ -463,25 +842,30 @@ kr_nexthop_add(struct bgpd_addr *addr)
bzero(&nh, sizeof(nh));
memcpy(&nh.nexthop, addr, sizeof(nh.nexthop));
nh.valid = 1;
@@ -49,25 +944,159 @@ diff -u -p -r1.1.1.7 -r1.9
}
send_nexthop_update(&nh);
-@@ -563,14 +568,14 @@ kr_show_route(struct imsg *imsg)
+@@ -492,7 +876,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 +884,25 @@ 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_add: 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 +911,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 +919,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(addr->v4.s_addr, 1);
++ 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->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(&addr->v6, 1);
++ kr6 = kroute6_match(kt, &addr->v6, 1);
if (kr6 != NULL)
- send_imsg_session(IMSG_CTL_KROUTE6,
-@@ -583,15 +588,21 @@ kr_show_route(struct imsg *imsg)
+- 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) {
@@ -92,7 +1121,407 @@ diff -u -p -r1.1.1.7 -r1.9
ifindex = kr6->r.ifindex;
break;
}
-@@ -846,26 +857,28 @@ kroute6_compare(struct kroute6_node *a,
+@@ -608,6 +1025,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 +1063,146 @@ 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;
++ }
++ }
++ 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;
++ }
++ }
++ return (NULL);
++}
++
++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 +1230,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 +1295,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 +1452,28 @@ kroute6_compare(struct kroute6_node *a,
int
knexthop_compare(struct knexthop_node *a, struct knexthop_node *b)
{
@@ -137,16 +1566,165 @@ diff -u -p -r1.1.1.7 -r1.9
}
return (0);
-@@ -945,7 +958,7 @@ kroute_insert(struct kroute_node *kr)
+@@ -883,7 +1491,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 +1501,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 +1536,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;
+@@ -944,10 +1553,10 @@ kroute_insert(struct kroute_node *kr)
+ if (kr->r.flags & F_KERNEL) {
mask = prefixlen2mask(kr->r.prefixlen);
ina = ntohl(kr->r.prefix.s_addr);
- RB_FOREACH(h, knexthop_tree, &knt)
+- 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(h);
++ knexthop_validate(kt, h);
+
+ if (kr->r.flags & F_CONNECTED)
+ if (kif_kr_insert(kr) == -1)
+@@ -955,19 +1564,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);
+ }
-@@ -1065,7 +1078,7 @@ kroute6_matchgw(struct kroute6_node *kr,
+
+ 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 +1584,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);
+@@ -1002,13 +1612,13 @@ 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)
++ 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 +1631,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 +1650,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 +1676,7 @@ kroute6_matchgw(struct kroute6_node *kr,
memcpy(&nexthop, &sa_in6->sin6_addr, sizeof(nexthop));
while (kr) {
@@ -155,24 +1733,213 @@ diff -u -p -r1.1.1.7 -r1.9
return (kr);
kr = kr->next;
}
-@@ -1091,7 +1104,7 @@ kroute6_insert(struct kroute6_node *kr)
+@@ -1074,13 +1685,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;
+@@ -1090,12 +1701,12 @@ kroute6_insert(struct kroute6_node *kr)
+
if (kr->r.flags & F_KERNEL) {
inet6applymask(&ina, &kr->r.prefix, kr->r.prefixlen);
- RB_FOREACH(h, knexthop_tree, &knt)
+- 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)
-@@ -1184,6 +1197,7 @@ knexthop_find(struct bgpd_addr *addr)
+- knexthop_validate(h);
++ knexthop_validate(kt, h);
+ }
+
+ if (kr->r.flags & F_CONNECTED)
+@@ -1104,19 +1715,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 +1735,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);
+@@ -1152,13 +1764,13 @@ 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)
++ 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 +1783,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));
-@@ -1480,17 +1494,17 @@ knexthop_validate(struct knexthop_node *
+- 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 +1833,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 +1870,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 +1879,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,25 +2090,25 @@ kroute6_validate(struct kroute6 *kr)
+ }
+
+ void
+-knexthop_validate(struct knexthop_node *kn)
++knexthop_validate(struct ktable *kt, struct knexthop_node *kn)
+ {
+ struct kroute_node *kr;
+ struct kroute6_node *kr6;
struct kroute_nexthop n;
int was_valid = 0;
@@ -185,16 +1952,19 @@ diff -u -p -r1.1.1.7 -r1.9
bzero(&n, sizeof(n));
memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
- kroute_detach_nexthop(kn);
+- kroute_detach_nexthop(kn);
++ kroute_detach_nexthop(kt, kn);
- switch (kn->nexthop.af) {
- case AF_INET:
+- if ((kr = kroute_match(kn->nexthop.v4.s_addr, 0)) == NULL) {
+ switch (kn->nexthop.aid) {
+ case AID_INET:
- if ((kr = kroute_match(kn->nexthop.v4.s_addr, 0)) == NULL) {
++ if ((kr = kroute_match(kt, kn->nexthop.v4.s_addr, 0)) == NULL) {
if (was_valid)
send_nexthop_update(&n);
-@@ -1500,8 +1514,10 @@ knexthop_validate(struct knexthop_node *
+ } else { /* match */
+@@ -1500,8 +2117,10 @@ knexthop_validate(struct knexthop_node *
n.connected = kr->r.flags & F_CONNECTED;
if ((n.gateway.v4.s_addr =
kr->r.nexthop.s_addr) != 0)
@@ -207,16 +1977,18 @@ diff -u -p -r1.1.1.7 -r1.9
send_nexthop_update(&n);
} else /* down */
if (was_valid)
-@@ -1511,7 +1527,7 @@ knexthop_validate(struct knexthop_node *
+@@ -1511,8 +2130,8 @@ knexthop_validate(struct knexthop_node *
kr->r.flags |= F_NEXTHOP;
}
break;
- case AF_INET6:
+- if ((kr6 = kroute6_match(&kn->nexthop.v6, 0)) == NULL) {
+ case AID_INET6:
- if ((kr6 = kroute6_match(&kn->nexthop.v6, 0)) == NULL) {
++ if ((kr6 = kroute6_match(kt, &kn->nexthop.v6, 0)) == NULL) {
if (was_valid)
send_nexthop_update(&n);
-@@ -1521,11 +1537,14 @@ knexthop_validate(struct knexthop_node *
+ } else { /* match */
+@@ -1521,11 +2140,14 @@ knexthop_validate(struct knexthop_node *
n.connected = kr6->r.flags & F_CONNECTED;
if (memcmp(&kr6->r.nexthop, &in6addr_any,
sizeof(struct in6_addr)) != 0) {
@@ -233,15 +2005,31 @@ diff -u -p -r1.1.1.7 -r1.9
send_nexthop_update(&n);
} else /* down */
if (was_valid)
-@@ -1551,27 +1570,32 @@ knexthop_track(void *krn)
+@@ -1539,39 +2161,44 @@ knexthop_validate(struct knexthop_node *
+ }
+
+ void
+-knexthop_track(void *krn)
++knexthop_track(struct ktable *kt, void *krp)
+ {
+ struct knexthop_node *kn;
+ struct kroute_node *kr;
+ struct kroute6_node *kr6;
+ struct kroute_nexthop n;
+
+- RB_FOREACH(kn, knexthop_tree, &knt)
+- if (kn->kroute == krn) {
++ RB_FOREACH(kn, knexthop_tree, KT2KNT(kt))
++ if (kn->kroute == krp) {
bzero(&n, sizeof(n));
memcpy(&n.nexthop, &kn->nexthop, sizeof(n.nexthop));
- switch (kn->nexthop.af) {
- case AF_INET:
+- kr = krn;
+ switch (kn->nexthop.aid) {
+ case AID_INET:
- kr = krn;
++ kr = krp;
n.valid = 1;
n.connected = kr->r.flags & F_CONNECTED;
if ((n.gateway.v4.s_addr =
@@ -254,8 +2042,9 @@ diff -u -p -r1.1.1.7 -r1.9
+ n.netlen = kr->r.prefixlen;
break;
- case AF_INET6:
+- kr6 = krn;
+ case AID_INET6:
- kr6 = krn;
++ kr6 = krp;
n.valid = 1;
n.connected = kr6->r.flags & F_CONNECTED;
if (memcmp(&kr6->r.nexthop, &in6addr_any,
@@ -273,7 +2062,73 @@ diff -u -p -r1.1.1.7 -r1.9
break;
}
send_nexthop_update(&n);
-@@ -1645,12 +1669,12 @@ kroute_detach_nexthop(struct knexthop_no
+@@ -1579,7 +2206,7 @@ knexthop_track(void *krn)
+ }
+
+ 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 +2216,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 +2230,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 +2239,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,7 +2253,7 @@ 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;
+@@ -1640,17 +2267,17 @@ kroute_detach_nexthop(struct knexthop_no
+ 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))
++ for (s = RB_MIN(knexthop_tree, KT2KNT(kt)); s != NULL &&
++ s->kroute != kn->kroute; s = RB_NEXT(knexthop_tree, KT2KNT(kt), s))
; /* nothing */
if (s == NULL) {
@@ -289,7 +2144,16 @@ diff -u -p -r1.1.1.7 -r1.9
k6 = kn->kroute;
k6->r.flags &= ~F_NEXTHOP;
break;
-@@ -1675,7 +1699,7 @@ protect_lo(void)
+@@ -1665,7 +2292,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 +2302,11 @@ protect_lo(void)
log_warn("protect_lo");
return (-1);
}
@@ -298,16 +2162,56 @@ diff -u -p -r1.1.1.7 -r1.9
kr->r.prefixlen = 8;
kr->r.flags = F_KERNEL|F_CONNECTED;
-@@ -1689,7 +1713,7 @@ protect_lo(void)
+- 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 +2316,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, &krt6, kr6) != NULL)
++ if (RB_INSERT(kroute6_tree, &kt->krt6, kr6) != NULL)
free(kr6); /* kernel route already there, no problem */
-@@ -1788,7 +1812,7 @@ prefixlen2mask6(u_int8_t prefixlen)
+
+ return (0);
+@@ -1726,17 +2353,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 +2391,7 @@ mask2prefixlen6(struct sockaddr_in6 *sa_
+ case 0x00:
+ return (l);
+ default:
+- fatalx("non continguous inet6 netmask");
++ fatalx("non contiguous inet6 netmask");
+ }
+ }
+
+@@ -1788,7 +2415,7 @@ prefixlen2mask6(u_int8_t prefixlen)
}
#define ROUNDUP(a) \
@@ -316,7 +2220,35 @@ diff -u -p -r1.1.1.7 -r1.9
void
get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
-@@ -1849,10 +1873,11 @@ if_change(u_short ifindex, int flags, st
+@@ -1808,6 +2435,7 @@ 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;
+@@ -1833,13 +2461,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 (kt == NULL)
++ continue;
++
++ RB_FOREACH(n, knexthop_tree, KT2KNT(kt))
+ if (n->kroute == kkr->kr) {
+ bzero(&nh, sizeof(nh));
+ memcpy(&nh.nexthop, &n->nexthop,
+@@ -1849,10 +2482,11 @@ if_change(u_short ifindex, int flags, st
nh.connected = 1;
if ((nh.gateway.v4.s_addr =
kkr->kr->r.nexthop.s_addr) != 0)
@@ -331,7 +2263,18 @@ diff -u -p -r1.1.1.7 -r1.9
send_nexthop_update(&nh);
}
}
-@@ -1873,14 +1898,16 @@ if_change(u_short ifindex, int flags, st
+@@ -1862,7 +2496,9 @@ if_change(u_short ifindex, int flags, st
+ else
+ kkr6->kr->r.flags |= F_DOWN;
+
+- RB_FOREACH(n, knexthop_tree, &knt)
++ if (kt == NULL)
++ continue;
++ RB_FOREACH(n, knexthop_tree, KT2KNT(kt))
+ if (n->kroute == kkr6->kr) {
+ bzero(&nh, sizeof(nh));
+ memcpy(&nh.nexthop, &n->nexthop,
+@@ -1873,14 +2509,16 @@ if_change(u_short ifindex, int flags, st
if (memcmp(&kkr6->kr->r.nexthop,
&in6addr_any, sizeof(struct
in6_addr))) {
@@ -351,39 +2294,88 @@ diff -u -p -r1.1.1.7 -r1.9
send_nexthop_update(&nh);
}
}
-@@ -1924,7 +1951,9 @@ send_rtmsg(int fd, int action, struct kr
+@@ -1917,25 +2555,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)
-@@ -1934,8 +1963,13 @@ send_rtmsg(int fd, int action, struct kr
+- 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 = kr_state.rtableid;
++ hdr.rtm_tableid = kt->rtableid;
+#endif /* !defined(__FreeBSD__) */
-+ hdr.rtm_flags = RTF_PROTO1;
+#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 +2018,7 @@ send_rtmsg(int fd, int action, struct kr
+@@ -1984,6 +2635,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,6 +2031,7 @@ send_rtmsg(int fd, int action, struct kr
+@@ -1996,11 +2678,11 @@ send_rtmsg(int fd, int action, struct kr
iov[iovcnt].iov_base = &label;
iov[iovcnt++].iov_len = sizeof(label);
}
@@ -391,7 +2383,45 @@ diff -u -p -r1.1.1.7 -r1.9
retry:
if (writev(fd, iov, iovcnt) == -1) {
-@@ -2037,7 +2073,9 @@ send_rt6msg(int fd, int action, struct k
+- switch (errno) {
+- case ESRCH:
++ if (errno == ESRCH) {
+ if (hdr.rtm_type == RTM_CHANGE) {
+ hdr.rtm_type = RTM_ADD;
+ goto retry;
+@@ -2009,27 +2691,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 +2710,23 @@ send_rt6msg(int fd, int action, struct k
struct sockaddr_in6 addr;
char pad[sizeof(long)];
} prefix, nexthop, mask;
@@ -400,19 +2430,23 @@ diff -u -p -r1.1.1.7 -r1.9
+#endif /* !defined(__FreeBSD__) */
int iovcnt = 0;
- if (kr_state.fib_sync == 0)
-@@ -2047,7 +2085,10 @@ send_rt6msg(int fd, int action, struct k
+- 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;
-+#endif /* !defined(__FreeBSD__) */
++#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 +2141,7 @@ send_rt6msg(int fd, int action, struct k
+@@ -2100,6 +2779,7 @@ send_rt6msg(int fd, int action, struct k
iov[iovcnt].iov_base = &mask;
iov[iovcnt++].iov_len = ROUNDUP(sizeof(struct sockaddr_in6));
@@ -420,7 +2454,7 @@ diff -u -p -r1.1.1.7 -r1.9
if (kroute->labelid) {
bzero(&label, sizeof(label));
label.sr_len = sizeof(label);
-@@ -2112,6 +2154,7 @@ send_rt6msg(int fd, int action, struct k
+@@ -2112,11 +2792,11 @@ send_rt6msg(int fd, int action, struct k
iov[iovcnt].iov_base = &label;
iov[iovcnt++].iov_len = sizeof(label);
}
@@ -428,42 +2462,104 @@ diff -u -p -r1.1.1.7 -r1.9
retry:
if (writev(fd, iov, iovcnt) == -1) {
-@@ -2147,8 +2190,8 @@ retry:
+- switch (errno) {
+- case ESRCH:
++ if (errno == ESRCH) {
+ if (hdr.rtm_type == RTM_CHANGE) {
+ hdr.rtm_type = RTM_ADD;
+ goto retry;
+@@ -2125,31 +2805,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(u_int rtableid, int connected_only)
++fetchtable(struct ktable *kt)
{
-- size_t len;
-- int mib[7];
-+ size_t len;
-+ int mib[6];
- char *buf, *next, *lim;
+ 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];
-@@ -2163,9 +2206,8 @@ fetchtable(u_int rtableid, int connected
+ struct sockaddr_in *sa_in;
+@@ -2163,22 +2838,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 (sysctl(mib, 7, NULL, &len, NULL, 0) == -1) {
++#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) {
- if (rtableid != 0 && errno == EINVAL) /* table nonexistent */
++#endif
++ if (kt->rtableid != 0 && errno == EINVAL)
++ /* table nonexistent */
return (0);
log_warn("sysctl");
-@@ -2175,7 +2217,7 @@ fetchtable(u_int rtableid, int connected
- log_warn("fetchtable");
return (-1);
}
+- if ((buf = malloc(len)) == NULL) {
+- log_warn("fetchtable");
+- return (-1);
+- }
- if (sysctl(mib, 7, buf, &len, NULL, 0) == -1) {
-+ if (sysctl(mib, 6, buf, &len, NULL, 0) == -1) {
- log_warn("sysctl");
- free(buf);
- return (-1);
-@@ -2186,7 +2228,11 @@ fetchtable(u_int rtableid, int connected
+- 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 +2874,11 @@ fetchtable(u_int rtableid, int connected
rtm = (struct rt_msghdr *)next;
if (rtm->rtm_version != RTM_VERSION)
continue;
-+#if !defined(__FreeBSD__) /* no rtm_hdrlen on FreeBSD */
++#if !defined(__FreeBSD__)
sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
+#else
+ sa = (struct sockaddr *)(next + sizeof(struct rt_msghdr));
@@ -471,7 +2567,7 @@ diff -u -p -r1.1.1.7 -r1.9
get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
if ((sa = rti_info[RTAX_DST]) == NULL)
-@@ -2205,7 +2251,11 @@ fetchtable(u_int rtableid, int connected
+@@ -2205,7 +2897,11 @@ fetchtable(u_int rtableid, int connected
}
kr->r.flags = F_KERNEL;
@@ -483,7 +2579,7 @@ diff -u -p -r1.1.1.7 -r1.9
kr->r.ifindex = rtm->rtm_index;
kr->r.prefix.s_addr =
((struct sockaddr_in *)sa)->sin_addr.s_addr;
-@@ -2223,8 +2273,12 @@ fetchtable(u_int rtableid, int connected
+@@ -2223,8 +2919,12 @@ fetchtable(u_int rtableid, int connected
break;
kr->r.prefixlen =
mask2prefixlen(sa_in->sin_addr.s_addr);
@@ -497,7 +2593,7 @@ diff -u -p -r1.1.1.7 -r1.9
else
kr->r.prefixlen =
prefixlen_classful(kr->r.prefix.s_addr);
-@@ -2238,7 +2292,11 @@ fetchtable(u_int rtableid, int connected
+@@ -2238,7 +2938,11 @@ fetchtable(u_int rtableid, int connected
}
kr6->r.flags = F_KERNEL;
@@ -509,7 +2605,7 @@ diff -u -p -r1.1.1.7 -r1.9
kr6->r.ifindex = rtm->rtm_index;
memcpy(&kr6->r.prefix,
&((struct sockaddr_in6 *)sa)->sin6_addr,
-@@ -2257,8 +2315,12 @@ fetchtable(u_int rtableid, int connected
+@@ -2257,8 +2961,12 @@ fetchtable(u_int rtableid, int connected
if (sa_in6->sin6_len == 0)
break;
kr6->r.prefixlen = mask2prefixlen6(sa_in6);
@@ -523,37 +2619,70 @@ diff -u -p -r1.1.1.7 -r1.9
else
fatalx("INET6 route without netmask");
break;
-@@ -2290,7 +2352,12 @@ fetchtable(u_int rtableid, int connected
+@@ -2290,23 +2998,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, &kr->r);
++ send_rtmsg(kr_state.fd, RTM_DELETE, kt, &kr->r);
free(kr);
- } else if (connected_only &&
-@@ -2299,7 +2366,12 @@ fetchtable(u_int rtableid, int connected
- else
- kroute_insert(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, &kr6->r);
++ send_rt6msg(kr_state.fd, RTM_DELETE, kt,
++ &kr6->r);
free(kr6);
- } else if (connected_only &&
-@@ -2418,7 +2490,11 @@ dispatch_rtmsg(void)
+- else
+- kroute6_insert(kr6);
++ } else
++ kroute6_insert(kt, kr6);
+ }
+ }
+ free(buf);
+@@ -2327,7 +3040,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 +3109,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 +3131,11 @@ dispatch_rtmsg(void)
case RTM_ADD:
case RTM_CHANGE:
case RTM_DELETE:
-+#if !defined(__FreeBSD__) /* no rtm_hdrlen on FreeBSD */
++#if !defined(__FreeBSD__)
sa = (struct sockaddr *)(next + rtm->rtm_hdrlen);
+#else
+ sa = (struct sockaddr *)(next + sizeof(struct rt_msghdr));
@@ -561,22 +2690,40 @@ diff -u -p -r1.1.1.7 -r1.9
get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
if (rtm->rtm_pid == kr_state.pid) /* cause by us */
-@@ -2431,12 +2507,14 @@ dispatch_rtmsg(void)
+@@ -2430,16 +3147,14 @@ dispatch_rtmsg(void)
+ if (rtm->rtm_flags & RTF_LLINFO) /* arp cache */
continue;
- connected_only = 0;
-+#if !defined(__FreeBSD__) /* XXX: FreeBSD has no multiple routing tables */
- if (rtm->rtm_tableid != kr_state.rtableid) {
- if (rtm->rtm_tableid == 0)
- connected_only = 1;
- else
- continue;
- }
-+#endif /* !defined(__FreeBSD__) */
+- 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 +3175,7 @@ dispatch_rtmsg(void)
- if (dispatch_rtmsg_addr(rtm, rti_info,
- connected_only) == -1)
-@@ -2494,31 +2572,44 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+ 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;
+@@ -2494,31 +3209,44 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
mpath = 1;
#endif
@@ -625,7 +2772,7 @@ diff -u -p -r1.1.1.7 -r1.9
else
fatalx("in6 net addr without netmask");
break;
-@@ -2537,8 +2628,8 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+@@ -2537,10 +3265,10 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
}
if (rtm->rtm_type == RTM_DELETE) {
@@ -634,18 +2781,45 @@ diff -u -p -r1.1.1.7 -r1.9
+ switch (prefix.aid) {
+ case AID_INET:
sa_in = (struct sockaddr_in *)sa;
- if ((kr = kroute_find(prefix.v4.s_addr,
+- if ((kr = kroute_find(prefix.v4.s_addr,
++ if ((kr = kroute_find(kt, prefix.v4.s_addr,
prefixlen, prio)) == NULL)
-@@ -2557,7 +2648,7 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
- if (kroute_remove(kr) == -1)
+ return (0);
+ if (!(kr->r.flags & F_KERNEL))
+@@ -2554,12 +3282,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(&prefix.v6, prefixlen,
++ if ((kr6 = kroute6_find(kt, &prefix.v6, prefixlen,
prio)) == NULL)
-@@ -2590,8 +2681,8 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+ return (0);
+ if (!(kr6->r.flags & F_KERNEL))
+@@ -2574,26 +3302,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);
}
@@ -654,14 +2828,74 @@ diff -u -p -r1.1.1.7 -r1.9
+ 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(prefix.v4.s_addr, prefixlen,
++ if ((kr = kroute_find(kt, prefix.v4.s_addr, prefixlen,
prio)) != NULL) {
-@@ -2654,7 +2745,7 @@ add4:
- kroute_insert(kr);
+ if (kr->r.flags & F_KERNEL) {
+ /* get the correct route */
+@@ -2619,16 +3344,16 @@ dispatch_rtmsg_addr(struct rt_msghdr *rt
+ !(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);
++ knexthop_track(kt, kr);
+ }
+ } else if (rtm->rtm_type == RTM_CHANGE) {
+ log_warnx("change req for %s/%u: not in table",
+@@ -2651,12 +3376,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(&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 &&
+@@ -2685,16 +3411,16 @@ add4:
+ !(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);
++ knexthop_track(kt, kr6);
+ }
+ } else if (rtm->rtm_type == RTM_CHANGE) {
+ log_warnx("change req for %s/%u: not in table",
+@@ -2720,7 +3446,7 @@ add6:
+ kr6->r.ifindex = ifindex;
+ kr6->r.priority = prio;
+
+- kroute6_insert(kr6);
++ kroute6_insert(kt, kr6);
+ }
+ break;
+ }