summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.c47
-rw-r--r--bgpd/bgp_attr.h3
-rw-r--r--bgpd/bgp_debug.c1
-rw-r--r--bgpd/bgp_fsm.c4
-rw-r--r--bgpd/bgp_open.c64
-rw-r--r--bgpd/bgp_open.h2
-rw-r--r--bgpd/bgp_route.c53
-rw-r--r--bgpd/bgp_updgrp.c11
-rw-r--r--bgpd/bgp_vty.c150
-rw-r--r--bgpd/bgpd.c51
-rw-r--r--bgpd/bgpd.h30
-rw-r--r--doc/user/bgp.rst59
-rw-r--r--doc/user/overview.rst2
-rw-r--r--lib/command.h3
-rw-r--r--tests/bgpd/test_capability.c29
-rw-r--r--tests/bgpd/test_capability.py3
-rw-r--r--tests/topotests/bgp_roles_capability/__init__.py0
-rw-r--r--tests/topotests/bgp_roles_capability/r1/bgpd.conf15
-rw-r--r--tests/topotests/bgp_roles_capability/r1/zebra.conf15
-rw-r--r--tests/topotests/bgp_roles_capability/r2/bgpd.conf4
-rw-r--r--tests/topotests/bgp_roles_capability/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_capability/r3/bgpd.conf3
-rw-r--r--tests/topotests/bgp_roles_capability/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_capability/r4/bgpd.conf2
-rw-r--r--tests/topotests/bgp_roles_capability/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_capability/r5/bgpd.conf2
-rw-r--r--tests/topotests/bgp_roles_capability/r5/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_capability/roles_capability_stand.dot15
-rw-r--r--tests/topotests/bgp_roles_capability/roles_capability_stand.jpgbin0 -> 22695 bytes
-rw-r--r--tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py136
-rw-r--r--tests/topotests/bgp_roles_filtering/__init__.py0
-rw-r--r--tests/topotests/bgp_roles_filtering/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_roles_filtering/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r10/bgpd.conf21
-rw-r--r--tests/topotests/bgp_roles_filtering/r10/zebra.conf24
-rw-r--r--tests/topotests/bgp_roles_filtering/r2/bgpd.conf12
-rw-r--r--tests/topotests/bgp_roles_filtering/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r3/bgpd.conf12
-rw-r--r--tests/topotests/bgp_roles_filtering/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r4/bgpd.conf11
-rw-r--r--tests/topotests/bgp_roles_filtering/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r5/bgpd.conf11
-rw-r--r--tests/topotests/bgp_roles_filtering/r5/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r6/bgpd.conf11
-rw-r--r--tests/topotests/bgp_roles_filtering/r6/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r7/bgpd.conf11
-rw-r--r--tests/topotests/bgp_roles_filtering/r7/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot21
-rw-r--r--tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpgbin0 -> 54044 bytes
-rw-r--r--tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py141
50 files changed, 1056 insertions, 1 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 6784e63206..454e134c8e 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -76,6 +76,7 @@ static const struct message attr_str[] = {
{BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT"},
{BGP_ATTR_PMSI_TUNNEL, "PMSI_TUNNEL_ATTRIBUTE"},
{BGP_ATTR_ENCAP, "ENCAP"},
+ {BGP_ATTR_OTC, "OTC"},
#ifdef ENABLE_BGP_VNC_ATTR
{BGP_ATTR_VNC, "VNC"},
#endif
@@ -700,6 +701,7 @@ unsigned int attrhash_key_make(const void *p)
MIX(attr->rmap_table_id);
MIX(attr->nh_type);
MIX(attr->bh_type);
+ MIX(attr->otc);
return key;
}
@@ -762,7 +764,8 @@ bool attrhash_cmp(const void *p1, const void *p2)
&& srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn)
&& attr1->srte_color == attr2->srte_color
&& attr1->nh_type == attr2->nh_type
- && attr1->bh_type == attr2->bh_type)
+ && attr1->bh_type == attr2->bh_type
+ && attr1->otc == attr2->otc)
return true;
}
@@ -1381,6 +1384,7 @@ const uint8_t attr_flags_values[] = {
[BGP_ATTR_PMSI_TUNNEL] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_LARGE_COMMUNITIES] =
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_OTC] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
[BGP_ATTR_IPV6_EXT_COMMUNITIES] =
BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
@@ -3027,6 +3031,28 @@ bgp_attr_pmsi_tunnel(struct bgp_attr_parser_args *args)
return BGP_ATTR_PARSE_PROCEED;
}
+/* OTC attribute. */
+static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* Length check. */
+ if (length != 4) {
+ flog_err(EC_BGP_ATTR_LEN, "OTC attribute length isn't 4 [%u]",
+ length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ attr->otc = stream_getl(peer->curr);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
/* BGP unknown attribute treatment. */
static enum bgp_attr_parse_ret
bgp_attr_unknown(struct bgp_attr_parser_args *args)
@@ -3375,6 +3401,9 @@ enum bgp_attr_parse_ret bgp_attr_parse(struct peer *peer, struct attr *attr,
case BGP_ATTR_IPV6_EXT_COMMUNITIES:
ret = bgp_attr_ipv6_ext_communities(&attr_args);
break;
+ case BGP_ATTR_OTC:
+ ret = bgp_attr_otc(&attr_args);
+ break;
default:
ret = bgp_attr_unknown(&attr_args);
break;
@@ -4393,6 +4422,14 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
// Unicast tunnel endpoint IP address
}
+ /* OTC */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_OTC);
+ stream_putc(s, 4);
+ stream_putl(s, attr->otc);
+ }
+
/* Unknown transit attribute. */
struct transit *transit = bgp_attr_get_transit(attr);
@@ -4640,6 +4677,14 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
}
}
+ /* OTC */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_OTC);
+ stream_putc(s, 4);
+ stream_putl(s, attr->otc);
+ }
+
/* Return total size of attribute. */
len = stream_get_endp(s) - cp - 2;
stream_putw_at(s, cp, len);
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 06f350b36f..5007fafc29 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -334,6 +334,9 @@ struct attr {
/* If NEXTHOP_TYPE_BLACKHOLE, then blackhole type */
enum blackhole_type bh_type;
+
+ /* OTC value if set */
+ uint32_t otc;
};
/* rmap_change_flags definition */
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 8976b3b674..fbe967d8b2 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -141,6 +141,7 @@ static const struct message bgp_notify_open_msg[] = {
{BGP_NOTIFY_OPEN_AUTH_FAILURE, "/Authentication Failure"},
{BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, "/Unacceptable Hold Time"},
{BGP_NOTIFY_OPEN_UNSUP_CAPBL, "/Unsupported Capability"},
+ {BGP_NOTIFY_OPEN_ROLE_MISMATCH, "/Role Mismatch"},
{0}};
static const struct message bgp_notify_update_msg[] = {
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index fe4ab272ac..044e72cc1e 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -243,6 +243,7 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
peer->v_delayopen = from_peer->v_delayopen;
peer->v_gr_restart = from_peer->v_gr_restart;
peer->cap = from_peer->cap;
+ peer->neighbor_role = from_peer->neighbor_role;
status = peer->status;
pstatus = peer->ostatus;
last_evt = peer->last_event;
@@ -1526,6 +1527,9 @@ int bgp_stop(struct peer *peer)
/* Reset capabilities. */
peer->cap = 0;
+ /* Resetting neighbor role to the default value */
+ peer->neighbor_role = ROLE_UNDEFINE;
+
FOREACH_AFI_SAFI (afi, safi) {
/* Reset all negotiated variables */
peer->afc_nego[afi][safi] = 0;
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index fa3fa3fcee..d8dd71788b 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -58,6 +58,7 @@ static const struct message capcode_str[] = {
{CAPABILITY_CODE_ENHANCED_RR, "Enhanced Route Refresh"},
{CAPABILITY_CODE_EXT_MESSAGE, "BGP Extended Message"},
{CAPABILITY_CODE_LLGR, "Long-lived BGP Graceful Restart"},
+ {CAPABILITY_CODE_ROLE, "Role"},
{0}};
/* Minimum sizes for length field of each cap (so not inc. the header) */
@@ -77,6 +78,7 @@ static const size_t cap_minsizes[] = {
[CAPABILITY_CODE_ENHANCED_RR] = CAPABILITY_CODE_ENHANCED_LEN,
[CAPABILITY_CODE_EXT_MESSAGE] = CAPABILITY_CODE_EXT_MESSAGE_LEN,
[CAPABILITY_CODE_LLGR] = CAPABILITY_CODE_LLGR_LEN,
+ [CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN,
};
/* value the capability must be a multiple of.
@@ -100,6 +102,7 @@ static const size_t cap_modsizes[] = {
[CAPABILITY_CODE_ENHANCED_RR] = 1,
[CAPABILITY_CODE_EXT_MESSAGE] = 1,
[CAPABILITY_CODE_LLGR] = 1,
+ [CAPABILITY_CODE_ROLE] = 1,
};
/* BGP-4 Multiprotocol Extentions lead us to the complex world. We can
@@ -887,6 +890,20 @@ static int bgp_capability_hostname(struct peer *peer,
return 0;
}
+static int bgp_capability_role(struct peer *peer, struct capability_header *hdr)
+{
+ SET_FLAG(peer->cap, PEER_CAP_ROLE_RCV);
+ if (hdr->length != CAPABILITY_CODE_ROLE_LEN) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Role: Received invalid length %d", hdr->length);
+ return -1;
+ }
+ uint8_t role = stream_getc(BGP_INPUT(peer));
+
+ peer->neighbor_role = role;
+ return 0;
+}
+
/**
* Parse given capability.
* XXX: This is reading into a stream, but not using stream API
@@ -954,6 +971,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_FQDN:
case CAPABILITY_CODE_ENHANCED_RR:
case CAPABILITY_CODE_EXT_MESSAGE:
+ case CAPABILITY_CODE_ROLE:
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code]) {
zlog_info(
@@ -1051,6 +1069,9 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_FQDN:
ret = bgp_capability_hostname(peer, &caphdr);
break;
+ case CAPABILITY_CODE_ROLE:
+ ret = bgp_capability_role(peer, &caphdr);
+ break;
default:
if (caphdr.code > 128) {
/* We don't send Notification for unknown vendor
@@ -1113,6 +1134,35 @@ static bool strict_capability_same(struct peer *peer)
return true;
}
+
+static bool bgp_role_violation(struct peer *peer)
+{
+ uint8_t local_role = peer->local_role;
+ uint8_t neigh_role = peer->neighbor_role;
+
+ if (local_role != ROLE_UNDEFINE && neigh_role != ROLE_UNDEFINE &&
+ !((local_role == ROLE_PEER && neigh_role == ROLE_PEER) ||
+ (local_role == ROLE_PROVIDER && neigh_role == ROLE_CUSTOMER) ||
+ (local_role == ROLE_CUSTOMER && neigh_role == ROLE_PROVIDER) ||
+ (local_role == ROLE_RS_SERVER && neigh_role == ROLE_RS_CLIENT) ||
+ (local_role == ROLE_RS_CLIENT && neigh_role == ROLE_RS_SERVER))) {
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_ROLE_MISMATCH);
+ return true;
+ }
+ if (neigh_role == ROLE_UNDEFINE &&
+ CHECK_FLAG(peer->flags, PEER_FLAG_STRICT_MODE)) {
+ const char *err_msg =
+ "Strict mode. Please set the role on your side.";
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_ROLE_MISMATCH,
+ (uint8_t *)err_msg, strlen(err_msg));
+ return true;
+ }
+ return false;
+}
+
+
/* peek into option, stores ASN to *as4 if the AS4 capability was found.
* Returns 0 if no as4 found, as4cap value otherwise.
*/
@@ -1297,6 +1347,10 @@ int bgp_open_option_parse(struct peer *peer, uint16_t length,
? BGP_EXTENDED_MESSAGE_MAX_PACKET_SIZE
: BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE;
+ /* Check that roles are corresponding to each other */
+ if (bgp_role_violation(peer))
+ return -1;
+
/* Check there are no common AFI/SAFIs and send Unsupported Capability
error. */
if (*mp_capability
@@ -1674,6 +1728,16 @@ uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
stream_putc(s, CAPABILITY_CODE_EXT_MESSAGE);
stream_putc(s, CAPABILITY_CODE_EXT_MESSAGE_LEN);
+ /* Role*/
+ if (peer->local_role != ROLE_UNDEFINE) {
+ SET_FLAG(peer->cap, PEER_CAP_ROLE_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ stream_putc(s, CAPABILITY_CODE_ROLE_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_ROLE);
+ stream_putc(s, CAPABILITY_CODE_ROLE_LEN);
+ stream_putc(s, peer->local_role);
+ }
+
/* AddPath */
FOREACH_AFI_SAFI (afi, safi) {
if (peer->afc[afi][safi]) {
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
index 1727b66041..19ddd9bd25 100644
--- a/bgpd/bgp_open.h
+++ b/bgpd/bgp_open.h
@@ -56,6 +56,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */
#define CAPABILITY_CODE_ORF_OLD 130 /* Cooperative Route Filtering Capability(cisco) */
#define CAPABILITY_CODE_EXT_MESSAGE 6 /* Extended Message Support */
+#define CAPABILITY_CODE_ROLE 9 /* Role Capability */
/* Capability Length */
#define CAPABILITY_CODE_MP_LEN 4
@@ -70,6 +71,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_LLGR_LEN 0
#define CAPABILITY_CODE_ORF_LEN 5
#define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
+#define CAPABILITY_CODE_ROLE_LEN 1
/* Cooperative Route Filtering Capability. */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index f9d01913a8..90e3f88058 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1561,6 +1561,43 @@ static bool bgp_cluster_filter(struct peer *peer, struct attr *attr)
return false;
}
+static bool bgp_otc_filter(struct peer *peer, struct attr *attr)
+{
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ if (peer->local_role == ROLE_PROVIDER ||
+ peer->local_role == ROLE_RS_SERVER)
+ return true;
+ if (peer->local_role == ROLE_PEER && attr->otc != peer->as)
+ return true;
+ return false;
+ }
+ if (peer->local_role == ROLE_CUSTOMER ||
+ peer->local_role == ROLE_PEER ||
+ peer->local_role == ROLE_RS_CLIENT) {
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
+ attr->otc = peer->as;
+ }
+ return false;
+}
+
+static bool bgp_otc_egress(struct peer *peer, struct attr *attr)
+{
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ if (peer->local_role == ROLE_CUSTOMER ||
+ peer->local_role == ROLE_RS_CLIENT ||
+ peer->local_role == ROLE_PEER)
+ return true;
+ return false;
+ }
+ if (peer->local_role == ROLE_PROVIDER ||
+ peer->local_role == ROLE_PEER ||
+ peer->local_role == ROLE_RS_SERVER) {
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
+ attr->otc = peer->bgp->as;
+ }
+ return false;
+}
+
static int bgp_input_modifier(struct peer *peer, const struct prefix *p,
struct attr *attr, afi_t afi, safi_t safi,
const char *rmap_name, mpls_label_t *label,
@@ -2165,6 +2202,9 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
memset(&attr->mp_nexthop_local, 0, IPV6_MAX_BYTELEN);
}
+ if (bgp_otc_egress(peer, attr))
+ return false;
+
bgp_peer_remove_private_as(bgp, afi, safi, peer, attr);
bgp_peer_as_override(bgp, afi, safi, peer, attr);
@@ -3961,6 +4001,12 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
goto filtered;
}
+ if (bgp_otc_filter(peer, &new_attr)) {
+ reason = "failing otc validation";
+ bgp_attr_flush(&new_attr);
+ goto filtered;
+ }
+
/* The flag BGP_NODE_FIB_INSTALL_PENDING is for the following
* condition :
* Suppress fib is enabled
@@ -10447,6 +10493,13 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
vty_out(vty, ", atomic-aggregate");
}
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ if (json_paths)
+ json_object_int_add(json_path, "otc", attr->otc);
+ else
+ vty_out(vty, ", otc %u", attr->otc);
+ }
+
if (CHECK_FLAG(path->flags, BGP_PATH_MULTIPATH)
|| (CHECK_FLAG(path->flags, BGP_PATH_SELECTED)
&& bgp_path_info_mpath_count(path))) {
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index c5d049f363..f113eccd39 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -159,6 +159,7 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
dst->local_as = src->local_as;
dst->change_local_as = src->change_local_as;
dst->shared_network = src->shared_network;
+ dst->local_role = src->local_role;
memcpy(&(dst->nexthop), &(src->nexthop), sizeof(struct bgp_nexthop));
dst->group = src->group;
@@ -308,6 +309,7 @@ static void *updgrp_hash_alloc(void *p)
* 15. If peer is configured to be a lonesoul, peer ip address
* 16. Local-as should match, if configured.
* 17. maximum-prefix-out
+ * 18. Local-role should also match, if configured.
* )
*/
static unsigned int updgrp_hash_key_make(const void *p)
@@ -411,6 +413,11 @@ static unsigned int updgrp_hash_key_make(const void *p)
|| CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT))
key = jhash_1word(jhash(peer->host, strlen(peer->host), SEED2),
key);
+ /*
+ * Multiple sessions with the same neighbor should get their own
+ * update-group if they have different roles.
+ */
+ key = jhash_1word(peer->local_role, key);
if (bgp_debug_neighbor_events(peer)) {
zlog_debug(
@@ -532,6 +539,10 @@ static bool updgrp_hash_cmp(const void *p1, const void *p2)
if (pe1->group != pe2->group)
return false;
+ /* Roles can affect filtering */
+ if (pe1->local_role != pe2->local_role)
+ return false;
+
/* route-map names should be the same */
if ((fl1->map[RMAP_OUT].name && !fl2->map[RMAP_OUT].name)
|| (!fl1->map[RMAP_OUT].name && fl2->map[RMAP_OUT].name)
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index bb88adbfc2..03b66c7d45 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -923,6 +923,12 @@ int bgp_vty_return(struct vty *vty, enum bgp_create_error_code ret)
case BGP_ERR_INVALID_AS:
str = "Confederation AS specified is the same AS as our AS.";
break;
+ case BGP_ERR_INVALID_ROLE_NAME:
+ str = "Invalid role name";
+ break;
+ case BGP_ERR_INVALID_INTERNAL_ROLE:
+ str = "Extrenal roles can be set only on eBGP session";
+ break;
}
if (str) {
vty_out(vty, "%% %s\n", str);
@@ -6405,6 +6411,91 @@ DEFUN (no_neighbor_ebgp_multihop,
return peer_ebgp_multihop_unset_vty(vty, argv[idx_peer]->arg);
}
+static uint8_t get_role_by_name(const char *role_str)
+{
+ if (strncmp(role_str, "peer", 2) == 0)
+ return ROLE_PEER;
+ if (strncmp(role_str, "provider", 2) == 0)
+ return ROLE_PROVIDER;
+ if (strncmp(role_str, "customer", 2) == 0)
+ return ROLE_CUSTOMER;
+ if (strncmp(role_str, "rs-server", 4) == 0)
+ return ROLE_RS_SERVER;
+ if (strncmp(role_str, "rs-client", 4) == 0)
+ return ROLE_RS_CLIENT;
+ return ROLE_UNDEFINE;
+}
+
+static int peer_role_set_vty(struct vty *vty, const char *ip_str,
+ const char *role_str, int strict_mode)
+{
+ struct peer *peer;
+
+ peer = peer_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+ uint8_t role = get_role_by_name(role_str);
+
+ if (role == ROLE_UNDEFINE)
+ return bgp_vty_return(vty, BGP_ERR_INVALID_ROLE_NAME);
+ return bgp_vty_return(vty, peer_role_set(peer, role, strict_mode));
+}
+
+static int peer_role_unset_vty(struct vty *vty, const char *ip_str)
+{
+ struct peer *peer;
+
+ peer = peer_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+ return bgp_vty_return(vty, peer_role_unset(peer));
+}
+
+DEFUN(neighbor_role,
+ neighbor_role_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> local-role <provider|rs-server|rs-client|customer|peer>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set session role\n"
+ ROLE_STR)
+{
+ int idx_peer = 1;
+ int idx_role = 3;
+
+ return peer_role_set_vty(vty, argv[idx_peer]->arg, argv[idx_role]->arg,
+ 0);
+}
+
+DEFUN(neighbor_role_strict,
+ neighbor_role_strict_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> local-role <provider|rs-server|rs-client|customer|peer> strict-mode",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set session role\n"
+ ROLE_STR
+ "Use additional restriction on peer\n")
+{
+ int idx_peer = 1;
+ int idx_role = 3;
+
+ return peer_role_set_vty(vty, argv[idx_peer]->arg, argv[idx_role]->arg,
+ 1);
+}
+
+DEFUN(no_neighbor_role,
+ no_neighbor_role_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> local-role <provider|rs-server|rs-client|customer|peer> [strict-mode]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Unset session role\n"
+ ROLE_STR
+ "Was used additional restriction on peer\n")
+{
+ int idx_peer = 2;
+
+ return peer_role_unset_vty(vty, argv[idx_peer]->arg);
+}
/* disable-connected-check */
DEFUN (neighbor_disable_connected_check,
@@ -12477,6 +12568,20 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
vty_out(vty, "unspecified link\n");
}
+ /* Roles */
+ if (use_json) {
+ json_object_string_add(json_neigh, "localRole",
+ get_name_by_role(p->local_role));
+ json_object_string_add(json_neigh, "neighRole",
+ get_name_by_role(p->neighbor_role));
+ } else {
+ vty_out(vty, " Local Role: %s\n",
+ get_name_by_role(p->local_role));
+ vty_out(vty, " Neighbor Role: %s\n",
+ get_name_by_role(p->neighbor_role));
+ }
+
+
/* Description. */
if (p->desc) {
if (use_json)
@@ -12941,6 +13046,22 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
"received");
}
+ /* Role */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV)) {
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV) &&
+ CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV))
+ json_object_string_add(
+ json_cap, "role",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV))
+ json_object_string_add(json_cap, "role",
+ "advertised");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV))
+ json_object_string_add(json_cap, "role",
+ "received");
+ }
+
/* Extended nexthop */
if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_ENHE_ADV)) {
@@ -13379,6 +13500,21 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
vty_out(vty, "\n");
}
+ /* Role */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV)) {
+ vty_out(vty, " Role:");
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(p->cap,
+ PEER_CAP_ROLE_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+ }
+
/* Extended nexthop */
if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_RCV) ||
CHECK_FLAG(p->cap, PEER_CAP_ENHE_ADV)) {
@@ -16657,6 +16793,15 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
}
}
+ /* role */
+ if (peer->local_role != ROLE_UNDEFINE) {
+ vty_out(vty, " neighbor %s local-role %s%s\n", addr,
+ get_name_by_role(peer->local_role),
+ CHECK_FLAG(peer->flags, PEER_FLAG_STRICT_MODE)
+ ? " strict-mode"
+ : "");
+ }
+
/* ttl-security hops */
if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED) {
if (!peer_group_active(peer)
@@ -17927,6 +18072,11 @@ void bgp_vty_init(void)
install_element(BGP_NODE, &bgp_maxmed_onstartup_cmd);
install_element(BGP_NODE, &no_bgp_maxmed_onstartup_cmd);
+ /* "neighbor role" commands. */
+ install_element(BGP_NODE, &neighbor_role_cmd);
+ install_element(BGP_NODE, &neighbor_role_strict_cmd);
+ install_element(BGP_NODE, &no_neighbor_role_cmd);
+
/* bgp disable-ebgp-connected-nh-check */
install_element(BGP_NODE, &bgp_disable_connected_route_check_cmd);
install_element(BGP_NODE, &no_bgp_disable_connected_route_check_cmd);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 7d284f28b3..4bf89a0375 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -1374,6 +1374,8 @@ struct peer *peer_new(struct bgp *bgp)
peer->cur_event = peer->last_event = peer->last_major_event = 0;
peer->bgp = bgp_lock(bgp);
peer = peer_lock(peer); /* initial reference */
+ peer->local_role = ROLE_UNDEFINE;
+ peer->neighbor_role = ROLE_UNDEFINE;
peer->password = NULL;
peer->max_packet_size = BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE;
@@ -1470,6 +1472,7 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
peer_dst->tcp_mss = peer_src->tcp_mss;
(void)peer_sort(peer_dst);
peer_dst->rmap_type = peer_src->rmap_type;
+ peer_dst->local_role = peer_src->local_role;
peer_dst->max_packet_size = peer_src->max_packet_size;
@@ -1996,6 +1999,17 @@ int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if,
return 0;
}
+const char *get_name_by_role(uint8_t role)
+{
+ static const char *const bgp_role_names[] = {
+ "provider", "rs-server", "rs-client", "customer", "peer"};
+ if (role == ROLE_UNDEFINE)
+ return "undefine";
+ if (role <= 5)
+ return bgp_role_names[role];
+ return "unknown";
+}
+
static void peer_group2peer_config_copy_af(struct peer_group *group,
struct peer *peer, afi_t afi,
safi_t safi)
@@ -4902,6 +4916,43 @@ int peer_ebgp_multihop_unset(struct peer *peer)
return 0;
}
+/* Set Open Policy Role and check its correctness */
+int peer_role_set(struct peer *peer, uint8_t role, int strict_mode)
+{
+ if (peer->local_role == role) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_STRICT_MODE) &&
+ !strict_mode)
+ /* TODO: Is session restart needed if it was down? */
+ UNSET_FLAG(peer->flags, PEER_FLAG_STRICT_MODE);
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_STRICT_MODE) &&
+ strict_mode) {
+ SET_FLAG(peer->flags, PEER_FLAG_STRICT_MODE);
+ /* Restart session to throw Role Mismatch Notification
+ */
+ if (peer->neighbor_role == ROLE_UNDEFINE)
+ bgp_session_reset(peer);
+ }
+ } else {
+ if (peer->sort == BGP_PEER_IBGP &&
+ (role == ROLE_CUSTOMER || role == ROLE_PROVIDER ||
+ role == ROLE_PEER || role == ROLE_RS_SERVER ||
+ role == ROLE_RS_CLIENT))
+ return BGP_ERR_INVALID_INTERNAL_ROLE;
+ peer->local_role = role;
+ if (strict_mode)
+ SET_FLAG(peer->flags, PEER_FLAG_STRICT_MODE);
+ else
+ UNSET_FLAG(peer->flags, PEER_FLAG_STRICT_MODE);
+ bgp_session_reset(peer);
+ }
+ return 0;
+}
+
+int peer_role_unset(struct peer *peer)
+{
+ return peer_role_set(peer, ROLE_UNDEFINE, 0);
+}
+
/* Neighbor description. */
void peer_description_set(struct peer *peer, const char *desc)
{
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 98e59bcc85..b30a3059b9 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1167,6 +1167,18 @@ struct peer {
int shared_network; /* Is this peer shared same network. */
struct bgp_nexthop nexthop; /* Nexthop */
+ /* Roles in bgp session */
+ uint8_t local_role;
+ uint8_t neighbor_role;
+#define ROLE_PROVIDER 0
+#define ROLE_RS_SERVER 1
+#define ROLE_RS_CLIENT 2
+#define ROLE_CUSTOMER 3
+#define ROLE_PEER 4
+#define ROLE_UNDEFINE 255
+
+#define ROLE_NAME_MAX_LEN 20
+
/* Peer address family configuration. */
uint8_t afc[AFI_MAX][SAFI_MAX];
uint8_t afc_nego[AFI_MAX][SAFI_MAX];
@@ -1204,6 +1216,8 @@ struct peer {
#define PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV (1U << 23)
/* received graceful-restart notification (N) bit */
#define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1U << 24)
+#define PEER_CAP_ROLE_ADV (1U << 25) /* role advertised */
+#define PEER_CAP_ROLE_RCV (1U << 26) /* role received */
/* Capability flags (reset in bgp_stop) */
uint32_t af_cap[AFI_MAX][SAFI_MAX];
@@ -1320,6 +1334,11 @@ struct peer {
/* force the extended format for Optional Parameters in OPEN message */
#define PEER_FLAG_EXTENDED_OPT_PARAMS (1U << 30)
+ /* BGP Open Policy flags.
+ * Enforce using roles on both sides
+ */
+#define PEER_FLAG_STRICT_MODE (1U << 31)
+
/*
*GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
*& PEER_FLAG_GRACEFUL_RESTART_HELPER
@@ -1803,6 +1822,7 @@ struct bgp_nlri {
#define BGP_ATTR_ENCAP 23
#define BGP_ATTR_IPV6_EXT_COMMUNITIES 25
#define BGP_ATTR_LARGE_COMMUNITIES 32
+#define BGP_ATTR_OTC 35
#define BGP_ATTR_PREFIX_SID 40
#define BGP_ATTR_SRTE_COLOR 51
#ifdef ENABLE_BGP_VNC_ATTR
@@ -1846,6 +1866,7 @@ struct bgp_nlri {
#define BGP_NOTIFY_OPEN_AUTH_FAILURE 5
#define BGP_NOTIFY_OPEN_UNACEP_HOLDTIME 6
#define BGP_NOTIFY_OPEN_UNSUP_CAPBL 7
+#define BGP_NOTIFY_OPEN_ROLE_MISMATCH 11
/* BGP_NOTIFY_UPDATE_ERR sub codes. */
#define BGP_NOTIFY_UPDATE_MAL_ATTR 1
@@ -1984,6 +2005,10 @@ enum bgp_create_error_code {
BGP_ERR_GR_INVALID_CMD = -32,
BGP_ERR_GR_OPERATION_FAILED = -33,
BGP_GR_NO_OPERATION = -34,
+
+ /*BGP Open Policy ERRORS */
+ BGP_ERR_INVALID_ROLE_NAME = -35,
+ BGP_ERR_INVALID_INTERNAL_ROLE = -36
};
/*
@@ -2154,6 +2179,9 @@ extern int peer_ebgp_multihop_set(struct peer *, int);
extern int peer_ebgp_multihop_unset(struct peer *);
extern int is_ebgp_multihop_configured(struct peer *peer);
+extern int peer_role_set(struct peer *peer, uint8_t role, int strict_mode);
+extern int peer_role_unset(struct peer *peer);
+
extern void peer_description_set(struct peer *, const char *);
extern void peer_description_unset(struct peer *);
@@ -2253,6 +2281,8 @@ extern void peer_tx_shutdown_message_set(struct peer *, const char *msg);
extern void peer_tx_shutdown_message_unset(struct peer *);
extern void bgp_route_map_update_timer(struct thread *thread);
+extern const char *get_name_by_role(uint8_t role);
+
extern void bgp_route_map_terminate(void);
extern int peer_cmp(struct peer *p1, struct peer *p2);
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index d4abf2c34d..76af844b37 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -2643,6 +2643,65 @@ Large Communities in Route Map
Note that the large expanded community is only used for `match` rule, not for
`set` actions.
+.. _bgp-roles-and-only-to-customers:
+
+BGP Roles and Only to Customers
+-------------------------------
+
+BGP roles are defined in :rfc:`9234` and provide an easy way to route leaks
+prevention, detection and mitigation.
+
+To enable its mechanics, you must set your local role to reflect your type of
+peering relationship with your neighbor. Possible values of ``LOCAL-ROLE`` are:
+<provider|rs-server|rs-client|customer|peer>.
+
+The local Role value is negotiated with the new BGP Role capability with a
+built-in check of the corresponding value. In case of mismatch the new OPEN
+Roles Mismatch Notification <2, 11> would be sent.
+
+The correct Role pairs are:
+
+* Provider - Customer
+* Peer - Peer
+* RS-Server - RS-Client
+
+.. code-block:: shell
+
+ ~# vtysh -c 'show bgp neighbor' | grep 'Role'
+ Local Role: customer
+ Neighbor Role: provider
+ Role: advertised and received
+
+If strict-mode is set BGP session won't become established until BGP neighbor
+set local Role on its side. This configuratoin parameter is defined in
+:rfc:`9234` and used to enforce corresponding configuration at your
+conter-part side. Default value - disabled.
+
+Routes that sent from provider, rs-server, or peer local-role (or if received
+by customer, rs-clinet, or peer local-role) will be marked with a new
+Only to Customer (OTC) attribute.
+
+Routes with this attribute can only be sent to your neighbor if your
+local-role is provider or rs-server. Routes with this attribute can be
+received only if your local-role is customer or rs-client.
+
+In case of peer-peer relaitonship routes can be received only if
+OTC value is equal to your neighbor AS number.
+
+All these rules with OTC help to detect and mitigate route leaks and
+happened automatically if local-role is set.
+
+.. clicmd:: neighbor PEER local-role LOCAL-ROLE [strict-mode]
+
+ This command set your local-role to ``LOCAL-ROLE``:
+ <provider|rs-server|rs-client|customer|peer>.
+
+ This role help to detect and prevent route leaks.
+
+ If ``strict-mode`` is set, your neighbor must send you Capability with the
+ value of his role (by setting local-role on his side). Otherwise, a Role
+ Mismatch Notification will be sent.
+
.. _bgp-l3vpn-vrfs:
L3VPN VRFs
diff --git a/doc/user/overview.rst b/doc/user/overview.rst
index 9bcd2af346..bf401825e0 100644
--- a/doc/user/overview.rst
+++ b/doc/user/overview.rst
@@ -383,6 +383,8 @@ BGP
:t:`Extended BGP Administrative Shutdown Communication. J. Snijders, J. Heitz, J. Scudder, A. Azimov. January 2021`
- :rfc:`9072`
:t:`Extended Optional Parameters Length for BGP OPEN Message. E. Chen, J. Scudder. July 2021`
+- :rfc:`9234`
+ :t:`Route Leak Prevention and Detection Using Roles in UPDATE and OPEN Messages. A. Azimov, E. Bogomazov, R. Bush, K. Patel, K. Sriram. May 2022`
OSPF
----
diff --git a/lib/command.h b/lib/command.h
index 047e550532..7363ed84c8 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -508,6 +508,9 @@ struct cmd_node {
EVPN_TYPE_4_HELP_STR EVPN_TYPE_4_HELP_STR \
EVPN_TYPE_5_HELP_STR EVPN_TYPE_5_HELP_STR
+/* Describing roles */
+#define ROLE_STR \
+ "Providing transit\nRoute server\nRS client\nUsing transit\nPublic/private peering\n"
/* Prototypes. */
extern void install_node(struct cmd_node *node);
diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c
index 44d15d6014..51792825da 100644
--- a/tests/bgpd/test_capability.c
+++ b/tests/bgpd/test_capability.c
@@ -649,6 +649,35 @@ static struct test_segment misc_segments[] =
2,
SHOULD_PARSE,
},
+ {
+ "Role",
+ "Role capability",
+ {
+ /* hdr */ 0x9, 0x1,
+ 0x1,
+ },
+ 3,
+ SHOULD_PARSE,
+ },
+ {
+ "Role-long",
+ "Role capability, but too long",
+ {
+ /* hdr */ 0x9, 0x4,
+ 0x0, 0x0, 0x0, 0x1,
+ },
+ 6,
+ SHOULD_ERR,
+ },
+ {
+ "Role-empty",
+ "Role capability, but empty.",
+ {
+ /* hdr */ 0x9, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
{NULL, NULL, {0}, 0, 0}};
/* DYNAMIC message */
diff --git a/tests/bgpd/test_capability.py b/tests/bgpd/test_capability.py
index e275195537..da9245bf04 100644
--- a/tests/bgpd/test_capability.py
+++ b/tests/bgpd/test_capability.py
@@ -36,6 +36,9 @@ TestCapability.okfail("ORF-empty: ORF capability, but empty.")
TestCapability.okfail("AS4-empty: AS4 capability, but empty.")
TestCapability.okfail("dyn-empty: Dynamic capability, but empty.")
TestCapability.okfail("dyn-old: Dynamic capability (deprecated version)")
+TestCapability.okfail("Role: Role capability")
+TestCapability.okfail("Role-long: Role capability, but too long")
+TestCapability.okfail("Role-empty: Role capability, but empty.")
TestCapability.okfail("Cap-singlets: One capability per Optional-Param")
TestCapability.okfail("Cap-series: Series of capability, one Optional-Param")
TestCapability.okfail("AS4more: AS4 capability after other caps (singlets)")
diff --git a/tests/topotests/bgp_roles_capability/__init__.py b/tests/topotests/bgp_roles_capability/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/__init__.py
diff --git a/tests/topotests/bgp_roles_capability/r1/bgpd.conf b/tests/topotests/bgp_roles_capability/r1/bgpd.conf
new file mode 100644
index 0000000000..4f152de960
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r1/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 64501
+ bgp router-id 192.168.2.1
+ network 192.0.2.0/24
+! Correct role pair
+ neighbor 192.168.2.2 remote-as 64502
+ neighbor 192.168.2.2 local-role provider
+! Incorrect role pair
+ neighbor 192.168.3.2 remote-as 64503
+ neighbor 192.168.3.2 local-role provider
+! Missed neighbor role
+ neighbor 192.168.4.2 remote-as 64504
+ neighbor 192.168.4.2 local-role provider
+! Missed neighbor role with strict-mode
+ neighbor 192.168.5.2 remote-as 64505
+ neighbor 192.168.5.2 local-role provider strict-mode
diff --git a/tests/topotests/bgp_roles_capability/r1/zebra.conf b/tests/topotests/bgp_roles_capability/r1/zebra.conf
new file mode 100644
index 0000000000..3e90a261c1
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r1/zebra.conf
@@ -0,0 +1,15 @@
+!
+interface r1-eth0
+ ip address 192.168.2.1/24
+!
+interface r1-eth1
+ ip address 192.168.3.1/24
+!
+interface r1-eth2
+ ip address 192.168.4.1/24
+!
+interface r1-eth3
+ ip address 192.168.5.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/r2/bgpd.conf b/tests/topotests/bgp_roles_capability/r2/bgpd.conf
new file mode 100644
index 0000000000..efca633daa
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r2/bgpd.conf
@@ -0,0 +1,4 @@
+router bgp 64502
+ bgp router-id 192.168.2.2
+ neighbor 192.168.2.1 remote-as 64501
+ neighbor 192.168.2.1 local-role customer
diff --git a/tests/topotests/bgp_roles_capability/r2/zebra.conf b/tests/topotests/bgp_roles_capability/r2/zebra.conf
new file mode 100644
index 0000000000..86a97846dc
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.2.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/r3/bgpd.conf b/tests/topotests/bgp_roles_capability/r3/bgpd.conf
new file mode 100644
index 0000000000..201c0afb2b
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r3/bgpd.conf
@@ -0,0 +1,3 @@
+router bgp 64503
+ neighbor 192.168.3.1 remote-as 64501
+ neighbor 192.168.3.1 local-role peer
diff --git a/tests/topotests/bgp_roles_capability/r3/zebra.conf b/tests/topotests/bgp_roles_capability/r3/zebra.conf
new file mode 100644
index 0000000000..9df578e1b0
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.3.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/r4/bgpd.conf b/tests/topotests/bgp_roles_capability/r4/bgpd.conf
new file mode 100644
index 0000000000..30b97bb3a4
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r4/bgpd.conf
@@ -0,0 +1,2 @@
+router bgp 64504
+ neighbor 192.168.4.1 remote-as 64501
diff --git a/tests/topotests/bgp_roles_capability/r4/zebra.conf b/tests/topotests/bgp_roles_capability/r4/zebra.conf
new file mode 100644
index 0000000000..03632ee1ac
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 192.168.4.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/r5/bgpd.conf b/tests/topotests/bgp_roles_capability/r5/bgpd.conf
new file mode 100644
index 0000000000..b4bf73fa3d
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r5/bgpd.conf
@@ -0,0 +1,2 @@
+router bgp 64505
+ neighbor 192.168.5.1 remote-as 64501
diff --git a/tests/topotests/bgp_roles_capability/r5/zebra.conf b/tests/topotests/bgp_roles_capability/r5/zebra.conf
new file mode 100644
index 0000000000..2e95d7777f
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r5/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r5-eth0
+ ip address 192.168.5.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/roles_capability_stand.dot b/tests/topotests/bgp_roles_capability/roles_capability_stand.dot
new file mode 100644
index 0000000000..c0b5c81364
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/roles_capability_stand.dot
@@ -0,0 +1,15 @@
+graph roles_filtering_stand {
+ layout="circo"
+ label="roles capability stand"
+ fontsize="20"
+
+ r1 [label="r1 provider"];
+ r2 [label="r2"];
+ r3 [label="r3"];
+ r4 [label="r4"];
+ r5 [label="r5"];
+ r1 -- r2 [headlabel="customer", taillabel="provider"];
+ r1 -- r3 [headlabel="peer", taillabel="provider"];
+ r1 -- r4 [headlabel="?", taillabel="provider"];
+ r1 -- r5 [headlabel="?", taillabel="provider", label="strict-mode"];
+}
diff --git a/tests/topotests/bgp_roles_capability/roles_capability_stand.jpg b/tests/topotests/bgp_roles_capability/roles_capability_stand.jpg
new file mode 100644
index 0000000000..b8dea2f0b3
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/roles_capability_stand.jpg
Binary files differ
diff --git a/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py b/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py
new file mode 100644
index 0000000000..55fc0972c1
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py
@@ -0,0 +1,136 @@
+#!/usr/bin/python
+#
+# test_bgp_roles_capability.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2022 by Eugene Bogomazov <eb@qrator.net>
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_roles_capability: test bgp roles negotiation
+"""
+
+import json
+import os
+import sys
+import functools
+import pytest
+import time
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+topodef = {f"s{i}": ("r1", f"r{i}") for i in range(2, 6)}
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_BGP, "bgpd.conf")
+ tgen.start_router()
+ time.sleep(1)
+ yield tgen
+ tgen.stop_topology()
+
+
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+def is_role_mismatch(neighbor_status):
+ return (
+ neighbor_status["bgpState"] != "Established"
+ and neighbor_status.get("lastErrorCodeSubcode") == "020B" # <2, 11>
+ and "Role Mismatch" in neighbor_status.get("lastNotificationReason", "")
+ )
+
+
+def test_correct_pair(tgen):
+ # provider-customer pair
+ neighbor_ip = "192.168.2.2"
+ neighbor_status = json.loads(
+ tgen.gears["r1"].vtysh_cmd(f"show bgp neighbors {neighbor_ip} json")
+ )[neighbor_ip]
+ assert neighbor_status["localRole"] == "provider"
+ assert neighbor_status["neighRole"] == "customer"
+ assert neighbor_status["bgpState"] == "Established"
+ assert (
+ neighbor_status["neighborCapabilities"].get("role") == "advertisedAndReceived"
+ )
+
+
+def test_role_pair_mismatch(tgen):
+ # provider-peer mistmatch
+ neighbor_ip = "192.168.3.2"
+ neighbor_status = json.loads(
+ tgen.gears["r1"].vtysh_cmd(f"show bgp neighbors {neighbor_ip} json")
+ )[neighbor_ip]
+ assert is_role_mismatch(neighbor_status)
+
+
+def test_single_role_advertising(tgen):
+ # provider-undefine pair; we set role
+ neighbor_ip = "192.168.4.2"
+ neighbor_status = json.loads(
+ tgen.gears["r1"].vtysh_cmd(f"show bgp neighbors {neighbor_ip} json")
+ )[neighbor_ip]
+ assert neighbor_status["localRole"] == "provider"
+ assert neighbor_status["neighRole"] == "undefine"
+ assert neighbor_status["bgpState"] == "Established"
+ assert neighbor_status["neighborCapabilities"].get("role") == "advertised"
+
+
+def test_single_role_receiving(tgen):
+ # provider-undefine pair; we receive role
+ neighbor_ip = "192.168.4.1"
+ neighbor_status = json.loads(
+ tgen.gears["r4"].vtysh_cmd(f"show bgp neighbors {neighbor_ip} json")
+ )[neighbor_ip]
+ assert neighbor_status["localRole"] == "undefine"
+ assert neighbor_status["neighRole"] == "provider"
+ assert neighbor_status["bgpState"] == "Established"
+ assert neighbor_status["neighborCapabilities"].get("role") == "received"
+
+
+def test_role_strict_mode(tgen):
+ # provider-undefine pair bur strict-mode was set
+ neighbor_ip = "192.168.5.2"
+ neighbor_status = json.loads(
+ tgen.gears["r1"].vtysh_cmd(f"show bgp neighbors {neighbor_ip} json")
+ )
+ assert is_role_mismatch(neighbor_status[neighbor_ip])
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_roles_filtering/__init__.py b/tests/topotests/bgp_roles_filtering/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/__init__.py
diff --git a/tests/topotests/bgp_roles_filtering/r1/bgpd.conf b/tests/topotests/bgp_roles_filtering/r1/bgpd.conf
new file mode 100644
index 0000000000..99f6211ac7
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r1/bgpd.conf
@@ -0,0 +1,12 @@
+! Provider on this side
+router bgp 64501
+ bgp router-id 192.168.1.1
+ no bgp network import-check
+ network 192.0.2.1/32
+ neighbor 192.168.1.2 remote-as 64510
+ neighbor 192.168.1.2 local-role provider
+ neighbor 192.168.1.2 route-map ALLOW_ALL out
+ neighbor 192.168.1.2 route-map ALLOW_ALL in
+ neighbor 192.168.1.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r1/zebra.conf b/tests/topotests/bgp_roles_filtering/r1/zebra.conf
new file mode 100644
index 0000000000..acf120b200
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r10/bgpd.conf b/tests/topotests/bgp_roles_filtering/r10/bgpd.conf
new file mode 100644
index 0000000000..f60bc6e38b
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r10/bgpd.conf
@@ -0,0 +1,21 @@
+! Customer on other side
+router bgp 64510
+ bgp router-id 192.168.10.1
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 64501
+ neighbor 192.168.1.1 timers 3 10
+ neighbor 192.168.2.1 remote-as 64502
+ neighbor 192.168.2.1 timers 3 10
+ neighbor 192.168.3.1 remote-as 64503
+ neighbor 192.168.3.1 timers 3 10
+ neighbor 192.168.4.1 remote-as 64504
+ neighbor 192.168.4.1 timers 3 10
+ neighbor 192.168.5.1 remote-as 64505
+ neighbor 192.168.5.1 local-role provider
+ neighbor 192.168.5.1 timers 3 10
+ neighbor 192.168.6.1 remote-as 64506
+ neighbor 192.168.6.1 local-role peer
+ neighbor 192.168.6.1 timers 3 10
+ neighbor 192.168.7.1 remote-as 64507
+ neighbor 192.168.7.1 local-role customer
+ neighbor 192.168.7.1 timers 3 10
diff --git a/tests/topotests/bgp_roles_filtering/r10/zebra.conf b/tests/topotests/bgp_roles_filtering/r10/zebra.conf
new file mode 100644
index 0000000000..f2733fe1aa
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r10/zebra.conf
@@ -0,0 +1,24 @@
+!
+interface r10-eth0
+ ip address 192.168.1.2/24
+!
+interface r10-eth1
+ ip address 192.168.2.2/24
+!
+interface r10-eth2
+ ip address 192.168.3.2/24
+!
+interface r10-eth3
+ ip address 192.168.4.2/24
+!
+interface r10-eth4
+ ip address 192.168.5.2/24
+!
+interface r10-eth5
+ ip address 192.168.6.2/24
+!
+interface r10-eth6
+ ip address 192.168.7.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r2/bgpd.conf b/tests/topotests/bgp_roles_filtering/r2/bgpd.conf
new file mode 100644
index 0000000000..b6db8c1414
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r2/bgpd.conf
@@ -0,0 +1,12 @@
+! With peer on this side
+router bgp 64502
+ bgp router-id 192.168.2.1
+ no bgp network import-check
+ network 192.0.2.2/32
+ neighbor 192.168.2.2 remote-as 64510
+ neighbor 192.168.2.2 local-role peer
+ neighbor 192.168.2.2 route-map ALLOW_ALL out
+ neighbor 192.168.2.2 route-map ALLOW_ALL in
+ neighbor 192.168.2.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r2/zebra.conf b/tests/topotests/bgp_roles_filtering/r2/zebra.conf
new file mode 100644
index 0000000000..f785ea1ab4
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r3/bgpd.conf b/tests/topotests/bgp_roles_filtering/r3/bgpd.conf
new file mode 100644
index 0000000000..70f10b192c
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r3/bgpd.conf
@@ -0,0 +1,12 @@
+! Customer role on this side
+router bgp 64503
+ bgp router-id 192.168.3.1
+ no bgp network import-check
+ network 192.0.2.3/32
+ neighbor 192.168.3.2 remote-as 64510
+ neighbor 192.168.3.2 local-role customer
+ neighbor 192.168.3.2 route-map ALLOW_ALL out
+ neighbor 192.168.3.2 route-map ALLOW_ALL in
+ neighbor 192.168.3.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r3/zebra.conf b/tests/topotests/bgp_roles_filtering/r3/zebra.conf
new file mode 100644
index 0000000000..b347257de4
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.3.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r4/bgpd.conf b/tests/topotests/bgp_roles_filtering/r4/bgpd.conf
new file mode 100644
index 0000000000..11e324e605
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r4/bgpd.conf
@@ -0,0 +1,11 @@
+! Without role on this side
+router bgp 64504
+ bgp router-id 192.168.4.1
+ no bgp network import-check
+ network 192.0.2.4/32
+ neighbor 192.168.4.2 remote-as 64510
+ neighbor 192.168.4.2 route-map ALLOW_ALL out
+ neighbor 192.168.4.2 route-map ALLOW_ALL in
+ neighbor 192.168.4.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r4/zebra.conf b/tests/topotests/bgp_roles_filtering/r4/zebra.conf
new file mode 100644
index 0000000000..3543c08d55
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 192.168.4.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r5/bgpd.conf b/tests/topotests/bgp_roles_filtering/r5/bgpd.conf
new file mode 100644
index 0000000000..39d2a8dcce
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r5/bgpd.conf
@@ -0,0 +1,11 @@
+! Provider on other side
+router bgp 64505
+ bgp router-id 192.168.5.1
+ no bgp network import-check
+ network 192.0.2.5/32
+ neighbor 192.168.5.2 remote-as 64510
+ neighbor 192.168.5.2 route-map ALLOW_ALL out
+ neighbor 192.168.5.2 route-map ALLOW_ALL in
+ neighbor 192.168.5.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r5/zebra.conf b/tests/topotests/bgp_roles_filtering/r5/zebra.conf
new file mode 100644
index 0000000000..4a1c2736b9
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r5/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r5-eth0
+ ip address 192.168.5.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r6/bgpd.conf b/tests/topotests/bgp_roles_filtering/r6/bgpd.conf
new file mode 100644
index 0000000000..25e5cd8660
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r6/bgpd.conf
@@ -0,0 +1,11 @@
+! Peer on other side
+router bgp 64506
+ bgp router-id 192.168.6.1
+ no bgp network import-check
+ network 192.0.2.6/32
+ neighbor 192.168.6.2 remote-as 64510
+ neighbor 192.168.6.2 route-map ALLOW_ALL out
+ neighbor 192.168.6.2 route-map ALLOW_ALL in
+ neighbor 192.168.6.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r6/zebra.conf b/tests/topotests/bgp_roles_filtering/r6/zebra.conf
new file mode 100644
index 0000000000..3644a69908
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r6/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r6-eth0
+ ip address 192.168.6.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r7/bgpd.conf b/tests/topotests/bgp_roles_filtering/r7/bgpd.conf
new file mode 100644
index 0000000000..5f5f25790f
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r7/bgpd.conf
@@ -0,0 +1,11 @@
+! Customer on other side
+router bgp 64507
+ bgp router-id 192.168.7.1
+ no bgp network import-check
+ network 192.0.2.7/32
+ neighbor 192.168.7.2 remote-as 64510
+ neighbor 192.168.7.2 route-map ALLOW_ALL out
+ neighbor 192.168.7.2 route-map ALLOW_ALL in
+ neighbor 192.168.7.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r7/zebra.conf b/tests/topotests/bgp_roles_filtering/r7/zebra.conf
new file mode 100644
index 0000000000..0407a488be
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r7/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r7-eth0
+ ip address 192.168.7.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot
new file mode 100644
index 0000000000..df0f6853e4
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot
@@ -0,0 +1,21 @@
+graph roles_filtering_stand {
+ layout="circo"
+ label="roles filtering stand"
+ fontsize="20"
+
+ r1 [label="r1 192.0.2.1/32"];
+ r2 [label="r2 192.0.2.2/32"];
+ r3 [label="r3 192.0.2.3/32"];
+ r4 [label="r4 192.0.2.4/32"];
+ r5 [label="r5 192.0.2.5/32"];
+ r6 [label="r6 192.0.2.6/32"];
+ r7 [label="r7 192.0.2.7/32"];
+ r10 [label="r10 intermediate"];
+ r10 -- r1 [headlabel="provider", taillabel="?"];
+ r10 -- r2 [headlabel="peer", taillabel="?"];
+ r10 -- r3 [headlabel="customer", taillabel="?"];
+ r10 -- r4 [headlabel="?", taillabel="?"];
+ r10 -- r5 [headlabel="?", taillabel="provider"];
+ r10 -- r6 [headlabel="?", taillabel="peer"];
+ r10 -- r7 [headlabel="?", taillabel="customer"];
+}
diff --git a/tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg
new file mode 100644
index 0000000000..dfedcf854d
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg
Binary files differ
diff --git a/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py b/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py
new file mode 100644
index 0000000000..77116f474b
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py
@@ -0,0 +1,141 @@
+#!/usr/bin/python
+#
+# test_bgp_roles_filtering.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2022 by Eugene Bogomazov <eb@qrator.net>
+# Copyright (c) 2017 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_roles_filtering: test leaks prevention and mitigation with roles
+"""
+
+import json
+import os
+import sys
+import functools
+import pytest
+import time
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.bgp import verify_bgp_convergence_from_running_config
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+topodef = {f"s{i}": (f"r{i}", "r10") for i in range(1, 8)}
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_BGP, "bgpd.conf")
+ tgen.start_router()
+ BGP_CONVERGENCE = verify_bgp_convergence_from_running_config(tgen)
+ assert BGP_CONVERGENCE, f"setup_module :Failed \n Error: {BGP_CONVERGENCE}"
+ # Todo: What is the indented way to wait for convergence without json?!
+ time.sleep(3)
+ yield tgen
+ tgen.stop_topology()
+
+
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+def test_r10_routes(tgen):
+ # provider-undefine pair bur strict-mode was set
+ routes = json.loads(tgen.gears["r10"].vtysh_cmd("show bgp ipv4 json"))["routes"]
+ route_list = sorted(routes.keys())
+ assert route_list == [
+ "192.0.2.1/32",
+ "192.0.2.2/32",
+ "192.0.2.3/32",
+ "192.0.2.4/32",
+ "192.0.2.5/32",
+ "192.0.2.6/32",
+ "192.0.2.7/32",
+ ]
+ routes_with_otc = list()
+ for number in range(1, 8):
+ prefix = f"192.0.2.{number}/32"
+ route_details = json.loads(
+ tgen.gears["r10"].vtysh_cmd(f"show bgp ipv4 {prefix} json")
+ )
+ if route_details["paths"][0].get("otc") is not None:
+ routes_with_otc.append(prefix)
+ assert routes_with_otc == [
+ "192.0.2.1/32",
+ "192.0.2.2/32",
+ "192.0.2.6/32",
+ "192.0.2.7/32",
+ ]
+
+
+def test_r1_routes(tgen):
+ routes = json.loads(tgen.gears["r1"].vtysh_cmd("show bgp ipv4 json"))["routes"]
+ routes_list = sorted(routes.keys())
+ assert routes_list == [
+ "192.0.2.1/32", # own
+ "192.0.2.3/32",
+ "192.0.2.4/32",
+ "192.0.2.5/32",
+ ]
+
+
+def test_r6_routes(tgen):
+ routes = json.loads(tgen.gears["r6"].vtysh_cmd("show bgp ipv4 json"))["routes"]
+ routes_list = sorted(routes.keys())
+ assert routes_list == [
+ "192.0.2.3/32",
+ "192.0.2.4/32",
+ "192.0.2.5/32",
+ "192.0.2.6/32", # own
+ ]
+
+
+def test_r4_routes(tgen):
+ routes = json.loads(tgen.gears["r4"].vtysh_cmd("show bgp ipv4 json"))["routes"]
+ routes_list = sorted(routes.keys())
+ assert routes_list == [
+ "192.0.2.1/32",
+ "192.0.2.2/32",
+ "192.0.2.3/32",
+ "192.0.2.4/32",
+ "192.0.2.5/32",
+ "192.0.2.6/32",
+ "192.0.2.7/32",
+ ]
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))