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;

Reply via email to