This diff implements 'max-prefix NUM out' which is a simple way to avoid leaking full tables to upstream or peers. If the limit is triggered the session will be closed with a NOTIFICATION (kind of suicide for the good of the Internet).
In bgpctl the counters are visible in the 'bgpctl show nei' output. Works for me (adopting the maxprefix regress test to test for 'max-prefix NUM out'. OK? -- :wq Claudio Index: bgpctl/bgpctl.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v retrieving revision 1.257 diff -u -p -r1.257 bgpctl.c --- bgpctl/bgpctl.c 21 Jan 2020 11:14:26 -0000 1.257 +++ bgpctl/bgpctl.c 21 Jan 2020 11:17:47 -0000 @@ -584,6 +584,8 @@ print_neighbor_msgstats(struct peer *p) p->stats.msg_rcvd_rrefresh); printf(" Update statistics:\n"); printf(" %-15s %-10s %-10s\n", "", "Sent", "Received"); + printf(" %-15s %10u %10u\n", "Prefixes", + p->stats.prefix_out_cnt, p->stats.prefix_cnt); printf(" %-15s %10llu %10llu\n", "Updates", p->stats.prefix_sent_update, p->stats.prefix_rcvd_update); printf(" %-15s %10llu %10llu\n", "Withdraws", Index: bgpctl/output.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v retrieving revision 1.4 diff -u -p -r1.4 output.c --- bgpctl/output.c 21 Jan 2020 11:16:35 -0000 1.4 +++ bgpctl/output.c 21 Jan 2020 11:56:11 -0000 @@ -166,8 +166,16 @@ show_neighbor_full(struct peer *p, struc if (p->conf.max_prefix_restart) printf(" (restart %u)", p->conf.max_prefix_restart); - printf("\n"); } + if (p->conf.max_out_prefix) { + printf(" Max-prefix out: %u", p->conf.max_out_prefix); + if (p->conf.max_out_prefix_restart) + printf(" (restart %u)", + p->conf.max_out_prefix_restart); + } + if (p->conf.max_prefix || p->conf.max_out_prefix) + printf("\n"); + printf(" BGP version 4, remote router-id %s", inet_ntoa(ina)); printf("%s\n", print_auth_method(p->auth.method)); Index: bgpd/bgpd.conf.5 =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v retrieving revision 1.197 diff -u -p -r1.197 bgpd.conf.5 --- bgpd/bgpd.conf.5 31 Oct 2019 09:09:04 -0000 1.197 +++ bgpd/bgpd.conf.5 25 Nov 2019 23:36:08 -0000 @@ -1057,6 +1057,21 @@ is specified, the session will be restar .Ar number minutes. .Pp +.Pp +.It Xo +.Ic max-prefix Ar number Ic out +.Op Ic restart Ar number +.Xc +Terminate the session when the maximum +.Ar number +of prefixes sent is exceeded +(no such limit is imposed by default). +If +.Ic restart +is specified, the session will be restarted after +.Ar number +minutes. +.Pp .It Ic multihop Ar hops Neighbors not in the same AS as the local .Xr bgpd 8 Index: bgpd/bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.398 diff -u -p -r1.398 bgpd.h --- bgpd/bgpd.h 21 Jan 2020 11:10:24 -0000 1.398 +++ bgpd/bgpd.h 21 Jan 2020 11:19:28 -0000 @@ -379,10 +379,12 @@ struct peer_config { u_int32_t remote_as; u_int32_t local_as; u_int32_t max_prefix; + u_int32_t max_out_prefix; enum export_type export_type; enum enforce_as enforce_as; enum enforce_as enforce_local_as; u_int16_t max_prefix_restart; + u_int16_t max_out_prefix_restart; u_int16_t holdtime; u_int16_t min_holdtime; u_int16_t local_short_as; Index: bgpd/parse.y =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v retrieving revision 1.402 diff -u -p -r1.402 parse.y --- bgpd/parse.y 27 Sep 2019 10:26:32 -0000 1.402 +++ bgpd/parse.y 30 Sep 2019 11:59:44 -0000 @@ -1391,6 +1391,14 @@ peeropts : REMOTEAS as4number { curpeer->conf.max_prefix = $2; curpeer->conf.max_prefix_restart = $3; } + | MAXPREFIX NUMBER OUT restart { + if ($2 < 0 || $2 > UINT_MAX) { + yyerror("bad maximum number of prefixes"); + YYERROR; + } + curpeer->conf.max_out_prefix = $2; + curpeer->conf.max_out_prefix_restart = $4; + } | TCP MD5SIG PASSWORD string { if (curpeer->conf.auth.method) { yyerror("auth method cannot be redefined"); Index: bgpd/printconf.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v retrieving revision 1.140 diff -u -p -r1.140 printconf.c --- bgpd/printconf.c 7 Aug 2019 10:26:41 -0000 1.140 +++ bgpd/printconf.c 7 Aug 2019 12:14:16 -0000 @@ -616,6 +616,12 @@ print_peer(struct peer_config *p, struct printf(" restart %u", p->max_prefix_restart); printf("\n"); } + if (p->max_out_prefix) { + printf("%s\tmax-prefix %u out", c, p->max_out_prefix); + if (p->max_out_prefix_restart) + printf(" restart %u", p->max_out_prefix_restart); + printf("\n"); + } if (p->holdtime) printf("%s\tholdtime %u\n", c, p->holdtime); if (p->min_holdtime) Index: bgpd/rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.499 diff -u -p -r1.499 rde.c --- bgpd/rde.c 10 Jan 2020 13:22:26 -0000 1.499 +++ bgpd/rde.c 22 Jan 2020 02:14:47 -0000 @@ -59,8 +59,6 @@ int rde_attr_add(struct filterstate *, u_int8_t rde_attr_missing(struct rde_aspath *, int, u_int16_t); int rde_get_mp_nexthop(u_char *, u_int16_t, u_int8_t, struct filterstate *); -void rde_update_err(struct rde_peer *, u_int8_t , u_int8_t, - void *, u_int16_t); void rde_as4byte_fixup(struct rde_peer *, struct rde_aspath *); void rde_reflector(struct rde_peer *, struct rde_aspath *); @@ -535,6 +533,7 @@ badnetdel: peer = peer_get(p.conf.id); if (peer != NULL) { p.stats.prefix_cnt = peer->prefix_cnt; + p.stats.prefix_out_cnt = peer->prefix_out_cnt; p.stats.prefix_rcvd_update = peer->prefix_rcvd_update; p.stats.prefix_rcvd_withdraw = Index: bgpd/rde.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v retrieving revision 1.232 diff -u -p -r1.232 rde.h --- bgpd/rde.h 9 Jan 2020 15:50:34 -0000 1.232 +++ bgpd/rde.h 22 Jan 2020 02:15:03 -0000 @@ -97,7 +97,8 @@ struct rde_peer { u_int64_t prefix_sent_update; u_int64_t prefix_sent_withdraw; u_int64_t prefix_sent_eor; - u_int32_t prefix_cnt; /* # of prefixes */ + u_int32_t prefix_cnt; + u_int32_t prefix_out_cnt; u_int32_t remote_bgpid; /* host byte order! */ u_int32_t up_nlricnt; u_int32_t up_wcnt; @@ -359,6 +360,8 @@ int mrt_dump_v2_hdr(struct mrt *, struc void mrt_dump_upcall(struct rib_entry *, void *); /* rde.c */ +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, const struct rde_peer *, const struct bgpd_addr *, const struct bgpd_addr *, u_int8_t); Index: bgpd/rde_peer.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v retrieving revision 1.3 diff -u -p -r1.3 rde_peer.c --- bgpd/rde_peer.c 21 Jan 2020 06:22:17 -0000 1.3 +++ bgpd/rde_peer.c 21 Jan 2020 06:50:01 -0000 @@ -420,6 +420,7 @@ peer_up(struct rde_peer *peer, struct se fatal("%s: prefix_dump_new", __func__); peer_flush(peer, AID_UNSPEC, 0); peer->prefix_cnt = 0; + peer->prefix_out_cnt = 0; peer->state = PEER_DOWN; } peer->remote_bgpid = ntohl(sup->remote_bgpid); @@ -461,6 +462,7 @@ peer_down(struct rde_peer *peer, void *b /* flush Adj-RIB-In */ peer_flush(peer, AID_UNSPEC, 0); peer->prefix_cnt = 0; + peer->prefix_out_cnt = 0; peer_imsg_flush(peer); Index: bgpd/rde_update.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v retrieving revision 1.122 diff -u -p -r1.122 rde_update.c --- bgpd/rde_update.c 13 Aug 2019 12:16:20 -0000 1.122 +++ bgpd/rde_update.c 22 Jan 2020 02:14:16 -0000 @@ -144,8 +144,10 @@ withdraw: /* withdraw prefix */ pt_getaddr(old->pt, &addr); if (prefix_adjout_withdraw(peer, &addr, - old->pt->prefixlen) == 1) + old->pt->prefixlen) == 1) { + peer->prefix_out_cnt--; peer->up_wcnt++; + } } else { switch (up_test_update(peer, new)) { case 1: @@ -169,10 +171,22 @@ withdraw: /* only send update if path changed */ if (prefix_adjout_update(peer, &state, &addr, - new->pt->prefixlen, prefix_vstate(new)) == 1) + new->pt->prefixlen, prefix_vstate(new)) == 1) { + peer->prefix_out_cnt++; peer->up_nlricnt++; + } rde_filterstate_clean(&state); + + /* max prefix checker outbound */ + if (peer->conf.max_out_prefix && + peer->prefix_out_cnt > peer->conf.max_out_prefix) { + log_peer_warnx(&peer->conf, + "outbound prefix limit reached (>%u/%u)", + peer->prefix_out_cnt, peer->conf.max_out_prefix); + rde_update_err(peer, ERR_CEASE, + ERR_CEASE_MAX_SENT_PREFIX, NULL, 0); + } } } @@ -214,11 +228,23 @@ up_generate_default(struct filter_head * return; } - if (prefix_adjout_update(peer, &state, &addr, 0, ROA_NOTFOUND) == 1) + if (prefix_adjout_update(peer, &state, &addr, 0, ROA_NOTFOUND) == 1) { + peer->prefix_out_cnt++; peer->up_nlricnt++; + } /* no longer needed */ rde_filterstate_clean(&state); + + /* max prefix checker outbound */ + if (peer->conf.max_out_prefix && + peer->prefix_out_cnt > peer->conf.max_out_prefix) { + log_peer_warnx(&peer->conf, + "outbound prefix limit reached (>%u/%u)", + peer->prefix_out_cnt, peer->conf.max_out_prefix); + rde_update_err(peer, ERR_CEASE, + ERR_CEASE_MAX_SENT_PREFIX, NULL, 0); + } } /* only for IPv4 */ Index: bgpd/session.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/session.c,v retrieving revision 1.397 diff -u -p -r1.397 session.c --- bgpd/session.c 21 Jan 2020 11:12:06 -0000 1.397 +++ bgpd/session.c 21 Jan 2020 11:17:57 -0000 @@ -2511,6 +2511,7 @@ session_dispatch_imsg(struct imsgbuf *ib struct kif *kif; u_char *data; int n, fd, depend_ok, restricted; + u_int16_t t; u_int8_t aid, errcode, subcode; while (ibuf) { @@ -2795,10 +2796,15 @@ session_dispatch_imsg(struct imsgbuf *ib case ERR_CEASE: switch (subcode) { case ERR_CEASE_MAX_PREFIX: + case ERR_CEASE_MAX_SENT_PREFIX: + t = p->conf.max_out_prefix_restart; + if (subcode == ERR_CEASE_MAX_PREFIX) + t = p->conf.max_prefix_restart; + bgp_fsm(p, EVNT_STOP); - if (p->conf.max_prefix_restart) - timer_set(p, Timer_IdleHold, 60 * - p->conf.max_prefix_restart); + if (t) + timer_set(p, Timer_IdleHold, + 60 * t); break; default: bgp_fsm(p, EVNT_CON_FATAL); Index: bgpd/session.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/session.h,v retrieving revision 1.143 diff -u -p -r1.143 session.h --- bgpd/session.h 21 Jan 2020 11:12:06 -0000 1.143 +++ bgpd/session.h 21 Jan 2020 11:17:57 -0000 @@ -169,6 +169,7 @@ struct peer_stats { time_t last_read; time_t last_write; u_int32_t prefix_cnt; + u_int32_t prefix_out_cnt; u_int8_t last_sent_errcode; u_int8_t last_sent_suberr; u_int8_t last_rcvd_errcode;