When I implemented RFC9234 support I was a bit to conservative and only enabled the loop detection if the capability was enabled. This is how every other capability works but RFC9234 is special. On top of this with the addition of ASPA support the way BGP roles are handled changed and the code turned up a bit strange. Considering all of this and rereading RFC9234 I came up with this diff:
- add role output to bgpctl, also adjust the capability output. Note, this changes the JSON output of neighbors a bit. - adjust the config parser to enable the RFC9234 role capability when there is a role set. iBGP and sessions with no role will not announce the role capability. - adjust the role capability announcement to be only on sessions that use either AFI IPv4 or IPv6 and SAFI 1 (AID_INET, AID_INET6). If a session has neither of the two then do not announce the role (even if set) - if there is an OPEN notification indicating that the role capability is bad only disable the capability if it is not enforced. If enforced do nothing and let the session fail (again and again). - Adjust capability negotiation, store remote_role on the peer since the neighbors role is no longer needed. - inject the OTC attribute on ingress only for AID_INET and AID_INET6. For other AIDs clear the F_ATTR_OTC_LOOP flag since OTC loop detection must be skipped for other address families. - Adjust the role logic in the RDE and use the peer->role (local role of the system) for all checks. Also remove the check if the role capability was present (removal of peer_has_open_policy()). This change switches all role checks from raw capability codes to enum role and at the same time flips the logic since the view of the checks is changed from remote system to local system. The roles change like this: rs <-> rs-client, customer <-> provider, peer <-> peer - In prefix_eligible() check also if the F_ATTR_OTC_LOOP flag is set. The RFC requires that prefixes must be considered ineligible (and not treat as withdraw) - When generating an UPDATE include the OTC attribute unless the AID is neither AID_INET or AID_INET6 or the roles do not require the addtion. No longer depend on peer_has_open_policy() there. Please test and check that my logic is actually correct and following the RFC. -- :wq Claudio Index: usr.sbin/bgpctl/output.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v retrieving revision 1.36 diff -u -p -r1.36 output.c --- usr.sbin/bgpctl/output.c 31 Jan 2023 14:32:43 -0000 1.36 +++ usr.sbin/bgpctl/output.c 28 Feb 2023 05:46:41 -0000 @@ -281,6 +281,8 @@ show_neighbor_full(struct peer *p, struc printf("\n"); if (p->conf.descr[0]) printf(" Description: %s\n", p->conf.descr); + if (p->conf.ebgp && p->conf.role != ROLE_NONE) + printf(" Role: %s\n", log_policy(p->conf.role)); if (p->conf.max_prefix) { printf(" Max-prefix: %u", p->conf.max_prefix); if (p->conf.max_prefix_restart) @@ -330,7 +332,7 @@ show_neighbor_full(struct peer *p, struc } if (hascapamp || hascapaap || p->capa.peer.grestart.restart || p->capa.peer.refresh || p->capa.peer.enhanced_rr || - p->capa.peer.as4byte || p->capa.peer.role_ena) { + p->capa.peer.as4byte || p->capa.peer.policy) { printf(" Neighbor capabilities:\n"); if (hascapamp) show_neighbor_capa_mp(&p->capa.peer); @@ -344,10 +346,10 @@ show_neighbor_full(struct peer *p, struc show_neighbor_capa_restart(&p->capa.peer); if (hascapaap) show_neighbor_capa_add_path(&p->capa.peer); - if (p->capa.peer.role_ena) + if (p->capa.peer.policy) printf(" Open Policy role %s (local %s)\n", - log_policy(p->capa.peer.role), - log_policy(p->capa.ann.role)); + log_policy(p->remote_role), + log_policy(p->conf.role)); } hascapamp = 0; @@ -360,7 +362,7 @@ show_neighbor_full(struct peer *p, struc } if (hascapamp || hascapaap || p->capa.neg.grestart.restart || p->capa.neg.refresh || p->capa.neg.enhanced_rr || - p->capa.neg.as4byte || p->capa.neg.role_ena) { + p->capa.neg.as4byte || p->capa.neg.policy) { printf(" Negotiated capabilities:\n"); if (hascapamp) show_neighbor_capa_mp(&p->capa.neg); @@ -374,10 +376,10 @@ show_neighbor_full(struct peer *p, struc show_neighbor_capa_restart(&p->capa.neg); if (hascapaap) show_neighbor_capa_add_path(&p->capa.neg); - if (p->capa.neg.role_ena) + if (p->capa.neg.policy) printf(" Open Policy role %s (local %s)\n", - log_policy(p->capa.neg.role), - log_policy(p->capa.ann.role)); + log_policy(p->remote_role), + log_policy(p->conf.role)); } printf("\n"); Index: usr.sbin/bgpctl/output_json.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/output_json.c,v retrieving revision 1.29 diff -u -p -r1.29 output_json.c --- usr.sbin/bgpctl/output_json.c 24 Jan 2023 11:29:34 -0000 1.29 +++ usr.sbin/bgpctl/output_json.c 28 Feb 2023 05:41:27 -0000 @@ -125,10 +125,9 @@ json_neighbor_capabilities(struct capabi json_do_end(); } - if (capa->role_ena) { - json_do_printf("open_policy_role", "%s%s", - log_policy(capa->role), - capa->role_ena == 2 ? " enforce" : ""); + if (capa->policy) { + json_do_printf("open_policy", "%s", + capa->policy == 2 ? "enforce" : "present"); } json_do_end(); @@ -248,6 +247,9 @@ json_neighbor_full(struct peer *p) json_do_bool("ttl_security", p->conf.ttlsec); json_do_uint("holdtime", p->conf.holdtime); json_do_uint("min_holdtime", p->conf.min_holdtime); + if (p->conf.ebgp && p->conf.role != ROLE_NONE) + json_do_printf("role", "%s", + log_policy(p->conf.role)); /* capabilities */ json_do_bool("announce_capabilities", p->conf.announce_capa); @@ -296,6 +298,12 @@ json_neighbor_full(struct peer *p) /* capabilities */ json_neighbor_capabilities(&p->capa.neg); + if (p->conf.ebgp && p->conf.role != ROLE_NONE) { + json_do_printf("remote_role", "%s", + log_policy(p->remote_role)); + json_do_printf("local_role", "%s", + log_policy(p->conf.role)); + } json_do_end(); } } Index: usr.sbin/bgpd/bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.461 diff -u -p -r1.461 bgpd.h --- usr.sbin/bgpd/bgpd.h 9 Feb 2023 13:43:23 -0000 1.461 +++ usr.sbin/bgpd/bgpd.h 28 Feb 2023 04:23:31 -0000 @@ -398,13 +398,12 @@ struct capabilities { int8_t flags[AID_MAX]; /* graceful restart per AID flags */ int8_t restart; /* graceful restart, RFC 4724 */ } grestart; - enum role role; /* Open Policy, RFC 9234 */ int8_t mp[AID_MAX]; /* multiprotocol extensions, RFC 4760 */ int8_t add_path[AID_MAX]; /* ADD_PATH, RFC 7911 */ int8_t refresh; /* route refresh, RFC 2918 */ int8_t as4byte; /* 4-byte ASnum, RFC 4893 */ int8_t enhanced_rr; /* enhanced route refresh, RFC 7313 */ - int8_t role_ena; /* 1 for enable, 2 for enforce */ + int8_t policy; /* Open Policy, RFC 9234, 2 = enforce */ }; /* flags for RFC 4724 - graceful restart */ Index: usr.sbin/bgpd/parse.y =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v retrieving revision 1.441 diff -u -p -r1.441 parse.y --- usr.sbin/bgpd/parse.y 30 Jan 2023 16:51:34 -0000 1.441 +++ usr.sbin/bgpd/parse.y 28 Feb 2023 11:35:51 -0000 @@ -1642,7 +1642,7 @@ peeropts : REMOTEAS as4number { curpeer->conf.eval.maxpaths = $6; } | ANNOUNCE POLICY enforce { - curpeer->conf.capabilities.role_ena = $3; + curpeer->conf.capabilities.policy = $3; } | ROLE STRING { if (strcmp($2, "provider") == 0) { @@ -4284,6 +4284,7 @@ alloc_peer(void) p->conf.capabilities.refresh = 1; p->conf.capabilities.grestart.restart = 1; p->conf.capabilities.as4byte = 1; + p->conf.capabilities.policy = 1; p->conf.local_as = conf->as; p->conf.local_short_as = conf->short_as; p->conf.remote_port = BGP_PORT; @@ -4736,12 +4737,13 @@ neighbor_consistent(struct peer *p) } /* BGP role and RFC 9234 role are only valid for EBGP neighbors */ - if (p->conf.ebgp) { - if (p->conf.role == ROLE_NONE) - p->conf.capabilities.role_ena = 0; - p->conf.capabilities.role = p->conf.role; - } else + if (!p->conf.ebgp) { p->conf.role = ROLE_NONE; + p->conf.capabilities.policy = 0; + } else if (p->conf.role == ROLE_NONE) { + /* no role, no policy capability */ + p->conf.capabilities.policy = 0; + } /* check for duplicate peer definitions */ RB_FOREACH(xp, peer_head, new_peers) Index: usr.sbin/bgpd/printconf.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v retrieving revision 1.163 diff -u -p -r1.163 printconf.c --- usr.sbin/bgpd/printconf.c 24 Jan 2023 14:13:12 -0000 1.163 +++ usr.sbin/bgpd/printconf.c 28 Feb 2023 04:24:06 -0000 @@ -847,9 +847,9 @@ print_announce(struct peer_config *p, co printf(" max %d", p->eval.maxpaths); printf("\n"); } - if (p->capabilities.role_ena) { + if (p->capabilities.policy) { printf("%s\tannounce policy %s\n", c, - p->capabilities.role_ena == 2 ? "enforce" : "yes"); + p->capabilities.policy == 2 ? "enforce" : "yes"); } } Index: usr.sbin/bgpd/rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.593 diff -u -p -r1.593 rde.c --- usr.sbin/bgpd/rde.c 13 Feb 2023 18:07:53 -0000 1.593 +++ usr.sbin/bgpd/rde.c 28 Feb 2023 05:52:08 -0000 @@ -1242,7 +1242,7 @@ rde_update_dispatch(struct rde_peer *pee uint16_t withdrawn_len; uint16_t attrpath_len; uint16_t nlri_len; - uint8_t aid, prefixlen, safi, subtype, role; + uint8_t aid, prefixlen, safi, subtype; uint32_t fas, pathid; p = imsg->data; @@ -1324,26 +1324,6 @@ rde_update_dispatch(struct rde_peer *pee } } - /* 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 && @@ -1520,6 +1500,28 @@ rde_update_dispatch(struct rde_peer *pee NULL, 0); goto done; } + + /* inject open policy OTC attribute if needed */ + if ((state.aspath.flags & F_ATTR_OTC) == 0) { + uint32_t tmp; + switch (peer->role) { + case ROLE_CUSTOMER: + case ROLE_RS_CLIENT: + case 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; + break; + default: + break; + } + } } while (nlri_len > 0) { if (peer_has_add_path(peer, AID_INET, CAPA_AP_RECV)) { @@ -1580,6 +1582,34 @@ rde_update_dispatch(struct rde_peer *pee goto done; } + if (aid == AID_INET6) { + /* inject open policy OTC attribute if needed */ + if ((state.aspath.flags & F_ATTR_OTC) == 0) { + uint32_t tmp; + switch (peer->role) { + case ROLE_CUSTOMER: + case ROLE_RS_CLIENT: + case 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; + break; + default: + break; + } + } + } else { + /* Only IPv4 and IPv6 unicast do OTC handling */ + state.aspath.flags &= ~F_ATTR_OTC_LOOP; + } + /* unlock the previously locked nexthop, it is no longer used */ nexthop_unref(state.nexthop); state.nexthop = NULL; @@ -1824,7 +1854,7 @@ rde_attr_parse(u_char *p, uint16_t len, int error; uint16_t attr_len, nlen; uint16_t plen = 0; - uint8_t flags, type, role, tmp8; + uint8_t flags, type, tmp8; if (len < 3) { bad_len: @@ -2160,20 +2190,19 @@ bad_flags: 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; - } + switch (peer->role) { + case ROLE_PROVIDER: + case ROLE_RS: + a->flags |= F_ATTR_OTC_LOOP; + break; + case ROLE_PEER: + memcpy(&tmp32, p, sizeof(tmp32)); + tmp32 = ntohl(tmp32); + if (tmp32 != peer->conf.remote_as) + a->flags |= F_ATTR_OTC_LOOP; + break; + default: + break; } a->flags |= F_ATTR_OTC; goto optattr; Index: usr.sbin/bgpd/rde.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v retrieving revision 1.283 diff -u -p -r1.283 rde.h --- usr.sbin/bgpd/rde.h 13 Feb 2023 18:07:53 -0000 1.283 +++ usr.sbin/bgpd/rde.h 28 Feb 2023 05:30:22 -0000 @@ -400,7 +400,6 @@ int rde_match_peer(struct rde_peer *, s /* 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(void); void peer_shutdown(void); Index: usr.sbin/bgpd/rde_decide.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_decide.c,v retrieving revision 1.99 diff -u -p -r1.99 rde_decide.c --- usr.sbin/bgpd/rde_decide.c 13 Feb 2023 18:07:53 -0000 1.99 +++ usr.sbin/bgpd/rde_decide.c 28 Feb 2023 05:31:32 -0000 @@ -487,7 +487,8 @@ prefix_eligible(struct prefix *p) struct rde_aspath *asp = prefix_aspath(p); /* The aspath needs to be loop and error free */ - if (asp == NULL || asp->flags & (F_ATTR_LOOP|F_ATTR_PARSE_ERR)) + if (asp == NULL || + asp->flags & (F_ATTR_LOOP|F_ATTR_OTC_LOOP|F_ATTR_PARSE_ERR)) return 0; /* The nexthop must be valid. */ Index: usr.sbin/bgpd/rde_peer.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v retrieving revision 1.29 diff -u -p -r1.29 rde_peer.c --- usr.sbin/bgpd/rde_peer.c 13 Feb 2023 18:07:53 -0000 1.29 +++ usr.sbin/bgpd/rde_peer.c 28 Feb 2023 05:30:09 -0000 @@ -62,13 +62,6 @@ peer_has_add_path(struct rde_peer *peer, } 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) { return (peer->flags & PEERFLAG_NO_AS_SET); Index: usr.sbin/bgpd/rde_update.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_update.c,v retrieving revision 1.156 diff -u -p -r1.156 rde_update.c --- usr.sbin/bgpd/rde_update.c 13 Feb 2023 18:07:53 -0000 1.156 +++ usr.sbin/bgpd/rde_update.c 28 Feb 2023 05:51:50 -0000 @@ -66,7 +66,7 @@ up_test_update(struct rde_peer *peer, st if (asp == NULL || asp->flags & F_ATTR_PARSE_ERR) fatalx("try to send out a botched path"); - if (asp->flags & F_ATTR_LOOP) + if (asp->flags & (F_ATTR_LOOP | F_ATTR_OTC_LOOP)) fatalx("try to send out a looped path"); if (peer == frompeer) @@ -104,27 +104,28 @@ up_test_update(struct rde_peer *peer, st /* RFC9234 open policy handling */ static int -up_enforce_open_policy(struct rde_peer *peer, struct filterstate *state) +up_enforce_open_policy(struct rde_peer *peer, struct filterstate *state, + uint8_t aid) { - uint8_t role; - - if (!peer_has_open_policy(peer, &role)) + /* only for IPv4 and IPv6 unicast */ + if (aid != AID_INET && aid != AID_INET6) return 0; /* * do not propagate (consider it filtered) if OTC is present and - * neighbor role is peer, provider or rs. + * local role is peer, customer or rs-client. */ - if (role == CAPA_ROLE_PEER || role == CAPA_ROLE_PROVIDER || - role == CAPA_ROLE_RS) + if (peer->role == ROLE_PEER || peer->role == ROLE_CUSTOMER || + peer->role == ROLE_RS_CLIENT) if (state->aspath.flags & F_ATTR_OTC) - return (1); + return 1; /* - * add OTC attribute if not present for peers, customers and rs-clients. + * add OTC attribute if not present towards peers, customers and + * rs-clients (local roles peer, provider, rs). */ - if (role == CAPA_ROLE_PEER || role == CAPA_ROLE_CUSTOMER || - role == CAPA_ROLE_RS_CLIENT) + if (peer->role == ROLE_PEER || peer->role == ROLE_PROVIDER || + peer->role == ROLE_RS) if ((state->aspath.flags & F_ATTR_OTC) == 0) { uint32_t tmp; @@ -172,7 +173,7 @@ up_process_prefix(struct filter_head *ru } /* Open Policy Check: acts like an output filter */ - if (up_enforce_open_policy(peer, &state)) { + if (up_enforce_open_policy(peer, &state, addr->aid)) { rde_filterstate_clean(&state); return UP_FILTERED; } Index: usr.sbin/bgpd/session.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/session.c,v retrieving revision 1.441 diff -u -p -r1.441 session.c --- usr.sbin/bgpd/session.c 14 Feb 2023 15:37:45 -0000 1.441 +++ usr.sbin/bgpd/session.c 28 Feb 2023 10:39:27 -0000 @@ -1475,10 +1475,12 @@ session_open(struct peer *p) errs += session_capa_add(opb, CAPA_REFRESH, 0); /* BGP open policy, RFC 9234, only for ebgp sessions */ - if (p->conf.ebgp && p->capa.ann.role_ena && - p->capa.ann.role != ROLE_NONE) { + if (p->conf.ebgp && p->capa.ann.policy && + p->conf.role != ROLE_NONE && + (p->capa.ann.mp[AID_INET] || p->capa.ann.mp[AID_INET6] || + mpcapa == 0)) { uint8_t val; - val = role2capa(p->capa.ann.role); + val = role2capa(p->conf.role); errs += session_capa_add(opb, CAPA_ROLE, 1); errs += ibuf_add(opb, &val, 1); } @@ -2515,9 +2517,15 @@ parse_notification(struct peer *peer) "disabling route refresh capability"); break; case CAPA_ROLE: - peer->capa.ann.role_ena = 0; - log_peer_warnx(&peer->conf, - "disabling role capability"); + if (peer->capa.ann.policy == 1) { + peer->capa.ann.policy = 0; + log_peer_warnx(&peer->conf, + "disabling role capability"); + } else { + log_peer_warnx(&peer->conf, + "role capability enforced, " + "not disabling"); + } break; case CAPA_RESTART: peer->capa.ann.grestart.restart = 0; @@ -2660,11 +2668,13 @@ parse_capabilities(struct peer *peer, u_ "Bad role capability length: %u", capa_len); break; } - if (!peer->conf.ebgp) + if (!peer->conf.ebgp) { log_peer_warnx(&peer->conf, "Received role capability on iBGP session"); - peer->capa.peer.role_ena = 1; - peer->capa.peer.role = capa2role(*capa_val); + break; + } + peer->capa.peer.policy = 1; + peer->remote_role = capa2role(*capa_val); break; case CAPA_RESTART: if (capa_len == 2) { @@ -2880,40 +2890,40 @@ capa_neg_calc(struct peer *p, uint8_t *s * See RFC 9234, section 4.2. * These checks should only happen on ebgp sessions. */ - if (p->capa.ann.role_ena != 0 && p->capa.peer.role_ena != 0 && + if (p->capa.ann.policy != 0 && p->capa.peer.policy != 0 && p->conf.ebgp) { - switch (p->capa.ann.role) { + switch (p->conf.role) { case ROLE_PROVIDER: - if (p->capa.peer.role != ROLE_CUSTOMER) + if (p->remote_role != ROLE_CUSTOMER) goto fail; break; case ROLE_RS: - if (p->capa.peer.role != ROLE_RS_CLIENT) + if (p->remote_role != ROLE_RS_CLIENT) goto fail; break; case ROLE_RS_CLIENT: - if (p->capa.peer.role != ROLE_RS) + if (p->remote_role != ROLE_RS) goto fail; break; case ROLE_CUSTOMER: - if (p->capa.peer.role != ROLE_PROVIDER) + if (p->remote_role != ROLE_PROVIDER) goto fail; break; case ROLE_PEER: - if (p->capa.peer.role != ROLE_PEER) + if (p->remote_role != 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)); + "our role %s, their role %s", + log_policy(p->conf.role), + log_policy(p->remote_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 && p->conf.ebgp) { + p->capa.neg.policy = 1; + } else if (p->capa.ann.policy == 2 && p->conf.ebgp) { /* enforce presence of open policy role capability */ log_peer_warnx(&p->conf, "open policy role enforced but " "not present"); Index: usr.sbin/bgpd/session.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/session.h,v retrieving revision 1.159 diff -u -p -r1.159 session.h --- usr.sbin/bgpd/session.h 9 Feb 2023 13:43:23 -0000 1.159 +++ usr.sbin/bgpd/session.h 28 Feb 2023 04:46:25 -0000 @@ -243,6 +243,7 @@ struct peer { enum session_state state; enum session_state prev_state; enum reconf_action reconf_action; + enum role remote_role; uint16_t short_as; uint16_t holdtime; uint16_t local_port;