diff options
Diffstat (limited to 'net/openbgpd/files/patch-bgpd_rde.c')
-rw-r--r-- | net/openbgpd/files/patch-bgpd_rde.c | 2614 |
1 files changed, 0 insertions, 2614 deletions
diff --git a/net/openbgpd/files/patch-bgpd_rde.c b/net/openbgpd/files/patch-bgpd_rde.c deleted file mode 100644 index 76761d859f1d..000000000000 --- a/net/openbgpd/files/patch-bgpd_rde.c +++ /dev/null @@ -1,2614 +0,0 @@ -Index: bgpd/rde.c -=================================================================== -RCS file: /home/cvs/private/hrs/openbgpd/bgpd/rde.c,v -retrieving revision 1.1.1.8 -retrieving revision 1.12 -diff -u -p -r1.1.1.8 -r1.12 ---- bgpd/rde.c 14 Feb 2010 20:19:57 -0000 1.1.1.8 -+++ bgpd/rde.c 16 May 2014 00:36:26 -0000 1.12 -@@ -1,4 +1,4 @@ --/* $OpenBSD: rde.c,v 1.264 2009/06/29 12:22:16 claudio Exp $ */ -+/* $OpenBSD: rde.c,v 1.320 2012/09/18 09:45:51 claudio Exp $ */ - - /* - * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> -@@ -18,10 +18,11 @@ - - #include <sys/types.h> - #include <sys/socket.h> -+#include <sys/time.h> -+#include <sys/resource.h> - - #include <errno.h> - #include <ifaddrs.h> --#include <limits.h> - #include <pwd.h> - #include <poll.h> - #include <signal.h> -@@ -50,13 +51,18 @@ void rde_update_withdraw(struct rde_pe - u_int8_t); - int rde_attr_parse(u_char *, u_int16_t, struct rde_peer *, - struct rde_aspath *, struct mpattr *); -+int rde_attr_add(struct rde_aspath *, u_char *, u_int16_t); - u_int8_t rde_attr_missing(struct rde_aspath *, int, u_int16_t); --int rde_get_mp_nexthop(u_char *, u_int16_t, u_int16_t, -- struct rde_aspath *); -+int rde_get_mp_nexthop(u_char *, u_int16_t, u_int8_t, -+ struct rde_aspath *, struct rde_peer *); -+int rde_update_extract_prefix(u_char *, u_int16_t, void *, -+ u_int8_t, u_int8_t); - int rde_update_get_prefix(u_char *, u_int16_t, struct bgpd_addr *, - u_int8_t *); - int rde_update_get_prefix6(u_char *, u_int16_t, struct bgpd_addr *, - u_int8_t *); -+int rde_update_get_vpn4(u_char *, u_int16_t, struct bgpd_addr *, -+ u_int8_t *); - void rde_update_err(struct rde_peer *, u_int8_t , u_int8_t, - void *, u_int16_t); - void rde_update_log(const char *, u_int16_t, -@@ -78,11 +84,15 @@ void rde_dump_ctx_new(struct ctl_show_ - void rde_dump_mrt_new(struct mrt *, pid_t, int); - void rde_dump_done(void *); - -+int rde_rdomain_import(struct rde_aspath *, struct rdomain *); - void rde_up_dump_upcall(struct rib_entry *, void *); - void rde_softreconfig_out(struct rib_entry *, void *); - void rde_softreconfig_in(struct rib_entry *, void *); -+void rde_softreconfig_load(struct rib_entry *, void *); -+void rde_softreconfig_load_peer(struct rib_entry *, void *); -+void rde_softreconfig_unload_peer(struct rib_entry *, void *); - void rde_update_queue_runner(void); --void rde_update6_queue_runner(void); -+void rde_update6_queue_runner(u_int8_t); - - void peer_init(u_int32_t); - void peer_shutdown(void); -@@ -91,10 +101,12 @@ struct rde_peer *peer_add(u_int32_t, str - struct rde_peer *peer_get(u_int32_t); - void peer_up(u_int32_t, struct session_up *); - void peer_down(u_int32_t); --void peer_dump(u_int32_t, u_int16_t, u_int8_t); --void peer_send_eor(struct rde_peer *, u_int16_t, u_int16_t); -+void peer_flush(struct rde_peer *, u_int8_t); -+void peer_stale(u_int32_t, u_int8_t); -+void peer_recv_eor(struct rde_peer *, u_int8_t); -+void peer_dump(u_int32_t, u_int8_t); -+void peer_send_eor(struct rde_peer *, u_int8_t); - --void network_init(struct network_head *); - void network_add(struct network_config *, int); - void network_delete(struct network_config *, int); - void network_dump_upcall(struct rib_entry *, void *); -@@ -108,6 +120,7 @@ time_t reloadtime; - struct rde_peer_head peerlist; - struct rde_peer *peerself; - struct filter_head *rules_l, *newrules; -+struct rdomain_head *rdomains_l, *newdomains; - struct imsgbuf *ibuf_se; - struct imsgbuf *ibuf_se_ctl; - struct imsgbuf *ibuf_main; -@@ -120,11 +133,12 @@ struct rde_dump_ctx { - }; - - struct rde_mrt_ctx { -- struct mrt mrt; -- struct rib_context ribctx; -+ struct mrt mrt; -+ struct rib_context ribctx; -+ LIST_ENTRY(rde_mrt_ctx) entry; - }; - --struct mrt_head rde_mrts = LIST_HEAD_INITIALIZER(rde_mrts); -+LIST_HEAD(, rde_mrt_ctx) rde_mrts = LIST_HEAD_INITIALIZER(rde_mrts); - u_int rde_mrt_cnt; - - void -@@ -144,24 +158,17 @@ u_int32_t attrhashsize = 512; - u_int32_t nexthophashsize = 64; - - pid_t --rde_main(struct bgpd_config *config, struct peer *peer_l, -- struct network_head *net_l, struct filter_head *rules, -- struct mrt_head *mrt_l, struct rib_names *rib_n, int pipe_m2r[2], -- int pipe_s2r[2], int pipe_m2s[2], int pipe_s2rctl[2], int debug) -+rde_main(int pipe_m2r[2], int pipe_s2r[2], int pipe_m2s[2], int pipe_s2rctl[2], -+ int debug) - { - pid_t pid; - struct passwd *pw; -- struct peer *p; -- struct listen_addr *la; - struct pollfd *pfd = NULL; -- struct filter_rule *f; -- struct filter_set *set; -- struct nexthop *nh; -- struct rde_rib *rr; -- struct mrt *mrt, *xmrt; -+ struct rde_mrt_ctx *mctx, *xmctx; - void *newp; - u_int pfd_elms = 0, i, j; - int timeout; -+ u_int8_t aid; - - switch (pid = fork()) { - case -1: -@@ -172,8 +179,6 @@ rde_main(struct bgpd_config *config, str - return (pid); - } - -- conf = config; -- - if ((pw = getpwnam(BGPD_USER)) == NULL) - fatal("getpwnam"); - -@@ -194,6 +199,8 @@ rde_main(struct bgpd_config *config, str - signal(SIGINT, rde_sighdlr); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, SIG_IGN); -+ signal(SIGALRM, SIG_IGN); -+ signal(SIGUSR1, SIG_IGN); - - close(pipe_s2r[0]); - close(pipe_s2rctl[0]); -@@ -210,50 +217,25 @@ rde_main(struct bgpd_config *config, str - imsg_init(ibuf_se_ctl, pipe_s2rctl[1]); - imsg_init(ibuf_main, pipe_m2r[1]); - -- /* peer list, mrt list and listener list are not used in the RDE */ -- while ((p = peer_l) != NULL) { -- peer_l = p->next; -- free(p); -- } -- -- while ((mrt = LIST_FIRST(mrt_l)) != NULL) { -- LIST_REMOVE(mrt, entry); -- free(mrt); -- } -- -- while ((la = TAILQ_FIRST(config->listen_addrs)) != NULL) { -- TAILQ_REMOVE(config->listen_addrs, la, entry); -- close(la->fd); -- free(la); -- } -- free(config->listen_addrs); -- - pt_init(); -- while ((rr = SIMPLEQ_FIRST(&ribnames))) { -- SIMPLEQ_REMOVE_HEAD(&ribnames, entry); -- rib_new(-1, rr->name, rr->flags); -- free(rr); -- } - path_init(pathhashsize); - aspath_init(pathhashsize); - attr_init(attrhashsize); - nexthop_init(nexthophashsize); - peer_init(peerhashsize); -- rules_l = rules; -- network_init(net_l); - -+ rules_l = calloc(1, sizeof(struct filter_head)); -+ if (rules_l == NULL) -+ fatal(NULL); -+ TAILQ_INIT(rules_l); -+ rdomains_l = calloc(1, sizeof(struct rdomain_head)); -+ if (rdomains_l == NULL) -+ fatal(NULL); -+ SIMPLEQ_INIT(rdomains_l); -+ if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL) -+ fatal(NULL); - log_info("route decision engine ready"); - -- TAILQ_FOREACH(f, rules, entry) { -- f->peer.ribid = rib_find(f->rib); -- TAILQ_FOREACH(set, &f->set, entry) { -- if (set->type == ACTION_SET_NEXTHOP) { -- nh = nexthop_get(&set->action.nexthop); -- nh->refcnt++; -- } -- } -- } -- - while (rde_quit == 0) { - if (pfd_elms < PFD_PIPE_COUNT + rde_mrt_cnt) { - if ((newp = realloc(pfd, sizeof(struct pollfd) * -@@ -287,11 +269,18 @@ rde_main(struct bgpd_config *config, str - timeout = 0; - - i = PFD_PIPE_COUNT; -- LIST_FOREACH(mrt, &rde_mrts, entry) { -- if (mrt->wbuf.queued) { -- pfd[i].fd = mrt->wbuf.fd; -+ for (mctx = LIST_FIRST(&rde_mrts); mctx != 0; mctx = xmctx) { -+ xmctx = LIST_NEXT(mctx, entry); -+ if (mctx->mrt.wbuf.queued) { -+ pfd[i].fd = mctx->mrt.wbuf.fd; - pfd[i].events = POLLOUT; - i++; -+ } else if (mctx->mrt.state == MRT_STATE_REMOVE) { -+ close(mctx->mrt.wbuf.fd); -+ LIST_REMOVE(&mctx->ribctx, entry); -+ LIST_REMOVE(mctx, entry); -+ free(mctx); -+ rde_mrt_cnt--; - } - } - -@@ -325,24 +314,17 @@ rde_main(struct bgpd_config *config, str - if (pfd[PFD_PIPE_SESSION_CTL].revents & POLLIN) - rde_dispatch_imsg_session(ibuf_se_ctl); - -- for (j = PFD_PIPE_COUNT, mrt = LIST_FIRST(&rde_mrts); -- j < i && mrt != 0; j++) { -- xmrt = LIST_NEXT(mrt, entry); -- if (pfd[j].fd == mrt->wbuf.fd && -+ for (j = PFD_PIPE_COUNT, mctx = LIST_FIRST(&rde_mrts); -+ j < i && mctx != 0; j++) { -+ if (pfd[j].fd == mctx->mrt.wbuf.fd && - pfd[j].revents & POLLOUT) -- mrt_write(mrt); -- if (mrt->wbuf.queued == 0 && -- mrt->state == MRT_STATE_REMOVE) { -- close(mrt->wbuf.fd); -- LIST_REMOVE(mrt, entry); -- free(mrt); -- rde_mrt_cnt--; -- } -- mrt = xmrt; -+ mrt_write(&mctx->mrt); -+ mctx = LIST_NEXT(mctx, entry); - } - - rde_update_queue_runner(); -- rde_update6_queue_runner(); -+ for (aid = AID_INET6; aid < AID_MAX; aid++) -+ rde_update6_queue_runner(aid); - if (ibuf_se_ctl->w.queued <= 0) - rib_dump_runner(); - } -@@ -351,11 +333,12 @@ rde_main(struct bgpd_config *config, str - if (debug) - rde_shutdown(); - -- while ((mrt = LIST_FIRST(&rde_mrts)) != NULL) { -- msgbuf_clear(&mrt->wbuf); -- close(mrt->wbuf.fd); -- LIST_REMOVE(mrt, entry); -- free(mrt); -+ while ((mctx = LIST_FIRST(&rde_mrts)) != NULL) { -+ msgbuf_clear(&mctx->mrt.wbuf); -+ close(mctx->mrt.wbuf.fd); -+ LIST_REMOVE(&mctx->ribctx, entry); -+ LIST_REMOVE(mctx, entry); -+ free(mctx); - } - - msgbuf_clear(&ibuf_se->w); -@@ -378,13 +361,18 @@ rde_dispatch_imsg_session(struct imsgbuf - struct imsg imsg; - struct peer p; - struct peer_config pconf; -- struct rrefresh r; -- struct rde_peer *peer; - struct session_up sup; -+ struct ctl_show_rib csr; - struct ctl_show_rib_request req; -+ struct rde_peer *peer; -+ struct rde_aspath *asp; - struct filter_set *s; - struct nexthop *nh; -- int n; -+ u_int8_t *asdata; -+ ssize_t n; -+ int verbose; -+ u_int16_t len; -+ u_int8_t aid; - - if ((n = imsg_read(ibuf)) == -1) - fatal("rde_dispatch_imsg_session: imsg_read error"); -@@ -422,13 +410,56 @@ rde_dispatch_imsg_session(struct imsgbuf - case IMSG_SESSION_DOWN: - peer_down(imsg.hdr.peerid); - break; -+ case IMSG_SESSION_STALE: -+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) { -+ log_warnx("rde_dispatch: wrong imsg len"); -+ break; -+ } -+ memcpy(&aid, imsg.data, sizeof(aid)); -+ if (aid >= AID_MAX) -+ fatalx("IMSG_SESSION_STALE: bad AID"); -+ peer_stale(imsg.hdr.peerid, aid); -+ break; -+ case IMSG_SESSION_FLUSH: -+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) { -+ log_warnx("rde_dispatch: wrong imsg len"); -+ break; -+ } -+ memcpy(&aid, imsg.data, sizeof(aid)); -+ if (aid >= AID_MAX) -+ fatalx("IMSG_SESSION_FLUSH: bad AID"); -+ if ((peer = peer_get(imsg.hdr.peerid)) == NULL) { -+ log_warnx("rde_dispatch: unknown peer id %d", -+ imsg.hdr.peerid); -+ break; -+ } -+ peer_flush(peer, aid); -+ break; -+ case IMSG_SESSION_RESTARTED: -+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) { -+ log_warnx("rde_dispatch: wrong imsg len"); -+ break; -+ } -+ memcpy(&aid, imsg.data, sizeof(aid)); -+ if (aid >= AID_MAX) -+ fatalx("IMSG_SESSION_RESTARTED: bad AID"); -+ if ((peer = peer_get(imsg.hdr.peerid)) == NULL) { -+ log_warnx("rde_dispatch: unknown peer id %d", -+ imsg.hdr.peerid); -+ break; -+ } -+ if (peer->staletime[aid]) -+ peer_flush(peer, aid); -+ break; - case IMSG_REFRESH: -- if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(r)) { -+ if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) { - log_warnx("rde_dispatch: wrong imsg len"); - break; - } -- memcpy(&r, imsg.data, sizeof(r)); -- peer_dump(imsg.hdr.peerid, r.afi, r.safi); -+ memcpy(&aid, imsg.data, sizeof(aid)); -+ if (aid >= AID_MAX) -+ fatalx("IMSG_REFRESH: bad AID"); -+ peer_dump(imsg.hdr.peerid, aid); - break; - case IMSG_NETWORK_ADD: - if (imsg.hdr.len - IMSG_HEADER_SIZE != -@@ -440,23 +471,68 @@ rde_dispatch_imsg_session(struct imsgbuf - TAILQ_INIT(&netconf_s.attrset); - session_set = &netconf_s.attrset; - break; -+ case IMSG_NETWORK_ASPATH: -+ if (imsg.hdr.len - IMSG_HEADER_SIZE < -+ sizeof(struct ctl_show_rib)) { -+ log_warnx("rde_dispatch: wrong imsg len"); -+ bzero(&netconf_s, sizeof(netconf_s)); -+ break; -+ } -+ asdata = imsg.data; -+ asdata += sizeof(struct ctl_show_rib); -+ memcpy(&csr, imsg.data, sizeof(csr)); -+ if (csr.aspath_len + sizeof(csr) > imsg.hdr.len - -+ IMSG_HEADER_SIZE) { -+ log_warnx("rde_dispatch: wrong aspath len"); -+ bzero(&netconf_s, sizeof(netconf_s)); -+ break; -+ } -+ asp = path_get(); -+ asp->lpref = csr.local_pref; -+ asp->med = csr.med; -+ asp->weight = csr.weight; -+ asp->flags = csr.flags; -+ asp->origin = csr.origin; -+ asp->flags |= F_PREFIX_ANNOUNCED | F_ANN_DYNAMIC; -+ asp->aspath = aspath_get(asdata, csr.aspath_len); -+ netconf_s.asp = asp; -+ break; -+ case IMSG_NETWORK_ATTR: -+ if (imsg.hdr.len <= IMSG_HEADER_SIZE) { -+ log_warnx("rde_dispatch: wrong imsg len"); -+ break; -+ } -+ /* parse path attributes */ -+ len = imsg.hdr.len - IMSG_HEADER_SIZE; -+ asp = netconf_s.asp; -+ if (rde_attr_add(asp, imsg.data, len) == -1) { -+ log_warnx("rde_dispatch: bad network " -+ "attribute"); -+ path_put(asp); -+ bzero(&netconf_s, sizeof(netconf_s)); -+ break; -+ } -+ break; - case IMSG_NETWORK_DONE: - if (imsg.hdr.len != IMSG_HEADER_SIZE) { - log_warnx("rde_dispatch: wrong imsg len"); - break; - } - session_set = NULL; -- switch (netconf_s.prefix.af) { -- case AF_INET: -+ switch (netconf_s.prefix.aid) { -+ case AID_INET: - if (netconf_s.prefixlen > 32) - goto badnet; - network_add(&netconf_s, 0); - break; -- case AF_INET6: -+ case AID_INET6: - if (netconf_s.prefixlen > 128) - goto badnet; - network_add(&netconf_s, 0); - break; -+ case 0: -+ /* something failed beforehands */ -+ break; - default: - badnet: - log_warnx("rde_dispatch: bad network"); -@@ -528,10 +604,14 @@ badnet: - peer->prefix_rcvd_update; - p.stats.prefix_rcvd_withdraw = - peer->prefix_rcvd_withdraw; -+ p.stats.prefix_rcvd_eor = -+ peer->prefix_rcvd_eor; - p.stats.prefix_sent_update = - peer->prefix_sent_update; - p.stats.prefix_sent_withdraw = - peer->prefix_sent_withdraw; -+ p.stats.prefix_sent_eor = -+ peer->prefix_sent_eor; - } - imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NEIGHBOR, 0, - imsg.hdr.pid, -1, &p, sizeof(struct peer)); -@@ -544,6 +624,11 @@ badnet: - imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_RIB_MEM, 0, - imsg.hdr.pid, -1, &rdemem, sizeof(rdemem)); - break; -+ case IMSG_CTL_LOG_VERBOSE: -+ /* already checked by SE */ -+ memcpy(&verbose, imsg.data, sizeof(verbose)); -+ log_verbose(verbose); -+ break; - default: - break; - } -@@ -554,14 +639,17 @@ badnet: - void - rde_dispatch_imsg_parent(struct imsgbuf *ibuf) - { -+ static struct rdomain *rd; - struct imsg imsg; - struct mrt xmrt; - struct rde_rib rn; - struct rde_peer *peer; -+ struct peer_config *pconf; - struct filter_rule *r; - struct filter_set *s; - struct nexthop *nh; -- int n, fd, reconf_in = 0, reconf_out = 0; -+ int n, fd, reconf_in = 0, reconf_out = 0, -+ reconf_rib = 0; - u_int16_t rid; - - if ((n = imsg_read(ibuf)) == -1) -@@ -576,20 +664,12 @@ rde_dispatch_imsg_parent(struct imsgbuf - break; - - switch (imsg.hdr.type) { -- case IMSG_RECONF_CONF: -- reloadtime = time(NULL); -- newrules = calloc(1, sizeof(struct filter_head)); -- if (newrules == NULL) -- fatal(NULL); -- TAILQ_INIT(newrules); -- if ((nconf = malloc(sizeof(struct bgpd_config))) == -- NULL) -- fatal(NULL); -- memcpy(nconf, imsg.data, sizeof(struct bgpd_config)); -- for (rid = 0; rid < rib_size; rid++) -- ribs[rid].state = RIB_DELETE; -- break; - case IMSG_NETWORK_ADD: -+ if (imsg.hdr.len - IMSG_HEADER_SIZE != -+ sizeof(struct network_config)) { -+ log_warnx("rde_dispatch: wrong imsg len"); -+ break; -+ } - memcpy(&netconf_p, imsg.data, sizeof(netconf_p)); - TAILQ_INIT(&netconf_p.attrset); - parent_set = &netconf_p.attrset; -@@ -608,6 +688,26 @@ rde_dispatch_imsg_parent(struct imsgbuf - TAILQ_INIT(&netconf_p.attrset); - network_delete(&netconf_p, 1); - break; -+ case IMSG_RECONF_CONF: -+ if (imsg.hdr.len - IMSG_HEADER_SIZE != -+ sizeof(struct bgpd_config)) -+ fatalx("IMSG_RECONF_CONF bad len"); -+ reloadtime = time(NULL); -+ newrules = calloc(1, sizeof(struct filter_head)); -+ if (newrules == NULL) -+ fatal(NULL); -+ TAILQ_INIT(newrules); -+ newdomains = calloc(1, sizeof(struct rdomain_head)); -+ if (newdomains == NULL) -+ fatal(NULL); -+ SIMPLEQ_INIT(newdomains); -+ if ((nconf = malloc(sizeof(struct bgpd_config))) == -+ NULL) -+ fatal(NULL); -+ memcpy(nconf, imsg.data, sizeof(struct bgpd_config)); -+ for (rid = 0; rid < rib_size; rid++) -+ ribs[rid].state = RECONF_DELETE; -+ break; - case IMSG_RECONF_RIB: - if (imsg.hdr.len - IMSG_HEADER_SIZE != - sizeof(struct rde_rib)) -@@ -615,9 +715,26 @@ rde_dispatch_imsg_parent(struct imsgbuf - memcpy(&rn, imsg.data, sizeof(rn)); - rid = rib_find(rn.name); - if (rid == RIB_FAILED) -- rib_new(-1, rn.name, rn.flags); -- else -- ribs[rid].state = RIB_ACTIVE; -+ rib_new(rn.name, rn.rtableid, rn.flags); -+ else if (ribs[rid].rtableid != rn.rtableid || -+ (ribs[rid].flags & F_RIB_HASNOFIB) != -+ (rn.flags & F_RIB_HASNOFIB)) { -+ /* Big hammer in the F_RIB_NOFIB case but -+ * not often enough used to optimise it more. */ -+ rib_free(&ribs[rid]); -+ rib_new(rn.name, rn.rtableid, rn.flags); -+ } else -+ ribs[rid].state = RECONF_KEEP; -+ break; -+ case IMSG_RECONF_PEER: -+ if (imsg.hdr.len - IMSG_HEADER_SIZE != -+ sizeof(struct peer_config)) -+ fatalx("IMSG_RECONF_PEER bad len"); -+ if ((peer = peer_get(imsg.hdr.peerid)) == NULL) -+ break; -+ pconf = imsg.data; -+ strlcpy(peer->conf.rib, pconf->rib, -+ sizeof(peer->conf.rib)); - break; - case IMSG_RECONF_FILTER: - if (imsg.hdr.len - IMSG_HEADER_SIZE != -@@ -631,12 +748,42 @@ rde_dispatch_imsg_parent(struct imsgbuf - parent_set = &r->set; - TAILQ_INSERT_TAIL(newrules, r, entry); - break; -+ case IMSG_RECONF_RDOMAIN: -+ if (imsg.hdr.len - IMSG_HEADER_SIZE != -+ sizeof(struct rdomain)) -+ fatalx("IMSG_RECONF_RDOMAIN bad len"); -+ if ((rd = malloc(sizeof(struct rdomain))) == NULL) -+ fatal(NULL); -+ memcpy(rd, imsg.data, sizeof(struct rdomain)); -+ TAILQ_INIT(&rd->import); -+ TAILQ_INIT(&rd->export); -+ SIMPLEQ_INSERT_TAIL(newdomains, rd, entry); -+ break; -+ case IMSG_RECONF_RDOMAIN_EXPORT: -+ if (rd == NULL) { -+ log_warnx("rde_dispatch_imsg_parent: " -+ "IMSG_RECONF_RDOMAIN_EXPORT unexpected"); -+ break; -+ } -+ parent_set = &rd->export; -+ break; -+ case IMSG_RECONF_RDOMAIN_IMPORT: -+ if (rd == NULL) { -+ log_warnx("rde_dispatch_imsg_parent: " -+ "IMSG_RECONF_RDOMAIN_IMPORT unexpected"); -+ break; -+ } -+ parent_set = &rd->import; -+ break; -+ case IMSG_RECONF_RDOMAIN_DONE: -+ parent_set = NULL; -+ break; - case IMSG_RECONF_DONE: - if (nconf == NULL) - fatalx("got IMSG_RECONF_DONE but no config"); - if ((nconf->flags & BGPD_FLAG_NO_EVALUATE) - != (conf->flags & BGPD_FLAG_NO_EVALUATE)) { -- log_warnx( "change to/from route-collector " -+ log_warnx("change to/from route-collector " - "mode ignored"); - if (conf->flags & BGPD_FLAG_NO_EVALUATE) - nconf->flags |= BGPD_FLAG_NO_EVALUATE; -@@ -644,10 +791,27 @@ rde_dispatch_imsg_parent(struct imsgbuf - nconf->flags &= ~BGPD_FLAG_NO_EVALUATE; - } - memcpy(conf, nconf, sizeof(struct bgpd_config)); -+ conf->listen_addrs = NULL; -+ conf->csock = NULL; -+ conf->rcsock = NULL; - free(nconf); - nconf = NULL; - parent_set = NULL; -- prefix_network_clean(peerself, reloadtime, 0); -+ /* sync peerself with conf */ -+ peerself->remote_bgpid = ntohl(conf->bgpid); -+ peerself->conf.local_as = conf->as; -+ peerself->conf.remote_as = conf->as; -+ peerself->short_as = conf->short_as; -+ -+ /* apply new set of rdomain, sync will be done later */ -+ while ((rd = SIMPLEQ_FIRST(rdomains_l)) != NULL) { -+ SIMPLEQ_REMOVE_HEAD(rdomains_l, entry); -+ filterset_free(&rd->import); -+ filterset_free(&rd->export); -+ free(rd); -+ } -+ free(rdomains_l); -+ rdomains_l = newdomains; - - /* check if filter changed */ - LIST_FOREACH(peer, &peerlist, peer_l) { -@@ -655,30 +819,59 @@ rde_dispatch_imsg_parent(struct imsgbuf - continue; - peer->reconf_out = 0; - peer->reconf_in = 0; -- if (peer->conf.softreconfig_out && -- !rde_filter_equal(rules_l, newrules, peer, -- DIR_OUT)) { -- peer->reconf_out = 1; -- reconf_out = 1; -- } -+ peer->reconf_rib = 0; - if (peer->conf.softreconfig_in && - !rde_filter_equal(rules_l, newrules, peer, - DIR_IN)) { - peer->reconf_in = 1; - reconf_in = 1; - } -+ if (peer->ribid != rib_find(peer->conf.rib)) { -+ rib_dump(&ribs[peer->ribid], -+ rde_softreconfig_unload_peer, peer, -+ AID_UNSPEC); -+ peer->ribid = rib_find(peer->conf.rib); -+ peer->reconf_rib = 1; -+ reconf_rib = 1; -+ continue; -+ } -+ if (peer->conf.softreconfig_out && -+ !rde_filter_equal(rules_l, newrules, peer, -+ DIR_OUT)) { -+ peer->reconf_out = 1; -+ reconf_out = 1; -+ } - } -- /* XXX this needs rework anyway */ -- /* sync local-RIB first */ -+ /* bring ribs in sync before softreconfig dance */ -+ for (rid = 0; rid < rib_size; rid++) { -+ if (ribs[rid].state == RECONF_DELETE) -+ rib_free(&ribs[rid]); -+ else if (ribs[rid].state == RECONF_REINIT) -+ rib_dump(&ribs[0], -+ rde_softreconfig_load, &ribs[rid], -+ AID_UNSPEC); -+ } -+ /* sync local-RIBs first */ - if (reconf_in) - rib_dump(&ribs[0], rde_softreconfig_in, NULL, -- AF_UNSPEC); -+ AID_UNSPEC); - /* then sync peers */ - if (reconf_out) { - int i; -- for (i = 1; i < rib_size; i++) -+ for (i = 1; i < rib_size; i++) { -+ if (ribs[i].state == RECONF_REINIT) -+ /* already synced by _load */ -+ continue; - rib_dump(&ribs[i], rde_softreconfig_out, -- NULL, AF_UNSPEC); -+ NULL, AID_UNSPEC); -+ } -+ } -+ if (reconf_rib) { -+ LIST_FOREACH(peer, &peerlist, peer_l) { -+ rib_dump(&ribs[peer->ribid], -+ rde_softreconfig_load_peer, -+ peer, AID_UNSPEC); -+ } - } - - while ((r = TAILQ_FIRST(rules_l)) != NULL) { -@@ -688,16 +881,18 @@ rde_dispatch_imsg_parent(struct imsgbuf - } - free(rules_l); - rules_l = newrules; -- for (rid = 0; rid < rib_size; rid++) { -- if (ribs[rid].state == RIB_DELETE) -- rib_free(&ribs[rid]); -- } -+ - log_info("RDE reconfigured"); -+ imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0, -+ -1, NULL, 0); - break; - case IMSG_NEXTHOP_UPDATE: - nexthop_update(imsg.data); - break; - case IMSG_FILTER_SET: -+ if (imsg.hdr.len > IMSG_HEADER_SIZE + -+ sizeof(struct filter_set)) -+ fatalx("IMSG_RECONF_CONF bad len"); - if (parent_set == NULL) { - log_warnx("rde_dispatch_imsg_parent: " - "IMSG_FILTER_SET unexpected"); -@@ -725,7 +920,8 @@ rde_dispatch_imsg_parent(struct imsgbuf - log_warnx("expected to receive fd for mrt dump " - "but didn't receive any"); - else if (xmrt.type == MRT_TABLE_DUMP || -- xmrt.type == MRT_TABLE_DUMP_MP) { -+ xmrt.type == MRT_TABLE_DUMP_MP || -+ xmrt.type == MRT_TABLE_DUMP_V2) { - rde_dump_mrt_new(&xmrt, imsg.hdr.pid, fd); - } else - close(fd); -@@ -744,6 +940,8 @@ rde_dispatch_imsg_parent(struct imsgbuf - int - rde_update_dispatch(struct imsg *imsg) - { -+ struct bgpd_addr prefix; -+ struct mpattr mpa; - struct rde_peer *peer; - struct rde_aspath *asp = NULL; - u_char *p, *mpp = NULL; -@@ -752,9 +950,8 @@ rde_update_dispatch(struct imsg *imsg) - u_int16_t withdrawn_len; - u_int16_t attrpath_len; - u_int16_t nlri_len; -- u_int8_t prefixlen, safi, subtype; -- struct bgpd_addr prefix; -- struct mpattr mpa; -+ u_int8_t aid, prefixlen, safi, subtype; -+ u_int32_t fas; - - peer = peer_get(imsg->hdr.peerid); - if (peer == NULL) /* unknown peer, cannot happen */ -@@ -810,26 +1007,21 @@ rde_update_dispatch(struct imsg *imsg) - goto done; - } - -- /* -- * if either ATTR_AS4_AGGREGATOR or ATTR_AS4_PATH is present -- * try to fixup the attributes. -- * XXX do not fixup if F_ATTR_LOOP is set. -- */ -- if (asp->flags & F_ATTR_AS4BYTE_NEW && -- !(asp->flags & F_ATTR_LOOP)) -- rde_as4byte_fixup(peer, asp); -+ rde_as4byte_fixup(peer, asp); - - /* enforce remote AS if requested */ - if (asp->flags & F_ATTR_ASPATH && -- peer->conf.enforce_as == ENFORCE_AS_ON) -- if (peer->conf.remote_as != -- aspath_neighbor(asp->aspath)) { -- log_peer_warnx(&peer->conf, "bad path, " -- "enforce remote-as enabled"); -- rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH, -+ peer->conf.enforce_as == ENFORCE_AS_ON) { -+ fas = aspath_neighbor(asp->aspath); -+ if (peer->conf.remote_as != fas) { -+ log_peer_warnx(&peer->conf, "bad path, " -+ "starting with %s, " -+ "enforce neighbor-as enabled", log_as(fas)); -+ rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH, - NULL, 0); -- goto done; -+ goto done; - } -+ } - - rde_reflector(peer, asp); - } -@@ -860,9 +1052,9 @@ rde_update_dispatch(struct imsg *imsg) - p += pos; - len -= pos; - -- if (peer->capa_received.mp_v4 == SAFI_NONE && -- peer->capa_received.mp_v6 != SAFI_NONE) { -- log_peer_warnx(&peer->conf, "bad AFI, IPv4 disabled"); -+ if (peer->capa.mp[AID_INET] == 0) { -+ log_peer_warnx(&peer->conf, -+ "bad withdraw, %s disabled", aid2str(AID_INET)); - rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, - NULL, 0); - goto done; -@@ -879,6 +1071,10 @@ rde_update_dispatch(struct imsg *imsg) - ERR_UPD_ATTRLIST, NULL, 0); - return (-1); - } -+ if (withdrawn_len == 0) { -+ /* EoR marker */ -+ peer_recv_eor(peer, AID_INET); -+ } - return (0); - } - -@@ -892,15 +1088,30 @@ rde_update_dispatch(struct imsg *imsg) - afi = ntohs(afi); - safi = *mpp++; - mplen--; -- switch (afi) { -- case AFI_IPv6: -- if (peer->capa_received.mp_v6 == SAFI_NONE) { -- log_peer_warnx(&peer->conf, "bad AFI, " -- "IPv6 disabled"); -- rde_update_err(peer, ERR_UPDATE, -- ERR_UPD_OPTATTR, NULL, 0); -- goto done; -- } -+ -+ if (afi2aid(afi, safi, &aid) == -1) { -+ log_peer_warnx(&peer->conf, -+ "bad AFI/SAFI pair in withdraw"); -+ rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, -+ NULL, 0); -+ goto done; -+ } -+ -+ if (peer->capa.mp[aid] == 0) { -+ log_peer_warnx(&peer->conf, -+ "bad withdraw, %s disabled", aid2str(aid)); -+ rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, -+ NULL, 0); -+ goto done; -+ } -+ -+ if ((asp->flags & ~F_ATTR_MP_UNREACH) == 0 && mplen == 0) { -+ /* EoR marker */ -+ peer_recv_eor(peer, aid); -+ } -+ -+ switch (aid) { -+ case AID_INET6: - while (mplen > 0) { - if ((pos = rde_update_get_prefix6(mpp, mplen, - &prefix, &prefixlen)) == -1) { -@@ -926,6 +1137,32 @@ rde_update_dispatch(struct imsg *imsg) - rde_update_withdraw(peer, &prefix, prefixlen); - } - break; -+ case AID_VPN_IPv4: -+ while (mplen > 0) { -+ if ((pos = rde_update_get_vpn4(mpp, mplen, -+ &prefix, &prefixlen)) == -1) { -+ log_peer_warnx(&peer->conf, -+ "bad VPNv4 withdraw prefix"); -+ rde_update_err(peer, ERR_UPDATE, -+ ERR_UPD_OPTATTR, -+ mpa.unreach, mpa.unreach_len); -+ goto done; -+ } -+ if (prefixlen > 32) { -+ log_peer_warnx(&peer->conf, -+ "bad VPNv4 withdraw prefix"); -+ rde_update_err(peer, ERR_UPDATE, -+ ERR_UPD_OPTATTR, -+ mpa.unreach, mpa.unreach_len); -+ goto done; -+ } -+ -+ mpp += pos; -+ mplen -= pos; -+ -+ rde_update_withdraw(peer, &prefix, prefixlen); -+ } -+ break; - default: - /* silently ignore unsupported multiprotocol AF */ - break; -@@ -963,9 +1200,9 @@ rde_update_dispatch(struct imsg *imsg) - p += pos; - nlri_len -= pos; - -- if (peer->capa_received.mp_v4 == SAFI_NONE && -- peer->capa_received.mp_v6 != SAFI_NONE) { -- log_peer_warnx(&peer->conf, "bad AFI, IPv4 disabled"); -+ if (peer->capa.mp[AID_INET] == 0) { -+ log_peer_warnx(&peer->conf, -+ "bad update, %s disabled", aid2str(AID_INET)); - rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, - NULL, 0); - goto done; -@@ -995,6 +1232,22 @@ rde_update_dispatch(struct imsg *imsg) - safi = *mpp++; - mplen--; - -+ if (afi2aid(afi, safi, &aid) == -1) { -+ log_peer_warnx(&peer->conf, -+ "bad AFI/SAFI pair in update"); -+ rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, -+ NULL, 0); -+ goto done; -+ } -+ -+ if (peer->capa.mp[aid] == 0) { -+ log_peer_warnx(&peer->conf, -+ "bad update, %s disabled", aid2str(aid)); -+ rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, -+ NULL, 0); -+ goto done; -+ } -+ - /* - * this works because asp is not linked. - * But first unlock the previously locked nexthop. -@@ -1004,8 +1257,8 @@ rde_update_dispatch(struct imsg *imsg) - (void)nexthop_delete(asp->nexthop); - asp->nexthop = NULL; - } -- if ((pos = rde_get_mp_nexthop(mpp, mplen, afi, asp)) == -1) { -- log_peer_warnx(&peer->conf, "bad IPv6 nlri prefix"); -+ if ((pos = rde_get_mp_nexthop(mpp, mplen, aid, asp, peer)) == -1) { -+ log_peer_warnx(&peer->conf, "bad nlri prefix"); - rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, - mpa.reach, mpa.reach_len); - goto done; -@@ -1013,16 +1266,8 @@ rde_update_dispatch(struct imsg *imsg) - mpp += pos; - mplen -= pos; - -- switch (afi) { -- case AFI_IPv6: -- if (peer->capa_received.mp_v6 == SAFI_NONE) { -- log_peer_warnx(&peer->conf, "bad AFI, " -- "IPv6 disabled"); -- rde_update_err(peer, ERR_UPDATE, -- ERR_UPD_OPTATTR, NULL, 0); -- goto done; -- } -- -+ switch (aid) { -+ case AID_INET6: - while (mplen > 0) { - if ((pos = rde_update_get_prefix6(mpp, mplen, - &prefix, &prefixlen)) == -1) { -@@ -1058,6 +1303,42 @@ rde_update_dispatch(struct imsg *imsg) - - } - break; -+ case AID_VPN_IPv4: -+ while (mplen > 0) { -+ if ((pos = rde_update_get_vpn4(mpp, mplen, -+ &prefix, &prefixlen)) == -1) { -+ log_peer_warnx(&peer->conf, -+ "bad VPNv4 nlri prefix"); -+ rde_update_err(peer, ERR_UPDATE, -+ ERR_UPD_OPTATTR, -+ mpa.reach, mpa.reach_len); -+ goto done; -+ } -+ if (prefixlen > 32) { -+ rde_update_err(peer, ERR_UPDATE, -+ ERR_UPD_OPTATTR, -+ mpa.reach, mpa.reach_len); -+ goto done; -+ } -+ -+ mpp += pos; -+ mplen -= pos; -+ -+ rde_update_update(peer, asp, &prefix, -+ prefixlen); -+ -+ /* max prefix checker */ -+ if (peer->conf.max_prefix && -+ peer->prefix_cnt >= peer->conf.max_prefix) { -+ log_peer_warnx(&peer->conf, -+ "prefix limit reached"); -+ rde_update_err(peer, ERR_CEASE, -+ ERR_CEASE_MAX_PREFIX, NULL, 0); -+ goto done; -+ } -+ -+ } -+ break; - default: - /* silently ignore unsupported multiprotocol AF */ - break; -@@ -1085,7 +1366,8 @@ rde_update_update(struct rde_peer *peer, - struct bgpd_addr *prefix, u_int8_t prefixlen) - { - struct rde_aspath *fasp; -- int r = 0; -+ enum filter_actions action; -+ int r = 0, f = 0; - u_int16_t i; - - peer->prefix_rcvd_update++; -@@ -1095,18 +1377,24 @@ rde_update_update(struct rde_peer *peer, - - for (i = 1; i < rib_size; i++) { - /* input filter */ -- if (rde_filter(i, &fasp, rules_l, peer, asp, prefix, prefixlen, -- peer, DIR_IN) == ACTION_DENY) -- goto done; -+ action = rde_filter(i, &fasp, rules_l, peer, asp, prefix, -+ prefixlen, peer, DIR_IN); - - if (fasp == NULL) - fasp = asp; - -- rde_update_log("update", i, peer, &fasp->nexthop->exit_nexthop, -- prefix, prefixlen); -- r += path_update(&ribs[i], peer, fasp, prefix, prefixlen); -+ if (action == ACTION_ALLOW) { -+ rde_update_log("update", i, peer, -+ &fasp->nexthop->exit_nexthop, prefix, prefixlen); -+ r += path_update(&ribs[i], peer, fasp, prefix, -+ prefixlen); -+ } else if (prefix_remove(&ribs[i], peer, prefix, prefixlen, -+ 0)) { -+ rde_update_log("filtered withdraw", i, peer, -+ NULL, prefix, prefixlen); -+ f++; -+ } - --done: - /* free modified aspath */ - if (fasp != asp) - path_put(fasp); -@@ -1114,6 +1402,8 @@ done: - - if (r) - peer->prefix_cnt++; -+ else if (f) -+ peer->prefix_cnt--; - } - - void -@@ -1152,7 +1442,7 @@ rde_update_withdraw(struct rde_peer *pee - } while (0) - - #define CHECK_FLAGS(s, t, m) \ -- (((s) & ~(ATTR_EXTLEN | (m))) == (t)) -+ (((s) & ~(ATTR_DEFMASK | (m))) == (t)) - - int - rde_attr_parse(u_char *p, u_int16_t len, struct rde_peer *peer, -@@ -1161,6 +1451,7 @@ rde_attr_parse(u_char *p, u_int16_t len, - struct bgpd_addr nexthop; - u_char *op = p, *npath; - u_int32_t tmp32; -+ int err; - u_int16_t attr_len, nlen; - u_int16_t plen = 0; - u_int8_t flags; -@@ -1195,6 +1486,7 @@ bad_len: - switch (type) { - case ATTR_UNDEF: - /* ignore and drop path attributes with a type code of 0 */ -+ plen += attr_len; - break; - case ATTR_ORIGIN: - if (attr_len != 1) -@@ -1220,7 +1512,17 @@ bad_flags: - case ATTR_ASPATH: - if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) - goto bad_flags; -- if (aspath_verify(p, attr_len, rde_as4byte(peer)) != 0) { -+ err = aspath_verify(p, attr_len, rde_as4byte(peer)); -+ if (err == AS_ERR_SOFT) { -+ /* -+ * soft errors like unexpected segment types are -+ * not considered fatal and the path is just -+ * marked invalid. -+ */ -+ a->flags |= F_ATTR_PARSE_ERR; -+ log_peer_warnx(&peer->conf, "bad ASPATH, " -+ "path invalidated and prefix withdrawn"); -+ } else if (err != 0) { - rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH, - NULL, 0); - return (-1); -@@ -1248,7 +1550,7 @@ bad_flags: - a->flags |= F_ATTR_NEXTHOP; - - bzero(&nexthop, sizeof(nexthop)); -- nexthop.af = AF_INET; -+ nexthop.aid = AID_INET; - UPD_READ(&nexthop.v4.s_addr, p, plen, 4); - /* - * Check if the nexthop is a valid IP address. We consider -@@ -1305,9 +1607,21 @@ bad_flags: - goto optattr; - case ATTR_AGGREGATOR: - if ((!rde_as4byte(peer) && attr_len != 6) || -- (rde_as4byte(peer) && attr_len != 8)) -- goto bad_len; -- if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, 0)) -+ (rde_as4byte(peer) && attr_len != 8)) { -+ /* -+ * ignore attribute in case of error as per -+ * draft-ietf-idr-optional-transitive-00.txt -+ * but only if partial bit is set -+ */ -+ if ((flags & ATTR_PARTIAL) == 0) -+ goto bad_len; -+ log_peer_warnx(&peer->conf, "bad AGGREGATOR, " -+ "partial attribute ignored"); -+ plen += attr_len; -+ break; -+ } -+ if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, -+ ATTR_PARTIAL)) - goto bad_flags; - if (!rde_as4byte(peer)) { - /* need to inflate aggregator AS to 4-byte */ -@@ -1323,8 +1637,35 @@ bad_flags: - /* 4-byte ready server take the default route */ - goto optattr; - case ATTR_COMMUNITIES: -- if ((attr_len & 0x3) != 0) -- goto bad_len; -+ if (attr_len % 4 != 0) { -+ /* -+ * mark update as bad and withdraw all routes as per -+ * draft-ietf-idr-optional-transitive-00.txt -+ * but only if partial bit is set -+ */ -+ if ((flags & ATTR_PARTIAL) == 0) -+ goto bad_len; -+ a->flags |= F_ATTR_PARSE_ERR; -+ log_peer_warnx(&peer->conf, "bad COMMUNITIES, " -+ "path invalidated and prefix withdrawn"); -+ } -+ if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, -+ ATTR_PARTIAL)) -+ goto bad_flags; -+ goto optattr; -+ case ATTR_EXT_COMMUNITIES: -+ if (attr_len % 8 != 0) { -+ /* -+ * mark update as bad and withdraw all routes as per -+ * draft-ietf-idr-optional-transitive-00.txt -+ * but only if partial bit is set -+ */ -+ if ((flags & ATTR_PARTIAL) == 0) -+ goto bad_len; -+ a->flags |= F_ATTR_PARSE_ERR; -+ log_peer_warnx(&peer->conf, "bad EXT_COMMUNITIES, " -+ "path invalidated and prefix withdrawn"); -+ } - if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, - ATTR_PARTIAL)) - goto bad_flags; -@@ -1336,7 +1677,7 @@ bad_flags: - goto bad_flags; - goto optattr; - case ATTR_CLUSTER_LIST: -- if ((attr_len & 0x3) != 0) -+ if (attr_len % 4 != 0) - goto bad_len; - if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) - goto bad_flags; -@@ -1370,8 +1711,15 @@ bad_flags: - plen += attr_len; - break; - case ATTR_AS4_AGGREGATOR: -- if (attr_len != 8) -- goto bad_len; -+ if (attr_len != 8) { -+ /* see ATTR_AGGREGATOR ... */ -+ if ((flags & ATTR_PARTIAL) == 0) -+ goto bad_len; -+ log_peer_warnx(&peer->conf, "bad AS4_AGGREGATOR, " -+ "partial attribute ignored"); -+ plen += attr_len; -+ break; -+ } - if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, - ATTR_PARTIAL)) - goto bad_flags; -@@ -1381,20 +1729,31 @@ bad_flags: - if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, - ATTR_PARTIAL)) - goto bad_flags; -- if (aspath_verify(p, attr_len, 1) != 0) { -+ if ((err = aspath_verify(p, attr_len, 1)) != 0) { - /* - * XXX RFC does not specify how to handle errors. - * XXX Instead of dropping the session because of a -- * XXX bad path just mark the full update as not -- * XXX loop-free the update is no longer eligible and -- * XXX will not be considered for routing or -- * XXX redistribution. Something better is needed. -+ * XXX bad path just mark the full update as having -+ * XXX a parse error which makes the update no longer -+ * XXX eligible and will not be considered for routing -+ * XXX or redistribution. -+ * XXX We follow draft-ietf-idr-optional-transitive -+ * XXX by looking at the partial bit. -+ * XXX Consider soft errors similar to a partial attr. - */ -- a->flags |= F_ATTR_LOOP; -- goto optattr; -- } -- a->flags |= F_ATTR_AS4BYTE_NEW; -- goto optattr; -+ if (flags & ATTR_PARTIAL || err == AS_ERR_SOFT) { -+ a->flags |= F_ATTR_PARSE_ERR; -+ log_peer_warnx(&peer->conf, "bad AS4_PATH, " -+ "path invalidated and prefix withdrawn"); -+ goto optattr; -+ } else { -+ rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH, -+ NULL, 0); -+ return (-1); -+ } -+ } -+ a->flags |= F_ATTR_AS4BYTE_NEW; -+ goto optattr; - default: - if ((flags & ATTR_OPTIONAL) == 0) { - rde_update_err(peer, ERR_UPDATE, ERR_UPD_UNKNWN_WK_ATTR, -@@ -1415,6 +1774,42 @@ bad_list: - - return (plen); - } -+ -+int -+rde_attr_add(struct rde_aspath *a, u_char *p, u_int16_t len) -+{ -+ u_int16_t attr_len; -+ u_int16_t plen = 0; -+ u_int8_t flags; -+ u_int8_t type; -+ u_int8_t tmp8; -+ -+ if (a == NULL) /* no aspath, nothing to do */ -+ return (0); -+ if (len < 3) -+ return (-1); -+ -+ UPD_READ(&flags, p, plen, 1); -+ UPD_READ(&type, p, plen, 1); -+ -+ if (flags & ATTR_EXTLEN) { -+ if (len - plen < 2) -+ return (-1); -+ UPD_READ(&attr_len, p, plen, 2); -+ attr_len = ntohs(attr_len); -+ } else { -+ UPD_READ(&tmp8, p, plen, 1); -+ attr_len = tmp8; -+ } -+ -+ if (len - plen < attr_len) -+ return (-1); -+ -+ if (attr_optadd(a, flags, type, p, attr_len) == -1) -+ return (-1); -+ return (0); -+} -+ - #undef UPD_READ - #undef CHECK_FLAGS - -@@ -1440,8 +1835,8 @@ rde_attr_missing(struct rde_aspath *a, i - } - - int --rde_get_mp_nexthop(u_char *data, u_int16_t len, u_int16_t afi, -- struct rde_aspath *asp) -+rde_get_mp_nexthop(u_char *data, u_int16_t len, u_int8_t aid, -+ struct rde_aspath *asp, struct rde_peer *peer) - { - struct bgpd_addr nexthop; - u_int8_t totlen, nhlen; -@@ -1457,8 +1852,9 @@ rde_get_mp_nexthop(u_char *data, u_int16 - return (-1); - - bzero(&nexthop, sizeof(nexthop)); -- switch (afi) { -- case AFI_IPv6: -+ nexthop.aid = aid; -+ switch (aid) { -+ case AID_INET6: - /* - * RFC2545 describes that there may be a link-local - * address carried in nexthop. Yikes! -@@ -1471,72 +1867,144 @@ rde_get_mp_nexthop(u_char *data, u_int16 - log_warnx("bad multiprotocol nexthop, bad size"); - return (-1); - } -- nexthop.af = AF_INET6; - memcpy(&nexthop.v6.s6_addr, data, 16); -- asp->nexthop = nexthop_get(&nexthop); -+#if defined(__KAME__) && defined(IPV6_LINKLOCAL_PEER) -+ if (IN6_IS_ADDR_LINKLOCAL(&nexthop.v6) && -+ peer->conf.lliface[0]) { -+ int ifindex; -+ -+ ifindex = if_nametoindex(peer->conf.lliface); -+ if (ifindex != 0) { -+ SET_IN6_LINKLOCAL_IFINDEX(nexthop.v6, ifindex); -+ nexthop.scope_id = ifindex; -+ } else -+ log_warnx("bad interface: %s", peer->conf.lliface); -+ } -+#endif -+ break; -+ case AID_VPN_IPv4: - /* -- * lock the nexthop because it is not yet linked else -- * withdraws may remove this nexthop which in turn would -- * cause a use after free error. -+ * Neither RFC4364 nor RFC3107 specify the format of the -+ * nexthop in an explicit way. The quality of RFC went down -+ * the toilet the larger the number got. -+ * RFC4364 is very confusing about VPN-IPv4 address and the -+ * VPN-IPv4 prefix that carries also a MPLS label. -+ * So the nexthop is a 12-byte address with a 64bit RD and -+ * an IPv4 address following. In the nexthop case the RD can -+ * be ignored. -+ * Since the nexthop has to be in the main IPv4 table just -+ * create an AID_INET nexthop. So we don't need to handle -+ * AID_VPN_IPv4 in nexthop and kroute. - */ -- asp->nexthop->refcnt++; -- -- /* ignore reserved (old SNPA) field as per RFC 4760 */ -- totlen += nhlen + 1; -- data += nhlen + 1; -- -- return (totlen); -- default: -- log_warnx("bad multiprotocol nexthop, bad AF"); -+ if (nhlen != 12) { -+ log_warnx("bad multiprotocol nexthop, bad size"); -+ return (-1); -+ } -+ data += sizeof(u_int64_t); -+ nexthop.aid = AID_INET; -+ memcpy(&nexthop.v4, data, sizeof(nexthop.v4)); - break; -+ default: -+ log_warnx("bad multiprotocol nexthop, bad AID"); -+ return (-1); - } - -- return (-1); -+ asp->nexthop = nexthop_get(&nexthop); -+ /* -+ * lock the nexthop because it is not yet linked else -+ * withdraws may remove this nexthop which in turn would -+ * cause a use after free error. -+ */ -+ asp->nexthop->refcnt++; -+ -+ /* ignore reserved (old SNPA) field as per RFC4760 */ -+ totlen += nhlen + 1; -+ data += nhlen + 1; -+ -+ return (totlen); -+} -+ -+int -+rde_update_extract_prefix(u_char *p, u_int16_t len, void *va, -+ u_int8_t pfxlen, u_int8_t max) -+{ -+ static u_char addrmask[] = { -+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; -+ u_char *a = va; -+ int i; -+ u_int16_t plen = 0; -+ -+ for (i = 0; pfxlen && i < max; i++) { -+ if (len <= plen) -+ return (-1); -+ if (pfxlen < 8) { -+ a[i] = *p++ & addrmask[pfxlen]; -+ plen++; -+ break; -+ } else { -+ a[i] = *p++; -+ plen++; -+ pfxlen -= 8; -+ } -+ } -+ return (plen); - } - - int - rde_update_get_prefix(u_char *p, u_int16_t len, struct bgpd_addr *prefix, - u_int8_t *prefixlen) - { -- int i; -- u_int8_t pfxlen; -- u_int16_t plen; -- union { -- struct in_addr a32; -- u_int8_t a8[4]; -- } addr; -+ u_int8_t pfxlen; -+ int plen; - - if (len < 1) - return (-1); - -- memcpy(&pfxlen, p, 1); -- p += 1; -- plen = 1; -+ pfxlen = *p++; -+ len--; - - bzero(prefix, sizeof(struct bgpd_addr)); -- addr.a32.s_addr = 0; -- for (i = 0; i <= 3; i++) { -- if (pfxlen > i * 8) { -- if (len - plen < 1) -- return (-1); -- memcpy(&addr.a8[i], p++, 1); -- plen++; -- } -- } -- prefix->af = AF_INET; -- prefix->v4.s_addr = addr.a32.s_addr; -+ prefix->aid = AID_INET; - *prefixlen = pfxlen; - -- return (plen); -+ if ((plen = rde_update_extract_prefix(p, len, &prefix->v4, pfxlen, -+ sizeof(prefix->v4))) == -1) -+ return (-1); -+ -+ return (plen + 1); /* pfxlen needs to be added */ - } - - int - rde_update_get_prefix6(u_char *p, u_int16_t len, struct bgpd_addr *prefix, - u_int8_t *prefixlen) - { -- int i; -+ int plen; - u_int8_t pfxlen; -- u_int16_t plen; -+ -+ if (len < 1) -+ return (-1); -+ -+ pfxlen = *p++; -+ len--; -+ -+ bzero(prefix, sizeof(struct bgpd_addr)); -+ prefix->aid = AID_INET6; -+ *prefixlen = pfxlen; -+ -+ if ((plen = rde_update_extract_prefix(p, len, &prefix->v6, pfxlen, -+ sizeof(prefix->v6))) == -1) -+ return (-1); -+ -+ return (plen + 1); /* pfxlen needs to be added */ -+} -+ -+int -+rde_update_get_vpn4(u_char *p, u_int16_t len, struct bgpd_addr *prefix, -+ u_int8_t *prefixlen) -+{ -+ int rv, done = 0; -+ u_int8_t pfxlen; -+ u_int16_t plen; - - if (len < 1) - return (-1); -@@ -1546,25 +2014,50 @@ rde_update_get_prefix6(u_char *p, u_int1 - plen = 1; - - bzero(prefix, sizeof(struct bgpd_addr)); -- for (i = 0; i <= 15; i++) { -- if (pfxlen > i * 8) { -- if (len - plen < 1) -- return (-1); -- memcpy(&prefix->v6.s6_addr[i], p++, 1); -- plen++; -- } -- } -- prefix->af = AF_INET6; -+ -+ /* label stack */ -+ do { -+ if (len - plen < 3 || pfxlen < 3 * 8) -+ return (-1); -+ if (prefix->vpn4.labellen + 3U > -+ sizeof(prefix->vpn4.labelstack)) -+ return (-1); -+ prefix->vpn4.labelstack[prefix->vpn4.labellen++] = *p++; -+ prefix->vpn4.labelstack[prefix->vpn4.labellen++] = *p++; -+ prefix->vpn4.labelstack[prefix->vpn4.labellen] = *p++; -+ if (prefix->vpn4.labelstack[prefix->vpn4.labellen] & -+ BGP_MPLS_BOS) -+ done = 1; -+ prefix->vpn4.labellen++; -+ plen += 3; -+ pfxlen -= 3 * 8; -+ } while (!done); -+ -+ /* RD */ -+ if (len - plen < (int)sizeof(u_int64_t) || -+ pfxlen < sizeof(u_int64_t) * 8) -+ return (-1); -+ memcpy(&prefix->vpn4.rd, p, sizeof(u_int64_t)); -+ pfxlen -= sizeof(u_int64_t) * 8; -+ p += sizeof(u_int64_t); -+ plen += sizeof(u_int64_t); -+ -+ /* prefix */ -+ prefix->aid = AID_VPN_IPv4; - *prefixlen = pfxlen; - -- return (plen); -+ if ((rv = rde_update_extract_prefix(p, len, &prefix->vpn4.addr, -+ pfxlen, sizeof(prefix->vpn4.addr))) == -1) -+ return (-1); -+ -+ return (plen + rv); - } - - void - rde_update_err(struct rde_peer *peer, u_int8_t error, u_int8_t suberr, - void *data, u_int16_t size) - { -- struct buf *wbuf; -+ struct ibuf *wbuf; - - if ((wbuf = imsg_create(ibuf_se, IMSG_UPDATE_ERR, peer->conf.id, 0, - size + sizeof(error) + sizeof(suberr))) == NULL) -@@ -1616,16 +2109,30 @@ rde_as4byte_fixup(struct rde_peer *peer, - struct attr *nasp, *naggr, *oaggr; - u_int32_t as; - -+ /* -+ * if either ATTR_AS4_AGGREGATOR or ATTR_AS4_PATH is present -+ * try to fixup the attributes. -+ * Do not fixup if F_ATTR_PARSE_ERR is set. -+ */ -+ if (!(a->flags & F_ATTR_AS4BYTE_NEW) || a->flags & F_ATTR_PARSE_ERR) -+ return; -+ - /* first get the attributes */ - nasp = attr_optget(a, ATTR_AS4_PATH); - naggr = attr_optget(a, ATTR_AS4_AGGREGATOR); - - if (rde_as4byte(peer)) { - /* NEW session using 4-byte ASNs */ -- if (nasp) -+ if (nasp) { -+ log_peer_warnx(&peer->conf, "uses 4-byte ASN " -+ "but sent AS4_PATH attribute."); - attr_free(a, nasp); -- if (naggr) -+ } -+ if (naggr) { -+ log_peer_warnx(&peer->conf, "uses 4-byte ASN " -+ "but sent AS4_AGGREGATOR attribute."); - attr_free(a, naggr); -+ } - return; - } - /* OLD session using 2-byte ASNs */ -@@ -1669,6 +2176,10 @@ rde_reflector(struct rde_peer *peer, str - u_int16_t len; - u_int32_t id; - -+ /* do not consider updates with parse errors */ -+ if (asp->flags & F_ATTR_PARSE_ERR) -+ return; -+ - /* check for originator id if eq router_id drop */ - if ((a = attr_optget(asp, ATTR_ORIGINATOR_ID)) != NULL) { - if (memcmp(&conf->bgpid, a->data, sizeof(conf->bgpid)) == 0) { -@@ -1677,10 +2188,10 @@ rde_reflector(struct rde_peer *peer, str - return; - } - } else if (conf->flags & BGPD_FLAG_REFLECTOR) { -- if (peer->conf.ebgp == 0) -- id = htonl(peer->remote_bgpid); -- else -+ if (peer->conf.ebgp) - id = conf->bgpid; -+ else -+ id = htonl(peer->remote_bgpid); - if (attr_optadd(asp, ATTR_OPTIONAL, ATTR_ORIGINATOR_ID, - &id, sizeof(u_int32_t)) == -1) - fatalx("attr_optadd failed but impossible"); -@@ -1724,17 +2235,17 @@ void - rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags) - { - struct ctl_show_rib rib; -- struct buf *wbuf; -+ struct ibuf *wbuf; - struct attr *a; - void *bp; -+ time_t staletime; - u_int8_t l; - - bzero(&rib, sizeof(rib)); - rib.lastchange = p->lastchange; - rib.local_pref = asp->lpref; - rib.med = asp->med; -- rib.prefix_cnt = asp->prefix_cnt; -- rib.active_cnt = asp->active_cnt; -+ rib.weight = asp->weight; - strlcpy(rib.descr, asp->peer->conf.descr, sizeof(rib.descr)); - memcpy(&rib.remote_addr, &asp->peer->remote_addr, - sizeof(rib.remote_addr)); -@@ -1748,23 +2259,26 @@ rde_dump_rib_as(struct prefix *p, struct - /* announced network may have a NULL nexthop */ - bzero(&rib.true_nexthop, sizeof(rib.true_nexthop)); - bzero(&rib.exit_nexthop, sizeof(rib.exit_nexthop)); -- rib.true_nexthop.af = p->prefix->af; -- rib.exit_nexthop.af = p->prefix->af; -+ rib.true_nexthop.aid = p->prefix->aid; -+ rib.exit_nexthop.aid = p->prefix->aid; - } - pt_getaddr(p->prefix, &rib.prefix); - rib.prefixlen = p->prefix->prefixlen; - rib.origin = asp->origin; - rib.flags = 0; - if (p->rib->active == p) -- rib.flags |= F_RIB_ACTIVE; -- if (asp->peer->conf.ebgp == 0) -- rib.flags |= F_RIB_INTERNAL; -+ rib.flags |= F_PREF_ACTIVE; -+ if (!asp->peer->conf.ebgp) -+ rib.flags |= F_PREF_INTERNAL; - if (asp->flags & F_PREFIX_ANNOUNCED) -- rib.flags |= F_RIB_ANNOUNCE; -+ rib.flags |= F_PREF_ANNOUNCE; - if (asp->nexthop == NULL || asp->nexthop->state == NEXTHOP_REACH) -- rib.flags |= F_RIB_ELIGIBLE; -+ rib.flags |= F_PREF_ELIGIBLE; - if (asp->flags & F_ATTR_LOOP) -- rib.flags &= ~F_RIB_ELIGIBLE; -+ rib.flags &= ~F_PREF_ELIGIBLE; -+ staletime = asp->peer->staletime[p->prefix->aid]; -+ if (staletime && p->lastchange <= staletime) -+ rib.flags |= F_PREF_STALE; - rib.aspath_len = aspath_length(asp->aspath); - - if ((wbuf = imsg_create(ibuf_se_ctl, IMSG_CTL_SHOW_RIB, 0, pid, -@@ -1784,13 +2298,13 @@ rde_dump_rib_as(struct prefix *p, struct - IMSG_CTL_SHOW_RIB_ATTR, 0, pid, - attr_optlen(a))) == NULL) - return; -- if ((bp = buf_reserve(wbuf, attr_optlen(a))) == NULL) { -- buf_free(wbuf); -+ if ((bp = ibuf_reserve(wbuf, attr_optlen(a))) == NULL) { -+ ibuf_free(wbuf); - return; - } - if (attr_write(bp, attr_optlen(a), a->flags, - a->type, a->data, a->len) == -1) { -- buf_free(wbuf); -+ ibuf_free(wbuf); - return; - } - imsg_close(ibuf_se_ctl, wbuf); -@@ -1828,17 +2342,20 @@ rde_dump_filter(struct prefix *p, struct - { - struct rde_peer *peer; - -- if (req->flags & F_CTL_ADJ_IN || -+ if (req->flags & F_CTL_ADJ_IN || - !(req->flags & (F_CTL_ADJ_IN|F_CTL_ADJ_OUT))) { - if (req->peerid && req->peerid != p->aspath->peer->conf.id) - return; -- if (req->type == IMSG_CTL_SHOW_RIB_AS && -- !aspath_match(p->aspath->aspath, req->as.type, req->as.as)) -+ if (req->type == IMSG_CTL_SHOW_RIB_AS && -+ !aspath_match(p->aspath->aspath->data, -+ p->aspath->aspath->len, req->as.type, req->as.as)) - return; - if (req->type == IMSG_CTL_SHOW_RIB_COMMUNITY && -- !rde_filter_community(p->aspath, req->community.as, -+ !community_match(p->aspath, req->community.as, - req->community.type)) - return; -+ if ((req->flags & F_CTL_ACTIVE) && p->rib->active != p) -+ return; - rde_dump_rib_as(p, p->aspath, req->pid, req->flags); - } else if (req->flags & F_CTL_ADJ_OUT) { - if (p->rib->active != p) -@@ -1872,7 +2389,7 @@ rde_dump_prefix_upcall(struct rib_entry - - pt = re->prefix; - pt_getaddr(pt, &addr); -- if (addr.af != ctx->req.prefix.af) -+ if (addr.aid != ctx->req.prefix.aid) - return; - if (ctx->req.prefixlen > pt->prefixlen) - return; -@@ -1889,6 +2406,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req - struct rib_entry *re; - u_int error; - u_int16_t id; -+ u_int8_t hostplen = 0; - - if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { - log_warn("rde_dump_ctx_new"); -@@ -1902,6 +2420,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req - error = CTL_RES_NOSUCHPEER; - imsg_compose(ibuf_se_ctl, IMSG_CTL_RESULT, 0, pid, -1, &error, - sizeof(error)); -+ free(ctx); - return; - } - -@@ -1924,7 +2443,18 @@ rde_dump_ctx_new(struct ctl_show_rib_req - ctx->ribctx.ctx_upcall = rde_dump_prefix_upcall; - break; - } -- if (req->prefixlen == 32) -+ switch (req->prefix.aid) { -+ case AID_INET: -+ case AID_VPN_IPv4: -+ hostplen = 32; -+ break; -+ case AID_INET6: -+ hostplen = 128; -+ break; -+ default: -+ fatalx("rde_dump_ctx_new: unknown af"); -+ } -+ if (req->prefixlen == hostplen) - re = rib_lookup(&ribs[id], &req->prefix); - else - re = rib_get(&ribs[id], &req->prefix, req->prefixlen); -@@ -1937,7 +2467,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req - } - ctx->ribctx.ctx_done = rde_dump_done; - ctx->ribctx.ctx_arg = ctx; -- ctx->ribctx.ctx_af = ctx->req.af; -+ ctx->ribctx.ctx_aid = ctx->req.aid; - rib_dump_r(&ctx->ribctx); - } - -@@ -1971,13 +2501,17 @@ rde_dump_mrt_new(struct mrt *mrt, pid_t - free(ctx); - return; - } -+ -+ if (ctx->mrt.type == MRT_TABLE_DUMP_V2) -+ mrt_dump_v2_hdr(&ctx->mrt, conf, &peerlist); -+ - ctx->ribctx.ctx_count = RDE_RUNNER_ROUNDS; - ctx->ribctx.ctx_rib = &ribs[id]; - ctx->ribctx.ctx_upcall = mrt_dump_upcall; -- ctx->ribctx.ctx_done = mrt_dump_done; -+ ctx->ribctx.ctx_done = mrt_done; - ctx->ribctx.ctx_arg = &ctx->mrt; -- ctx->ribctx.ctx_af = AF_UNSPEC; -- LIST_INSERT_HEAD(&rde_mrts, &ctx->mrt, entry); -+ ctx->ribctx.ctx_aid = AID_UNSPEC; -+ LIST_INSERT_HEAD(&rde_mrts, ctx, entry); - rde_mrt_cnt++; - rib_dump_r(&ctx->ribctx); - } -@@ -1985,13 +2519,25 @@ rde_dump_mrt_new(struct mrt *mrt, pid_t - /* - * kroute specific functions - */ -+int -+rde_rdomain_import(struct rde_aspath *asp, struct rdomain *rd) -+{ -+ struct filter_set *s; -+ -+ TAILQ_FOREACH(s, &rd->import, entry) { -+ if (community_ext_match(asp, &s->action.ext_community, 0)) -+ return (1); -+ } -+ return (0); -+} -+ - void --rde_send_kroute(struct prefix *new, struct prefix *old) -+rde_send_kroute(struct prefix *new, struct prefix *old, u_int16_t ribid) - { -- struct kroute_label kl; -- struct kroute6_label kl6; -+ struct kroute_full kr; - struct bgpd_addr addr; - struct prefix *p; -+ struct rdomain *rd; - enum imsg_type type; - - /* -@@ -2011,43 +2557,43 @@ rde_send_kroute(struct prefix *new, stru - } - - pt_getaddr(p->prefix, &addr); -- switch (addr.af) { -- case AF_INET: -- bzero(&kl, sizeof(kl)); -- kl.kr.prefix.s_addr = addr.v4.s_addr; -- kl.kr.prefixlen = p->prefix->prefixlen; -- if (p->aspath->flags & F_NEXTHOP_REJECT) -- kl.kr.flags |= F_REJECT; -- if (p->aspath->flags & F_NEXTHOP_BLACKHOLE) -- kl.kr.flags |= F_BLACKHOLE; -- if (type == IMSG_KROUTE_CHANGE) -- kl.kr.nexthop.s_addr = -- p->aspath->nexthop->true_nexthop.v4.s_addr; -- strlcpy(kl.label, rtlabel_id2name(p->aspath->rtlabelid), -- sizeof(kl.label)); -- if (imsg_compose(ibuf_main, type, 0, 0, -1, &kl, -- sizeof(kl)) == -1) -- fatal("imsg_compose error"); -+ bzero(&kr, sizeof(kr)); -+ memcpy(&kr.prefix, &addr, sizeof(kr.prefix)); -+ kr.prefixlen = p->prefix->prefixlen; -+ if (p->aspath->flags & F_NEXTHOP_REJECT) -+ kr.flags |= F_REJECT; -+ if (p->aspath->flags & F_NEXTHOP_BLACKHOLE) -+ kr.flags |= F_BLACKHOLE; -+ if (type == IMSG_KROUTE_CHANGE) -+ memcpy(&kr.nexthop, &p->aspath->nexthop->true_nexthop, -+ sizeof(kr.nexthop)); -+ strlcpy(kr.label, rtlabel_id2name(p->aspath->rtlabelid), -+ sizeof(kr.label)); -+ -+ switch (addr.aid) { -+ case AID_VPN_IPv4: -+ if (ribid != 1) -+ /* not Loc-RIB, no update for VPNs */ -+ break; -+ -+ SIMPLEQ_FOREACH(rd, rdomains_l, entry) { -+ if (!rde_rdomain_import(p->aspath, rd)) -+ continue; -+ /* must send exit_nexthop so that correct MPLS tunnel -+ * is chosen -+ */ -+ if (type == IMSG_KROUTE_CHANGE) -+ memcpy(&kr.nexthop, -+ &p->aspath->nexthop->exit_nexthop, -+ sizeof(kr.nexthop)); -+ if (imsg_compose(ibuf_main, type, rd->rtableid, 0, -1, -+ &kr, sizeof(kr)) == -1) -+ fatal("imsg_compose error"); -+ } - break; -- case AF_INET6: -- bzero(&kl6, sizeof(kl6)); -- memcpy(&kl6.kr.prefix, &addr.v6, sizeof(struct in6_addr)); -- kl6.kr.prefixlen = p->prefix->prefixlen; -- if (p->aspath->flags & F_NEXTHOP_REJECT) -- kl6.kr.flags |= F_REJECT; -- if (p->aspath->flags & F_NEXTHOP_BLACKHOLE) -- kl6.kr.flags |= F_BLACKHOLE; -- if (type == IMSG_KROUTE_CHANGE) { -- type = IMSG_KROUTE6_CHANGE; -- memcpy(&kl6.kr.nexthop, -- &p->aspath->nexthop->true_nexthop.v6, -- sizeof(struct in6_addr)); -- } else -- type = IMSG_KROUTE6_DELETE; -- strlcpy(kl6.label, rtlabel_id2name(p->aspath->rtlabelid), -- sizeof(kl6.label)); -- if (imsg_compose(ibuf_main, type, 0, 0, -1, &kl6, -- sizeof(kl6)) == -1) -+ default: -+ if (imsg_compose(ibuf_main, type, ribs[ribid].rtableid, 0, -1, -+ &kr, sizeof(kr)) == -1) - fatal("imsg_compose error"); - break; - } -@@ -2098,7 +2644,6 @@ rde_send_pftable_commit(void) - void - rde_send_nexthop(struct bgpd_addr *next, int valid) - { -- size_t size; - int type; - - if (valid) -@@ -2106,8 +2651,6 @@ rde_send_nexthop(struct bgpd_addr *next, - else - type = IMSG_NEXTHOP_REMOVE; - -- size = sizeof(struct bgpd_addr); -- - if (imsg_compose(ibuf_main, type, 0, 0, -1, next, - sizeof(struct bgpd_addr)) == -1) - fatal("imsg_compose error"); -@@ -2201,6 +2744,10 @@ rde_softreconfig_in(struct rib_entry *re - continue; - - for (i = 1; i < rib_size; i++) { -+ /* only active ribs need a softreconfig rerun */ -+ if (ribs[i].state != RECONF_KEEP) -+ continue; -+ - /* check if prefix changed */ - oa = rde_filter(i, &oasp, rules_l, peer, asp, &addr, - pt->prefixlen, peer, DIR_IN); -@@ -2228,7 +2775,7 @@ rde_softreconfig_in(struct rib_entry *re - if (path_compare(nasp, oasp) == 0) - goto done; - /* send update */ -- path_update(&ribs[1], peer, nasp, &addr, -+ path_update(&ribs[i], peer, nasp, &addr, - pt->prefixlen); - } - -@@ -2241,6 +2788,104 @@ done: - } - } - -+void -+rde_softreconfig_load(struct rib_entry *re, void *ptr) -+{ -+ struct rib *rib = ptr; -+ struct prefix *p, *np; -+ struct pt_entry *pt; -+ struct rde_peer *peer; -+ struct rde_aspath *asp, *nasp; -+ enum filter_actions action; -+ struct bgpd_addr addr; -+ -+ pt = re->prefix; -+ pt_getaddr(pt, &addr); -+ for (p = LIST_FIRST(&re->prefix_h); p != NULL; p = np) { -+ np = LIST_NEXT(p, rib_l); -+ -+ /* store aspath as prefix may change till we're done */ -+ asp = p->aspath; -+ peer = asp->peer; -+ -+ action = rde_filter(rib->id, &nasp, newrules, peer, asp, &addr, -+ pt->prefixlen, peer, DIR_IN); -+ nasp = nasp != NULL ? nasp : asp; -+ -+ if (action == ACTION_ALLOW) { -+ /* update Local-RIB */ -+ path_update(rib, peer, nasp, &addr, pt->prefixlen); -+ } -+ -+ if (nasp != asp) -+ path_put(nasp); -+ } -+} -+ -+void -+rde_softreconfig_load_peer(struct rib_entry *re, void *ptr) -+{ -+ struct rde_peer *peer = ptr; -+ struct prefix *p = re->active; -+ struct pt_entry *pt; -+ struct rde_aspath *nasp; -+ enum filter_actions na; -+ struct bgpd_addr addr; -+ -+ pt = re->prefix; -+ pt_getaddr(pt, &addr); -+ -+ /* check if prefix was announced */ -+ if (up_test_update(peer, p) != 1) -+ return; -+ -+ na = rde_filter(re->ribid, &nasp, newrules, peer, p->aspath, -+ &addr, pt->prefixlen, p->aspath->peer, DIR_OUT); -+ nasp = nasp != NULL ? nasp : p->aspath; -+ -+ if (na == ACTION_DENY) -+ /* nothing todo */ -+ goto done; -+ -+ /* send update */ -+ up_generate(peer, nasp, &addr, pt->prefixlen); -+done: -+ if (nasp != p->aspath) -+ path_put(nasp); -+} -+ -+void -+rde_softreconfig_unload_peer(struct rib_entry *re, void *ptr) -+{ -+ struct rde_peer *peer = ptr; -+ struct prefix *p = re->active; -+ struct pt_entry *pt; -+ struct rde_aspath *oasp; -+ enum filter_actions oa; -+ struct bgpd_addr addr; -+ -+ pt = re->prefix; -+ pt_getaddr(pt, &addr); -+ -+ /* check if prefix was announced */ -+ if (up_test_update(peer, p) != 1) -+ return; -+ -+ oa = rde_filter(re->ribid, &oasp, rules_l, peer, p->aspath, -+ &addr, pt->prefixlen, p->aspath->peer, DIR_OUT); -+ oasp = oasp != NULL ? oasp : p->aspath; -+ -+ if (oa == ACTION_DENY) -+ /* nothing todo */ -+ goto done; -+ -+ /* send withdraw */ -+ up_generate(peer, NULL, &addr, pt->prefixlen); -+done: -+ if (oasp != p->aspath) -+ path_put(oasp); -+} -+ - /* - * update specific functions - */ -@@ -2252,7 +2897,7 @@ rde_up_dump_upcall(struct rib_entry *re, - struct rde_peer *peer = ptr; - - if (re->ribid != peer->ribid) -- fatalx("King Bula: monsterous evil horror."); -+ fatalx("King Bula: monstrous evil horror."); - if (re->active == NULL) - return; - up_generate_updates(rules_l, peer, re->active, NULL); -@@ -2265,7 +2910,7 @@ rde_generate_updates(u_int16_t ribid, st - - /* - * If old is != NULL we know it was active and should be removed. -- * If new is != NULL we know it is reachable and then we should -+ * If new is != NULL we know it is reachable and then we should - * generate an update. - */ - if (old == NULL && new == NULL) -@@ -2286,7 +2931,7 @@ void - rde_update_queue_runner(void) - { - struct rde_peer *peer; -- int r, sent, max = RDE_RUNNER_ROUNDS; -+ int r, sent, max = RDE_RUNNER_ROUNDS, eor = 0; - u_int16_t len, wd_len, wpos; - - len = sizeof(queue_buf) - MSGSIZE_HEADER; -@@ -2300,7 +2945,7 @@ rde_update_queue_runner(void) - /* first withdraws */ - wpos = 2; /* reserve space for the length field */ - r = up_dump_prefix(queue_buf + wpos, len - wpos - 2, -- &peer->withdraws, peer); -+ &peer->withdraws[AID_INET], peer); - wd_len = r; - /* write withdraws length filed */ - wd_len = htons(wd_len); -@@ -2310,31 +2955,49 @@ rde_update_queue_runner(void) - /* now bgp path attributes */ - r = up_dump_attrnlri(queue_buf + wpos, len - wpos, - peer); -- wpos += r; -- -- if (wpos == 4) -- /* -- * No packet to send. The 4 bytes are the -- * needed withdraw and path attribute length. -- */ -- continue; -+ switch (r) { -+ case -1: -+ eor = 1; -+ if (wd_len == 0) { -+ /* no withdraws queued just send EoR */ -+ peer_send_eor(peer, AID_INET); -+ continue; -+ } -+ break; -+ case 2: -+ if (wd_len == 0) { -+ /* -+ * No packet to send. No withdraws and -+ * no path attributes. Skip. -+ */ -+ continue; -+ } -+ /* FALLTHROUGH */ -+ default: -+ wpos += r; -+ break; -+ } - - /* finally send message to SE */ - if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id, - 0, -1, queue_buf, wpos) == -1) - fatal("imsg_compose error"); - sent++; -+ if (eor) { -+ eor = 0; -+ peer_send_eor(peer, AID_INET); -+ } - } - max -= sent; - } while (sent != 0 && max > 0); - } - - void --rde_update6_queue_runner(void) -+rde_update6_queue_runner(u_int8_t aid) - { - struct rde_peer *peer; - u_char *b; -- int sent, max = RDE_RUNNER_ROUNDS / 2; -+ int r, sent, max = RDE_RUNNER_ROUNDS / 2; - u_int16_t len; - - /* first withdraws ... */ -@@ -2346,7 +3009,7 @@ rde_update6_queue_runner(void) - if (peer->state != PEER_UP) - continue; - len = sizeof(queue_buf) - MSGSIZE_HEADER; -- b = up_dump_mp_unreach(queue_buf, &len, peer); -+ b = up_dump_mp_unreach(queue_buf, &len, peer, aid); - - if (b == NULL) - continue; -@@ -2369,10 +3032,18 @@ rde_update6_queue_runner(void) - if (peer->state != PEER_UP) - continue; - len = sizeof(queue_buf) - MSGSIZE_HEADER; -- b = up_dump_mp_reach(queue_buf, &len, peer); -- -- if (b == NULL) -+ r = up_dump_mp_reach(queue_buf, &len, peer, aid); -+ switch (r) { -+ case -2: -+ continue; -+ case -1: -+ peer_send_eor(peer, aid); - continue; -+ default: -+ b = queue_buf + r; -+ break; -+ } -+ - /* finally send message to SE */ - if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id, - 0, -1, b, len) == -1) -@@ -2411,7 +3082,7 @@ rde_decisionflags(void) - int - rde_as4byte(struct rde_peer *peer) - { -- return (peer->capa_announced.as4byte && peer->capa_received.as4byte); -+ return (peer->capa.as4byte); - } - - /* -@@ -2429,7 +3100,6 @@ void - peer_init(u_int32_t hashsize) - { - struct peer_config pc; -- struct in_addr id; - u_int32_t hs, i; - - for (hs = 1; hs < hashsize; hs <<= 1) -@@ -2445,17 +3115,13 @@ peer_init(u_int32_t hashsize) - peertable.peer_hashmask = hs - 1; - - bzero(&pc, sizeof(pc)); -- pc.remote_as = conf->as; -- id.s_addr = conf->bgpid; -- snprintf(pc.descr, sizeof(pc.descr), "LOCAL: ID %s", inet_ntoa(id)); -+ snprintf(pc.descr, sizeof(pc.descr), "LOCAL"); - - peerself = peer_add(0, &pc); - if (peerself == NULL) - fatalx("peer_init add self"); - - peerself->state = PEER_UP; -- peerself->remote_bgpid = ntohl(conf->bgpid); -- peerself->short_as = conf->short_as; - } - - void -@@ -2534,14 +3200,10 @@ peer_localaddrs(struct rde_peer *peer, s - if (ifa->ifa_addr->sa_family == - match->ifa_addr->sa_family) - ifa = match; -- peer->local_v4_addr.af = AF_INET; -- peer->local_v4_addr.v4.s_addr = -- ((struct sockaddr_in *)ifa->ifa_addr)-> -- sin_addr.s_addr; -+ sa2addr(ifa->ifa_addr, &peer->local_v4_addr); - break; - } - } -- - for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr->sa_family == AF_INET6 && - strcmp(ifa->ifa_name, match->ifa_name) == 0) { -@@ -2559,13 +3221,7 @@ peer_localaddrs(struct rde_peer *peer, s - &((struct sockaddr_in6 *)ifa-> - ifa_addr)->sin6_addr)) - continue; -- peer->local_v6_addr.af = AF_INET6; -- memcpy(&peer->local_v6_addr.v6, -- &((struct sockaddr_in6 *)ifa->ifa_addr)-> -- sin6_addr, sizeof(struct in6_addr)); -- peer->local_v6_addr.scope_id = -- ((struct sockaddr_in6 *)ifa->ifa_addr)-> -- sin6_scope_id; -+ sa2addr(ifa->ifa_addr, &peer->local_v6_addr); - break; - } - } -@@ -2577,23 +3233,22 @@ void - peer_up(u_int32_t id, struct session_up *sup) - { - struct rde_peer *peer; -+ u_int8_t i; - - peer = peer_get(id); - if (peer == NULL) { -- log_warnx("peer_up: peer id %d already exists", id); -+ log_warnx("peer_up: unknown peer id %d", id); - return; - } - -- if (peer->state != PEER_DOWN && peer->state != PEER_NONE) -+ if (peer->state != PEER_DOWN && peer->state != PEER_NONE && -+ peer->state != PEER_UP) - fatalx("peer_up: bad state"); - peer->remote_bgpid = ntohl(sup->remote_bgpid); - peer->short_as = sup->short_as; - memcpy(&peer->remote_addr, &sup->remote_addr, - sizeof(peer->remote_addr)); -- memcpy(&peer->capa_announced, &sup->capa_announced, -- sizeof(peer->capa_announced)); -- memcpy(&peer->capa_received, &sup->capa_received, -- sizeof(peer->capa_received)); -+ memcpy(&peer->capa, &sup->capa, sizeof(peer->capa)); - - peer_localaddrs(peer, &sup->local_addr); - -@@ -2607,7 +3262,10 @@ peer_up(u_int32_t id, struct session_up - */ - return; - -- peer_dump(id, AFI_ALL, SAFI_ALL); -+ for (i = 0; i < AID_MAX; i++) { -+ if (peer->capa.mp[i] == 1) -+ peer_dump(id, i); -+ } - } - - void -@@ -2641,43 +3299,90 @@ peer_down(u_int32_t id) - free(peer); - } - -+/* -+ * Flush all routes older then staletime. If staletime is 0 all routes will -+ * be flushed. -+ */ -+void -+peer_flush(struct rde_peer *peer, u_int8_t aid) -+{ -+ struct rde_aspath *asp, *nasp; -+ -+ /* walk through per peer RIB list and remove all stale prefixes. */ -+ for (asp = LIST_FIRST(&peer->path_h); asp != NULL; asp = nasp) { -+ nasp = LIST_NEXT(asp, peer_l); -+ path_remove_stale(asp, aid); -+ } -+ -+ /* Deletions are performed in path_remove() */ -+ rde_send_pftable_commit(); -+ -+ /* flushed no need to keep staletime */ -+ peer->staletime[aid] = 0; -+} -+ - void --peer_dump(u_int32_t id, u_int16_t afi, u_int8_t safi) -+peer_stale(u_int32_t id, u_int8_t aid) - { - struct rde_peer *peer; -+ time_t now; - - peer = peer_get(id); - if (peer == NULL) { -- log_warnx("peer_down: unknown peer id %d", id); -+ log_warnx("peer_stale: unknown peer id %d", id); - return; - } - -- if (afi == AFI_ALL || afi == AFI_IPv4) -- if (safi == SAFI_ALL || safi == SAFI_UNICAST) { -- if (peer->conf.announce_type == ANNOUNCE_DEFAULT_ROUTE) -- up_generate_default(rules_l, peer, AF_INET); -- else -- rib_dump(&ribs[peer->ribid], rde_up_dump_upcall, -- peer, AF_INET); -- } -- if (afi == AFI_ALL || afi == AFI_IPv6) -- if (safi == SAFI_ALL || safi == SAFI_UNICAST) { -- if (peer->conf.announce_type == ANNOUNCE_DEFAULT_ROUTE) -- up_generate_default(rules_l, peer, AF_INET6); -- else -- rib_dump(&ribs[peer->ribid], rde_up_dump_upcall, -- peer, AF_INET6); -- } -+ if (peer->staletime[aid]) -+ peer_flush(peer, aid); -+ peer->staletime[aid] = now = time(NULL); - -- if (peer->capa_received.restart && peer->capa_announced.restart) -- peer_send_eor(peer, afi, safi); -+ /* make sure new prefixes start on a higher timestamp */ -+ do { -+ sleep(1); -+ } while (now >= time(NULL)); - } - --/* End-of-RIB marker, draft-ietf-idr-restart-13.txt */ - void --peer_send_eor(struct rde_peer *peer, u_int16_t afi, u_int16_t safi) -+peer_dump(u_int32_t id, u_int8_t aid) - { -- if (afi == AFI_IPv4 && safi == SAFI_UNICAST) { -+ struct rde_peer *peer; -+ -+ peer = peer_get(id); -+ if (peer == NULL) { -+ log_warnx("peer_dump: unknown peer id %d", id); -+ return; -+ } -+ -+ if (peer->conf.announce_type == ANNOUNCE_DEFAULT_ROUTE) -+ up_generate_default(rules_l, peer, aid); -+ else -+ rib_dump(&ribs[peer->ribid], rde_up_dump_upcall, peer, aid); -+ if (peer->capa.grestart.restart) -+ up_generate_marker(peer, aid); -+} -+ -+/* End-of-RIB marker, RFC 4724 */ -+void -+peer_recv_eor(struct rde_peer *peer, u_int8_t aid) -+{ -+ peer->prefix_rcvd_eor++; -+ -+ /* First notify SE to remove possible race with the timeout. */ -+ if (imsg_compose(ibuf_se, IMSG_SESSION_RESTARTED, peer->conf.id, -+ 0, -1, &aid, sizeof(aid)) == -1) -+ fatal("imsg_compose error"); -+} -+ -+void -+peer_send_eor(struct rde_peer *peer, u_int8_t aid) -+{ -+ u_int16_t afi; -+ u_int8_t safi; -+ -+ peer->prefix_sent_eor++; -+ -+ if (aid == AID_INET) { - u_char null[4]; - - bzero(&null, 4); -@@ -2688,6 +3393,9 @@ peer_send_eor(struct rde_peer *peer, u_i - u_int16_t i; - u_char buf[10]; - -+ if (aid2afi(aid, &afi, &safi) == -1) -+ fatalx("peer_send_eor: bad AID"); -+ - i = 0; /* v4 withdrawn len */ - bcopy(&i, &buf[0], sizeof(i)); - i = htons(6); /* path attr len */ -@@ -2709,39 +3417,61 @@ peer_send_eor(struct rde_peer *peer, u_i - * network announcement stuff - */ - void --network_init(struct network_head *net_l) --{ -- struct network *n; -- -- reloadtime = time(NULL); -- -- while ((n = TAILQ_FIRST(net_l)) != NULL) { -- TAILQ_REMOVE(net_l, n, entry); -- network_add(&n->net, 1); -- free(n); -- } --} -- --void - network_add(struct network_config *nc, int flagstatic) - { -+ struct rdomain *rd; - struct rde_aspath *asp; -+ struct filter_set_head *vpnset = NULL; -+ in_addr_t prefix4; - u_int16_t i; - -- asp = path_get(); -- asp->aspath = aspath_get(NULL, 0); -- asp->origin = ORIGIN_IGP; -- asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH | -- F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED; -- /* the nexthop is unset unless a default set overrides it */ -+ if (nc->rtableid) { -+ SIMPLEQ_FOREACH(rd, rdomains_l, entry) { -+ if (rd->rtableid != nc->rtableid) -+ continue; -+ switch (nc->prefix.aid) { -+ case AID_INET: -+ prefix4 = nc->prefix.v4.s_addr; -+ bzero(&nc->prefix, sizeof(nc->prefix)); -+ nc->prefix.aid = AID_VPN_IPv4; -+ nc->prefix.vpn4.rd = rd->rd; -+ nc->prefix.vpn4.addr.s_addr = prefix4; -+ nc->prefix.vpn4.labellen = 3; -+ nc->prefix.vpn4.labelstack[0] = -+ (rd->label >> 12) & 0xff; -+ nc->prefix.vpn4.labelstack[1] = -+ (rd->label >> 4) & 0xff; -+ nc->prefix.vpn4.labelstack[2] = -+ (rd->label << 4) & 0xf0; -+ nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS; -+ vpnset = &rd->export; -+ break; -+ default: -+ log_warnx("unable to VPNize prefix"); -+ filterset_free(&nc->attrset); -+ return; -+ } -+ } -+ } -+ -+ if (nc->type == NETWORK_MRTCLONE) { -+ asp = nc->asp; -+ } else { -+ asp = path_get(); -+ asp->aspath = aspath_get(NULL, 0); -+ asp->origin = ORIGIN_IGP; -+ asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH | -+ F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED; -+ /* the nexthop is unset unless a default set overrides it */ -+ } - if (!flagstatic) - asp->flags |= F_ANN_DYNAMIC; -- -- rde_apply_set(asp, &nc->attrset, nc->prefix.af, peerself, peerself); -+ rde_apply_set(asp, &nc->attrset, nc->prefix.aid, peerself, peerself); -+ if (vpnset) -+ rde_apply_set(asp, vpnset, nc->prefix.aid, peerself, peerself); - for (i = 1; i < rib_size; i++) - path_update(&ribs[i], peerself, asp, &nc->prefix, - nc->prefixlen); -- - path_put(asp); - filterset_free(&nc->attrset); - } -@@ -2749,12 +3479,41 @@ network_add(struct network_config *nc, i - void - network_delete(struct network_config *nc, int flagstatic) - { -- u_int32_t flags = F_PREFIX_ANNOUNCED; -- u_int32_t i; -+ struct rdomain *rd; -+ in_addr_t prefix4; -+ u_int32_t flags = F_PREFIX_ANNOUNCED; -+ u_int32_t i; - - if (!flagstatic) - flags |= F_ANN_DYNAMIC; - -+ if (nc->rtableid) { -+ SIMPLEQ_FOREACH(rd, rdomains_l, entry) { -+ if (rd->rtableid != nc->rtableid) -+ continue; -+ switch (nc->prefix.aid) { -+ case AID_INET: -+ prefix4 = nc->prefix.v4.s_addr; -+ bzero(&nc->prefix, sizeof(nc->prefix)); -+ nc->prefix.aid = AID_VPN_IPv4; -+ nc->prefix.vpn4.rd = rd->rd; -+ nc->prefix.vpn4.addr.s_addr = prefix4; -+ nc->prefix.vpn4.labellen = 3; -+ nc->prefix.vpn4.labelstack[0] = -+ (rd->label >> 12) & 0xff; -+ nc->prefix.vpn4.labelstack[1] = -+ (rd->label >> 4) & 0xff; -+ nc->prefix.vpn4.labelstack[2] = -+ (rd->label << 4) & 0xf0; -+ nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS; -+ break; -+ default: -+ log_warnx("unable to VPNize prefix"); -+ return; -+ } -+ } -+ } -+ - for (i = rib_size - 1; i > 0; i--) - prefix_remove(&ribs[i], peerself, &nc->prefix, nc->prefixlen, - flags); -@@ -2764,38 +3523,31 @@ void - network_dump_upcall(struct rib_entry *re, void *ptr) - { - struct prefix *p; -- struct kroute k; -- struct kroute6 k6; -+ struct kroute_full k; - struct bgpd_addr addr; - struct rde_dump_ctx *ctx = ptr; - - LIST_FOREACH(p, &re->prefix_h, rib_l) { - if (!(p->aspath->flags & F_PREFIX_ANNOUNCED)) - continue; -- if (p->prefix->af == AF_INET) { -- bzero(&k, sizeof(k)); -- pt_getaddr(p->prefix, &addr); -- k.prefix.s_addr = addr.v4.s_addr; -- k.prefixlen = p->prefix->prefixlen; -- if (p->aspath->peer == peerself) -- k.flags = F_KERNEL; -- if (imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NETWORK, 0, -- ctx->req.pid, -1, &k, sizeof(k)) == -1) -- log_warnx("network_dump_upcall: " -- "imsg_compose error"); -- } -- if (p->prefix->af == AF_INET6) { -- bzero(&k6, sizeof(k6)); -- pt_getaddr(p->prefix, &addr); -- memcpy(&k6.prefix, &addr.v6, sizeof(k6.prefix)); -- k6.prefixlen = p->prefix->prefixlen; -- if (p->aspath->peer == peerself) -- k6.flags = F_KERNEL; -- if (imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NETWORK6, 0, -- ctx->req.pid, -1, &k6, sizeof(k6)) == -1) -- log_warnx("network_dump_upcall: " -- "imsg_compose error"); -- } -+ pt_getaddr(p->prefix, &addr); -+ -+ bzero(&k, sizeof(k)); -+ memcpy(&k.prefix, &addr, sizeof(k.prefix)); -+ if (p->aspath->nexthop == NULL || -+ p->aspath->nexthop->state != NEXTHOP_REACH) -+ k.nexthop.aid = k.prefix.aid; -+ else -+ memcpy(&k.nexthop, &p->aspath->nexthop->true_nexthop, -+ sizeof(k.nexthop)); -+ k.prefixlen = p->prefix->prefixlen; -+ k.flags = F_KERNEL; -+ if ((p->aspath->flags & F_ANN_DYNAMIC) == 0) -+ k.flags = F_STATIC; -+ if (imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NETWORK, 0, -+ ctx->req.pid, -1, &k, sizeof(k)) == -1) -+ log_warnx("network_dump_upcall: " -+ "imsg_compose error"); - } - } - -@@ -2841,10 +3593,10 @@ sa_cmp(struct bgpd_addr *a, struct socka - struct sockaddr_in *in_b; - struct sockaddr_in6 *in6_b; - -- if (a->af != b->sa_family) -+ if (aid2af(a->aid) != b->sa_family) - return (1); - -- switch (a->af) { -+ switch (b->sa_family) { - case AF_INET: - in_b = (struct sockaddr_in *)b; - if (a->v4.s_addr != in_b->sin_addr.s_addr) -@@ -2855,10 +3607,11 @@ sa_cmp(struct bgpd_addr *a, struct socka - #ifdef __KAME__ - /* directly stolen from sbin/ifconfig/ifconfig.c */ - if (IN6_IS_ADDR_LINKLOCAL(&in6_b->sin6_addr)) { -- in6_b->sin6_scope_id = -- ntohs(*(u_int16_t *)&in6_b->sin6_addr.s6_addr[2]); -- in6_b->sin6_addr.s6_addr[2] = -- in6_b->sin6_addr.s6_addr[3] = 0; -+ if (in6_b->sin6_scope_id == 0) { -+ in6_b->sin6_scope_id = -+ IN6_LINKLOCAL_IFINDEX(in6_b->sin6_addr); -+ } -+ SET_IN6_LINKLOCAL_IFINDEX(in6_b->sin6_addr, 0); - } - #endif - if (bcmp(&a->v6, &in6_b->sin6_addr, |