Implement RFC 7313 enhanced route refresh.

While there also change when graceful restart EoR markers are sent.
In short the graceful restart marker should only be sent initally. After
that the End of Route Refresh message should be sent instead.
Because of this track if an EoR marker was received or should be sent in
the peer config.

For now this setting is off by default but that may be changed at a later
state.

Please try this out and tell me if it works for you. The message and
prefix/rrefresh counters in bgpctl show nei output help a lot to see what
is going on.
-- 
:wq Claudio

Index: bgpd.8
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.8,v
retrieving revision 1.67
diff -u -p -r1.67 bgpd.8
--- bgpd.8      25 May 2021 14:18:44 -0000      1.67
+++ bgpd.8      27 May 2021 16:03:30 -0000
@@ -380,6 +380,15 @@ has been started.
 .Re
 .Pp
 .Rs
+.%A K. Patel
+.%A E. Chen
+.%A B. Venkatachalapathy
+.%D July 2014
+.%R RFC 7313
+.%T Enhanced Route Refresh Capability for BGP-4
+.Re
+.Pp
+.Rs
 .%A W. Kumari
 .%A R. Bush
 .%A H. Schiller
Index: bgpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v
retrieving revision 1.210
diff -u -p -r1.210 bgpd.conf.5
--- bgpd.conf.5 6 May 2021 09:21:35 -0000       1.210
+++ bgpd.conf.5 27 May 2021 16:03:30 -0000
@@ -831,6 +831,16 @@ The default is
 .Ic yes .
 .Pp
 .It Xo
+.Ic announce enhanced refresh
+.Pq Ic yes Ns | Ns Ic no
+.Xc
+If set to
+.Ic yes ,
+the enhanced route refresh capability is announced.
+The default is
+.Ic no .
+.Pp
+.It Xo
 .Ic announce refresh
 .Pq Ic yes Ns | Ns Ic no
 .Xc
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.414
diff -u -p -r1.414 bgpd.h
--- bgpd.h      27 May 2021 08:27:48 -0000      1.414
+++ bgpd.h      27 May 2021 16:03:30 -0000
@@ -159,7 +159,7 @@ extern const struct aid aid_vals[];
 #define        AID_INET6       2
 #define        AID_VPN_IPv4    3
 #define        AID_VPN_IPv6    4
-#define        AID_MAX         5
+#define        AID_MAX         5       /* check rde_peer.recv_eor when max 
reaches 7 */
 #define        AID_MIN         1       /* skip AID_UNSPEC since that is a 
dummy */
 
 #define AID_VALS       {                                       \
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.416
diff -u -p -r1.416 parse.y
--- parse.y     20 May 2021 10:06:20 -0000      1.416
+++ parse.y     27 May 2021 16:03:30 -0000
@@ -204,7 +204,7 @@ typedef struct {
 %token GROUP NEIGHBOR NETWORK
 %token EBGP IBGP
 %token LOCALAS REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART
-%token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY
+%token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY ENHANCED
 %token DEMOTE ENFORCE NEIGHBORAS ASOVERRIDE REFLECTOR DEPEND DOWN
 %token DUMP IN OUT SOCKET RESTRICTED
 %token LOG TRANSPARENT
@@ -1446,6 +1446,9 @@ peeropts  : REMOTEAS as4number    {
                | ANNOUNCE REFRESH yesno {
                        curpeer->conf.capabilities.refresh = $3;
                }
+               | ANNOUNCE ENHANCED REFRESH yesno {
+                       curpeer->conf.capabilities.enhanced_rr = $4;
+               }
                | ANNOUNCE RESTART yesno {
                        curpeer->conf.capabilities.grestart.restart = $3;
                }
@@ -2898,6 +2901,7 @@ lookup(char *s)
                { "dump",               DUMP},
                { "ebgp",               EBGP},
                { "enforce",            ENFORCE},
+               { "enhanced",           ENHANCED },
                { "esp",                ESP},
                { "evaluate",           EVALUATE},
                { "export",             EXPORT},
Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.523
diff -u -p -r1.523 rde.c
--- rde.c       27 May 2021 14:32:08 -0000      1.523
+++ rde.c       27 May 2021 16:03:30 -0000
@@ -1068,6 +1068,7 @@ rde_dispatch_imsg_rtr(struct imsgbuf *ib
 void
 rde_dispatch_imsg_peer(struct rde_peer *peer, void *bula)
 {
+       struct route_refresh rr;
        struct session_up sup;
        struct imsg imsg;
        u_int8_t aid;
@@ -1095,7 +1096,6 @@ rde_dispatch_imsg_peer(struct rde_peer *
        case IMSG_SESSION_STALE:
        case IMSG_SESSION_FLUSH:
        case IMSG_SESSION_RESTARTED:
-       case IMSG_REFRESH:
                if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) {
                        log_warnx("%s: wrong imsg len", __func__);
                        break;
@@ -1117,8 +1117,44 @@ rde_dispatch_imsg_peer(struct rde_peer *
                        if (peer->staletime[aid])
                                peer_flush(peer, aid, peer->staletime[aid]);
                        break;
-               case IMSG_REFRESH:
-                       peer_dump(peer, aid);
+               }
+               break;
+       case IMSG_REFRESH:
+               if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(rr)) {
+                       log_warnx("%s: wrong imsg len", __func__);
+                       break;
+               }
+               memcpy(&rr, imsg.data, sizeof(rr));
+               if (rr.aid >= AID_MAX) {
+                       log_warnx("%s: bad AID", __func__);
+                       break;
+               }
+               switch (rr.subtype) {
+               case ROUTE_REFRESH_REQUEST:
+                       peer_dump(peer, rr.aid);
+                       break;
+               case ROUTE_REFRESH_BEGIN_RR:
+                       /* check if graceful restart EOR was received */
+                       if ((peer->recv_eor & (1 << rr.aid)) == 0) {
+                               log_peer_warnx(&peer->conf,
+                                   "received %s BoRR before EoR",
+                                   aid2str(rr.aid));
+                               break;
+                       }
+                       peer_begin_rrefresh(peer, rr.aid);
+                       break;
+               case ROUTE_REFRESH_END_RR:
+                       if ((peer->recv_eor & (1 << rr.aid)) != 0 &&
+                           peer->staletime[rr.aid])
+                               peer_flush(peer, rr.aid,
+                                   peer->staletime[rr.aid]);
+                       else
+                               log_peer_warnx(&peer->conf,
+                                   "received unexpected %s EoRR",
+                                   aid2str(rr.aid));
+                       break;
+               default:
+                       log_warnx("%s: bad subtype %d", __func__, rr.subtype);
                        break;
                }
                break;
@@ -3002,8 +3038,14 @@ rde_update_queue_runner(void)
                                            __func__, __LINE__);
                                sent++;
                        }
-                       if (eor)
-                               rde_peer_send_eor(peer, AID_INET);
+                       if (eor) {
+                               int sent_eor = peer->sent_eor & (1 << AID_INET);
+                               if (peer->capa.grestart.restart && !sent_eor)
+                                       rde_peer_send_eor(peer, AID_INET);
+                               if (peer->capa.enhanced_rr && sent_eor)
+                                       rde_peer_send_rrefresh(peer, AID_INET,
+                                           ROUTE_REFRESH_END_RR);
+                       }
                }
                max -= sent;
        } while (sent != 0 && max > 0);
@@ -3053,7 +3095,12 @@ rde_update6_queue_runner(u_int8_t aid)
                                continue;
                        len = sizeof(queue_buf) - MSGSIZE_HEADER;
                        if (up_is_eor(peer, aid)) {
-                               rde_peer_send_eor(peer, aid);
+                               int sent_eor = peer->sent_eor & (1 << aid);
+                               if (peer->capa.grestart.restart && !sent_eor)
+                                       rde_peer_send_eor(peer, aid);
+                               if (peer->capa.enhanced_rr && sent_eor)
+                                       rde_peer_send_rrefresh(peer, aid,
+                                           ROUTE_REFRESH_END_RR);
                                continue;
                        }
                        r = up_dump_mp_reach(queue_buf, len, peer, aid);
@@ -3745,6 +3792,7 @@ static void
 rde_peer_recv_eor(struct rde_peer *peer, u_int8_t aid)
 {
        peer->prefix_rcvd_eor++;
+       peer->recv_eor |= 1 << aid;
 
        /*
         * First notify SE to avert a possible race with the restart timeout.
@@ -3769,6 +3817,7 @@ rde_peer_send_eor(struct rde_peer *peer,
        u_int8_t        safi;
 
        peer->prefix_sent_eor++;
+       peer->sent_eor |= 1 << aid;
 
        if (aid == AID_INET) {
                u_char null[4];
@@ -3803,6 +3852,33 @@ rde_peer_send_eor(struct rde_peer *peer,
 
        log_peer_info(&peer->conf, "sending %s EOR marker",
            aid2str(aid));
+}
+
+void
+rde_peer_send_rrefresh(struct rde_peer *peer, u_int8_t aid, u_int8_t subtype)
+{
+       struct route_refresh rr;
+
+       /* not strickly needed, the SE checks as well */
+        if (peer->capa.enhanced_rr == 0)
+               return;
+
+       switch (subtype) {
+       case ROUTE_REFRESH_END_RR:
+       case ROUTE_REFRESH_BEGIN_RR:
+               break;
+       default:
+               fatalx("%s unexpected subtype %d", __func__, subtype);
+       }
+
+       rr.aid = aid;
+       rr.subtype = subtype;
+
+       if (imsg_compose(ibuf_se, IMSG_REFRESH, peer->conf.id, 0, -1,
+           &rr, sizeof(rr)) == -1)
+
+       log_peer_info(&peer->conf, "sending %s %s marker",
+           aid2str(aid), subtype == ROUTE_REFRESH_END_RR ? "EoRR" : "BoRR");
 }
 
 /*
Index: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.239
diff -u -p -r1.239 rde.h
--- rde.h       27 May 2021 14:32:08 -0000      1.239
+++ rde.h       27 May 2021 16:03:30 -0000
@@ -107,6 +107,8 @@ struct rde_peer {
        u_int16_t                        loc_rib_id;
        u_int16_t                        short_as;
        u_int16_t                        mrt_idx;
+       u_int8_t                         recv_eor;      /* bitfield per AID */
+       u_int8_t                         sent_eor;      /* bitfield per AID */
        u_int8_t                         reconf_out;    /* out filter changed */
        u_int8_t                         reconf_rib;    /* rib changed */
        u_int8_t                         throttled;
@@ -378,6 +380,7 @@ void                rde_generate_updates(struct rib *,
                    struct prefix *, int);
 u_int32_t      rde_local_as(void);
 int            rde_decisionflags(void);
+void           rde_peer_send_rrefresh(struct rde_peer *, u_int8_t, u_int8_t);
 int            rde_match_peer(struct rde_peer *, struct ctl_neighbor *);
 
 /* rde_peer.c */
@@ -395,6 +398,7 @@ void                 peer_down(struct rde_peer *, void
 void            peer_flush(struct rde_peer *, u_int8_t, time_t);
 void            peer_stale(struct rde_peer *, u_int8_t);
 void            peer_dump(struct rde_peer *, u_int8_t);
+void            peer_begin_rrefresh(struct rde_peer *, u_int8_t);
 
 void            peer_imsg_push(struct rde_peer *, struct imsg *);
 int             peer_imsg_pop(struct rde_peer *, struct imsg *);
Index: rde_peer.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v
retrieving revision 1.9
diff -u -p -r1.9 rde_peer.c
--- rde_peer.c  27 May 2021 14:32:08 -0000      1.9
+++ rde_peer.c  27 May 2021 16:03:30 -0000
@@ -345,6 +345,15 @@ peer_up(struct rde_peer *peer, struct se
        peer->local_v6_addr = sup->local_v6_addr;
        memcpy(&peer->capa, &sup->capa, sizeof(peer->capa));
 
+       /* clear eor markers depending on GR flags */
+       if (peer->capa.grestart.restart) {
+               peer->sent_eor = 0;
+               peer->recv_eor = 0;
+       } else {
+               /* no EOR expected */
+               peer->sent_eor = ~0;
+               peer->recv_eor = ~0;
+       }
        peer->state = PEER_UP;
 
        for (i = 0; i < AID_MAX; i++) {
@@ -443,6 +452,9 @@ peer_stale(struct rde_peer *peer, u_int8
 void
 peer_dump(struct rde_peer *peer, u_int8_t aid)
 {
+       if (peer->capa.enhanced_rr && (peer->sent_eor & (1 << aid)))
+               rde_peer_send_rrefresh(peer, aid, ROUTE_REFRESH_BEGIN_RR);
+
        if (peer->export_type == EXPORT_NONE) {
                /* nothing to send apart from the marker */
                if (peer->capa.grestart.restart)
@@ -457,6 +469,27 @@ peer_dump(struct rde_peer *peer, u_int8_
                /* throttle peer until dump is done */
                peer->throttled = 1;
        }
+}
+
+/*
+ * Start of an enhanced route refresh. Mark all routes as stale.
+ * Once the route refresh ends a End of Route Refresh message is sent
+ * which calls peer_flush() to remove all stale routes.
+ */
+void
+peer_begin_rrefresh(struct rde_peer *peer, u_int8_t aid)
+{
+       time_t now;
+
+       /* flush the now even staler routes out */
+       if (peer->staletime[aid])
+               peer_flush(peer, aid, peer->staletime[aid]);
+
+       peer->staletime[aid] = now = getmonotime();
+
+       /* make sure new prefixes start on a higher timestamp */
+       while (now >= getmonotime())
+               sleep(1);
 }
 
 /*
Index: session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
retrieving revision 1.420
diff -u -p -r1.420 session.c
--- session.c   27 May 2021 09:15:51 -0000      1.420
+++ session.c   27 May 2021 16:03:30 -0000
@@ -2256,6 +2256,7 @@ parse_update(struct peer *peer)
 int
 parse_rrefresh(struct peer *peer)
 {
+       struct route_refresh rr;
        u_int16_t afi, datalen;
        u_int8_t aid, safi, subtype;
        u_char *p;
@@ -2343,7 +2344,10 @@ parse_rrefresh(struct peer *peer)
                return (0);
        }
 
-       if (imsg_rde(IMSG_REFRESH, peer->conf.id, &aid, sizeof(aid)) == -1)
+       rr.aid = aid;
+       rr.subtype = subtype;
+
+       if (imsg_rde(IMSG_REFRESH, peer->conf.id, &rr, sizeof(rr)) == -1)
                return (-1);
 
        return (0);
@@ -2768,6 +2772,7 @@ session_dispatch_imsg(struct imsgbuf *ib
 {
        struct imsg              imsg;
        struct mrt               xmrt;
+       struct route_refresh     rr;
        struct mrt              *mrt;
        struct imsgbuf          *i;
        struct peer             *p;
@@ -3094,6 +3099,23 @@ session_dispatch_imsg(struct imsgbuf *ib
                                bgp_fsm(p, EVNT_CON_FATAL);
                                break;
                        }
+                       break;
+               case IMSG_REFRESH:
+                       if (idx != PFD_PIPE_ROUTE)
+                               fatalx("route refresh request not from RDE");
+                       if (imsg.hdr.len < IMSG_HEADER_SIZE + sizeof(rr)) {
+                               log_warnx("RDE sent invalid refresh msg");
+                               break;
+                       }
+                       if ((p = getpeerbyid(conf, imsg.hdr.peerid)) == NULL) {
+                               log_warnx("no such peer: id=%u",
+                                   imsg.hdr.peerid);
+                               break;
+                       }
+                       memcpy(&rr, imsg.data, sizeof(rr));
+                       if (rr.aid >= AID_MAX)
+                               fatalx("IMSG_REFRESH: bad AID");
+                       session_rrefresh(p, rr.aid, rr.subtype);
                        break;
                case IMSG_SESSION_RESTARTED:
                        if (idx != PFD_PIPE_ROUTE)

Reply via email to