From: claudio Date: Mon, 27 Jun 2022 13:26:51 +0000 (+0000) Subject: Add support for RFC 9234 - Route Leak Prevention and Detection Using Roles X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=1114d9c29bf857e7ee24290cd067ad5afc3af097;p=openbsd Add support for RFC 9234 - Route Leak Prevention and Detection Using Roles With this it is possible to send a role in the OPEN message and if that was successful the RDE will add the new OTC attribute if necessary. OK tb@ --- diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index db9f354627b..3d78b0abef3 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.437 2022/06/23 13:09:03 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.438 2022/06/27 13:26:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -371,9 +371,11 @@ struct capabilities { 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 */ + uint8_t role; /* Open Policy, RFC 9234 */ + int8_t role_ena; /* 1 for enable, 2 for enforce */ }; -/* flags for RFC4724 - graceful restart */ +/* flags for RFC 4724 - graceful restart */ #define CAPA_GR_PRESENT 0x01 #define CAPA_GR_RESTART 0x02 #define CAPA_GR_FORWARD 0x04 @@ -382,11 +384,18 @@ struct capabilities { #define CAPA_GR_R_FLAG 0x8000 #define CAPA_GR_F_FLAG 0x80 -/* flags for RFC7911 - enhanced router refresh */ +/* flags for RFC 7911 - enhanced router refresh */ #define CAPA_AP_RECV 0x01 #define CAPA_AP_SEND 0x02 #define CAPA_AP_BIDIR 0x03 +/* values for RFC 9234 - BGP Open Policy */ +#define CAPA_ROLE_PROVIDER 0x00 +#define CAPA_ROLE_RS 0x01 +#define CAPA_ROLE_RS_CLIENT 0x02 +#define CAPA_ROLE_CUSTOMER 0x03 +#define CAPA_ROLE_PEER 0x04 + struct peer_config { struct bgpd_addr remote_addr; struct bgpd_addr local_addr_v4; @@ -774,6 +783,7 @@ struct ctl_neighbor { #define F_PREF_STALE 0x10 #define F_PREF_INVALID 0x20 #define F_PREF_PATH_ID 0x40 +#define F_PREF_OTC_LOOP 0x80 struct ctl_show_rib { struct bgpd_addr true_nexthop; @@ -948,7 +958,7 @@ struct filter_peers { #define EXT_COMMUNITY_TRANS_IPV4 0x01 /* IPv4 specific */ #define EXT_COMMUNITY_TRANS_FOUR_AS 0x02 /* 4 octet AS specific */ #define EXT_COMMUNITY_TRANS_OPAQUE 0x03 /* opaque ext community */ -#define EXT_COMMUNITY_TRANS_EVPN 0x06 /* EVPN RFC7432 */ +#define EXT_COMMUNITY_TRANS_EVPN 0x06 /* EVPN RFC 7432 */ /* extended types non-transitive */ #define EXT_COMMUNITY_NON_TRANS_TWO_AS 0x40 /* 2 octet AS specific */ #define EXT_COMMUNITY_NON_TRANS_IPV4 0x41 /* IPv4 specific */ @@ -956,7 +966,7 @@ struct filter_peers { #define EXT_COMMUNITY_NON_TRANS_OPAQUE 0x43 /* opaque ext community */ #define EXT_COMMUNITY_UNKNOWN -1 -/* BGP Origin Validation State Extended Community RFC8097 */ +/* BGP Origin Validation State Extended Community RFC 8097 */ #define EXT_COMMUNITY_SUBTYPE_OVS 0 #define EXT_COMMUNITY_OVS_VALID 0 #define EXT_COMMUNITY_OVS_NOTFOUND 1 @@ -1380,6 +1390,7 @@ const char *log_rd(uint64_t); const char *log_ext_subtype(int, uint8_t); const char *log_reason(const char *); const char *log_rtr_error(enum rtr_error); +const char *log_policy(uint8_t); int aspath_snprint(char *, size_t, void *, uint16_t); int aspath_asprint(char **, void *, uint16_t); size_t aspath_strlen(void *, uint16_t); @@ -1485,8 +1496,10 @@ static const char * const suberr_open_names[] = { "authentication error", "unacceptable holdtime", "unsupported capability", - "group membership conflict", /* draft-ietf-idr-bgp-multisession-07 */ - "group membership required" /* draft-ietf-idr-bgp-multisession-07 */ + NULL, + NULL, + NULL, + "role mismatch", }; static const char * const suberr_fsm_names[] = { diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 26b731f06d9..a7c553c0475 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.430 2022/06/15 14:09:30 claudio Exp $ */ +/* $OpenBSD: parse.y,v 1.431 2022/06/27 13:26:51 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -210,7 +210,7 @@ typedef struct { %token EBGP IBGP %token LOCALAS REMOTEAS DESCR LOCALADDR MULTIHOP PASSIVE MAXPREFIX RESTART %token ANNOUNCE CAPABILITIES REFRESH AS4BYTE CONNECTRETRY ENHANCED ADDPATH -%token SEND RECV +%token SEND RECV POLICY %token DEMOTE ENFORCE NEIGHBORAS ASOVERRIDE REFLECTOR DEPEND DOWN %token DUMP IN OUT SOCKET RESTRICTED %token LOG TRANSPARENT @@ -235,7 +235,7 @@ typedef struct { %token NUMBER %type asnumber as4number as4number_any optnumber %type espah family safi restart origincode nettype -%type yesno inout restricted validity expires +%type yesno inout restricted validity expires enforce %type string %type address %type prefix addrspec @@ -1515,6 +1515,33 @@ peeropts : REMOTEAS as4number { else *ap++ &= ~CAPA_AP_RECV; } + | ANNOUNCE POLICY STRING enforce { + curpeer->conf.capabilities.role_ena = $4; + if (strcmp($3, "no") == 0) { + curpeer->conf.capabilities.role_ena = 0; + } else if (strcmp($3, "provider") == 0) { + curpeer->conf.capabilities.role = + CAPA_ROLE_PROVIDER; + } else if (strcmp($3, "rs") == 0) { + curpeer->conf.capabilities.role = + CAPA_ROLE_RS; + } else if (strcmp($3, "rs-client") == 0) { + curpeer->conf.capabilities.role = + CAPA_ROLE_RS_CLIENT; + } else if (strcmp($3, "customer") == 0) { + curpeer->conf.capabilities.role = + CAPA_ROLE_CUSTOMER; + } else if (strcmp($3, "peer") == 0) { + curpeer->conf.capabilities.role = + CAPA_ROLE_PEER; + } else { + yyerror("syntax error, one of no, provider, " + "rs, rs-client, customer, peer expected"); + free($3); + YYERROR; + } + free($3); + } | EXPORT NONE { curpeer->conf.export_type = EXPORT_NONE; } @@ -2590,6 +2617,10 @@ delete : /* empty */ { $$ = 0; } | DELETE { $$ = 1; } ; +enforce : /* empty */ { $$ = 1; } + | ENFORCE { $$ = 2; } + ; + filter_set_opt : LOCALPREF NUMBER { if ($2 < -INT_MAX || $2 > UINT_MAX) { yyerror("bad localpref %lld", $2); @@ -3067,6 +3098,7 @@ lookup(char *s) { "password", PASSWORD}, { "peer-as", PEERAS}, { "pftable", PFTABLE}, + { "policy", POLICY}, { "port", PORT}, { "prefix", PREFIX}, { "prefix-set", PREFIXSET}, diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index c7107a8799c..6ec6205c4ac 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.153 2022/06/15 14:09:30 claudio Exp $ */ +/* $OpenBSD: printconf.c,v 1.154 2022/06/27 13:26:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -659,14 +659,6 @@ print_peer(struct peer_config *p, struct bgpd_config *conf, const char *c) printf("%s\tholdtime %u\n", c, p->holdtime); if (p->min_holdtime) printf("%s\tholdtime min %u\n", c, p->min_holdtime); - if (p->announce_capa == 0) - printf("%s\tannounce capabilities no\n", c); - if (p->capabilities.refresh == 0) - printf("%s\tannounce refresh no\n", c); - if (p->capabilities.grestart.restart == 0) - printf("%s\tannounce restart no\n", c); - if (p->capabilities.as4byte == 0) - printf("%s\tannounce as4byte no\n", c); if (p->export_type == EXPORT_NONE) printf("%s\texport none\n", c); else if (p->export_type == EXPORT_DEFAULT_ROUTE) @@ -781,9 +773,29 @@ print_announce(struct peer_config *p, const char *c) { uint8_t aid; + if (p->announce_capa == 0) + printf("%s\tannounce capabilities no\n", c); + for (aid = 0; aid < AID_MAX; aid++) if (p->capabilities.mp[aid]) printf("%s\tannounce %s\n", c, aid2str(aid)); + + if (p->capabilities.refresh == 0) + printf("%s\tannounce refresh no\n", c); + if (p->capabilities.enhanced_rr == 0) + printf("%s\tannounce enhanced refresh no\n", c); + if (p->capabilities.grestart.restart == 0) + printf("%s\tannounce restart no\n", c); + if (p->capabilities.as4byte == 0) + printf("%s\tannounce as4byte no\n", c); + if (p->capabilities.add_path[0] & CAPA_AP_RECV) + printf("%s\tannounce add-path recv yes\n", c); + if (p->capabilities.role_ena) { + printf("%s\tannounce policy %s%s\n", c, + log_policy(p->capabilities.role), + p->capabilities.role_ena == 2 ? " enforce" : ""); + } + } void diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index d3f1e0e4daf..45b22bfbbc4 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.546 2022/05/25 16:03:34 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.547 2022/06/27 13:26:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -1182,7 +1182,7 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) uint16_t withdrawn_len; uint16_t attrpath_len; uint16_t nlri_len; - uint8_t aid, prefixlen, safi, subtype; + uint8_t aid, prefixlen, safi, subtype, role; uint32_t fas, pathid; p = imsg->data; @@ -1264,6 +1264,26 @@ rde_update_dispatch(struct rde_peer *peer, struct imsg *imsg) } } + /* inject open policy OTC attribute if needed */ + if (peer_has_open_policy(peer, &role) && + (state.aspath.flags & F_ATTR_OTC) == 0) { + uint32_t tmp; + switch (role) { + case CAPA_ROLE_PROVIDER: + case CAPA_ROLE_RS: + case CAPA_ROLE_PEER: + tmp = htonl(peer->conf.remote_as); + if (attr_optadd(&state.aspath, + ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_OTC, + &tmp, sizeof(tmp)) == -1) { + rde_update_err(peer, ERR_UPDATE, + ERR_UPD_ATTRLIST, NULL, 0); + goto done; + } + state.aspath.flags |= F_ATTR_OTC; + } + } + /* aspath needs to be loop free. This is not a hard error. */ if (state.aspath.flags & F_ATTR_ASPATH && peer->conf.ebgp && @@ -1679,9 +1699,7 @@ rde_attr_parse(u_char *p, uint16_t len, struct rde_peer *peer, int error; uint16_t attr_len, nlen; uint16_t plen = 0; - uint8_t flags; - uint8_t type; - uint8_t tmp8; + uint8_t flags, type, role, tmp8; if (len < 3) { bad_len: @@ -2006,6 +2024,34 @@ bad_flags: } a->flags |= F_ATTR_AS4BYTE_NEW; goto optattr; + case ATTR_OTC: + if (attr_len != 4) { + /* treat-as-withdraw */ + a->flags |= F_ATTR_PARSE_ERR; + log_peer_warnx(&peer->conf, "bad OTC, " + "path invalidated and prefix withdrawn"); + break; + } + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, + ATTR_PARTIAL)) + goto bad_flags; + if (peer_has_open_policy(peer, &role)) { + switch (role) { + case CAPA_ROLE_CUSTOMER: + case CAPA_ROLE_RS_CLIENT: + a->flags |= F_ATTR_OTC_LOOP | F_ATTR_PARSE_ERR; + break; + case CAPA_ROLE_PEER: + memcpy(&tmp32, p, sizeof(tmp32)); + tmp32 = ntohl(tmp32); + if (tmp32 != peer->conf.remote_as) + a->flags |= F_ATTR_OTC_LOOP | + F_ATTR_PARSE_ERR; + break; + } + } + a->flags |= F_ATTR_OTC; + goto optattr; default: if ((flags & ATTR_OPTIONAL) == 0) { rde_update_err(peer, ERR_UPDATE, ERR_UPD_UNKNWN_WK_ATTR, @@ -2410,7 +2456,10 @@ 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_LOOP) rib.flags &= ~F_PREF_ELIGIBLE; - if (asp->flags & F_ATTR_PARSE_ERR) + /* otc loop includes parse err so skip the latter if the first is set */ + if (asp->flags & F_ATTR_OTC_LOOP) + rib.flags |= F_PREF_OTC_LOOP; + else if (asp->flags & F_ATTR_PARSE_ERR) rib.flags |= F_PREF_INVALID; staletime = peer->staletime[p->pt->aid]; if (staletime && p->lastchange <= staletime) diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index d78711d6b99..3f74b06c1c2 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.253 2022/05/31 09:45:33 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.254 2022/06/27 13:26:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker and @@ -148,6 +148,7 @@ enum attrtypes { ATTR_AS4_PATH=17, ATTR_AS4_AGGREGATOR=18, ATTR_LARGE_COMMUNITIES=32, + ATTR_OTC=35, ATTR_FIRST_UNKNOWN, /* after this all attributes are unknown */ }; @@ -205,10 +206,11 @@ struct rde_community { #define F_ATTR_LOOP 0x00200 /* path would cause a route loop */ #define F_PREFIX_ANNOUNCED 0x00400 #define F_ANN_DYNAMIC 0x00800 +#define F_ATTR_OTC 0x01000 /* OTC present */ +#define F_ATTR_OTC_LOOP 0x02000 /* otc loop, not eligable */ #define F_ATTR_PARSE_ERR 0x10000 /* parse error, not eligable */ #define F_ATTR_LINKED 0x20000 /* if set path is on various lists */ - #define ORIGIN_IGP 0 #define ORIGIN_EGP 1 #define ORIGIN_INCOMPLETE 2 @@ -391,6 +393,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 *, uint8_t, int); +int peer_has_open_policy(struct rde_peer *, uint8_t *); int peer_accept_no_as_set(struct rde_peer *); void peer_init(uint32_t); void peer_shutdown(void); diff --git a/usr.sbin/bgpd/rde_peer.c b/usr.sbin/bgpd/rde_peer.c index 2b5741905fd..6d22c1925bd 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.16 2022/05/23 13:40:12 deraadt Exp $ */ +/* $OpenBSD: rde_peer.c,v 1.17 2022/06/27 13:26:51 claudio Exp $ */ /* * Copyright (c) 2019 Claudio Jeker @@ -61,6 +61,13 @@ peer_has_add_path(struct rde_peer *peer, uint8_t aid, int mode) return (peer->capa.add_path[aid] & mode); } +int +peer_has_open_policy(struct rde_peer *peer, uint8_t *role) +{ + *role = peer->capa.role; + return (peer->capa.role_ena != 0); +} + int peer_accept_no_as_set(struct rde_peer *peer) { diff --git a/usr.sbin/bgpd/rde_update.c b/usr.sbin/bgpd/rde_update.c index 9982fadb7df..a14e7784504 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.140 2022/05/23 13:40:12 deraadt Exp $ */ +/* $OpenBSD: rde_update.c,v 1.141 2022/06/27 13:26:51 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker @@ -104,7 +104,7 @@ up_generate_updates(struct filter_head *rules, struct rde_peer *peer, struct bgpd_addr addr; struct prefix *p; int need_withdraw; - uint8_t prefixlen; + uint8_t prefixlen, role; if (new == NULL) { if (old == NULL) @@ -154,6 +154,45 @@ up_generate_updates(struct filter_head *rules, struct rde_peer *peer, break; } + /* RFC9234 open policy handling */ + if (peer_has_open_policy(peer, &role)) { + /* + * do not propagate (consider it filtered) if + * OTC is present and neighbor role is peer, + * provider or rs. + */ + if ((role == CAPA_ROLE_PEER || + role == CAPA_ROLE_PROVIDER || + role == CAPA_ROLE_RS) && + state.aspath.flags & F_ATTR_OTC) { + rde_filterstate_clean(&state); + if (peer->flags & PEERFLAG_EVALUATE_ALL) { + new = TAILQ_NEXT(new, entry.list.rib); + if (new != NULL && prefix_eligible(new)) + continue; + } + break; + } + /* + * add OTC attribute if not present for peers, + * customers and rs-clients. + */ + if ((role == CAPA_ROLE_PEER || + role == CAPA_ROLE_CUSTOMER || + role == CAPA_ROLE_RS_CLIENT) && + (state.aspath.flags & F_ATTR_OTC) == 0) { + uint32_t tmp; + + tmp = htonl(peer->conf.local_as); + if (attr_optadd(&state.aspath, + ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_OTC, + &tmp, sizeof(tmp)) == -1) + log_peer_warnx(&peer->conf, + "failed to add OTC attribute"); + state.aspath.flags |= F_ATTR_OTC; + } + } + /* check if this was actually a withdraw */ if (need_withdraw) break; @@ -535,6 +574,7 @@ up_generate_attr(u_char *buf, int len, struct rde_peer *peer, /* FALLTHROUGH */ case ATTR_ORIGINATOR_ID: case ATTR_CLUSTER_LIST: + case ATTR_OTC: if (oa == NULL || oa->type != type) break; if ((!(oa->flags & ATTR_TRANSITIVE)) && diff --git a/usr.sbin/bgpd/session.c b/usr.sbin/bgpd/session.c index 258ff196ff8..093f943c320 100644 --- a/usr.sbin/bgpd/session.c +++ b/usr.sbin/bgpd/session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: session.c,v 1.429 2022/06/23 13:09:03 claudio Exp $ */ +/* $OpenBSD: session.c,v 1.430 2022/06/27 13:26:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004, 2005 Henning Brauer @@ -87,7 +87,7 @@ int parse_update(struct peer *); int parse_rrefresh(struct peer *); int parse_notification(struct peer *); int parse_capabilities(struct peer *, u_char *, uint16_t, uint32_t *); -int capa_neg_calc(struct peer *); +int capa_neg_calc(struct peer *, uint8_t *); void session_dispatch_imsg(struct imsgbuf *, int, u_int *); void session_up(struct peer *); void session_down(struct peer *); @@ -1439,6 +1439,12 @@ session_open(struct peer *p) if (p->capa.ann.refresh) /* no data */ errs += session_capa_add(opb, CAPA_REFRESH, 0); + /* BGP open policy, RFC 9234 */ + if (p->capa.ann.role_ena) { + errs += session_capa_add(opb, CAPA_ROLE, 1); + errs += ibuf_add(opb, &p->capa.ann.role, 1); + } + /* graceful restart and End-of-RIB marker, RFC 4724 */ if (p->capa.ann.grestart.restart) { int rst = 0; @@ -2070,7 +2076,7 @@ parse_open(struct peer *peer) uint16_t holdtime, oholdtime, myholdtime; uint32_t as, bgpid; uint16_t optparamlen, extlen, plen, op_len; - uint8_t op_type; + uint8_t op_type, suberr = 0; p = peer->rbuf->rptr; p += MSGSIZE_HEADER_MARKER; @@ -2258,10 +2264,8 @@ bad_len: return (-1); } - if (capa_neg_calc(peer) == -1) { - log_peer_warnx(&peer->conf, - "capability negotiation calculation failed"); - session_notification(peer, ERR_OPEN, 0, NULL, 0); + if (capa_neg_calc(peer, &suberr) == -1) { + session_notification(peer, ERR_OPEN, suberr, NULL, 0); change_state(peer, STATE_IDLE, EVNT_RCVD_OPEN); return (-1); } @@ -2607,6 +2611,16 @@ parse_capabilities(struct peer *peer, u_char *d, uint16_t dlen, uint32_t *as) case CAPA_REFRESH: peer->capa.peer.refresh = 1; break; + case CAPA_ROLE: + if (capa_len != 1) { + log_peer_warnx(&peer->conf, + "Bad open policy capability length: " + "%u", capa_len); + break; + } + peer->capa.peer.role_ena = 1; + peer->capa.peer.role = *capa_val; + break; case CAPA_RESTART: if (capa_len == 2) { /* peer only supports EoR marker */ @@ -2722,7 +2736,7 @@ parse_capabilities(struct peer *peer, u_char *d, uint16_t dlen, uint32_t *as) } int -capa_neg_calc(struct peer *p) +capa_neg_calc(struct peer *p, uint8_t *suberr) { uint8_t i, hasmp = 0; @@ -2775,8 +2789,11 @@ capa_neg_calc(struct peer *p) CAPA_GR_RESTARTING; } else { if (imsg_rde(IMSG_SESSION_FLUSH, p->conf.id, - &i, sizeof(i)) == -1) + &i, sizeof(i)) == -1) { + log_peer_warnx(&p->conf, + "imsg send failed"); return (-1); + } log_peer_warnx(&p->conf, "graceful restart of " "%s, not restarted, flushing", aid2str(i)); } @@ -2810,6 +2827,53 @@ capa_neg_calc(struct peer *p) } } + /* + * Open policy: check that the policy is sensible. + * + * Make sure that the roles match and set the negotiated capability + * to the role of the peer. So the RDE can inject the OTC attribute. + * See RFC 9234, section 4.2. + */ + if (p->capa.ann.role_ena != 0 && p->capa.peer.role_ena != 0) { + switch (p->capa.ann.role) { + case CAPA_ROLE_PROVIDER: + if (p->capa.peer.role != CAPA_ROLE_CUSTOMER) + goto fail; + break; + case CAPA_ROLE_RS: + if (p->capa.peer.role != CAPA_ROLE_RS_CLIENT) + goto fail; + break; + case CAPA_ROLE_RS_CLIENT: + if (p->capa.peer.role != CAPA_ROLE_RS) + goto fail; + break; + case CAPA_ROLE_CUSTOMER: + if (p->capa.peer.role != CAPA_ROLE_PROVIDER) + goto fail; + break; + case CAPA_ROLE_PEER: + if (p->capa.peer.role != CAPA_ROLE_PEER) + goto fail; + break; + default: + fail: + log_peer_warnx(&p->conf, "open policy role mismatch: " + "%s vs %s", log_policy(p->capa.ann.role), + log_policy(p->capa.peer.role)); + *suberr = ERR_OPEN_ROLE; + return (-1); + } + p->capa.neg.role_ena = 1; + p->capa.neg.role = p->capa.peer.role; + } else if (p->capa.ann.role_ena == 2) { + /* enforce presence of open policy role capability */ + log_peer_warnx(&p->conf, "open policy role enforced but " + "not present"); + *suberr = ERR_OPEN_ROLE; + return (-1); + } + return (0); } diff --git a/usr.sbin/bgpd/session.h b/usr.sbin/bgpd/session.h index a27bb774980..696cf2aa4db 100644 --- a/usr.sbin/bgpd/session.h +++ b/usr.sbin/bgpd/session.h @@ -1,4 +1,4 @@ -/* $OpenBSD: session.h,v 1.155 2022/06/15 14:09:30 claudio Exp $ */ +/* $OpenBSD: session.h,v 1.156 2022/06/27 13:26:51 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -86,11 +86,10 @@ enum suberr_open { ERR_OPEN_AS, ERR_OPEN_BGPID, ERR_OPEN_OPT, - ERR_OPEN_AUTH, + ERR_OPEN_AUTH, /* deprecated */ ERR_OPEN_HOLDTIME, ERR_OPEN_CAPA, - ERR_OPEN_GROUP_CONFLICT, /* draft-ietf-idr-bgp-multisession-07 */ - ERR_OPEN_GROUP_REQUIRED /* draft-ietf-idr-bgp-multisession-07 */ + ERR_OPEN_ROLE = 11, }; enum suberr_fsm { @@ -111,6 +110,7 @@ enum capa_codes { CAPA_NONE = 0, CAPA_MP = 1, CAPA_REFRESH = 2, + CAPA_ROLE = 9, CAPA_RESTART = 64, CAPA_AS4BYTE = 65, CAPA_ADD_PATH = 69,