mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-28 21:20:48 +00:00
bgpd: Add RFC9234 implementation
RFC9234 is a way to establish correct connection roles (Customer/ Provider, Peer or with RS) between bgp speakers. This patch: - Add a new configuration/terminal option to set the appropriate local role; - Add a mechanism for checking used roles, implemented by exchanging the corresponding capabilities in OPEN messages; - Add strict mode to force other party to use this feature; - Add basic support for a new transitive optional bgp attribute - OTC (Only to Customer); - Add logic for default setting OTC attribute and filtering routes with this attribute by the edge speakers, if the appropriate conditions are met; - Add two test stands to check role negotiation and route filtering during role usage. Signed-off-by: Eugene Bogomazov <eb@qrator.net>
This commit is contained in:
parent
4151ca0ada
commit
d864dd9eb1
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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[] = {
|
||||
|
@ -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;
|
||||
|
@ -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]) {
|
||||
|
@ -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. */
|
||||
|
||||
|
@ -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))) {
|
||||
|
@ -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)
|
||||
|
150
bgpd/bgp_vty.c
150
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);
|
||||
|
51
bgpd/bgpd.c
51
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)
|
||||
{
|
||||
|
30
bgpd/bgpd.h
30
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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
----
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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)")
|
||||
|
0
tests/topotests/bgp_roles_capability/__init__.py
Normal file
0
tests/topotests/bgp_roles_capability/__init__.py
Normal file
15
tests/topotests/bgp_roles_capability/r1/bgpd.conf
Normal file
15
tests/topotests/bgp_roles_capability/r1/bgpd.conf
Normal file
@ -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
|
15
tests/topotests/bgp_roles_capability/r1/zebra.conf
Normal file
15
tests/topotests/bgp_roles_capability/r1/zebra.conf
Normal file
@ -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
|
||||
!
|
4
tests/topotests/bgp_roles_capability/r2/bgpd.conf
Normal file
4
tests/topotests/bgp_roles_capability/r2/bgpd.conf
Normal file
@ -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
|
6
tests/topotests/bgp_roles_capability/r2/zebra.conf
Normal file
6
tests/topotests/bgp_roles_capability/r2/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r2-eth0
|
||||
ip address 192.168.2.2/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
3
tests/topotests/bgp_roles_capability/r3/bgpd.conf
Normal file
3
tests/topotests/bgp_roles_capability/r3/bgpd.conf
Normal file
@ -0,0 +1,3 @@
|
||||
router bgp 64503
|
||||
neighbor 192.168.3.1 remote-as 64501
|
||||
neighbor 192.168.3.1 local-role peer
|
6
tests/topotests/bgp_roles_capability/r3/zebra.conf
Normal file
6
tests/topotests/bgp_roles_capability/r3/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r3-eth0
|
||||
ip address 192.168.3.2/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
2
tests/topotests/bgp_roles_capability/r4/bgpd.conf
Normal file
2
tests/topotests/bgp_roles_capability/r4/bgpd.conf
Normal file
@ -0,0 +1,2 @@
|
||||
router bgp 64504
|
||||
neighbor 192.168.4.1 remote-as 64501
|
6
tests/topotests/bgp_roles_capability/r4/zebra.conf
Normal file
6
tests/topotests/bgp_roles_capability/r4/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r4-eth0
|
||||
ip address 192.168.4.2/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
2
tests/topotests/bgp_roles_capability/r5/bgpd.conf
Normal file
2
tests/topotests/bgp_roles_capability/r5/bgpd.conf
Normal file
@ -0,0 +1,2 @@
|
||||
router bgp 64505
|
||||
neighbor 192.168.5.1 remote-as 64501
|
6
tests/topotests/bgp_roles_capability/r5/zebra.conf
Normal file
6
tests/topotests/bgp_roles_capability/r5/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r5-eth0
|
||||
ip address 192.168.5.2/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
@ -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"];
|
||||
}
|
BIN
tests/topotests/bgp_roles_capability/roles_capability_stand.jpg
Normal file
BIN
tests/topotests/bgp_roles_capability/roles_capability_stand.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
@ -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))
|
0
tests/topotests/bgp_roles_filtering/__init__.py
Normal file
0
tests/topotests/bgp_roles_filtering/__init__.py
Normal file
12
tests/topotests/bgp_roles_filtering/r1/bgpd.conf
Normal file
12
tests/topotests/bgp_roles_filtering/r1/bgpd.conf
Normal file
@ -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
|
6
tests/topotests/bgp_roles_filtering/r1/zebra.conf
Normal file
6
tests/topotests/bgp_roles_filtering/r1/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r1-eth0
|
||||
ip address 192.168.1.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
21
tests/topotests/bgp_roles_filtering/r10/bgpd.conf
Normal file
21
tests/topotests/bgp_roles_filtering/r10/bgpd.conf
Normal file
@ -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
|
24
tests/topotests/bgp_roles_filtering/r10/zebra.conf
Normal file
24
tests/topotests/bgp_roles_filtering/r10/zebra.conf
Normal file
@ -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
|
||||
!
|
12
tests/topotests/bgp_roles_filtering/r2/bgpd.conf
Normal file
12
tests/topotests/bgp_roles_filtering/r2/bgpd.conf
Normal file
@ -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
|
6
tests/topotests/bgp_roles_filtering/r2/zebra.conf
Normal file
6
tests/topotests/bgp_roles_filtering/r2/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r2-eth0
|
||||
ip address 192.168.2.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
12
tests/topotests/bgp_roles_filtering/r3/bgpd.conf
Normal file
12
tests/topotests/bgp_roles_filtering/r3/bgpd.conf
Normal file
@ -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
|
6
tests/topotests/bgp_roles_filtering/r3/zebra.conf
Normal file
6
tests/topotests/bgp_roles_filtering/r3/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r3-eth0
|
||||
ip address 192.168.3.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
11
tests/topotests/bgp_roles_filtering/r4/bgpd.conf
Normal file
11
tests/topotests/bgp_roles_filtering/r4/bgpd.conf
Normal file
@ -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
|
6
tests/topotests/bgp_roles_filtering/r4/zebra.conf
Normal file
6
tests/topotests/bgp_roles_filtering/r4/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r4-eth0
|
||||
ip address 192.168.4.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
11
tests/topotests/bgp_roles_filtering/r5/bgpd.conf
Normal file
11
tests/topotests/bgp_roles_filtering/r5/bgpd.conf
Normal file
@ -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
|
6
tests/topotests/bgp_roles_filtering/r5/zebra.conf
Normal file
6
tests/topotests/bgp_roles_filtering/r5/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r5-eth0
|
||||
ip address 192.168.5.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
11
tests/topotests/bgp_roles_filtering/r6/bgpd.conf
Normal file
11
tests/topotests/bgp_roles_filtering/r6/bgpd.conf
Normal file
@ -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
|
6
tests/topotests/bgp_roles_filtering/r6/zebra.conf
Normal file
6
tests/topotests/bgp_roles_filtering/r6/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r6-eth0
|
||||
ip address 192.168.6.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
11
tests/topotests/bgp_roles_filtering/r7/bgpd.conf
Normal file
11
tests/topotests/bgp_roles_filtering/r7/bgpd.conf
Normal file
@ -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
|
6
tests/topotests/bgp_roles_filtering/r7/zebra.conf
Normal file
6
tests/topotests/bgp_roles_filtering/r7/zebra.conf
Normal file
@ -0,0 +1,6 @@
|
||||
!
|
||||
interface r7-eth0
|
||||
ip address 192.168.7.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
@ -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"];
|
||||
}
|
BIN
tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg
Normal file
BIN
tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 53 KiB |
141
tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py
Normal file
141
tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py
Normal file
@ -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))
|
Loading…
Reference in New Issue
Block a user