From 75ddeeb96142ae8ed7b129f6b6ba801c7db7dc8a Mon Sep 17 00:00:00 2001 From: claudio Date: Thu, 17 Jun 2021 16:05:25 +0000 Subject: [PATCH] Implement RFC 7313 enhanced route refresh. It is off by default and can be enabled with 'announce enhanced refresh yes' Similar to graceful restart this allows to mark routes as stale, refresh them and the flush out routes that are still stale. Enhanced route refresh uses a begin of rr and a end of rr message to signal the various stages. A future enhancement would be the addition of a timeout in case the EoRR message is not sent in reasonable time. OK denis@ job@ --- usr.sbin/bgpd/bgpd.8 | 13 +++++- usr.sbin/bgpd/bgpd.conf.5 | 14 +++++- usr.sbin/bgpd/bgpd.h | 5 ++- usr.sbin/bgpd/parse.y | 8 +++- usr.sbin/bgpd/rde.c | 90 ++++++++++++++++++++++++++++++++++++--- usr.sbin/bgpd/rde.h | 6 ++- usr.sbin/bgpd/rde_peer.c | 38 ++++++++++++++++- usr.sbin/bgpd/session.c | 26 ++++++++++- 8 files changed, 182 insertions(+), 18 deletions(-) diff --git a/usr.sbin/bgpd/bgpd.8 b/usr.sbin/bgpd/bgpd.8 index 58ce1cfcd14..345e0db5da2 100644 --- a/usr.sbin/bgpd/bgpd.8 +++ b/usr.sbin/bgpd/bgpd.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: bgpd.8,v 1.68 2021/06/16 16:24:12 job Exp $ +.\" $OpenBSD: bgpd.8,v 1.69 2021/06/17 16:05:25 claudio Exp $ .\" .\" Copyright (c) 2003, 2004 Henning Brauer .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: June 16 2021 $ +.Dd $Mdocdate: June 17 2021 $ .Dt BGPD 8 .Os .Sh NAME @@ -382,6 +382,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 diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5 index dbb8f950afc..91dc05ef186 100644 --- a/usr.sbin/bgpd/bgpd.conf.5 +++ b/usr.sbin/bgpd/bgpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: bgpd.conf.5,v 1.210 2021/05/06 09:21:35 claudio Exp $ +.\" $OpenBSD: bgpd.conf.5,v 1.211 2021/06/17 16:05:25 claudio Exp $ .\" .\" Copyright (c) 2004 Claudio Jeker .\" Copyright (c) 2003, 2004 Henning Brauer @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 6 2021 $ +.Dd $Mdocdate: June 17 2021 $ .Dt BGPD.CONF 5 .Os .Sh NAME @@ -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 diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index f17010fb169..ffd27b1f43d 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.414 2021/05/27 08:27:48 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.415 2021/06/17 16:05:26 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -96,6 +96,9 @@ #define F_CTL_OVS_NOTFOUND 0x200000 #define F_CTL_NEIGHBORS 0x400000 /* only used by bgpctl */ +#define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \ + __attribute__((__unused__)) + /* * Note that these numeric assignments differ from the numbers commonly * used in route origin validation context. diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index df91c68f8bc..82ea2289875 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.416 2021/05/20 10:06:20 claudio Exp $ */ +/* $OpenBSD: parse.y,v 1.417 2021/06/17 16:05:26 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -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}, diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 85d8172df26..89c00175362 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.526 2021/06/17 10:28:36 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.527 2021/06/17 16:05:26 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -1068,6 +1068,7 @@ rde_dispatch_imsg_rtr(struct imsgbuf *ibuf) 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 *peer, void *bula) 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 *peer, void *bula) 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; @@ -3016,8 +3052,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); @@ -3067,7 +3109,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); @@ -3765,6 +3812,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. @@ -3789,6 +3837,7 @@ rde_peer_send_eor(struct rde_peer *peer, u_int8_t aid) u_int8_t safi; peer->prefix_sent_eor++; + peer->sent_eor |= 1 << aid; if (aid == AID_INET) { u_char null[4]; @@ -3825,6 +3874,33 @@ rde_peer_send_eor(struct rde_peer *peer, u_int8_t aid) 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"); +} + /* * network announcement stuff */ diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index 3cbfec7a53a..52bee3adccf 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.239 2021/05/27 14:32:08 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.240 2021/06/17 16:05:26 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker and @@ -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 *, 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 *); diff --git a/usr.sbin/bgpd/rde_peer.c b/usr.sbin/bgpd/rde_peer.c index 23ec2f7a6bd..502826ddf4c 100644 --- a/usr.sbin/bgpd/rde_peer.c +++ b/usr.sbin/bgpd/rde_peer.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_peer.c,v 1.10 2021/06/17 08:45:37 claudio Exp $ */ +/* $OpenBSD: rde_peer.c,v 1.11 2021/06/17 16:05:26 claudio Exp $ */ /* * Copyright (c) 2019 Claudio Jeker @@ -37,6 +37,9 @@ struct peer_table { struct rde_peer_head peerlist; struct rde_peer *peerself; +CTASSERT(sizeof(peerself->recv_eor) * 8 > AID_MAX); +CTASSERT(sizeof(peerself->sent_eor) * 8 > AID_MAX); + struct iq { SIMPLEQ_ENTRY(iq) entry; struct imsg imsg; @@ -353,6 +356,15 @@ peer_up(struct rde_peer *peer, struct session_up *sup) 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++) { @@ -451,6 +463,9 @@ peer_stale(struct rde_peer *peer, u_int8_t aid) 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) @@ -467,6 +482,27 @@ peer_dump(struct rde_peer *peer, u_int8_t aid) } } +/* + * 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); +} + /* * move an imsg from src to dst, disconnecting any dynamic memory from src. */ diff --git a/usr.sbin/bgpd/session.c b/usr.sbin/bgpd/session.c index 20a8bf4f94c..93debc14496 100644 --- a/usr.sbin/bgpd/session.c +++ b/usr.sbin/bgpd/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.420 2021/05/27 09:15:51 claudio Exp $ */ +/* $OpenBSD: session.c,v 1.421 2021/06/17 16:05:26 claudio Exp $ */ /* * Copyright (c) 2003, 2004, 2005 Henning Brauer @@ -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 *ibuf, int idx, u_int *listener_cnt) { struct imsg imsg; struct mrt xmrt; + struct route_refresh rr; struct mrt *mrt; struct imsgbuf *i; struct peer *p; @@ -3095,6 +3100,23 @@ session_dispatch_imsg(struct imsgbuf *ibuf, int idx, u_int *listener_cnt) 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) fatalx("update request not from RDE"); -- 2.20.1