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:
Eugene Bogomazov 2022-06-17 13:14:46 +03:00
parent 4151ca0ada
commit d864dd9eb1
50 changed files with 1056 additions and 1 deletions

View File

@ -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);

View File

@ -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 */

View File

@ -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[] = {

View File

@ -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;

View File

@ -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]) {

View File

@ -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. */

View File

@ -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))) {

View File

@ -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)

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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
----

View File

@ -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);

View File

@ -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 */

View File

@ -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)")

View 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

View 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
!

View 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

View File

@ -0,0 +1,6 @@
!
interface r2-eth0
ip address 192.168.2.2/24
!
ip forwarding
!

View File

@ -0,0 +1,3 @@
router bgp 64503
neighbor 192.168.3.1 remote-as 64501
neighbor 192.168.3.1 local-role peer

View File

@ -0,0 +1,6 @@
!
interface r3-eth0
ip address 192.168.3.2/24
!
ip forwarding
!

View File

@ -0,0 +1,2 @@
router bgp 64504
neighbor 192.168.4.1 remote-as 64501

View File

@ -0,0 +1,6 @@
!
interface r4-eth0
ip address 192.168.4.2/24
!
ip forwarding
!

View File

@ -0,0 +1,2 @@
router bgp 64505
neighbor 192.168.5.1 remote-as 64501

View File

@ -0,0 +1,6 @@
!
interface r5-eth0
ip address 192.168.5.2/24
!
ip forwarding
!

View File

@ -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"];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -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))

View 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

View File

@ -0,0 +1,6 @@
!
interface r1-eth0
ip address 192.168.1.1/24
!
ip forwarding
!

View 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

View 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
!

View 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

View File

@ -0,0 +1,6 @@
!
interface r2-eth0
ip address 192.168.2.1/24
!
ip forwarding
!

View 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

View File

@ -0,0 +1,6 @@
!
interface r3-eth0
ip address 192.168.3.1/24
!
ip forwarding
!

View 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

View File

@ -0,0 +1,6 @@
!
interface r4-eth0
ip address 192.168.4.1/24
!
ip forwarding
!

View 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

View File

@ -0,0 +1,6 @@
!
interface r5-eth0
ip address 192.168.5.1/24
!
ip forwarding
!

View 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

View File

@ -0,0 +1,6 @@
!
interface r6-eth0
ip address 192.168.6.1/24
!
ip forwarding
!

View 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

View File

@ -0,0 +1,6 @@
!
interface r7-eth0
ip address 192.168.7.1/24
!
ip forwarding
!

View File

@ -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"];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View 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))