From 63c2de87d6b8649533885376cec26dc58482316b Mon Sep 17 00:00:00 2001 From: claudio Date: Thu, 27 May 2021 08:27:48 +0000 Subject: [PATCH] bgpd(8) will soon support ADD-PATH (RFC7911) and enhanced route refresh (RFC7313). This is the frist step toward this. It adds the capability parsers for the two no capabilities, extends the capability struct and adds the capability negotiation bits. The route refresh message parser and generator are extended to support the BoRR and EoRR message. Also add the new NOTIFICATION type and subtype for the route refresh message. --- usr.sbin/bgpd/bgpd.h | 34 +++++- usr.sbin/bgpd/logmsg.c | 8 +- usr.sbin/bgpd/session.c | 233 ++++++++++++++++++++++++++++++++++------ usr.sbin/bgpd/session.h | 21 ++-- 4 files changed, 254 insertions(+), 42 deletions(-) diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index f56a320c6e6..f17010fb169 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.413 2021/03/02 09:45:07 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.414 2021/05/27 08:27:48 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -352,17 +352,24 @@ struct capabilities { int8_t mp[AID_MAX]; /* multiprotocol extensions, RFC 4760 */ int8_t refresh; /* route refresh, RFC 2918 */ int8_t as4byte; /* 4-byte ASnum, RFC 4893 */ + int8_t enhanced_rr; /* enhanced route refresh, RFC 7313 */ + int8_t add_path[AID_MAX]; /* ADD_PATH, RFC 7911 */ }; +/* flags for RFC4724 - graceful restart */ #define CAPA_GR_PRESENT 0x01 #define CAPA_GR_RESTART 0x02 #define CAPA_GR_FORWARD 0x04 #define CAPA_GR_RESTARTING 0x08 - #define CAPA_GR_TIMEMASK 0x0fff #define CAPA_GR_R_FLAG 0x8000 #define CAPA_GR_F_FLAG 0x80 +/* flags for RFC7911 - enhanced router refresh */ +#define CAPA_AP_RECV 0x01 +#define CAPA_AP_SEND 0x02 +#define CAPA_AP_BIDIR 0x03 + struct peer_config { struct bgpd_addr remote_addr; struct bgpd_addr local_addr_v4; @@ -595,7 +602,8 @@ enum err_codes { ERR_UPDATE, ERR_HOLDTIMEREXPIRED, ERR_FSM, - ERR_CEASE + ERR_CEASE, + ERR_RREFRESH }; enum suberr_update { @@ -626,6 +634,10 @@ enum suberr_cease { ERR_CEASE_MAX_SENT_PREFIX }; +enum suberr_rrefresh { + ERR_RR_INV_LEN = 1 +}; + struct kroute_node; struct kroute6_node; struct knexthop_node; @@ -713,6 +725,14 @@ struct session_up { u_int16_t short_as; }; +struct route_refresh { + u_int8_t aid; + u_int8_t subtype; +}; +#define ROUTE_REFRESH_REQUEST 0 +#define ROUTE_REFRESH_BEGIN_RR 1 +#define ROUTE_REFRESH_END_RR 2 + struct pftable_msg { struct bgpd_addr addr; char pftable[PFTABLE_LEN]; @@ -1457,7 +1477,8 @@ static const char * const errnames[] = { "error in UPDATE message", "HoldTimer expired", "Finite State Machine error", - "Cease" + "Cease", + "error in ROUTE-REFRESH message" }; static const char * const suberr_header_names[] = { @@ -1516,6 +1537,11 @@ static const char * const suberr_cease_names[] = { "sent max-prefix exceeded" }; +static const char * const suberr_rrefresh_names[] = { + "none", + "invalid message length" +}; + static const char * const ctl_res_strerror[] = { "no error", "no such neighbor", diff --git a/usr.sbin/bgpd/logmsg.c b/usr.sbin/bgpd/logmsg.c index a75386aa4d6..8bd17242b2b 100644 --- a/usr.sbin/bgpd/logmsg.c +++ b/usr.sbin/bgpd/logmsg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: logmsg.c,v 1.4 2019/02/18 09:43:57 claudio Exp $ */ +/* $OpenBSD: logmsg.c,v 1.5 2021/05/27 08:27:48 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -176,6 +176,12 @@ log_notification(const struct peer *peer, u_int8_t errcode, u_int8_t subcode, else suberrname = suberr_fsm_names[subcode]; break; + case ERR_RREFRESH: + if (subcode >= sizeof(suberr_rrefresh_names)/sizeof(char *)) + uk = 1; + else + suberrname = suberr_rrefresh_names[subcode]; + break; default: logit(LOG_ERR, "%s: %s notification, unknown errcode " "%u, subcode %u", p, dir, errcode, subcode); diff --git a/usr.sbin/bgpd/session.c b/usr.sbin/bgpd/session.c index 9cfefc05bf3..2844562e64b 100644 --- a/usr.sbin/bgpd/session.c +++ b/usr.sbin/bgpd/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.417 2021/05/27 08:20:39 claudio Exp $ */ +/* $OpenBSD: session.c,v 1.418 2021/05/27 08:27:48 claudio Exp $ */ /* * Copyright (c) 2003, 2004, 2005 Henning Brauer @@ -68,7 +68,7 @@ void session_tcp_established(struct peer *); void session_capa_ann_none(struct peer *); int session_capa_add(struct ibuf *, u_int8_t, u_int8_t); int session_capa_add_mp(struct ibuf *, u_int8_t); -int session_capa_add_gr(struct peer *, struct ibuf *, u_int8_t); +int session_capa_add_afi(struct peer *, struct ibuf *, u_int8_t, u_int8_t); struct bgp_msg *session_newmsg(enum msg_type, u_int16_t); int session_sendmsg(struct bgp_msg *, struct peer *); void session_open(struct peer *); @@ -76,7 +76,7 @@ void session_keepalive(struct peer *); void session_update(u_int32_t, void *, size_t); void session_notification(struct peer *, u_int8_t, u_int8_t, void *, ssize_t); -void session_rrefresh(struct peer *, u_int8_t); +void session_rrefresh(struct peer *, u_int8_t, u_int8_t); int session_graceful_restart(struct peer *); int session_graceful_stop(struct peer *); int session_dispatch_msg(struct pollfd *, struct peer *); @@ -84,7 +84,7 @@ void session_process_msg(struct peer *); int parse_header(struct peer *, u_char *, u_int16_t *, u_int8_t *); int parse_open(struct peer *); int parse_update(struct peer *); -int parse_refresh(struct peer *); +int parse_rrefresh(struct peer *); int parse_notification(struct peer *); int parse_capabilities(struct peer *, u_char *, u_int16_t, u_int32_t *); int capa_neg_calc(struct peer *); @@ -1329,20 +1329,17 @@ session_capa_add_mp(struct ibuf *buf, u_int8_t aid) } int -session_capa_add_gr(struct peer *p, struct ibuf *b, u_int8_t aid) +session_capa_add_afi(struct peer *p, struct ibuf *b, u_int8_t aid, + u_int8_t flags) { u_int errs = 0; u_int16_t afi; - u_int8_t flags, safi; + u_int8_t safi; if (aid2afi(aid, &afi, &safi)) { - log_warn("session_capa_add_gr: bad AID"); + log_warn("session_capa_add_afi: bad AID"); return (1); } - if (p->capa.neg.grestart.flags[aid] & CAPA_GR_RESTARTING) - flags = CAPA_GR_F_FLAG; - else - flags = 0; afi = htons(afi); errs += ibuf_add(b, &afi, sizeof(afi)); @@ -1459,7 +1456,7 @@ session_open(struct peer *p) errs += ibuf_add(opb, &hdr, sizeof(hdr)); } - /* 4-bytes AS numbers, draft-ietf-idr-as4bytes-13 */ + /* 4-bytes AS numbers, RFC6793 */ if (p->capa.ann.as4byte) { /* 4 bytes data */ u_int32_t nas; @@ -1468,6 +1465,32 @@ session_open(struct peer *p) errs += ibuf_add(opb, &nas, sizeof(nas)); } + /* advertisement of multiple paths, RFC7911 */ + if (p->capa.ann.add_path[0]) { /* variable */ + u_int8_t aplen; + + if (mpcapa) + aplen = 2 + 4 * mpcapa; + else /* AID_INET */ + aplen = 2 + 4; + errs += session_capa_add(opb, CAPA_ADD_PATH, aplen); + if (mpcapa) { + for (i = AID_MIN; i < AID_MAX; i++) { + if (p->capa.ann.mp[i]) { + errs += session_capa_add_afi(p, opb, + i, p->capa.ann.add_path[i]); + } + } + } else { /* AID_INET */ + errs += session_capa_add_afi(p, opb, AID_INET, + p->capa.ann.add_path[AID_INET]); + } + } + + /* enhanced route-refresh, RFC6793 */ + if (p->capa.ann.enhanced_rr) /* no data */ + errs += session_capa_add(opb, CAPA_ENHANCED_RR, 0); + if (ibuf_size(opb)) optparamlen = ibuf_size(opb) + sizeof(op_type) + sizeof(optparamlen); @@ -1581,6 +1604,13 @@ session_notification(struct peer *p, u_int8_t errcode, u_int8_t subcode, log_notification(p, errcode, subcode, data, datalen, "sending"); + /* cap to maximum size */ + if (datalen > MAX_PKTSIZE - MSGSIZE_NOTIFICATION_MIN) { + log_peer_warnx(&p->conf, + "oversized notification, data trunkated"); + datalen = MAX_PKTSIZE - MSGSIZE_NOTIFICATION_MIN; + } + if ((buf = session_newmsg(NOTIFICATION, MSGSIZE_NOTIFICATION_MIN + datalen)) == NULL) { bgp_fsm(p, EVNT_CON_FATAL); @@ -1615,24 +1645,42 @@ session_neighbor_rrefresh(struct peer *p) { u_int8_t i; - if (!p->capa.neg.refresh) + if (!p->capa.neg.refresh && !p->capa.peer.enhanced_rr) return (-1); for (i = 0; i < AID_MAX; i++) { if (p->capa.peer.mp[i] != 0) - session_rrefresh(p, i); + session_rrefresh(p, i, ROUTE_REFRESH_REQUEST); } return (0); } void -session_rrefresh(struct peer *p, u_int8_t aid) +session_rrefresh(struct peer *p, u_int8_t aid, u_int8_t subtype) { struct bgp_msg *buf; int errs = 0; u_int16_t afi; - u_int8_t safi, null8 = 0; + u_int8_t safi; + + switch (subtype) { + case ROUTE_REFRESH_REQUEST: + p->stats.refresh_sent_req++; + break; + case ROUTE_REFRESH_BEGIN_RR: + case ROUTE_REFRESH_END_RR: + /* requires enhanced route refresh */ + if (!p->capa.neg.enhanced_rr) + return; + if (subtype == ROUTE_REFRESH_BEGIN_RR) + p->stats.refresh_sent_borr++; + else + p->stats.refresh_sent_eorr++; + break; + default: + fatalx("session_rrefresh: bad subtype %d", subtype); + } if (aid2afi(aid, &afi, &safi) == -1) fatalx("session_rrefresh: bad afi/safi pair"); @@ -1644,7 +1692,7 @@ session_rrefresh(struct peer *p, u_int8_t aid) afi = htons(afi); errs += ibuf_add(buf->buf, &afi, sizeof(afi)); - errs += ibuf_add(buf->buf, &null8, sizeof(null8)); + errs += ibuf_add(buf->buf, &subtype, sizeof(subtype)); errs += ibuf_add(buf->buf, &safi, sizeof(safi)); if (errs) { @@ -1865,7 +1913,7 @@ session_process_msg(struct peer *p) p->stats.msg_rcvd_keepalive++; break; case RREFRESH: - parse_refresh(p); + parse_rrefresh(p); p->stats.msg_rcvd_rrefresh++; break; default: /* cannot happen */ @@ -1966,7 +2014,7 @@ parse_header(struct peer *peer, u_char *data, u_int16_t *len, u_int8_t *type) } break; case RREFRESH: - if (*len != MSGSIZE_RREFRESH) { + if (*len < MSGSIZE_RREFRESH_MIN) { log_peer_warnx(&peer->conf, "received RREFRESH: illegal len: %u byte", *len); session_notification(peer, ERR_HEADER, ERR_HDR_LEN, @@ -2206,11 +2254,16 @@ parse_update(struct peer *peer) } int -parse_refresh(struct peer *peer) +parse_rrefresh(struct peer *peer) { - u_char *p; - u_int16_t afi; - u_int8_t aid, safi; + u_int16_t afi, datalen; + u_int8_t aid, safi, subtype; + u_char *p; + + p = peer->rbuf->rptr; + p += MSGSIZE_HEADER_MARKER; + memcpy(&datalen, p, sizeof(datalen)); + datalen = ntohs(datalen); p = peer->rbuf->rptr; p += MSGSIZE_HEADER; /* header is already checked */ @@ -2224,10 +2277,59 @@ parse_refresh(struct peer *peer) memcpy(&afi, p, sizeof(afi)); afi = ntohs(afi); p += 2; - /* reserved, 1 byte */ + /* subtype, 1 byte */ + subtype = *p; p += 1; /* safi, 1 byte */ - memcpy(&safi, p, sizeof(safi)); + safi = *p; + + /* check subtype if peer announced enhanced route refresh */ + if (peer->capa.neg.enhanced_rr) { + switch (subtype) { + case ROUTE_REFRESH_REQUEST: + /* no ORF support, so no oversized RREFRESH msgs */ + if (datalen != MSGSIZE_RREFRESH) { + log_peer_warnx(&peer->conf, + "received RREFRESH: illegal len: %u byte", + datalen); + datalen = htons(datalen); + session_notification(peer, ERR_HEADER, + ERR_HDR_LEN, &datalen, sizeof(datalen)); + bgp_fsm(peer, EVNT_CON_FATAL); + return (-1); + } + peer->stats.refresh_rcvd_req++; + break; + case ROUTE_REFRESH_BEGIN_RR: + case ROUTE_REFRESH_END_RR: + /* special handling for RFC7313 */ + if (datalen != MSGSIZE_RREFRESH) { + log_peer_warnx(&peer->conf, + "received RREFRESH: illegal len: %u byte", + datalen); + p = peer->rbuf->rptr; + p += MSGSIZE_HEADER; + datalen -= MSGSIZE_HEADER; + session_notification(peer, ERR_RREFRESH, + ERR_RR_INV_LEN, p, datalen); + bgp_fsm(peer, EVNT_CON_FATAL); + return (-1); + } + if (subtype == ROUTE_REFRESH_BEGIN_RR) + peer->stats.refresh_rcvd_borr++; + else + peer->stats.refresh_rcvd_eorr++; + break; + default: + log_peer_warnx(&peer->conf, "peer sent bad refresh, " + "bad subtype %d", subtype); + return (0); + } + } else { + /* force subtype to default */ + subtype = ROUTE_REFRESH_REQUEST; + peer->stats.refresh_rcvd_req++; + } /* afi/safi unchecked - unrecognized values will be ignored anyway */ if (afi2aid(afi, safi, &aid) == -1) { @@ -2236,7 +2338,7 @@ parse_refresh(struct peer *peer) return (0); } - if (!peer->capa.neg.refresh) { + if (!peer->capa.neg.refresh && !peer->capa.neg.enhanced_rr) { log_peer_warnx(&peer->conf, "peer sent unexpected refresh"); return (0); } @@ -2334,6 +2436,18 @@ parse_notification(struct peer *peer) log_peer_warnx(&peer->conf, "disabling 4-byte AS num capability"); break; + case CAPA_ADD_PATH: + memset(peer->capa.ann.add_path, 0, + sizeof(peer->capa.ann.add_path)); + log_peer_warnx(&peer->conf, + "disabling ADD-PATH capability"); + break; + case CAPA_ENHANCED_RR: + peer->capa.ann.enhanced_rr = 0; + log_peer_warnx(&peer->conf, + "disabling enhanced route refresh " + "capability"); + break; default: /* should not happen... */ log_peer_warnx(&peer->conf, "received " "\"unsupported capability\" notification " @@ -2392,7 +2506,7 @@ parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen, u_int32_t *as) u_int16_t gr_header; u_int8_t safi; u_int8_t aid; - u_int8_t gr_flags; + u_int8_t flags; u_int8_t capa_code; u_int8_t capa_len; u_int8_t i; @@ -2476,7 +2590,8 @@ parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen, u_int32_t *as) for (i = 2; i <= capa_len - 4; i += 4) { memcpy(&afi, capa_val + i, sizeof(afi)); afi = ntohs(afi); - memcpy(&safi, capa_val + i + 2, sizeof(safi)); + safi = capa_val[i + 2]; + flags = capa_val[i + 3]; if (afi2aid(afi, safi, &aid) == -1) { log_peer_warnx(&peer->conf, "Received graceful restart capa: " @@ -2484,11 +2599,9 @@ parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen, u_int32_t *as) afi, safi); continue; } - memcpy(&gr_flags, capa_val + i + 3, - sizeof(gr_flags)); peer->capa.peer.grestart.flags[aid] |= CAPA_GR_PRESENT; - if (gr_flags & CAPA_GR_F_FLAG) + if (flags & CAPA_GR_F_FLAG) peer->capa.peer.grestart.flags[aid] |= CAPA_GR_FORWARD; if (gr_header & CAPA_GR_R_FLAG) @@ -2517,6 +2630,43 @@ parse_capabilities(struct peer *peer, u_char *d, u_int16_t dlen, u_int32_t *as) } peer->capa.peer.as4byte = 1; break; + case CAPA_ADD_PATH: + if (capa_len % 4 != 0) { + log_peer_warnx(&peer->conf, + "Bad ADD-PATH capability length: " + "%u", capa_len); + memset(peer->capa.peer.add_path, 0, + sizeof(peer->capa.peer.add_path)); + break; + } + for (i = 0; i <= capa_len - 4; i += 4) { + memcpy(&afi, capa_val + i, sizeof(afi)); + afi = ntohs(afi); + safi = capa_val[i + 2]; + flags = capa_val[i + 3]; + if (afi2aid(afi, safi, &aid) == -1) { + log_peer_warnx(&peer->conf, + "Received ADD-PATH capa: " + " unknown AFI %u, safi %u pair", + afi, safi); + memset(peer->capa.peer.add_path, 0, + sizeof(peer->capa.peer.add_path)); + break; + } + if (flags & ~CAPA_AP_BIDIR) { + log_peer_warnx(&peer->conf, + "Received ADD-PATH capa: " + " bad flags %x", flags); + memset(peer->capa.peer.add_path, 0, + sizeof(peer->capa.peer.add_path)); + break; + } + peer->capa.peer.add_path[aid] = flags; + } + break; + case CAPA_ENHANCED_RR: + peer->capa.peer.enhanced_rr = 1; + break; default: break; } @@ -2534,6 +2684,8 @@ capa_neg_calc(struct peer *p) p->capa.neg.refresh = (p->capa.ann.refresh && p->capa.peer.refresh) != 0; + p->capa.neg.enhanced_rr = + (p->capa.ann.enhanced_rr && p->capa.peer.enhanced_rr) != 0; p->capa.neg.as4byte = (p->capa.ann.as4byte && p->capa.peer.as4byte) != 0; @@ -2589,6 +2741,25 @@ capa_neg_calc(struct peer *p) if (p->capa.ann.grestart.restart == 0) p->capa.neg.grestart.restart = 0; + + /* + * ADD-PATH: set only those bits where both sides agree. + * For this compare our send bit with the recv bit from the peer + * and vice versa. + * The flags are stored from this systems view point. + */ + memset(p->capa.neg.add_path, 0, sizeof(p->capa.neg.add_path)); + if (p->capa.ann.add_path[0]) { + for (i = AID_MIN; i < AID_MAX; i++) { + if ((p->capa.ann.add_path[i] & CAPA_AP_RECV) && + (p->capa.peer.add_path[i] & CAPA_AP_SEND)) + p->capa.neg.add_path[i] |= CAPA_AP_RECV; + if ((p->capa.ann.add_path[i] & CAPA_AP_SEND) && + (p->capa.peer.add_path[i] & CAPA_AP_RECV)) + p->capa.neg.add_path[i] |= CAPA_AP_SEND; + } + } + return (0); } diff --git a/usr.sbin/bgpd/session.h b/usr.sbin/bgpd/session.h index ddf1caf4ab4..073759d26f3 100644 --- a/usr.sbin/bgpd/session.h +++ b/usr.sbin/bgpd/session.h @@ -1,4 +1,4 @@ -/* $OpenBSD: session.h,v 1.150 2021/02/16 08:29:16 claudio Exp $ */ +/* $OpenBSD: session.h,v 1.151 2021/05/27 08:27:48 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -34,7 +34,8 @@ #define MSGSIZE_OPEN_MIN 29 #define MSGSIZE_UPDATE_MIN 23 #define MSGSIZE_KEEPALIVE MSGSIZE_HEADER -#define MSGSIZE_RREFRESH MSGSIZE_HEADER + 4 +#define MSGSIZE_RREFRESH (MSGSIZE_HEADER + 4) +#define MSGSIZE_RREFRESH_MIN MSGSIZE_RREFRESH #define MSG_PROCESS_LIMIT 25 #define SESSION_CLEAR_DELAY 5 @@ -106,11 +107,13 @@ enum opt_params { }; enum capa_codes { - CAPA_NONE, - CAPA_MP, - CAPA_REFRESH, + CAPA_NONE = 0, + CAPA_MP = 1, + CAPA_REFRESH = 2, CAPA_RESTART = 64, - CAPA_AS4BYTE = 65 + CAPA_AS4BYTE = 65, + CAPA_ADD_PATH = 69, + CAPA_ENHANCED_RR = 70, }; struct bgp_msg { @@ -158,6 +161,12 @@ struct peer_stats { unsigned long long msg_sent_notification; unsigned long long msg_sent_keepalive; unsigned long long msg_sent_rrefresh; + unsigned long long refresh_rcvd_req; + unsigned long long refresh_rcvd_borr; + unsigned long long refresh_rcvd_eorr; + unsigned long long refresh_sent_req; + unsigned long long refresh_sent_borr; + unsigned long long refresh_sent_eorr; unsigned long long prefix_rcvd_update; unsigned long long prefix_rcvd_withdraw; unsigned long long prefix_rcvd_eor; -- 2.20.1