Merge branch 'master' into backport
This commit is contained in:
commit
2e484f8d29
6 changed files with 195 additions and 6 deletions
|
@ -2377,6 +2377,7 @@ avoid routing loops.
|
|||
<item> <rfc id="8203"> - BGP Administrative Shutdown Communication
|
||||
<item> <rfc id="8212"> - Default EBGP Route Propagation Behavior without Policies
|
||||
<item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
|
||||
<item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
|
||||
</itemize>
|
||||
|
||||
<sect1>Route selection rules
|
||||
|
@ -2817,6 +2818,29 @@ using the following configuration parameters:
|
|||
protocol itself (for example, if a route is received through eBGP and
|
||||
therefore does not have such attribute). Default: 100 (0 in pre-1.2.0
|
||||
versions of BIRD).
|
||||
|
||||
<tag><label id="bgp-local-role">local role <m/role-name/</tag>
|
||||
BGP roles are a mechanism for route leak prevention and automatic route
|
||||
filtering based on common BGP topology relationships. They are defined
|
||||
in <rfc id="9234">. Instead of manually configuring filters and
|
||||
communities, automatic filtering is done with the help of the OTC
|
||||
attribute - a flag for routes that should be sent only to customers.
|
||||
The same attribute is also used to automatically detect and filter route
|
||||
leaks created by third parties.
|
||||
|
||||
This option is valid for EBGP sessions, but it is not recommended to be
|
||||
used within AS confederations (which would require manual filtering of
|
||||
<cf/bgp_otc/ attribute on confederation boundaries).
|
||||
|
||||
Possible <cf><m/role-name/</cf> values are: <cf/provider/,
|
||||
<cf/rs_server/, <cf/rs_client/, <cf/customer/ and <cf/peer/.
|
||||
Default: No local role assigned.
|
||||
|
||||
<tag><label id="bgp-require-roles">require roles <m/switch/</tag>
|
||||
If this option is set, the BGP roles must be defined on both sides,
|
||||
otherwise the session will not be established. This behavior is defined
|
||||
in <rfc id="9234"> as "strict mode" and is used to enforce corresponding
|
||||
configuration at your conterpart side. Default: disabled.
|
||||
</descrip>
|
||||
|
||||
<sect1>Channel configuration
|
||||
|
@ -3124,6 +3148,11 @@ some of them (marked with `<tt/O/') are optional.
|
|||
This attribute contains accumulated IGP metric, which is a total
|
||||
distance to the destination through multiple autonomous systems.
|
||||
Currently, the attribute is not accessible from filters.
|
||||
|
||||
<tag><label id="bgp-otc">int bgp_otc [O]</tag>
|
||||
This attribute is defined in <rfc id="9234">. OTC is a flag that marks
|
||||
routes that should be sent only to customers. If <ref id="bgp-role"
|
||||
name="local Role"> is configured it set automatically.
|
||||
</descrip>
|
||||
|
||||
<sect1>Example
|
||||
|
|
|
@ -903,6 +903,18 @@ bgp_decode_large_community(struct bgp_parse_state *s, uint code UNUSED, uint fla
|
|||
bgp_set_attr_ptr(to, s->pool, BA_LARGE_COMMUNITY, flags, ad);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
bgp_decode_otc(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data UNUSED, uint len, ea_list **to)
|
||||
{
|
||||
if (len != 4)
|
||||
WITHDRAW(BAD_LENGTH, "OTC", len);
|
||||
|
||||
u32 val = get_u32(data);
|
||||
bgp_set_attr_u32(to, s->pool, BA_ONLY_TO_CUSTOMER, flags, val);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a)
|
||||
{
|
||||
|
@ -1116,6 +1128,13 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
|
|||
.encode = bgp_encode_u32s,
|
||||
.decode = bgp_decode_large_community,
|
||||
},
|
||||
[BA_ONLY_TO_CUSTOMER] = {
|
||||
.name = "otc",
|
||||
.type = EAF_TYPE_INT,
|
||||
.flags = BAF_OPTIONAL | BAF_TRANSITIVE,
|
||||
.encode = bgp_encode_u32,
|
||||
.decode = bgp_decode_otc,
|
||||
},
|
||||
[BA_MPLS_LABEL_STACK] = {
|
||||
.name = "mpls_label_stack",
|
||||
.type = EAF_TYPE_INT_SET,
|
||||
|
@ -1452,6 +1471,29 @@ bgp_finish_attrs(struct bgp_parse_state *s, rta *a)
|
|||
REPORT("Discarding AIGP attribute received on non-AIGP session");
|
||||
bgp_unset_attr(&a->eattrs, s->pool, BA_AIGP);
|
||||
}
|
||||
|
||||
/* Handle OTC ingress procedure, RFC 9234 */
|
||||
if (bgp_channel_is_role_applicable(s->channel))
|
||||
{
|
||||
struct bgp_proto *p = s->proto;
|
||||
eattr *e = bgp_find_attr(a->eattrs, BA_ONLY_TO_CUSTOMER);
|
||||
|
||||
/* Reject routes from downstream if they are leaked */
|
||||
if (e && (p->cf->local_role == BGP_ROLE_PROVIDER ||
|
||||
p->cf->local_role == BGP_ROLE_RS_SERVER))
|
||||
WITHDRAW("Route leak detected - OTC attribute from downstream");
|
||||
|
||||
/* Reject routes from peers if they are leaked */
|
||||
if (e && (p->cf->local_role == BGP_ROLE_PEER) && (e->u.data != p->cf->remote_as))
|
||||
WITHDRAW("Route leak detected - OTC attribute with mismatched ASN (%u)",
|
||||
(uint) e->u.data);
|
||||
|
||||
/* Mark routes from upstream if it did not happened before */
|
||||
if (!e && (p->cf->local_role == BGP_ROLE_CUSTOMER ||
|
||||
p->cf->local_role == BGP_ROLE_PEER ||
|
||||
p->cf->local_role == BGP_ROLE_RS_CLIENT))
|
||||
bgp_set_attr_u32(&a->eattrs, s->pool, BA_ONLY_TO_CUSTOMER, 0, p->cf->remote_as);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1681,6 +1723,7 @@ bgp_preexport(struct channel *C, rte *e)
|
|||
struct proto *SRC = e->src->proto;
|
||||
struct bgp_proto *p = (struct bgp_proto *) C->proto;
|
||||
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
|
||||
struct bgp_channel *c = (struct bgp_channel *) C;
|
||||
|
||||
/* Reject our routes */
|
||||
if (src == p)
|
||||
|
@ -1708,11 +1751,11 @@ bgp_preexport(struct channel *C, rte *e)
|
|||
}
|
||||
|
||||
/* Handle well-known communities, RFC 1997 */
|
||||
struct eattr *c;
|
||||
struct eattr *a;
|
||||
if (p->cf->interpret_communities &&
|
||||
(c = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY))))
|
||||
(a = bgp_find_attr(e->attrs->eattrs, BA_COMMUNITY)))
|
||||
{
|
||||
const struct adata *d = c->u.ptr;
|
||||
const struct adata *d = a->u.ptr;
|
||||
|
||||
/* Do not export anywhere */
|
||||
if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
|
||||
|
@ -1731,6 +1774,16 @@ bgp_preexport(struct channel *C, rte *e)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Do not export routes marked with OTC to upstream, RFC 9234 */
|
||||
if (bgp_channel_is_role_applicable(c))
|
||||
{
|
||||
a = bgp_find_attr(e->attrs->eattrs, BA_ONLY_TO_CUSTOMER);
|
||||
if (a && (p->cf->local_role==BGP_ROLE_CUSTOMER ||
|
||||
p->cf->local_role==BGP_ROLE_PEER ||
|
||||
p->cf->local_role==BGP_ROLE_RS_CLIENT))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1840,6 +1893,16 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
|
|||
}
|
||||
}
|
||||
|
||||
/* Mark routes for downstream with OTC, RFC 9234 */
|
||||
if (bgp_channel_is_role_applicable(c))
|
||||
{
|
||||
a = bgp_find_attr(attrs, BA_ONLY_TO_CUSTOMER);
|
||||
if (!a && (p->cf->local_role == BGP_ROLE_PROVIDER ||
|
||||
p->cf->local_role == BGP_ROLE_PEER ||
|
||||
p->cf->local_role == BGP_ROLE_RS_SERVER))
|
||||
bgp_set_attr_u32(&attrs, pool, BA_ONLY_TO_CUSTOMER, 0, p->public_as);
|
||||
}
|
||||
|
||||
/*
|
||||
* Presence of mandatory attributes ORIGIN and AS_PATH is ensured by above
|
||||
* conditions. Presence and validity of quasi-mandatory NEXT_HOP attribute
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
* RFC 8212 - Default EBGP Route Propagation Behavior without Policies
|
||||
* RFC 8654 - Extended Message Support for BGP
|
||||
* RFC 9117 - Revised Validation Procedure for BGP Flow Specifications
|
||||
* RFC 9234 - Route Leak Prevention and Detection Using Roles
|
||||
* draft-ietf-idr-ext-opt-param-07
|
||||
* draft-uttaro-idr-bgp-persistence-04
|
||||
* draft-walton-bgp-hostname-capability-02
|
||||
|
@ -1963,6 +1964,15 @@ bgp_postconfig(struct proto_config *CF)
|
|||
if (internal && cf->rs_client)
|
||||
cf_error("Only external neighbor can be RS client");
|
||||
|
||||
if (internal && (cf->local_role != BGP_ROLE_UNDEFINED))
|
||||
cf_error("Local role cannot be set on IBGP sessions");
|
||||
|
||||
if (interior && (cf->local_role != BGP_ROLE_UNDEFINED))
|
||||
log(L_WARN "BGP roles are not recommended to be used within AS confederations");
|
||||
|
||||
if (cf->require_roles && (cf->local_role == BGP_ROLE_UNDEFINED))
|
||||
cf_error("Local role must be set if roles are required");
|
||||
|
||||
if (!cf->confederation && cf->confederation_member)
|
||||
cf_error("Confederation ID must be set for member sessions");
|
||||
|
||||
|
@ -2325,6 +2335,15 @@ bgp_show_afis(int code, char *s, u32 *afis, uint count)
|
|||
cli_msg(code, b.start);
|
||||
}
|
||||
|
||||
static const char *
|
||||
bgp_format_role_name(u8 role)
|
||||
{
|
||||
static const char *bgp_role_names[] = { "provider", "rs_server", "rs_client", "customer", "peer" };
|
||||
if (role == BGP_ROLE_UNDEFINED) return "undefined";
|
||||
if (role < ARRAY_SIZE(bgp_role_names)) return bgp_role_names[role];
|
||||
return "?";
|
||||
}
|
||||
|
||||
static void
|
||||
bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps)
|
||||
{
|
||||
|
@ -2453,6 +2472,9 @@ bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps)
|
|||
|
||||
if (caps->hostname)
|
||||
cli_msg(-1006, " Hostname: %s", caps->hostname);
|
||||
|
||||
if (caps->role != BGP_ROLE_UNDEFINED)
|
||||
cli_msg(-1006, " Role: %s", bgp_format_role_name(caps->role));
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -113,6 +113,8 @@ struct bgp_config {
|
|||
int gr_mode; /* Graceful restart mode (BGP_GR_*) */
|
||||
int llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */
|
||||
int setkey; /* Set MD5 password to system SA/SP database */
|
||||
u8 local_role; /* Set peering role with neighbor [RFC 9234] */
|
||||
int require_roles; /* Require configured roles on both sides */
|
||||
/* Times below are in seconds */
|
||||
unsigned gr_time; /* Graceful restart timeout */
|
||||
unsigned llgr_time; /* Long-lived graceful restart stale time */
|
||||
|
@ -166,6 +168,13 @@ struct bgp_channel_config {
|
|||
#define BGP_PT_INTERNAL 1
|
||||
#define BGP_PT_EXTERNAL 2
|
||||
|
||||
#define BGP_ROLE_UNDEFINED 255
|
||||
#define BGP_ROLE_PROVIDER 0
|
||||
#define BGP_ROLE_RS_SERVER 1
|
||||
#define BGP_ROLE_RS_CLIENT 2
|
||||
#define BGP_ROLE_CUSTOMER 3
|
||||
#define BGP_ROLE_PEER 4
|
||||
|
||||
#define NH_NO 0
|
||||
#define NH_ALL 1
|
||||
#define NH_IBGP 2
|
||||
|
@ -226,6 +235,7 @@ struct bgp_caps {
|
|||
u8 ext_messages; /* Extended message length, RFC draft */
|
||||
u8 route_refresh; /* Route refresh capability, RFC 2918 */
|
||||
u8 enhanced_refresh; /* Enhanced route refresh, RFC 7313 */
|
||||
u8 role; /* BGP role capability, RFC 9234 */
|
||||
|
||||
u8 gr_aware; /* Graceful restart capability, RFC 4724 */
|
||||
u8 gr_flags; /* Graceful restart flags */
|
||||
|
@ -487,6 +497,12 @@ static inline int bgp_cc_is_ipv4(struct bgp_channel_config *c)
|
|||
static inline int bgp_cc_is_ipv6(struct bgp_channel_config *c)
|
||||
{ return BGP_AFI(c->afi) == BGP_AFI_IPV6; }
|
||||
|
||||
static inline int bgp_channel_is_role_applicable(struct bgp_channel *c)
|
||||
{ return (c->afi == BGP_AF_IPV4 || c->afi == BGP_AF_IPV6); }
|
||||
|
||||
static inline int bgp_cc_is_role_applicable(struct bgp_channel_config *c)
|
||||
{ return (c->afi == BGP_AF_IPV4 || c->afi == BGP_AF_IPV6); }
|
||||
|
||||
static inline uint bgp_max_packet_length(struct bgp_conn *conn)
|
||||
{ return conn->ext_messages ? BGP_MAX_EXT_MSG_LENGTH : BGP_MAX_MESSAGE_LENGTH; }
|
||||
|
||||
|
@ -658,6 +674,7 @@ void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
|
|||
#define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */
|
||||
#define BA_AIGP 0x1a /* RFC 7311 */
|
||||
#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
|
||||
#define BA_ONLY_TO_CUSTOMER 0x23 /* RFC 9234 */
|
||||
|
||||
/* Bird's private internal BGP attributes */
|
||||
#define BA_MPLS_LABEL_STACK 0xfe /* MPLS label stack transfer attribute */
|
||||
|
|
|
@ -31,7 +31,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
|||
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG,
|
||||
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
|
||||
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
|
||||
FIRST, FREE, VALIDATE, BASE)
|
||||
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
|
||||
RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC)
|
||||
|
||||
%type <i> bgp_nh
|
||||
%type <i32> bgp_afi
|
||||
|
@ -40,7 +41,7 @@ CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
|
|||
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
|
||||
OUT, OF, RESOURCES)
|
||||
|
||||
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag
|
||||
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
|
@ -72,6 +73,7 @@ bgp_proto_start: proto_start BGP {
|
|||
BGP_CFG->llgr_mode = -1;
|
||||
BGP_CFG->llgr_time = 3600;
|
||||
BGP_CFG->setkey = 1;
|
||||
BGP_CFG->local_role = BGP_ROLE_UNDEFINED;
|
||||
BGP_CFG->dynamic_name = "dynbgp";
|
||||
BGP_CFG->check_link = -1;
|
||||
}
|
||||
|
@ -114,6 +116,14 @@ bgp_cease_flag:
|
|||
| OUT OF RESOURCES { $$ = 1 << 8; }
|
||||
;
|
||||
|
||||
bgp_role_name:
|
||||
PEER { $$ = BGP_ROLE_PEER; }
|
||||
| PROVIDER { $$ = BGP_ROLE_PROVIDER; }
|
||||
| CUSTOMER { $$ = BGP_ROLE_CUSTOMER; }
|
||||
| RS_SERVER { $$ = BGP_ROLE_RS_SERVER; }
|
||||
| RS_CLIENT { $$ = BGP_ROLE_RS_CLIENT; }
|
||||
;
|
||||
|
||||
bgp_proto:
|
||||
bgp_proto_start proto_name '{'
|
||||
| bgp_proto proto_item ';'
|
||||
|
@ -197,6 +207,8 @@ bgp_proto:
|
|||
| bgp_proto BFD GRACEFUL ';' { init_bfd_opts(&BGP_CFG->bfd); BGP_CFG->bfd->mode = BGP_BFD_GRACEFUL; }
|
||||
| bgp_proto BFD { open_bfd_opts(&BGP_CFG->bfd); } bfd_opts { close_bfd_opts(); } ';'
|
||||
| bgp_proto ENFORCE FIRST AS bool ';' { BGP_CFG->enforce_first_as = $5; }
|
||||
| bgp_proto LOCAL ROLE bgp_role_name ';' { BGP_CFG->local_role = $4; }
|
||||
| bgp_proto REQUIRE ROLES bool ';' { BGP_CFG->require_roles = $4; }
|
||||
;
|
||||
|
||||
bgp_afi:
|
||||
|
@ -343,6 +355,8 @@ dynamic_attr: BGP_AIGP
|
|||
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ;
|
||||
dynamic_attr: BGP_LARGE_COMMUNITY
|
||||
{ $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ;
|
||||
dynamic_attr: BGP_OTC
|
||||
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_ONLY_TO_CUSTOMER)); } ;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -238,6 +238,7 @@ bgp_prepare_capabilities(struct bgp_conn *conn)
|
|||
caps->ext_messages = p->cf->enable_extended_messages;
|
||||
caps->route_refresh = p->cf->enable_refresh;
|
||||
caps->enhanced_refresh = p->cf->enable_refresh;
|
||||
caps->role = p->cf->local_role;
|
||||
|
||||
if (caps->as4_support)
|
||||
caps->as4_number = p->public_as;
|
||||
|
@ -350,6 +351,13 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
|
|||
*buf++ = 0; /* Capability data length */
|
||||
}
|
||||
|
||||
if (caps->role != BGP_ROLE_UNDEFINED)
|
||||
{
|
||||
*buf++ = 9; /* Capability 9: Announce chosen BGP role */
|
||||
*buf++ = 1; /* Capability data length */
|
||||
*buf++ = caps->role;
|
||||
}
|
||||
|
||||
if (caps->gr_aware)
|
||||
{
|
||||
*buf++ = 64; /* Capability 64: Support for graceful restart */
|
||||
|
@ -449,11 +457,15 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
|||
struct bgp_proto *p = conn->bgp;
|
||||
struct bgp_caps *caps;
|
||||
struct bgp_af_caps *ac;
|
||||
uint err_subcode = 0;
|
||||
int i, cl;
|
||||
u32 af;
|
||||
|
||||
if (!conn->remote_caps)
|
||||
{
|
||||
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps));
|
||||
caps->role = BGP_ROLE_UNDEFINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
caps = conn->remote_caps;
|
||||
|
@ -513,6 +525,21 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
|||
caps->ext_messages = 1;
|
||||
break;
|
||||
|
||||
case 9: /* BGP role capability, RFC 9234 */
|
||||
if (cl != 1)
|
||||
goto err;
|
||||
|
||||
/* Reserved value */
|
||||
if (pos[2] == BGP_ROLE_UNDEFINED)
|
||||
{ err_subcode = 11; goto err; }
|
||||
|
||||
/* Multiple inconsistent values */
|
||||
if ((caps->role != BGP_ROLE_UNDEFINED) && (caps->role != pos[2]))
|
||||
{ err_subcode = 11; goto err; }
|
||||
|
||||
caps->role = pos[2];
|
||||
break;
|
||||
|
||||
case 64: /* Graceful restart capability, RFC 4724 */
|
||||
if (cl % 4 != 2)
|
||||
goto err;
|
||||
|
@ -638,7 +665,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
|||
|
||||
err:
|
||||
mb_free(caps);
|
||||
bgp_error(conn, 2, 0, NULL, 0);
|
||||
bgp_error(conn, 2, err_subcode, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -854,6 +881,22 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
|
|||
conn->received_as = asn;
|
||||
}
|
||||
|
||||
/* RFC 9234 4.2 - check role agreement */
|
||||
u8 local_role = p->cf->local_role;
|
||||
u8 neigh_role = caps->role;
|
||||
|
||||
if ((local_role != BGP_ROLE_UNDEFINED) &&
|
||||
(neigh_role != BGP_ROLE_UNDEFINED) &&
|
||||
!((local_role == BGP_ROLE_PEER && neigh_role == BGP_ROLE_PEER) ||
|
||||
(local_role == BGP_ROLE_CUSTOMER && neigh_role == BGP_ROLE_PROVIDER) ||
|
||||
(local_role == BGP_ROLE_PROVIDER && neigh_role == BGP_ROLE_CUSTOMER) ||
|
||||
(local_role == BGP_ROLE_RS_CLIENT && neigh_role == BGP_ROLE_RS_SERVER) ||
|
||||
(local_role == BGP_ROLE_RS_SERVER && neigh_role == BGP_ROLE_RS_CLIENT)))
|
||||
{ bgp_error(conn, 2, 11, NULL, 0); return; }
|
||||
|
||||
if ((p->cf->require_roles) && (neigh_role == BGP_ROLE_UNDEFINED))
|
||||
{ bgp_error(conn, 2, 11, NULL, 0); return; }
|
||||
|
||||
/* Check the other connection */
|
||||
other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
|
||||
switch (other->state)
|
||||
|
@ -2985,6 +3028,7 @@ static struct {
|
|||
{ 2, 6, "Unacceptable hold time" },
|
||||
{ 2, 7, "Required capability missing" }, /* [RFC5492] */
|
||||
{ 2, 8, "No supported AFI/SAFI" }, /* This error msg is nonstandard */
|
||||
{ 2,11, "Role mismatch" }, /* From Open Policy, RFC 9234 */
|
||||
{ 3, 0, "Invalid UPDATE message" },
|
||||
{ 3, 1, "Malformed attribute list" },
|
||||
{ 3, 2, "Unrecognized well-known attribute" },
|
||||
|
|
Loading…
Reference in a new issue