On Thu, May 27, 2021 at 06:24:06PM +0200, Claudio Jeker wrote:
> 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.
Ping.
--
: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.524
diff -u -p -r1.524 rde.c
--- rde.c 27 May 2021 16:32:13 -0000 1.524
+++ rde.c 2 Jun 2021 10:09:23 -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;
@@ -1097,7 +1098,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;
@@ -1119,8 +1119,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;
@@ -3004,8 +3040,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);
@@ -3055,7 +3097,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);
@@ -3747,6 +3794,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.
@@ -3771,6 +3819,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];
@@ -3805,6 +3854,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)