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