diff options
-rw-r--r-- | usr.sbin/bgpd/bgpd.conf.5 | 15 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 6 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 18 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 179 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 14 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_decide.c | 9 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_peer.c | 14 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_rib.c | 44 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_update.c | 11 |
9 files changed, 229 insertions, 81 deletions
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 <claudio@openbsd.org> .\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -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 <henning@openbsd.org> @@ -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 <henning@openbsd.org> @@ -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 <henning@openbsd.org> @@ -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 <claudio@openbsd.org> 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 <claudio@openbsd.org> @@ -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 <claudio@openbsd.org> @@ -54,6 +54,14 @@ peer_has_as4byte(struct rde_peer *peer) } 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) { return (peer->flags & PEERFLAG_NO_AS_SET); @@ -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 <claudio@openbsd.org> @@ -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 <claudio@openbsd.org> @@ -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) |