Add support for RFC 9234 - Route Leak Prevention and Detection Using Roles
authorclaudio <claudio@openbsd.org>
Mon, 27 Jun 2022 13:26:51 +0000 (13:26 +0000)
committerclaudio <claudio@openbsd.org>
Mon, 27 Jun 2022 13:26:51 +0000 (13:26 +0000)
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@

usr.sbin/bgpd/bgpd.h
usr.sbin/bgpd/parse.y
usr.sbin/bgpd/printconf.c
usr.sbin/bgpd/rde.c
usr.sbin/bgpd/rde.h
usr.sbin/bgpd/rde_peer.c
usr.sbin/bgpd/rde_update.c
usr.sbin/bgpd/session.c
usr.sbin/bgpd/session.h

index db9f354..3d78b0a 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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[] = {
index 26b731f..a7c553c 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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 <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
@@ -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},
index c7107a8..6ec6205 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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
index d3f1e0e..45b22bf 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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)
index d78711d..3f74b06 100644 (file)
@@ -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 <claudio@openbsd.org> 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);
index 2b57419..6d22c19 100644 (file)
@@ -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 <claudio@openbsd.org>
@@ -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)
 {
index 9982fad..a14e778 100644 (file)
@@ -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 <claudio@openbsd.org>
@@ -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)) &&
index 258ff19..093f943 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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);
 }
 
index a27bb77..696cf2a 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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,