Index: Makefile =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/Makefile,v retrieving revision 1.36 diff -u -p -r1.36 Makefile --- Makefile 1 Jan 2020 07:25:04 -0000 1.36 +++ Makefile 9 Nov 2020 09:24:03 -0000 @@ -15,5 +15,6 @@ YFLAGS= LDADD+= -lutil DPADD+= ${LIBUTIL} MAN= bgpd.8 bgpd.conf.5 +DEBUG=-g .include Index: bgpd.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v retrieving revision 1.229 diff -u -p -r1.229 bgpd.c --- bgpd.c 11 May 2020 16:59:19 -0000 1.229 +++ bgpd.c 9 Nov 2020 09:24:03 -0000 @@ -325,6 +325,13 @@ BROKEN if (pledge("stdio rpath wpath cpa } } + if (reconfpending == 0) { + if (kr_ifchanged()) { + log_info("interfaces changed"); + reconfig = 1; + } + } + if (reconfig) { u_int error; @@ -507,6 +514,14 @@ send_config(struct bgpd_config *conf) reconfpending = 2; /* one per child */ expand_networks(conf); + kr_ifdepend_reset(); + TAILQ_FOREACH(r, conf->filters, entry) { + const char *iface = r->match.depend_on.iface; + if (iface[0] == '\0') + continue; + + r->match.depend_on.state = kr_ifdepend(iface); + } cflags = conf->flags; Index: bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.403 diff -u -p -r1.403 bgpd.h --- bgpd.h 10 May 2020 13:38:46 -0000 1.403 +++ bgpd.h 9 Nov 2020 09:24:03 -0000 @@ -657,7 +657,9 @@ struct kif { u_int8_t if_type; u_int8_t link_state; u_int8_t nh_reachable; /* for nexthop verification */ - u_int8_t depend_state; /* for session depend on */ + u_int8_t depend_state; /* for session/filter depend on */ + u_int8_t filter_depend; /* for filter depend on rules */ + u_int8_t state_changed; }; struct session_up { @@ -784,6 +786,12 @@ struct filter_ovs { u_int8_t is_set; }; +struct filter_depend_on { + char iface[SET_NAME_LEN]; + int depend_state; + int state; +}; + /* * Communities are encoded depending on their type. The low byte of flags * is the COMMUNITY_TYPE (BASIC, LARGE, EXT). BASIC encoding is just using @@ -965,6 +973,7 @@ struct filter_match { struct filter_prefixset prefixset; struct filter_originset originset; struct filter_ovs ovs; + struct filter_depend_on depend_on; }; struct filter_rule { @@ -1209,6 +1218,9 @@ void kr_nexthop_delete(u_int32_t, stru struct bgpd_config *); void kr_show_route(struct imsg *); void kr_ifinfo(char *); +int kr_ifchanged(void); +int kr_ifdepend(const char *); +void kr_ifdepend_reset(void); void kr_net_reload(u_int, u_int64_t, struct network_head *); int kr_reload(void); struct in6_addr *prefixlen2mask6(u_int8_t prefixlen); Index: kroute.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/kroute.c,v retrieving revision 1.239 diff -u -p -r1.239 kroute.c --- kroute.c 1 Oct 2019 08:57:48 -0000 1.239 +++ kroute.c 9 Nov 2020 09:24:03 -0000 @@ -1248,6 +1248,48 @@ kr_ifinfo(char *ifname) } } +int +kr_ifchanged(void) +{ + struct kif_node *kif; + int changed = 0; + + RB_FOREACH(kif, kif_tree, &kit) { + if (kif->k.state_changed) { + log_info("depended-on interface %s changed state", + kif->k.ifname); + changed = 1; + kif->k.state_changed = 0; + } + } + + return changed; +} + +int +kr_ifdepend(const char *ifname) +{ + struct kif_node *kif; + + RB_FOREACH(kif, kif_tree, &kit) { + if (!strcmp(ifname, kif->k.ifname)) { + kif->k.filter_depend = 1; + return kif->k.depend_state; + } + } + + return 0; +} + +void +kr_ifdepend_reset(void) +{ + struct kif_node *kif; + + RB_FOREACH(kif, kif_tree, &kit) + kif->k.filter_depend = 0; +} + void kr_net_delete(struct network *n) { @@ -1450,7 +1492,8 @@ kr_redistribute(int type, struct ktable return; /* Consider networks with nexthop loopback as not redistributable. */ - if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK)) + if (kr->nexthop.s_addr == htonl(INADDR_LOOPBACK) && + (kr->flags & (F_BLACKHOLE|F_REJECT)) == 0) return; /* @@ -1506,7 +1549,8 @@ kr_redistribute6(int type, struct ktable /* * Consider networks with nexthop loopback as not redistributable. */ - if (IN6_IS_ADDR_LOOPBACK(&kr6->nexthop)) + if (IN6_IS_ADDR_LOOPBACK(&kr6->nexthop) && + (kr6->flags & (F_BLACKHOLE|F_REJECT)) == 0) return; /* @@ -2817,6 +2861,8 @@ if_change(u_short ifindex, int flags, st kif->k.rdomain = ifd->ifi_rdomain; kif->k.baudrate = ifd->ifi_baudrate; kif->k.depend_state = kif_depend_state(&kif->k); + if (kif->k.filter_depend) + kif->k.state_changed = 1; send_imsg_session(IMSG_IFINFO, 0, &kif->k, sizeof(kif->k)); @@ -2867,6 +2913,7 @@ if_announce(void *msg, u_int rdomain) } kif->k.ifindex = ifan->ifan_index; + kif->k.state_changed = 1; strlcpy(kif->k.ifname, ifan->ifan_name, sizeof(kif->k.ifname)); kif_insert(kif); break; Index: parse.y =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v retrieving revision 1.410 diff -u -p -r1.410 parse.y --- parse.y 27 Oct 2020 19:13:34 -0000 1.410 +++ parse.y 9 Nov 2020 09:24:03 -0000 @@ -201,7 +201,7 @@ typedef struct { %token EBGP IBGP %token LOCALAS REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART %token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY -%token DEMOTE ENFORCE NEIGHBORAS ASOVERRIDE REFLECTOR DEPEND DOWN +%token DEMOTE ENFORCE NEIGHBORAS ASOVERRIDE REFLECTOR DEPEND DOWN UP %token DUMP IN OUT SOCKET RESTRICTED %token LOG TRANSPARENT %token TCP MD5SIG PASSWORD KEY TTLSECURITY @@ -229,7 +229,7 @@ typedef struct { %type address %type prefix addrspec %type prefixset_item -%type action quick direction delete community +%type action quick direction delete community dependstate %type filter_rib_h filter_rib_l filter_rib %type filter_peer filter_peer_l filter_peer_h %type filter_match filter_elm filter_match_h @@ -1008,7 +1008,6 @@ prefix : STRING '/' NUMBER { | NUMBER '/' NUMBER { char *s; - /* does not match IPv6 */ if ($1 < 0 || $1 > 255 || $3 < 0 || $3 > 32) { yyerror("bad prefix %lld/%lld", $1, $3); YYERROR; @@ -2263,6 +2262,19 @@ filter_elm : filter_prefix_h { fmopts.m.ovs.validity = $2; fmopts.m.ovs.is_set = 1; } + | DEPEND ON STRING dependstate { + if (strlcpy(fmopts.m.depend_on.iface, $3, + sizeof(fmopts.m.depend_on.iface)) >= + sizeof(fmopts.m.depend_on.iface)) { + yyerror("interface name \"%s\" too long: " + "max %zu", $3, + sizeof(fmopts.m.depend_on.iface) - 1); + free($3); + YYERROR; + } + fmopts.m.depend_on.depend_state = $4; + free($3); + } ; prefixlenop : /* empty */ { bzero(&$$, sizeof($$)); } @@ -2381,6 +2393,11 @@ delete : /* empty */ { $$ = 0; } | DELETE { $$ = 1; } ; +dependstate : /* empty */ { $$ = 1; } + | UP { $$ = 1; } + | DOWN { $$ = 0; } + ; + filter_set_opt : LOCALPREF NUMBER { if ($2 < -INT_MAX || $2 > UINT_MAX) { yyerror("bad localpref %lld", $2); @@ -2885,6 +2902,7 @@ lookup(char *s) { "transparent-as", TRANSPARENT}, { "ttl-security", TTLSECURITY}, { "unicast", UNICAST}, + { "up", UP}, { "via", VIA}, { "vpn", VPN}, { "weight", WEIGHT} Index: printconf.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v retrieving revision 1.142 diff -u -p -r1.142 printconf.c --- printconf.c 23 Apr 2020 16:13:11 -0000 1.142 +++ printconf.c 9 Nov 2020 09:24:03 -0000 @@ -832,6 +832,11 @@ print_rule(struct bgpd_config *conf, str } } + if (r->match.depend_on.iface[0] != '\0') { + printf("depend on %s %s ", r->match.depend_on.iface, + r->match.depend_on.depend_state ? "up" : "down"); + } + if (r->match.prefix.addr.aid != AID_UNSPEC) { printf("prefix "); print_prefix(&r->match.prefix); Index: rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.503 diff -u -p -r1.503 rde.c --- rde.c 21 Oct 2020 06:56:32 -0000 1.503 +++ rde.c 9 Nov 2020 09:24:03 -0000 @@ -69,6 +69,7 @@ void rde_dump_ctx_terminate(pid_t); void rde_dump_mrt_new(struct mrt *, pid_t, int); int rde_l3vpn_import(struct rde_community *, struct l3vpn *); +static void rde_commit_pftable(void); void rde_reload_done(void); static void rde_softreconfig_in_done(void *, u_int8_t); static void rde_softreconfig_out_done(void *, u_int8_t); @@ -296,6 +297,8 @@ rde_main(int debug, int verbose) for (aid = AID_INET6; aid < AID_MAX; aid++) rde_update6_queue_runner(aid); } + /* commit pftable once per poll loop */ + rde_commit_pftable(); } /* do not clean up on shutdown on production, it takes ages. */ @@ -497,8 +500,6 @@ badnetdel: RDE_RUNNER_ROUNDS, peerself, network_flush_upcall, NULL, NULL) == -1) log_warn("rde_dispatch: IMSG_NETWORK_FLUSH"); - /* Deletions were performed in network_flush_upcall */ - rde_send_pftable_commit(); break; case IMSG_FILTER_SET: if (imsg.hdr.len - IMSG_HEADER_SIZE != @@ -1383,7 +1384,6 @@ rde_update_dispatch(struct rde_peer *pee done: rde_filterstate_clean(&state); - rde_send_pftable_commit(); } int @@ -2944,10 +2944,32 @@ rde_update6_queue_runner(u_int8_t aid) /* * pf table specific functions */ +struct rde_pftable_node { + RB_ENTRY(rde_pftable_node) entry; + struct pt_entry *prefix; + int refcnt; + u_int16_t id; +}; +RB_HEAD(rde_pftable_tree, rde_pftable_node); + +static inline int +rde_pftable_cmp(struct rde_pftable_node *a, struct rde_pftable_node *b) +{ + if (a->prefix > b->prefix) + return 1; + if (a->prefix < b->prefix) + return -1; + return (a->id - b->id); +} + +RB_GENERATE_STATIC(rde_pftable_tree, rde_pftable_node, entry, rde_pftable_cmp); + +struct rde_pftable_tree pftable_tree = RB_INITIALIZER(&pftable_tree); + int need_commit; -void -rde_send_pftable(u_int16_t id, struct bgpd_addr *addr, - u_int8_t len, int del) + +static void +rde_pftable_send(u_int16_t id, struct pt_entry *pt, int del) { struct pftable_msg pfm; @@ -2960,8 +2982,8 @@ rde_send_pftable(u_int16_t id, struct bg bzero(&pfm, sizeof(pfm)); strlcpy(pfm.pftable, pftable_id2name(id), sizeof(pfm.pftable)); - memcpy(&pfm.addr, addr, sizeof(pfm.addr)); - pfm.len = len; + pt_getaddr(pt, &pfm.addr); + pfm.len = pt->prefixlen; if (imsg_compose(ibuf_main, del ? IMSG_PFTABLE_REMOVE : IMSG_PFTABLE_ADD, @@ -2972,7 +2994,55 @@ rde_send_pftable(u_int16_t id, struct bg } void -rde_send_pftable_commit(void) +rde_pftable_add(u_int16_t id, struct prefix *p) +{ + struct rde_pftable_node *pfn, node; + + memset(&node, 0, sizeof(node)); + node.prefix = p->pt; + node.id = id; + + pfn = RB_FIND(rde_pftable_tree, &pftable_tree, &node); + if (pfn == NULL) { + if ((pfn = calloc(1, sizeof(*pfn))) == NULL) + fatal("%s", __func__); + pfn->prefix = pt_ref(p->pt); + pfn->id = id; + + if (RB_INSERT(rde_pftable_tree, &pftable_tree, pfn) != NULL) + fatalx("%s: tree corrupt", __func__); + + rde_pftable_send(id, p->pt, 0); + } + pfn->refcnt++; +} + +void +rde_pftable_del(u_int16_t id, struct prefix *p) +{ + struct rde_pftable_node *pfn, node; + + memset(&node, 0, sizeof(node)); + node.prefix = p->pt; + node.id = id; + + pfn = RB_FIND(rde_pftable_tree, &pftable_tree, &node); + if (pfn == NULL) + return; + + if (--pfn->refcnt <= 0) { + rde_pftable_send(id, p->pt, 1); + + if (RB_REMOVE(rde_pftable_tree, &pftable_tree, pfn) == NULL) + fatalx("%s: tree corrupt", __func__); + + pt_unref(pfn->prefix); + free(pfn); + } +} + +void +rde_commit_pftable(void) { /* do not run while cleaning up */ if (rde_quit) @@ -3159,9 +3229,6 @@ rde_reload_done(void) free_rde_prefixsets(&originsets_old); as_sets_free(&as_sets_old); - /* Deletions may have been performed in rib_free() */ - rde_send_pftable_commit(); - log_info("RDE reconfigured"); if (reload > 0) { @@ -3190,8 +3257,6 @@ rde_softreconfig_in_done(void *arg, u_in log_info("softreconfig in done"); } - /* Changes may have been performed during softreconfig_in run */ - rde_send_pftable_commit(); /* now do the Adj-RIB-Out sync and a possible FIB sync */ softreconfig = 0; @@ -3615,7 +3680,6 @@ network_add(struct network_config *nc, s nc->prefixlen, vstate); } filterset_free(&nc->attrset); - rde_send_pftable_commit(); } void @@ -3682,8 +3746,6 @@ network_delete(struct network_config *nc if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peerself, &nc->prefix, nc->prefixlen)) peerself->prefix_cnt--; - - rde_send_pftable_commit(); } static void Index: rde.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v retrieving revision 1.234 diff -u -p -r1.234 rde.h --- rde.h 5 Jun 2020 19:50:59 -0000 1.234 +++ rde.h 9 Nov 2020 09:24:03 -0000 @@ -368,9 +368,8 @@ void rde_update_log(const char *, u_in void rde_send_kroute_flush(struct rib *); void rde_send_kroute(struct rib *, struct prefix *, struct prefix *); void rde_send_nexthop(struct bgpd_addr *, int); -void rde_send_pftable(u_int16_t, struct bgpd_addr *, - u_int8_t, int); -void rde_send_pftable_commit(void); +void rde_pftable_add(u_int16_t, struct prefix *); +void rde_pftable_del(u_int16_t, struct prefix *); void rde_generate_updates(struct rib *, struct prefix *, struct prefix *); Index: rde_filter.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_filter.c,v retrieving revision 1.123 diff -u -p -r1.123 rde_filter.c --- rde_filter.c 14 Feb 2020 13:54:31 -0000 1.123 +++ rde_filter.c 9 Nov 2020 09:24:03 -0000 @@ -224,6 +224,10 @@ rde_filter_match(struct filter_rule *f, if (vstate != f->match.ovs.validity) return (0); } + if (f->match.depend_on.iface[0] != '\0') { + if (f->match.depend_on.state != f->match.depend_on.depend_state) + return (0); + } if (asp != NULL && f->match.as.type != AS_UNDEF) { if (aspath_match(asp->aspath, &f->match.as, Index: rde_peer.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v retrieving revision 1.5 diff -u -p -r1.5 rde_peer.c --- rde_peer.c 12 Feb 2020 10:33:56 -0000 1.5 +++ rde_peer.c 9 Nov 2020 09:24:03 -0000 @@ -236,9 +236,8 @@ peer_flush_upcall(struct rib_entry *re, rp = prefix_get(rib, peer, &addr, prefixlen); if (rp) { asp = prefix_aspath(rp); - if (asp->pftableid) - rde_send_pftable(asp->pftableid, &addr, - prefixlen, 1); + if (asp && asp->pftableid) + rde_pftable_del(asp->pftableid, rp); prefix_destroy(rp); rde_update_log("flush", i, peer, NULL, @@ -384,9 +383,6 @@ peer_flush(struct rde_peer *peer, u_int8 if (rib_dump_new(RIB_ADJ_IN, aid, 0, &pf, peer_flush_upcall, NULL, NULL) == -1) fatal("%s: rib_dump_new", __func__); - - /* Deletions may have been performed in peer_flush_upcall */ - rde_send_pftable_commit(); /* every route is gone so reset staletime */ if (aid == AID_UNSPEC) { Index: rde_rib.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v retrieving revision 1.215 diff -u -p -r1.215 rde_rib.c --- rde_rib.c 25 Jan 2020 23:54:21 -0000 1.215 +++ rde_rib.c 9 Nov 2020 09:24:03 -0000 @@ -252,14 +252,8 @@ rib_free(struct rib *rib) */ while ((p = LIST_FIRST(&re->prefix_h))) { struct rde_aspath *asp = prefix_aspath(p); - if (asp && asp->pftableid) { - struct bgpd_addr addr; - - pt_getaddr(p->pt, &addr); - /* Commit is done in rde_reload_done() */ - rde_send_pftable(asp->pftableid, &addr, - p->pt->prefixlen, 1); - } + if (asp && asp->pftableid) + rde_pftable_del(asp->pftableid, p); prefix_destroy(p); } } @@ -967,9 +961,6 @@ prefix_update(struct rib *rib, struct rd struct rde_community *comm, *ncomm = &state->communities; struct prefix *p; - if (nasp->pftableid) - rde_send_pftable(nasp->pftableid, prefix, prefixlen, 0); - /* * First try to find a prefix in the specified RIB. */ @@ -1056,6 +1047,10 @@ prefix_move(struct prefix *p, struct rde nexthop_link(np); np->lastchange = getmonotime(); + /* add possible pftable reference from new aspath */ + if (asp && asp->pftableid) + rde_pftable_add(asp->pftableid, np); + /* * no need to update the peer prefix count because we are only moving * the prefix without changing the peer. @@ -1075,6 +1070,10 @@ prefix_move(struct prefix *p, struct rde /* remove old prefix node */ /* as before peer count needs no update because of move */ + /* remove possible pftable reference first */ + if (p->aspath && p->aspath->pftableid) + rde_pftable_del(p->aspath->pftableid, p); + /* destroy all references to other objects and free the old prefix */ nexthop_unlink(p); nexthop_unref(p->nexthop); @@ -1109,7 +1108,7 @@ prefix_withdraw(struct rib *rib, struct asp = prefix_aspath(p); if (asp && asp->pftableid) /* only prefixes in the local RIB were pushed into pf */ - rde_send_pftable(asp->pftableid, prefix, prefixlen, 1); + rde_pftable_del(asp->pftableid, p); prefix_destroy(p); @@ -1596,6 +1595,10 @@ prefix_link(struct prefix *p, struct rib p->nexthop = nexthop_ref(nexthop); nexthop_link(p); p->lastchange = getmonotime(); + + /* add possible pftable reference from aspath */ + if (asp && asp->pftableid) + rde_pftable_add(asp->pftableid, p); /* make route decision */ prefix_evaluate(p, re);