From: claudio Date: Mon, 9 Aug 2021 08:15:34 +0000 (+0000) Subject: Implement reception of multiple paths per BGP session. This is one X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=29b527fbceb4acb28e73921375a1dc40b0b8ad97;p=openbsd Implement reception of multiple paths per BGP session. This is one side of RFC7911 and the send portion will follow. The path-id is extracted from the NLRI encoding an put into struct prefix. To do this the prefix_by_peer() function gets a path-id argument. If a session is not path-id enabled this argument will be always 0. If a session is path-id enabled the value is taken from the NLRI and can be anything, including 0. The value has no meaning in itself. Still to make sure the decision process is able to break a tie the path-id is checked as the last step (this is not part of the RFC but required). OK benno@ --- diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5 index 65672160ebd..6ee7bc244b3 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.212 2021/07/13 08:44:18 claudio Exp $ +.\" $OpenBSD: bgpd.conf.5,v 1.213 2021/08/09 08:15:34 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: July 13 2021 $ +.Dd $Mdocdate: August 9 2021 $ .Dt BGPD.CONF 5 .Os .Sh NAME @@ -809,6 +809,17 @@ The default is for the same address family of the session. .Pp .It Xo +.Ic announce add-path recv +.Pq Ic yes Ns | Ns Ic no +.Xc +If set to +.Ic yes , +the receive add-path capability is announced which allows reception of multiple +paths per prefix. +The default is +.Ic no . +.Pp +.It Xo .Ic announce as-4byte .Pq Ic yes Ns | Ns Ic no .Xc diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index 4c0952d5829..e897e077c36 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.416 2021/07/27 07:32:08 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.417 2021/08/09 08:15:34 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -95,6 +95,7 @@ #define F_CTL_OVS_INVALID 0x100000 #define F_CTL_OVS_NOTFOUND 0x200000 #define F_CTL_NEIGHBORS 0x400000 /* only used by bgpctl */ +#define F_CTL_HAS_PATHID 0x800000 /* only set on requests */ #define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \ __attribute__((__unused__)) @@ -891,9 +892,10 @@ struct ctl_show_rib_request { struct filter_as as; struct community community; u_int32_t flags; - u_int8_t validation_state; + u_int32_t path_id; pid_t pid; enum imsg_type type; + u_int8_t validation_state; u_int8_t prefixlen; u_int8_t aid; }; diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 82ea2289875..37f1862c922 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.417 2021/06/17 16:05:26 claudio Exp $ */ +/* $OpenBSD: parse.y,v 1.418 2021/08/09 08:15:34 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -204,7 +204,8 @@ typedef struct { %token GROUP NEIGHBOR NETWORK %token EBGP IBGP %token LOCALAS REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART -%token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY ENHANCED +%token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY ENHANCED ADDPATH +%token SEND RECV %token DEMOTE ENFORCE NEIGHBORAS ASOVERRIDE REFLECTOR DEPEND DOWN %token DUMP IN OUT SOCKET RESTRICTED %token LOG TRANSPARENT @@ -1455,6 +1456,16 @@ peeropts : REMOTEAS as4number { | ANNOUNCE AS4BYTE yesno { curpeer->conf.capabilities.as4byte = $3; } + | ANNOUNCE ADDPATH RECV yesno { + int8_t *ap = curpeer->conf.capabilities.add_path; + u_int8_t i; + + for (i = 0; i < AID_MAX; i++) + if ($4) + *ap++ |= CAPA_AP_RECV; + else + *ap++ &= ~CAPA_AP_RECV; + } | EXPORT NONE { curpeer->conf.export_type = EXPORT_NONE; } @@ -2878,6 +2889,7 @@ lookup(char *s) { "AS", AS}, { "IPv4", IPV4}, { "IPv6", IPV6}, + { "add-path", ADDPATH}, { "ah", AH}, { "allow", ALLOW}, { "announce", ANNOUNCE}, @@ -2965,6 +2977,7 @@ lookup(char *s) { "quick", QUICK}, { "rd", RD}, { "rde", RDE}, + { "recv", RECV}, { "refresh", REFRESH }, { "reject", REJECT}, { "remote-as", REMOTEAS}, @@ -2978,6 +2991,7 @@ lookup(char *s) { "rtlabel", RTLABEL}, { "rtr", RTR}, { "self", SELF}, + { "send", SEND}, { "set", SET}, { "socket", SOCKET }, { "source-as", SOURCEAS}, diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 24dc6716f65..65696e253e2 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.531 2021/07/27 07:50:01 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.532 2021/08/09 08:15:34 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -50,10 +50,10 @@ void rde_dispatch_imsg_parent(struct imsgbuf *); void rde_dispatch_imsg_rtr(struct imsgbuf *); void rde_dispatch_imsg_peer(struct rde_peer *, void *); void rde_update_dispatch(struct rde_peer *, struct imsg *); -int rde_update_update(struct rde_peer *, struct filterstate *, +int rde_update_update(struct rde_peer *, u_int32_t, + struct filterstate *, struct bgpd_addr *, u_int8_t); +void rde_update_withdraw(struct rde_peer *, u_int32_t, struct bgpd_addr *, u_int8_t); -void rde_update_withdraw(struct rde_peer *, struct bgpd_addr *, - u_int8_t); int rde_attr_parse(u_char *, u_int16_t, struct rde_peer *, struct filterstate *, struct mpattr *); int rde_attr_add(struct filterstate *, u_char *, u_int16_t); @@ -1183,7 +1183,7 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) u_int16_t attrpath_len; u_int16_t nlri_len; u_int8_t aid, prefixlen, safi, subtype; - u_int32_t fas; + u_int32_t fas, pathid; p = imsg->data; @@ -1288,6 +1288,21 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) goto done; } + if (peer_has_add_path(peer, AID_INET, CAPA_AP_RECV)) { + if (len <= sizeof(pathid)) { + log_peer_warnx(&peer->conf, + "bad withdraw prefix"); + rde_update_err(peer, ERR_UPDATE, + ERR_UPD_NETWORK, NULL, 0); + goto done; + } + memcpy(&pathid, p, sizeof(pathid)); + pathid = ntohl(pathid); + p += sizeof(pathid); + len -= sizeof(pathid); + } else + pathid = 0; + if ((pos = nlri_get_prefix(p, len, &prefix, &prefixlen)) == -1) { /* @@ -1302,7 +1317,7 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) p += pos; len -= pos; - rde_update_withdraw(peer, &prefix, prefixlen); + rde_update_withdraw(peer, pathid, &prefix, prefixlen); } /* withdraw MP_UNREACH_NLRI if available */ @@ -1339,6 +1354,23 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) } while (mplen > 0) { + if (peer_has_add_path(peer, aid, CAPA_AP_RECV)) { + if (mplen <= sizeof(pathid)) { + log_peer_warnx(&peer->conf, + "bad %s withdraw prefix", + aid2str(aid)); + rde_update_err(peer, ERR_UPDATE, + ERR_UPD_OPTATTR, + mpa.unreach, mpa.unreach_len); + goto done; + } + memcpy(&pathid, mpp, sizeof(pathid)); + pathid = ntohl(pathid); + mpp += sizeof(pathid); + mplen -= sizeof(pathid); + } else + pathid = 0; + switch (aid) { case AID_INET6: if ((pos = nlri_get_prefix6(mpp, mplen, @@ -1381,7 +1413,7 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) mpp += pos; mplen -= pos; - rde_update_withdraw(peer, &prefix, prefixlen); + rde_update_withdraw(peer, pathid, &prefix, prefixlen); } if ((state.aspath.flags & ~F_ATTR_MP_UNREACH) == 0) @@ -1401,6 +1433,21 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) goto done; } + if (peer_has_add_path(peer, AID_INET, CAPA_AP_RECV)) { + if (nlri_len <= sizeof(pathid)) { + log_peer_warnx(&peer->conf, + "bad nlri prefix"); + rde_update_err(peer, ERR_UPDATE, + ERR_UPD_NETWORK, NULL, 0); + goto done; + } + memcpy(&pathid, p, sizeof(pathid)); + pathid = ntohl(pathid); + p += sizeof(pathid); + nlri_len -= sizeof(pathid); + } else + pathid = 0; + if ((pos = nlri_get_prefix(p, nlri_len, &prefix, &prefixlen)) == -1) { log_peer_warnx(&peer->conf, "bad nlri prefix"); @@ -1411,7 +1458,8 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) p += pos; nlri_len -= pos; - if (rde_update_update(peer, &state, &prefix, prefixlen) == -1) + if (rde_update_update(peer, pathid, &state, + &prefix, prefixlen) == -1) goto done; } @@ -1456,6 +1504,22 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) mplen -= pos; while (mplen > 0) { + if (peer_has_add_path(peer, aid, CAPA_AP_RECV)) { + if (mplen <= sizeof(pathid)) { + log_peer_warnx(&peer->conf, + "bad %s nlri prefix", aid2str(aid)); + rde_update_err(peer, ERR_UPDATE, + ERR_UPD_OPTATTR, + mpa.reach, mpa.reach_len); + goto done; + } + memcpy(&pathid, mpp, sizeof(pathid)); + pathid = ntohl(pathid); + mpp += sizeof(pathid); + mplen -= sizeof(pathid); + } else + pathid = 0; + switch (aid) { case AID_INET6: if ((pos = nlri_get_prefix6(mpp, mplen, @@ -1498,7 +1562,7 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) mpp += pos; mplen -= pos; - if (rde_update_update(peer, &state, + if (rde_update_update(peer, pathid, &state, &prefix, prefixlen) == -1) goto done; } @@ -1509,8 +1573,8 @@ done: } int -rde_update_update(struct rde_peer *peer, struct filterstate *in, - struct bgpd_addr *prefix, u_int8_t prefixlen) +rde_update_update(struct rde_peer *peer, u_int32_t path_id, + struct filterstate *in, struct bgpd_addr *prefix, u_int8_t prefixlen) { struct filterstate state; enum filter_actions action; @@ -1523,8 +1587,8 @@ rde_update_update(struct rde_peer *peer, struct filterstate *in, aspath_origin(in->aspath.aspath)); /* add original path to the Adj-RIB-In */ - if (prefix_update(rib_byid(RIB_ADJ_IN), peer, in, prefix, prefixlen, - vstate) == 1) + if (prefix_update(rib_byid(RIB_ADJ_IN), peer, path_id, in, + prefix, prefixlen, vstate) == 1) peer->prefix_cnt++; /* max prefix checker */ @@ -1552,9 +1616,9 @@ rde_update_update(struct rde_peer *peer, struct filterstate *in, rde_update_log("update", i, peer, &state.nexthop->exit_nexthop, prefix, prefixlen); - prefix_update(rib, peer, &state, prefix, + prefix_update(rib, peer, path_id, &state, prefix, prefixlen, vstate); - } else if (prefix_withdraw(rib, peer, prefix, + } else if (prefix_withdraw(rib, peer, path_id, prefix, prefixlen)) { rde_update_log(wmsg, i, peer, NULL, prefix, prefixlen); @@ -1567,8 +1631,8 @@ rde_update_update(struct rde_peer *peer, struct filterstate *in, } void -rde_update_withdraw(struct rde_peer *peer, struct bgpd_addr *prefix, - u_int8_t prefixlen) +rde_update_withdraw(struct rde_peer *peer, u_int32_t path_id, + struct bgpd_addr *prefix, u_int8_t prefixlen) { u_int16_t i; @@ -1576,13 +1640,14 @@ rde_update_withdraw(struct rde_peer *peer, struct bgpd_addr *prefix, struct rib *rib = rib_byid(i); if (rib == NULL) continue; - if (prefix_withdraw(rib, peer, prefix, prefixlen)) + if (prefix_withdraw(rib, peer, path_id, prefix, prefixlen)) rde_update_log("withdraw", i, peer, NULL, prefix, prefixlen); } /* remove original path form the Adj-RIB-In */ - if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peer, prefix, prefixlen)) + if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peer, path_id, + prefix, prefixlen)) peer->prefix_cnt--; peer->prefix_rcvd_withdraw++; @@ -2292,28 +2357,31 @@ rde_reflector(struct rde_peer *peer, struct rde_aspath *asp) * control specific functions */ static void -rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags) +rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags, + int adjout) { struct ctl_show_rib rib; struct ibuf *wbuf; struct attr *a; struct nexthop *nexthop; struct rib_entry *re; + struct rde_peer *peer; void *bp; time_t staletime; size_t aslen; u_int8_t l; nexthop = prefix_nexthop(p); + peer = prefix_peer(p); bzero(&rib, sizeof(rib)); rib.age = getmonotime() - p->lastchange; rib.local_pref = asp->lpref; rib.med = asp->med; rib.weight = asp->weight; - strlcpy(rib.descr, prefix_peer(p)->conf.descr, sizeof(rib.descr)); - memcpy(&rib.remote_addr, &prefix_peer(p)->remote_addr, + strlcpy(rib.descr, peer->conf.descr, sizeof(rib.descr)); + memcpy(&rib.remote_addr, &peer->remote_addr, sizeof(rib.remote_addr)); - rib.remote_id = prefix_peer(p)->remote_bgpid; + rib.remote_id = peer->remote_bgpid; if (nexthop != NULL) { memcpy(&rib.true_nexthop, &nexthop->true_nexthop, sizeof(rib.true_nexthop)); @@ -2334,7 +2402,7 @@ rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags) re = prefix_re(p); if (re != NULL && re->active == p) rib.flags |= F_PREF_ACTIVE; - if (!prefix_peer(p)->conf.ebgp) + if (!peer->conf.ebgp) rib.flags |= F_PREF_INTERNAL; if (asp->flags & F_PREFIX_ANNOUNCED) rib.flags |= F_PREF_ANNOUNCE; @@ -2344,9 +2412,20 @@ rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags) rib.flags &= ~F_PREF_ELIGIBLE; if (asp->flags & F_ATTR_PARSE_ERR) rib.flags |= F_PREF_INVALID; - staletime = prefix_peer(p)->staletime[p->pt->aid]; + staletime = peer->staletime[p->pt->aid]; if (staletime && p->lastchange <= staletime) rib.flags |= F_PREF_STALE; + if (!adjout) { + if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_RECV)) { + rib.path_id = p->path_id; + rib.flags |= F_PREF_PATH_ID; + } + } else { + if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_SEND)) { + rib.path_id = 0; /* XXX add-path send */ + rib.flags |= F_PREF_PATH_ID; + } + } aslen = aspath_length(asp->aspath); if ((wbuf = imsg_create(ibuf_se_ctl, IMSG_CTL_SHOW_RIB, 0, pid, @@ -2411,7 +2490,7 @@ rde_match_peer(struct rde_peer *p, struct ctl_neighbor *n) } static void -rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req) +rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req, int adjout) { struct rde_aspath *asp; struct rib_entry *re; @@ -2428,6 +2507,12 @@ rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req) if ((req->flags & F_CTL_INVALID) && (asp->flags & F_ATTR_PARSE_ERR) == 0) return; + /* + * XXX handle out specially since then we want to match against our + * path ids. + */ + if ((req->flags & F_CTL_HAS_PATHID) && req->path_id != p->path_id) + return; if (req->as.type != AS_UNDEF && !aspath_match(asp->aspath, &req->as, 0)) return; @@ -2438,7 +2523,7 @@ rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req) } if (!ovs_match(p, req->flags)) return; - rde_dump_rib_as(p, asp, req->pid, req->flags); + rde_dump_rib_as(p, asp, req->pid, req->flags, adjout); } static void @@ -2448,7 +2533,7 @@ rde_dump_upcall(struct rib_entry *re, void *ptr) struct prefix *p; LIST_FOREACH(p, &re->prefix_h, entry.list.rib) - rde_dump_filter(p, &ctx->req); + rde_dump_filter(p, &ctx->req, 0); } static void @@ -2469,14 +2554,14 @@ rde_dump_prefix_upcall(struct rib_entry *re, void *ptr) if (!prefix_compare(&ctx->req.prefix, &addr, ctx->req.prefixlen)) LIST_FOREACH(p, &re->prefix_h, entry.list.rib) - rde_dump_filter(p, &ctx->req); + rde_dump_filter(p, &ctx->req, 0); } else { if (ctx->req.prefixlen < pt->prefixlen) return; if (!prefix_compare(&addr, &ctx->req.prefix, pt->prefixlen)) LIST_FOREACH(p, &re->prefix_h, entry.list.rib) - rde_dump_filter(p, &ctx->req); + rde_dump_filter(p, &ctx->req, 0); } } @@ -2487,7 +2572,7 @@ rde_dump_adjout_upcall(struct prefix *p, void *ptr) if (p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD)) return; - rde_dump_filter(p, &ctx->req); + rde_dump_filter(p, &ctx->req, 1); } static void @@ -2507,13 +2592,13 @@ rde_dump_adjout_prefix_upcall(struct prefix *p, void *ptr) return; if (!prefix_compare(&ctx->req.prefix, &addr, ctx->req.prefixlen)) - rde_dump_filter(p, &ctx->req); + rde_dump_filter(p, &ctx->req, 1); } else { if (ctx->req.prefixlen < p->pt->prefixlen) return; if (!prefix_compare(&addr, &ctx->req.prefix, p->pt->prefixlen)) - rde_dump_filter(p, &ctx->req); + rde_dump_filter(p, &ctx->req, 1); } } @@ -3580,11 +3665,12 @@ rde_softreconfig_in(struct rib_entry *re, void *bula) if (action == ACTION_ALLOW) { /* update Local-RIB */ - prefix_update(rib, peer, &state, &prefix, - pt->prefixlen, p->validation_state); + prefix_update(rib, peer, p->path_id, &state, + &prefix, pt->prefixlen, + p->validation_state); } else if (action == ACTION_DENY) { /* remove from Local-RIB */ - prefix_withdraw(rib, peer, &prefix, + prefix_withdraw(rib, peer, p->path_id, &prefix, pt->prefixlen); } @@ -3724,11 +3810,12 @@ rde_roa_softreload(struct rib_entry *re, void *bula) if (action == ACTION_ALLOW) { /* update Local-RIB */ - prefix_update(rib, peer, &state, &prefix, - pt->prefixlen, p->validation_state); + prefix_update(rib, peer, p->path_id, &state, + &prefix, pt->prefixlen, + p->validation_state); } else if (action == ACTION_DENY) { /* remove from Local-RIB */ - prefix_withdraw(rib, peer, &prefix, + prefix_withdraw(rib, peer, p->path_id, &prefix, pt->prefixlen); } @@ -3952,7 +4039,7 @@ network_add(struct network_config *nc, struct filterstate *state) vstate = rde_roa_validity(&rde_roa, &nc->prefix, nc->prefixlen, aspath_origin(state->aspath.aspath)); - if (prefix_update(rib_byid(RIB_ADJ_IN), peerself, state, &nc->prefix, + if (prefix_update(rib_byid(RIB_ADJ_IN), peerself, 0, state, &nc->prefix, nc->prefixlen, vstate) == 1) peerself->prefix_cnt++; for (i = RIB_LOC_START; i < rib_size; i++) { @@ -3962,7 +4049,7 @@ network_add(struct network_config *nc, struct filterstate *state) rde_update_log("announce", i, peerself, state->nexthop ? &state->nexthop->exit_nexthop : NULL, &nc->prefix, nc->prefixlen); - prefix_update(rib, peerself, state, &nc->prefix, + prefix_update(rib, peerself, 0, state, &nc->prefix, nc->prefixlen, vstate); } filterset_free(&nc->attrset); @@ -4022,12 +4109,12 @@ network_delete(struct network_config *nc) struct rib *rib = rib_byid(i); if (rib == NULL) continue; - if (prefix_withdraw(rib, peerself, &nc->prefix, + if (prefix_withdraw(rib, peerself, 0, &nc->prefix, nc->prefixlen)) rde_update_log("withdraw announce", i, peerself, NULL, &nc->prefix, nc->prefixlen); } - if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peerself, &nc->prefix, + if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peerself, 0, &nc->prefix, nc->prefixlen)) peerself->prefix_cnt--; } @@ -4074,7 +4161,7 @@ network_flush_upcall(struct rib_entry *re, void *ptr) u_int32_t i; u_int8_t prefixlen; - p = prefix_bypeer(re, peerself); + p = prefix_bypeer(re, peerself, 0); if (p == NULL) return; if ((prefix_aspath(p)->flags & F_ANN_DYNAMIC) != F_ANN_DYNAMIC) @@ -4087,12 +4174,12 @@ network_flush_upcall(struct rib_entry *re, void *ptr) struct rib *rib = rib_byid(i); if (rib == NULL) continue; - if (prefix_withdraw(rib, peerself, &addr, prefixlen) == 1) + if (prefix_withdraw(rib, peerself, 0, &addr, prefixlen) == 1) rde_update_log("flush announce", i, peerself, NULL, &addr, prefixlen); } - if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peerself, &addr, + if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peerself, 0, &addr, prefixlen) == 1) peerself->prefix_cnt--; } diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index 6088026030f..54f718d2b73 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.241 2021/07/27 07:50:02 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.242 2021/08/09 08:15:34 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker and @@ -329,6 +329,8 @@ struct prefix { struct rde_peer *peer; struct nexthop *nexthop; /* may be NULL */ time_t lastchange; + u_int32_t path_id; + u_int32_t path_id_tx; u_int8_t validation_state; u_int8_t nhflags; u_int8_t eor; @@ -386,6 +388,7 @@ int rde_match_peer(struct rde_peer *, struct ctl_neighbor *); /* rde_peer.c */ int peer_has_as4byte(struct rde_peer *); +int peer_has_add_path(struct rde_peer *, u_int8_t, int); int peer_accept_no_as_set(struct rde_peer *); void peer_init(u_int32_t); void peer_shutdown(void); @@ -577,13 +580,13 @@ void path_clean(struct rde_aspath *); void path_put(struct rde_aspath *); #define PREFIX_SIZE(x) (((x) + 7) / 8 + 1) -struct prefix *prefix_get(struct rib *, struct rde_peer *, +struct prefix *prefix_get(struct rib *, struct rde_peer *, u_int32_t, struct bgpd_addr *, int); struct prefix *prefix_lookup(struct rde_peer *, struct bgpd_addr *, int); struct prefix *prefix_match(struct rde_peer *, struct bgpd_addr *); -int prefix_update(struct rib *, struct rde_peer *, +int prefix_update(struct rib *, struct rde_peer *, u_int32_t, struct filterstate *, struct bgpd_addr *, int, u_int8_t); -int prefix_withdraw(struct rib *, struct rde_peer *, +int prefix_withdraw(struct rib *, struct rde_peer *, u_int32_t, struct bgpd_addr *, int); void prefix_add_eor(struct rde_peer *, u_int8_t); int prefix_adjout_update(struct rde_peer *, struct filterstate *, @@ -598,7 +601,8 @@ int prefix_dump_new(struct rde_peer *, u_int8_t, unsigned int, void (*)(void *, u_int8_t), int (*)(void *)); int prefix_write(u_char *, int, struct bgpd_addr *, u_int8_t, int); int prefix_writebuf(struct ibuf *, struct bgpd_addr *, u_int8_t); -struct prefix *prefix_bypeer(struct rib_entry *, struct rde_peer *); +struct prefix *prefix_bypeer(struct rib_entry *, struct rde_peer *, + u_int32_t); void prefix_destroy(struct prefix *); void prefix_relink(struct prefix *, struct rde_aspath *, int); diff --git a/usr.sbin/bgpd/rde_decide.c b/usr.sbin/bgpd/rde_decide.c index d071beb83bc..f77f615030a 100644 --- a/usr.sbin/bgpd/rde_decide.c +++ b/usr.sbin/bgpd/rde_decide.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_decide.c,v 1.85 2021/05/04 09:21:05 claudio Exp $ */ +/* $OpenBSD: rde_decide.c,v 1.86 2021/08/09 08:15:34 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker @@ -281,6 +281,13 @@ prefix_cmp(struct prefix *p1, struct prefix *p2, int *testall) if (i > 0) return -1; + /* XXX RFC7911 does not specify this but it is needed. */ + /* 13. lowest path identifier wins */ + if (p1->path_id < p2->path_id) + return 1; + if (p1->path_id > p2->path_id) + return -1; + fatalx("Uh, oh a politician in the decision process"); } diff --git a/usr.sbin/bgpd/rde_peer.c b/usr.sbin/bgpd/rde_peer.c index 502826ddf4c..fafc1c8f6df 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.11 2021/06/17 16:05:26 claudio Exp $ */ +/* $OpenBSD: rde_peer.c,v 1.12 2021/08/09 08:15:35 claudio Exp $ */ /* * Copyright (c) 2019 Claudio Jeker @@ -53,6 +53,14 @@ peer_has_as4byte(struct rde_peer *peer) return (peer->capa.as4byte); } +int +peer_has_add_path(struct rde_peer *peer, u_int8_t aid, int mode) +{ + if (aid > AID_MAX) + return 0; + return (peer->capa.add_path[aid] & mode); +} + int peer_accept_no_as_set(struct rde_peer *peer) { @@ -250,7 +258,8 @@ peer_flush_upcall(struct rib_entry *re, void *arg) struct rib *rib = rib_byid(i); if (rib == NULL) continue; - rp = prefix_get(rib, peer, &addr, prefixlen); + rp = prefix_get(rib, peer, p->path_id, + &addr, prefixlen); if (rp) { asp = prefix_aspath(rp); if (asp && asp->pftableid) @@ -264,7 +273,6 @@ peer_flush_upcall(struct rib_entry *re, void *arg) prefix_destroy(p); peer->prefix_cnt--; - break; /* optimization, only one match per peer possible */ } } diff --git a/usr.sbin/bgpd/rde_rib.c b/usr.sbin/bgpd/rde_rib.c index dc852519549..9cca822a94f 100644 --- a/usr.sbin/bgpd/rde_rib.c +++ b/usr.sbin/bgpd/rde_rib.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_rib.c,v 1.223 2021/07/27 07:50:02 claudio Exp $ */ +/* $OpenBSD: rde_rib.c,v 1.224 2021/08/09 08:15:35 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker @@ -842,7 +842,7 @@ path_put(struct rde_aspath *asp) /* prefix specific functions */ static int prefix_add(struct bgpd_addr *, int, struct rib *, - struct rde_peer *, struct rde_aspath *, + struct rde_peer *, u_int32_t, struct rde_aspath *, struct rde_community *, struct nexthop *, u_int8_t, u_int8_t); static int prefix_move(struct prefix *, struct rde_peer *, @@ -850,7 +850,7 @@ static int prefix_move(struct prefix *, struct rde_peer *, struct nexthop *, u_int8_t, u_int8_t); static void prefix_link(struct prefix *, struct rib_entry *, - struct rde_peer *, struct rde_aspath *, + struct rde_peer *, u_int32_t, struct rde_aspath *, struct rde_community *, struct nexthop *, u_int8_t, u_int8_t); static void prefix_unlink(struct prefix *); @@ -876,12 +876,14 @@ prefix_cmp(struct prefix *a, struct prefix *b) return (a->nexthop > b->nexthop ? 1 : -1); if (a->nhflags != b->nhflags) return (a->nhflags > b->nhflags ? 1 : -1); + /* XXX path_id ??? */ return pt_prefix_cmp(a->pt, b->pt); } static inline int prefix_index_cmp(struct prefix *a, struct prefix *b) { + /* XXX path_id ??? */ return pt_prefix_cmp(a->pt, b->pt); } @@ -892,15 +894,15 @@ RB_GENERATE_STATIC(prefix_index, prefix, entry.tree.index, prefix_index_cmp) * search for specified prefix of a peer. Returns NULL if not found. */ struct prefix * -prefix_get(struct rib *rib, struct rde_peer *peer, struct bgpd_addr *prefix, - int prefixlen) +prefix_get(struct rib *rib, struct rde_peer *peer, u_int32_t path_id, + struct bgpd_addr *prefix, int prefixlen) { struct rib_entry *re; re = rib_get(rib, prefix, prefixlen); if (re == NULL) return (NULL); - return (prefix_bypeer(re, peer)); + return (prefix_bypeer(re, peer, path_id)); } /* @@ -954,8 +956,9 @@ prefix_match(struct rde_peer *peer, struct bgpd_addr *addr) * Return 1 if prefix was newly added, 0 if it was just changed. */ int -prefix_update(struct rib *rib, struct rde_peer *peer, struct filterstate *state, - struct bgpd_addr *prefix, int prefixlen, u_int8_t vstate) +prefix_update(struct rib *rib, struct rde_peer *peer, u_int32_t path_id, + struct filterstate *state, struct bgpd_addr *prefix, int prefixlen, + u_int8_t vstate) { struct rde_aspath *asp, *nasp = &state->aspath; struct rde_community *comm, *ncomm = &state->communities; @@ -964,7 +967,7 @@ prefix_update(struct rib *rib, struct rde_peer *peer, struct filterstate *state, /* * First try to find a prefix in the specified RIB. */ - if ((p = prefix_get(rib, peer, prefix, prefixlen)) != NULL) { + if ((p = prefix_get(rib, peer, path_id, prefix, prefixlen)) != NULL) { if (prefix_nexthop(p) == state->nexthop && prefix_nhflags(p) == state->nhflags && communities_equal(ncomm, prefix_communities(p)) && @@ -997,8 +1000,8 @@ prefix_update(struct rib *rib, struct rde_peer *peer, struct filterstate *state, return (prefix_move(p, peer, asp, comm, state->nexthop, state->nhflags, vstate)); else - return (prefix_add(prefix, prefixlen, rib, peer, asp, comm, - state->nexthop, state->nhflags, vstate)); + return (prefix_add(prefix, prefixlen, rib, peer, path_id, asp, + comm, state->nexthop, state->nhflags, vstate)); } /* @@ -1006,8 +1009,9 @@ prefix_update(struct rib *rib, struct rde_peer *peer, struct filterstate *state, */ static int prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib, - struct rde_peer *peer, struct rde_aspath *asp, struct rde_community *comm, - struct nexthop *nexthop, u_int8_t nhflags, u_int8_t vstate) + struct rde_peer *peer, u_int32_t path_id, struct rde_aspath *asp, + struct rde_community *comm, struct nexthop *nexthop, u_int8_t nhflags, + u_int8_t vstate) { struct prefix *p; struct rib_entry *re; @@ -1017,7 +1021,7 @@ prefix_add(struct bgpd_addr *prefix, int prefixlen, struct rib *rib, re = rib_add(rib, prefix, prefixlen); p = prefix_alloc(); - prefix_link(p, re, peer, asp, comm, nexthop, nhflags, vstate); + prefix_link(p, re, peer, path_id, asp, comm, nexthop, nhflags, vstate); return (1); } @@ -1045,6 +1049,7 @@ prefix_move(struct prefix *p, struct rde_peer *peer, np->peer = peer; np->entry.list.re = prefix_re(p); np->pt = p->pt; /* skip refcnt update since ref is moved */ + np->path_id = p->path_id; np->validation_state = vstate; np->nhflags = nhflags; np->nexthop = nexthop_ref(nexthop); @@ -1086,13 +1091,13 @@ prefix_move(struct prefix *p, struct rde_peer *peer, * or pt_entry -- become empty remove them too. */ int -prefix_withdraw(struct rib *rib, struct rde_peer *peer, +prefix_withdraw(struct rib *rib, struct rde_peer *peer, u_int32_t path_id, struct bgpd_addr *prefix, int prefixlen) { struct prefix *p; struct rde_aspath *asp; - p = prefix_get(rib, peer, prefix, prefixlen); + p = prefix_get(rib, peer, path_id, prefix, prefixlen); if (p == NULL) /* Got a dummy withdrawn request. */ return (0); @@ -1487,12 +1492,12 @@ prefix_writebuf(struct ibuf *buf, struct bgpd_addr *prefix, u_int8_t plen) * belonging to the peer peer. Returns NULL if no match found. */ struct prefix * -prefix_bypeer(struct rib_entry *re, struct rde_peer *peer) +prefix_bypeer(struct rib_entry *re, struct rde_peer *peer, u_int32_t path_id) { struct prefix *p; LIST_FOREACH(p, &re->prefix_h, entry.list.rib) - if (prefix_peer(p) == peer) + if (prefix_peer(p) == peer && p->path_id == path_id) return (p); return (NULL); } @@ -1544,7 +1549,7 @@ prefix_destroy(struct prefix *p) */ static void prefix_link(struct prefix *p, struct rib_entry *re, struct rde_peer *peer, - struct rde_aspath *asp, struct rde_community *comm, + u_int32_t path_id, struct rde_aspath *asp, struct rde_community *comm, struct nexthop *nexthop, u_int8_t nhflags, u_int8_t vstate) { if (p->flags & PREFIX_FLAG_ADJOUT) @@ -1555,6 +1560,7 @@ prefix_link(struct prefix *p, struct rib_entry *re, struct rde_peer *peer, p->communities = communities_ref(comm); p->peer = peer; p->pt = pt_ref(re->prefix); + p->path_id = path_id; p->validation_state = vstate; p->nhflags = nhflags; p->nexthop = nexthop_ref(nexthop); diff --git a/usr.sbin/bgpd/rde_update.c b/usr.sbin/bgpd/rde_update.c index 3296227bb39..3c4c8b04dec 100644 --- a/usr.sbin/bgpd/rde_update.c +++ b/usr.sbin/bgpd/rde_update.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_update.c,v 1.130 2021/06/17 08:14:50 claudio Exp $ */ +/* $OpenBSD: rde_update.c,v 1.131 2021/08/09 08:15:35 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker @@ -625,9 +625,18 @@ up_dump_prefix(u_char *buf, int len, struct prefix_tree *prefix_head, { struct prefix *p, *np; struct bgpd_addr addr; + u_int32_t pathid; int r, wpos = 0, done = 0; RB_FOREACH_SAFE(p, prefix_tree, prefix_head, np) { + if (peer_has_add_path(peer, p->pt->aid, CAPA_AP_SEND)) { + if (len <= wpos + (int)sizeof(pathid)) + break; + /* XXX add-path send side */ + pathid = 0; + memcpy(buf + wpos, &pathid, sizeof(pathid)); + wpos += sizeof(pathid); + } pt_getaddr(p->pt, &addr); if ((r = prefix_write(buf + wpos, len - wpos, &addr, p->pt->prefixlen, withdraw)) == -1)