-/* $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 <henning@openbsd.org>
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
#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;
#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;
#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 */
#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
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);
"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[] = {
-/* $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 <henning@openbsd.org>
%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
%token <v.number> NUMBER
%type <v.number> asnumber as4number as4number_any optnumber
%type <v.number> espah family safi restart origincode nettype
-%type <v.number> yesno inout restricted validity expires
+%type <v.number> yesno inout restricted validity expires enforce
%type <v.string> string
%type <v.addr> address
%type <v.prefix> prefix addrspec
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;
}
| DELETE { $$ = 1; }
;
+enforce : /* empty */ { $$ = 1; }
+ | ENFORCE { $$ = 2; }
+ ;
+
filter_set_opt : LOCALPREF NUMBER {
if ($2 < -INT_MAX || $2 > UINT_MAX) {
yyerror("bad localpref %lld", $2);
{ "password", PASSWORD},
{ "peer-as", PEERAS},
{ "pftable", PFTABLE},
+ { "policy", POLICY},
{ "port", PORT},
{ "prefix", PREFIX},
{ "prefix-set", PREFIXSET},
-/* $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 <henning@openbsd.org>
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)
{
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
-/* $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 <henning@openbsd.org>
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;
}
}
+ /* 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 &&
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:
}
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,
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)
-/* $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 <claudio@openbsd.org> and
ATTR_AS4_PATH=17,
ATTR_AS4_AGGREGATOR=18,
ATTR_LARGE_COMMUNITIES=32,
+ ATTR_OTC=35,
ATTR_FIRST_UNKNOWN, /* after this all attributes are unknown */
};
#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
/* 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);
-/* $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 <claudio@openbsd.org>
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)
{
-/* $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 <claudio@openbsd.org>
struct bgpd_addr addr;
struct prefix *p;
int need_withdraw;
- uint8_t prefixlen;
+ uint8_t prefixlen, role;
if (new == NULL) {
if (old == NULL)
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;
/* FALLTHROUGH */
case ATTR_ORIGINATOR_ID:
case ATTR_CLUSTER_LIST:
+ case ATTR_OTC:
if (oa == NULL || oa->type != type)
break;
if ((!(oa->flags & ATTR_TRANSITIVE)) &&
-/* $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 <henning@openbsd.org>
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 *);
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;
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;
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);
}
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 */
}
int
-capa_neg_calc(struct peer *p)
+capa_neg_calc(struct peer *p, uint8_t *suberr)
{
uint8_t i, hasmp = 0;
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));
}
}
}
+ /*
+ * 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);
}
-/* $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 <henning@openbsd.org>
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 {
CAPA_NONE = 0,
CAPA_MP = 1,
CAPA_REFRESH = 2,
+ CAPA_ROLE = 9,
CAPA_RESTART = 64,
CAPA_AS4BYTE = 65,
CAPA_ADD_PATH = 69,