diff options
59 files changed, 2955 insertions, 3018 deletions
diff --git a/.github/workflows/docker-daily-master.yml b/.github/workflows/docker-daily-master.yml index 5b521a870d..59787b4067 100644 --- a/.github/workflows/docker-daily-master.yml +++ b/.github/workflows/docker-daily-master.yml @@ -38,8 +38,9 @@ jobs: - name: Login to Docker Hub uses: docker/login-action@v2 with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + registry: quay.io + username: ${{ secrets.QUAY_USERNAME }} + password: ${{ secrets.QUAY_ROBOT_TOKEN }} - name: Build and push uses: docker/build-push-action@v3 @@ -47,6 +48,6 @@ jobs: context: . file: ./docker/alpine/Dockerfile push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/frr:master + tags: quay.io/frrouting/frr:master build-args: PKGVER=${{ steps.vars.outputs.date }} platforms: linux/amd64,linux/arm64,linux/arm/v7 diff --git a/.github/workflows/docker-stable.yml b/.github/workflows/docker-stable.yml deleted file mode 100644 index 1e069a722e..0000000000 --- a/.github/workflows/docker-stable.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Build stable branch images for Docker - -on: - push: - branches: - - 'stable/**' - -concurrency: - group: ${{ github.ref }} - cancel-in-progress: true - -jobs: - docker_daily_master: - if: github.repository == 'frrouting/frr' - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Custom variables - id: vars - run: | - # To package a specific git commit, the date of the commit gets - # appended to the latest release, e.g. 1.0.0_git20180204. - # This is the requirement by APKBUILD (abuild). - # More details: https://wiki.alpinelinux.org/wiki/APKBUILD_Reference. - echo ::set-output name=date::$(date +'%Y%m%d') - # Get the real version specified in configure.ac file. - echo ::set-output name=frr_version::$(grep AC_INIT configure.ac | cut -d '[' -f3 | cut -d ']' -f 1) - - - name: Checkout - uses: actions/checkout@v3 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - file: ./docker/alpine/Dockerfile - push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/frr:v${{ steps.vars.outputs.frr_version }} - build-args: PKGVER=${{ steps.vars.outputs.date }} - platforms: linux/amd64,linux/arm64,linux/arm/v7 diff --git a/alpine/APKBUILD.in b/alpine/APKBUILD.in index 3aad9549b5..fef7a61ccc 100644 --- a/alpine/APKBUILD.in +++ b/alpine/APKBUILD.in @@ -10,7 +10,7 @@ depends="json-c c-ares iproute2 python3 bash" makedepends="ncurses-dev net-snmp-dev gawk texinfo perl acct autoconf automake bash binutils bison bsd-compat-headers build-base c-ares c-ares-dev ca-certificates cryptsetup-libs curl device-mapper-libs - expat fakeroot flex fortify-headers gdbm git gmp isl json-c-dev kmod + expat fakeroot flex fortify-headers gdbm git gmp json-c-dev kmod lddtree libacl libatomic libattr libblkid libburn libbz2 libc-dev libcap-dev libcurl libedit libffi libgcc libgomp libisoburn libisofs libltdl libressl libssh2 libstdc++ libtool libuuid diff --git a/bfdd/control.c b/bfdd/control.c index 473843fe25..01cfed8f90 100644 --- a/bfdd/control.c +++ b/bfdd/control.c @@ -435,6 +435,15 @@ static void control_read(struct thread *t) return; } +#define FRR_BFD_MAXLEN 10 * 1024 + + if (plen > FRR_BFD_MAXLEN) { + zlog_debug("%s: client closed, invalid message length: %d", + __func__, bcm.bcm_length); + control_free(bcs); + return; + } + if (bcm.bcm_ver != BMV_VERSION_1) { zlog_debug("%s: client closed due bad version: %d", __func__, bcm.bcm_ver); diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 3d93e8a1ac..908c024e9d 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -73,7 +73,6 @@ static const struct message attr_str[] = { {BGP_ATTR_EXT_COMMUNITIES, "EXT_COMMUNITIES"}, {BGP_ATTR_AS4_PATH, "AS4_PATH"}, {BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR"}, - {BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT"}, {BGP_ATTR_PMSI_TUNNEL, "PMSI_TUNNEL_ATTRIBUTE"}, {BGP_ATTR_ENCAP, "ENCAP"}, {BGP_ATTR_OTC, "OTC"}, @@ -825,56 +824,55 @@ bool attrhash_cmp(const void *p1, const void *p2) && attr1->med == attr2->med && attr1->local_pref == attr2->local_pref && attr1->rmap_change_flags == attr2->rmap_change_flags) { - if (attr1->aggregator_as == attr2->aggregator_as && - attr1->aggregator_addr.s_addr == - attr2->aggregator_addr.s_addr && - attr1->weight == attr2->weight && - attr1->tag == attr2->tag && - attr1->label_index == attr2->label_index && - attr1->mp_nexthop_len == attr2->mp_nexthop_len && - bgp_attr_get_ecommunity(attr1) == - bgp_attr_get_ecommunity(attr2) && - bgp_attr_get_ipv6_ecommunity(attr1) == - bgp_attr_get_ipv6_ecommunity(attr2) && - bgp_attr_get_lcommunity(attr1) == - bgp_attr_get_lcommunity(attr2) && - bgp_attr_get_cluster(attr1) == - bgp_attr_get_cluster(attr2) && - bgp_attr_get_transit(attr1) == - bgp_attr_get_transit(attr2) && - bgp_attr_get_aigp_metric(attr1) == - bgp_attr_get_aigp_metric(attr2) && - attr1->rmap_table_id == attr2->rmap_table_id && - (attr1->encap_tunneltype == attr2->encap_tunneltype) && - encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs) + if (attr1->aggregator_as == attr2->aggregator_as + && attr1->aggregator_addr.s_addr + == attr2->aggregator_addr.s_addr + && attr1->weight == attr2->weight + && attr1->tag == attr2->tag + && attr1->label_index == attr2->label_index + && attr1->mp_nexthop_len == attr2->mp_nexthop_len + && bgp_attr_get_ecommunity(attr1) + == bgp_attr_get_ecommunity(attr2) + && bgp_attr_get_ipv6_ecommunity(attr1) + == bgp_attr_get_ipv6_ecommunity(attr2) + && bgp_attr_get_lcommunity(attr1) + == bgp_attr_get_lcommunity(attr2) + && bgp_attr_get_cluster(attr1) + == bgp_attr_get_cluster(attr2) + && bgp_attr_get_transit(attr1) + == bgp_attr_get_transit(attr2) + && bgp_attr_get_aigp_metric(attr1) + == bgp_attr_get_aigp_metric(attr2) + && attr1->rmap_table_id == attr2->rmap_table_id + && (attr1->encap_tunneltype == attr2->encap_tunneltype) + && encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs) #ifdef ENABLE_BGP_VNC && encap_same(bgp_attr_get_vnc_subtlvs(attr1), bgp_attr_get_vnc_subtlvs(attr2)) #endif && IPV6_ADDR_SAME(&attr1->mp_nexthop_global, - &attr2->mp_nexthop_global) && - IPV6_ADDR_SAME(&attr1->mp_nexthop_local, - &attr2->mp_nexthop_local) && - IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in, - &attr2->mp_nexthop_global_in) && - IPV4_ADDR_SAME(&attr1->originator_id, - &attr2->originator_id) && - overlay_index_same(attr1, attr2) && - !memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t)) && - attr1->es_flags == attr2->es_flags && - attr1->mm_sync_seqnum == attr2->mm_sync_seqnum && - attr1->df_pref == attr2->df_pref && - attr1->df_alg == attr2->df_alg && - attr1->nh_ifindex == attr2->nh_ifindex && - attr1->nh_flag == attr2->nh_flag && - attr1->nh_lla_ifindex == attr2->nh_lla_ifindex && - attr1->distance == attr2->distance && - srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) && - 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->otc == attr2->otc) + &attr2->mp_nexthop_global) + && IPV6_ADDR_SAME(&attr1->mp_nexthop_local, + &attr2->mp_nexthop_local) + && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in, + &attr2->mp_nexthop_global_in) + && IPV4_ADDR_SAME(&attr1->originator_id, + &attr2->originator_id) + && overlay_index_same(attr1, attr2) + && !memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t)) + && attr1->es_flags == attr2->es_flags + && attr1->mm_sync_seqnum == attr2->mm_sync_seqnum + && attr1->df_pref == attr2->df_pref + && attr1->df_alg == attr2->df_alg + && attr1->nh_ifindex == attr2->nh_ifindex + && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex + && attr1->distance == attr2->distance + && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) + && 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->otc == attr2->otc) return true; } @@ -2255,12 +2253,6 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, return BGP_ATTR_PARSE_WITHDRAW; } attr->nh_ifindex = peer->nexthop.ifp->ifindex; - if (if_is_operative(peer->nexthop.ifp)) - SET_FLAG(attr->nh_flag, - BGP_ATTR_NH_IF_OPERSTATE); - else - UNSET_FLAG(attr->nh_flag, - BGP_ATTR_NH_IF_OPERSTATE); } break; case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: @@ -2278,12 +2270,6 @@ int bgp_mp_reach_parse(struct bgp_attr_parser_args *args, return BGP_ATTR_PARSE_WITHDRAW; } attr->nh_ifindex = peer->nexthop.ifp->ifindex; - if (if_is_operative(peer->nexthop.ifp)) - SET_FLAG(attr->nh_flag, - BGP_ATTR_NH_IF_OPERSTATE); - else - UNSET_FLAG(attr->nh_flag, - BGP_ATTR_NH_IF_OPERSTATE); } if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) { diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index f8beb5fba9..a34da1a6de 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -170,12 +170,6 @@ struct attr { uint32_t med; uint32_t local_pref; ifindex_t nh_ifindex; - uint8_t nh_flag; - -#define BGP_ATTR_NH_VALID 0x01 -#define BGP_ATTR_NH_IF_OPERSTATE 0x02 -#define BGP_ATTR_NH_MP_PREFER_GLOBAL 0x04 /* MP Nexthop preference */ -#define BGP_ATTR_NH_REFRESH 0x08 /* Path origin attribute */ uint8_t origin; @@ -226,6 +220,9 @@ struct attr { /* MP Nexthop length */ uint8_t mp_nexthop_len; + /* MP Nexthop preference */ + uint8_t mp_nexthop_prefer_global; + /* Static MAC for EVPN */ uint8_t sticky; diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c index d66b916b95..11b074af2e 100644 --- a/bgpd/bgp_bfd.c +++ b/bgpd/bgp_bfd.c @@ -521,10 +521,8 @@ DEFUN (neighbor_bfd_check_controlplane_failure, else idx_peer = 1; peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg); - if (!peer) { - vty_out(vty, "%% Specify remote-as or peer-group commands first\n"); + if (!peer) return CMD_WARNING_CONFIG_FAILED; - } if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) bgp_group_configure_bfd(peer); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 7a8a91b00b..286f47b2bc 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -6436,14 +6436,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni) /* Locate VNI hash */ vpn = bgp_evpn_lookup_vni(bgp, vni); - if (!vpn) { - if (bgp_debug_zebra(NULL)) - flog_warn( - EC_BGP_EVPN_VPN_VNI, - "%u: VNI hash entry for VNI %u not found at DEL", - bgp->vrf_id, vni); + if (!vpn) return 0; - } /* Remove all local EVPN routes and schedule for processing (to * withdraw from peers). diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index c3f648b720..ca6f079401 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -3192,6 +3192,21 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, } } +int bgp_evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, + bool use_json, int detail) +{ + json_object *json = NULL; + + if (use_json) + json = json_object_new_object(); + + evpn_show_all_routes(vty, bgp, type, json, detail); + + if (use_json) + vty_json(vty, json); + return CMD_SUCCESS; +} + /* * Display specified VNI (vty handler) */ diff --git a/bgpd/bgp_evpn_vty.h b/bgpd/bgp_evpn_vty.h index 6b17a83b74..8901644d73 100644 --- a/bgpd/bgp_evpn_vty.h +++ b/bgpd/bgp_evpn_vty.h @@ -42,4 +42,7 @@ extern int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, extern int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv, int argc); +extern int bgp_evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type, + bool use_json, int detail); + #endif /* _QUAGGA_BGP_EVPN_VTY_H */ diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c index 84c847d796..32a5e14b11 100644 --- a/bgpd/bgp_mpath.c +++ b/bgpd/bgp_mpath.c @@ -142,21 +142,15 @@ int bgp_path_info_nexthop_cmp(struct bgp_path_info *bpi1, &bpi2->attr->mp_nexthop_global); break; case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: - addr1 = (CHECK_FLAG( - bpi1->attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) + addr1 = (bpi1->attr->mp_nexthop_prefer_global) ? bpi1->attr->mp_nexthop_global : bpi1->attr->mp_nexthop_local; - addr2 = (CHECK_FLAG( - bpi2->attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) + addr2 = (bpi2->attr->mp_nexthop_prefer_global) ? bpi2->attr->mp_nexthop_global : bpi2->attr->mp_nexthop_local; - if (!CHECK_FLAG(bpi1->attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL) && - !CHECK_FLAG(bpi2->attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) + if (!bpi1->attr->mp_nexthop_prefer_global + && !bpi2->attr->mp_nexthop_prefer_global) compare = !bgp_interface_same( bpi1->peer->ifp, bpi2->peer->ifp); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 9b86c9b4b1..c92d678eff 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -1060,11 +1060,9 @@ static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn, { struct bgp_path_info *bpi_ultimate; struct bgp *bgp_nexthop; - struct bgp_table *table; bool nh_valid; bpi_ultimate = bgp_get_imported_bpi_ultimate(source_bpi); - table = bgp_dest_table(bpi_ultimate->net); if (bpi->extra && bpi->extra->bgp_orig) bgp_nexthop = bpi->extra->bgp_orig; @@ -1072,25 +1070,13 @@ static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn, bgp_nexthop = bgp_orig; /* - * No nexthop tracking for redistributed routes, - * for static (i.e. coming from the bgp network statement or for + * No nexthop tracking for redistributed routes or for * EVPN-imported routes that get leaked. */ if (bpi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE || is_pi_family_evpn(bpi_ultimate)) nh_valid = 1; - else if (bpi_ultimate->type == ZEBRA_ROUTE_BGP && - bpi_ultimate->sub_type == BGP_ROUTE_STATIC && table && - (table->safi == SAFI_UNICAST || - table->safi == SAFI_LABELED_UNICAST)) { - /* Routes from network statement */ - if (CHECK_FLAG(bgp_nexthop->flags, BGP_FLAG_IMPORT_CHECK)) - nh_valid = bgp_find_or_add_nexthop( - to_bgp, bgp_nexthop, afi, safi, bpi_ultimate, - NULL, 0, p); - else - nh_valid = 1; - } else + else /* * TBD do we need to do anything about the * 'connected' parameter? @@ -1280,7 +1266,6 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, if (debug) zlog_debug("%s: ->%s: %pBD Found route, changed attr", __func__, to_bgp->name_pretty, bn); - UNSET_FLAG(bpi->attr->nh_flag, BGP_ATTR_NH_REFRESH); return bpi; } @@ -1879,31 +1864,11 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ uint32_t num_labels = 0; int nexthop_self_flag = 1; struct bgp_path_info *bpi_ultimate = NULL; - struct bgp_path_info *bpi; int origin_local = 0; struct bgp *src_vrf; - struct interface *ifp; int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); - /* - * For VRF-2-VRF route-leaking, - * the source will be the originating VRF. - * - * If ACCEPT_OWN mechanism is enabled, then we SHOULD(?) - * get the source VRF (BGP) by looking at the RD. - */ - struct bgp *src_bgp = bgp_lookup_by_rd(path_vpn, prd, afi); - - if (path_vpn->extra && path_vpn->extra->bgp_orig) - src_vrf = path_vpn->extra->bgp_orig; - else if (src_bgp) - src_vrf = src_bgp; - else - src_vrf = from_bgp; - - bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p, NULL); - if (!vpn_leak_from_vpn_active(to_bgp, afi, &debugmsg)) { if (debug) zlog_debug("%s: skipping: %s", __func__, debugmsg); @@ -1963,18 +1928,6 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ community_strip_accept_own(&static_attr); - for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { - if (bpi->extra && bpi->extra->parent == path_vpn) - break; - } - - if (bpi && - leak_update_nexthop_valid(to_bgp, bn, &static_attr, afi, safi, - path_vpn, bpi, src_vrf, p, debug)) - SET_FLAG(static_attr.nh_flag, BGP_ATTR_NH_VALID); - else - UNSET_FLAG(static_attr.nh_flag, BGP_ATTR_NH_VALID); - /* * Nexthop: stash and clear * @@ -2017,22 +1970,6 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ break; } - if (static_attr.nexthop.s_addr == INADDR_ANY && - IN6_IS_ADDR_UNSPECIFIED(&static_attr.mp_nexthop_global)) { - ifp = if_get_vrf_loopback(src_vrf->vrf_id); - if (ifp) - static_attr.nh_ifindex = ifp->ifindex; - } else if (static_attr.nh_ifindex) - ifp = if_lookup_by_index(static_attr.nh_ifindex, - src_vrf->vrf_id); - else - ifp = NULL; - - if (ifp && if_is_operative(ifp)) - SET_FLAG(static_attr.nh_flag, BGP_ATTR_NH_IF_OPERSTATE); - else - UNSET_FLAG(static_attr.nh_flag, BGP_ATTR_NH_IF_OPERSTATE); - /* * route map handling */ @@ -2114,6 +2051,22 @@ static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */ zlog_debug("%s: pfx %pBD: num_labels %d", __func__, path_vpn->net, num_labels); + /* + * For VRF-2-VRF route-leaking, + * the source will be the originating VRF. + * + * If ACCEPT_OWN mechanism is enabled, then we SHOULD(?) + * get the source VRF (BGP) by looking at the RD. + */ + struct bgp *src_bgp = bgp_lookup_by_rd(path_vpn, prd, afi); + + if (path_vpn->extra && path_vpn->extra->bgp_orig) + src_vrf = path_vpn->extra->bgp_orig; + else if (src_bgp) + src_vrf = src_bgp; + else + src_vrf = from_bgp; + leak_update(to_bgp, bn, new_attr, afi, safi, path_vpn, pLabels, num_labels, src_vrf, &nexthop_orig, nexthop_self_flag, debug); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index b6b0c584d7..cf8ff524e9 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -46,7 +46,6 @@ #include "bgpd/bgp_flowspec_util.h" #include "bgpd/bgp_evpn.h" #include "bgpd/bgp_rd.h" -#include "bgpd/bgp_mplsvpn.h" extern struct zclient *zclient; @@ -389,7 +388,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, if (pi && is_route_parent_evpn(pi)) bnc->is_evpn_gwip_nexthop = true; - if (is_bgp_static_route && !CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)) { + if (is_bgp_static_route) { SET_FLAG(bnc->flags, BGP_STATIC_ROUTE); /* If we're toggling the type, re-register */ @@ -424,8 +423,8 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, SET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); - } else if (peer && !connected && - CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) { + } else if (peer && !connected + && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) { UNSET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID); @@ -835,13 +834,10 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) { struct bgp_nexthop_cache_head *tree = NULL; struct bgp_nexthop_cache *bnc_nhc, *bnc_import; - struct bgp_path_info *pi; - struct bgp_dest *dest; struct bgp *bgp; struct prefix match; struct zapi_route nhr; afi_t afi; - safi_t safi; bgp = bgp_lookup_by_vrf_id(vrf_id); if (!bgp) { @@ -862,37 +858,25 @@ void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id) tree = &bgp->nexthop_cache_table[afi]; bnc_nhc = bnc_find(tree, &match, nhr.srte_color, 0); - if (bnc_nhc) + if (!bnc_nhc) { + if (BGP_DEBUG(nht, NHT)) + zlog_debug( + "parse nexthop update(%pFX(%u)(%s)): bnc info not found for nexthop cache", + &nhr.prefix, nhr.srte_color, bgp->name_pretty); + } else bgp_process_nexthop_update(bnc_nhc, &nhr, false); - else if (BGP_DEBUG(nht, NHT)) - zlog_debug( - "parse nexthop update(%pFX(%u)(%s)): bnc info not found for nexthop cache", - &nhr.prefix, nhr.srte_color, bgp->name_pretty); tree = &bgp->import_check_table[afi]; bnc_import = bnc_find(tree, &match, nhr.srte_color, 0); - if (bnc_import) { + if (!bnc_import) { + if (BGP_DEBUG(nht, NHT)) + zlog_debug( + "parse nexthop update(%pFX(%u)(%s)): bnc info not found for import check", + &nhr.prefix, nhr.srte_color, bgp->name_pretty); + } else bgp_process_nexthop_update(bnc_import, &nhr, true); - safi = nhr.safi; - if (bgp->rib[afi][safi]) { - dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, - &match, NULL); - - for (pi = bgp_dest_get_bgp_path_info(dest); pi; - pi = pi->next) - if (pi->peer == bgp->peer_self && - pi->type == ZEBRA_ROUTE_BGP && - pi->sub_type == BGP_ROUTE_STATIC) - vpn_leak_from_vrf_update( - bgp_get_default(), bgp, pi); - } - } else if (BGP_DEBUG(nht, NHT)) - zlog_debug( - "parse nexthop update(%pFX(%u)(%s)): bnc info not found for import check", - &nhr.prefix, nhr.srte_color, bgp->name_pretty); - /* * HACK: if any BGP route is dependant on an SR-policy that doesn't * exist, zebra will never send NH updates relative to that policy. In @@ -1005,8 +989,7 @@ static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p) */ else if (pi->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) { - if (CHECK_FLAG(pi->attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) + if (pi->attr->mp_nexthop_prefer_global) p->u.prefix6 = pi->attr->mp_nexthop_global; else diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 0ef939875a..87871573f0 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -8684,7 +8684,6 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, afi_t afi; route_map_result_t ret; struct bgp_redist *red; - struct interface *ifp; /* Make default attribute. */ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE); @@ -8734,11 +8733,6 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, } attr.nh_type = nhtype; attr.nh_ifindex = ifindex; - ifp = if_lookup_by_index(ifindex, bgp->vrf_id); - if (ifp && if_is_operative(ifp)) - SET_FLAG(attr.nh_flag, BGP_ATTR_NH_IF_OPERSTATE); - else - UNSET_FLAG(attr.nh_flag, BGP_ATTR_NH_IF_OPERSTATE); attr.med = metric; attr.distance = distance; @@ -9425,10 +9419,9 @@ void route_vty_out(struct vty *vty, const struct prefix *p, "link-local"); if ((IPV6_ADDR_CMP(&attr->mp_nexthop_global, - &attr->mp_nexthop_local) != - 0) && - !CHECK_FLAG(attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) + &attr->mp_nexthop_local) + != 0) + && !attr->mp_nexthop_prefer_global) json_object_boolean_true_add( json_nexthop_ll, "used"); else @@ -9440,11 +9433,10 @@ void route_vty_out(struct vty *vty, const struct prefix *p, } else { /* Display LL if LL/Global both in table unless * prefer-global is set */ - if (((attr->mp_nexthop_len == - BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) && - !CHECK_FLAG(attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) || - (path->peer->conf_if)) { + if (((attr->mp_nexthop_len + == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) + && !attr->mp_nexthop_prefer_global) + || (path->peer->conf_if)) { if (path->peer->conf_if) { len = vty_out(vty, "%s", path->peer->conf_if); @@ -10706,8 +10698,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, json_object_boolean_true_add(json_nexthop_ll, "accessible"); - if (!CHECK_FLAG(attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) + if (!attr->mp_nexthop_prefer_global) json_object_boolean_true_add(json_nexthop_ll, "used"); else @@ -10717,8 +10708,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn, vty_out(vty, " (%s) %s\n", inet_ntop(AF_INET6, &attr->mp_nexthop_local, buf, INET6_ADDRSTRLEN), - CHECK_FLAG(attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL) + attr->mp_nexthop_prefer_global ? "(prefer-global)" : "(used)"); } @@ -11812,6 +11802,9 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, 1, NULL, NULL); } + if (safi == SAFI_EVPN) + return bgp_evpn_show_all_routes(vty, bgp, type, use_json, 0); + return bgp_show_table(vty, bgp, safi, table, type, output_arg, NULL, 1, NULL, NULL, &json_header_depth, show_flags, rpki_target_state); @@ -14010,7 +14003,8 @@ static void show_adj_route_header(struct vty *vty, struct peer *peer, struct bgp_table *table, int *header1, int *header2, json_object *json, json_object *json_scode, - json_object *json_ocode, bool wide) + json_object *json_ocode, bool wide, + bool detail) { uint64_t version = table ? table->version : 0; @@ -14044,15 +14038,17 @@ static void show_adj_route_header(struct vty *vty, struct peer *peer, vty_out(vty, "local AS %u\n", peer->change_local_as ? peer->change_local_as : peer->local_as); - vty_out(vty, BGP_SHOW_SCODE_HEADER); - vty_out(vty, BGP_SHOW_NCODE_HEADER); - vty_out(vty, BGP_SHOW_OCODE_HEADER); - vty_out(vty, BGP_SHOW_RPKI_HEADER); + if (!detail) { + vty_out(vty, BGP_SHOW_SCODE_HEADER); + vty_out(vty, BGP_SHOW_NCODE_HEADER); + vty_out(vty, BGP_SHOW_OCODE_HEADER); + vty_out(vty, BGP_SHOW_RPKI_HEADER); + } } *header1 = 0; } if (*header2) { - if (!json) + if (!json && !detail) vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE : BGP_SHOW_HEADER)); *header2 = 0; @@ -14076,12 +14072,15 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, struct update_subgroup *subgrp; struct peer_af *paf; bool route_filtered; + bool detail = CHECK_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL); bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON); bool wide = CHECK_FLAG(show_flags, BGP_SHOW_OPT_WIDE); bool show_rd = ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN)) ? true : false; + int display = 0; + json_object *json_net = NULL; bgp = peer->bgp; @@ -14122,10 +14121,12 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, vty_out(vty, "local AS %u\n", peer->change_local_as ? peer->change_local_as : peer->local_as); - vty_out(vty, BGP_SHOW_SCODE_HEADER); - vty_out(vty, BGP_SHOW_NCODE_HEADER); - vty_out(vty, BGP_SHOW_OCODE_HEADER); - vty_out(vty, BGP_SHOW_RPKI_HEADER); + if (!detail) { + vty_out(vty, BGP_SHOW_SCODE_HEADER); + vty_out(vty, BGP_SHOW_NCODE_HEADER); + vty_out(vty, BGP_SHOW_OCODE_HEADER); + vty_out(vty, BGP_SHOW_RPKI_HEADER); + } vty_out(vty, "Originating default network %s\n\n", (afi == AFI_IP) ? "0.0.0.0/0" : "::/0"); @@ -14143,7 +14144,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, show_adj_route_header(vty, peer, table, header1, header2, json, json_scode, - json_ocode, wide); + json_ocode, wide, detail); if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) @@ -14187,8 +14188,23 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, && (route_filtered || ret == RMAP_DENY)) (*filtered_count)++; - route_vty_out_tmp(vty, dest, rn_p, &attr, safi, - use_json, json_ar, wide); + if (detail) { + if (use_json) + json_net = + json_object_new_object(); + bgp_show_path_info( + NULL /* prefix_rd */, dest, vty, + bgp, afi, safi, json_net, + BGP_PATH_SHOW_ALL, &display, + RPKI_NOT_BEING_USED); + if (use_json) + json_object_object_addf( + json_ar, json_net, + "%pFX", rn_p); + } else + route_vty_out_tmp(vty, dest, rn_p, + &attr, safi, use_json, + json_ar, wide); bgp_attr_flush(&attr); (*output_count)++; } @@ -14198,10 +14214,10 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, if (paf->peer != peer || !adj->attr) continue; - show_adj_route_header(vty, peer, table, - header1, header2, - json, json_scode, - json_ocode, wide); + show_adj_route_header( + vty, peer, table, header1, + header2, json, json_scode, + json_ocode, wide, detail); const struct prefix *rn_p = bgp_dest_get_prefix(dest); @@ -14228,10 +14244,32 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, show_rd = false; } } - route_vty_out_tmp( - vty, dest, rn_p, &attr, - safi, use_json, json_ar, - wide); + if (detail) { + if (use_json) + json_net = + json_object_new_object(); + bgp_show_path_info( + NULL /* prefix_rd + */ + , + dest, vty, bgp, + afi, safi, + json_net, + BGP_PATH_SHOW_ALL, + &display, + RPKI_NOT_BEING_USED); + if (use_json) + json_object_object_addf( + json_ar, + json_net, + "%pFX", + rn_p); + } else + route_vty_out_tmp( + vty, dest, rn_p, + &attr, safi, + use_json, + json_ar, wide); (*output_count)++; } else { (*filtered_count)++; @@ -14244,7 +14282,7 @@ show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table, show_adj_route_header(vty, peer, table, header1, header2, json, json_scode, - json_ocode, wide); + json_ocode, wide, detail); for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { @@ -14496,27 +14534,28 @@ DEFPY (show_ip_bgp_instance_neighbor_bestpath_route, show_flags); } -DEFPY (show_ip_bgp_instance_neighbor_advertised_route, - show_ip_bgp_instance_neighbor_advertised_route_cmd, - "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [all$all] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map RMAP_NAME$route_map] [json$uj | wide$wide]", - SHOW_STR - IP_STR - BGP_STR - BGP_INSTANCE_HELP_STR - BGP_AFI_HELP_STR - BGP_SAFI_WITH_LABEL_HELP_STR - "Display the entries for all address families\n" - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Neighbor on BGP configured interface\n" - "Display the routes advertised to a BGP neighbor\n" - "Display the received routes from neighbor\n" - "Display the filtered routes received from neighbor\n" - "Route-map to modify the attributes\n" - "Name of the route map\n" - JSON_STR - "Increase table width for longer prefixes\n") +DEFPY(show_ip_bgp_instance_neighbor_advertised_route, + show_ip_bgp_instance_neighbor_advertised_route_cmd, + "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR " [" BGP_SAFI_WITH_LABEL_CMD_STR "]] [all$all] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map RMAP_NAME$route_map] [detail$detail] [json$uj | wide$wide]", + SHOW_STR + IP_STR + BGP_STR + BGP_INSTANCE_HELP_STR + BGP_AFI_HELP_STR + BGP_SAFI_WITH_LABEL_HELP_STR + "Display the entries for all address families\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Neighbor on BGP configured interface\n" + "Display the routes advertised to a BGP neighbor\n" + "Display the received routes from neighbor\n" + "Display the filtered routes received from neighbor\n" + "Route-map to modify the attributes\n" + "Name of the route map\n" + "Display detailed version of routes\n" + JSON_STR + "Increase table width for longer prefixes\n") { afi_t afi = AFI_IP6; safi_t safi = SAFI_UNICAST; @@ -14530,6 +14569,9 @@ DEFPY (show_ip_bgp_instance_neighbor_advertised_route, struct listnode *node; struct bgp *abgp; + if (detail) + SET_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL); + if (uj) { argc--; SET_FLAG(show_flags, BGP_SHOW_OPT_JSON); diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index f779b34371..1ce2eb4352 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3531,11 +3531,11 @@ route_set_ipv6_nexthop_prefer_global(void *rule, const struct prefix *prefix, if (CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IN) || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IMPORT)) { /* Set next hop preference to global */ - SET_FLAG(path->attr->nh_flag, BGP_ATTR_NH_MP_PREFER_GLOBAL); + path->attr->mp_nexthop_prefer_global = true; SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED); } else { - UNSET_FLAG(path->attr->nh_flag, BGP_ATTR_NH_MP_PREFER_GLOBAL); + path->attr->mp_nexthop_prefer_global = false; SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED); } diff --git a/bgpd/bgp_snmp_bgp4v2.c b/bgpd/bgp_snmp_bgp4v2.c index fe0c33251e..d8d8549960 100644 --- a/bgpd/bgp_snmp_bgp4v2.c +++ b/bgpd/bgp_snmp_bgp4v2.c @@ -704,8 +704,7 @@ static uint8_t *bgp4v2PathAttrTable(struct variable *v, oid name[], case BGP_ATTR_NHLEN_IPV6_GLOBAL: return SNMP_INTEGER(2); case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: - if (CHECK_FLAG(path->attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) + if (path->attr->mp_nexthop_prefer_global) return SNMP_INTEGER(2); else return SNMP_INTEGER(4); @@ -719,8 +718,7 @@ static uint8_t *bgp4v2PathAttrTable(struct variable *v, oid name[], case BGP_ATTR_NHLEN_IPV6_GLOBAL: return SNMP_IP6ADDRESS(path->attr->mp_nexthop_global); case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL: - if (CHECK_FLAG(path->attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) + if (path->attr->mp_nexthop_prefer_global) return SNMP_IP6ADDRESS( path->attr->mp_nexthop_global); else diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 8ea9c1996b..4acf4f76aa 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -819,6 +819,9 @@ struct peer *peer_and_group_lookup_vty(struct vty *vty, const char *peer_str) if (peer) { if (peer_dynamic_neighbor(peer)) { + zlog_warn( + "%pBP: Operation not allowed on a dynamic neighbor", + peer); vty_out(vty, "%% Operation not allowed on a dynamic neighbor\n"); return NULL; @@ -830,6 +833,8 @@ struct peer *peer_and_group_lookup_vty(struct vty *vty, const char *peer_str) if (group) return group->conf; + zlog_warn("Specify remote-as or peer-group commands first before: %s", + vty->buf); vty_out(vty, "%% Specify remote-as or peer-group commands first\n"); return NULL; @@ -10263,35 +10268,158 @@ DEFUN (show_bgp_views, return CMD_SUCCESS; } -DEFUN (show_bgp_vrfs, +static inline void calc_peers_cfgd_estbd(struct bgp *bgp, int *peers_cfgd, + int *peers_estbd) +{ + struct peer *peer; + struct listnode *node; + + *peers_cfgd = *peers_estbd = 0; + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + continue; + (*peers_cfgd)++; + if (peer_established(peer)) + (*peers_estbd)++; + } +} + +static void print_bgp_vrfs(struct bgp *bgp, struct vty *vty, json_object *json, + const char *type) +{ + int peers_cfg, peers_estb; + + calc_peers_cfgd_estbd(bgp, &peers_cfg, &peers_estb); + + if (json) { + int64_t vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN) + ? -1 + : (int64_t)bgp->vrf_id; + json_object_string_add(json, "type", type); + json_object_int_add(json, "vrfId", vrf_id_ui); + json_object_string_addf(json, "routerId", "%pI4", + &bgp->router_id); + json_object_int_add(json, "numConfiguredPeers", peers_cfg); + json_object_int_add(json, "numEstablishedPeers", peers_estb); + json_object_int_add(json, "l3vni", bgp->l3vni); + json_object_string_addf(json, "rmac", "%pEA", &bgp->rmac); + json_object_string_add( + json, "interface", + ifindex2ifname(bgp->l3vni_svi_ifindex, bgp->vrf_id)); + } +} + +static int show_bgp_vrfs_detail_common(struct vty *vty, struct bgp *bgp, + json_object *json, const char *name, + const char *type, bool use_vrf) +{ + int peers_cfg, peers_estb; + + calc_peers_cfgd_estbd(bgp, &peers_cfg, &peers_estb); + + if (use_vrf) { + if (json) { + print_bgp_vrfs(bgp, vty, json, type); + } else { + vty_out(vty, "BGP instance %s VRF id %d\n", + bgp->name_pretty, + bgp->vrf_id == VRF_UNKNOWN ? -1 + : (int)bgp->vrf_id); + vty_out(vty, "Router Id %pI4\n", &bgp->router_id); + vty_out(vty, + "Num Configured Peers %d, Established %d\n", + peers_cfg, peers_estb); + if (bgp->l3vni) { + vty_out(vty, + "L3VNI %u, L3VNI-SVI %s, Router MAC %pEA\n", + bgp->l3vni, + ifindex2ifname(bgp->l3vni_svi_ifindex, + bgp->vrf_id), + &bgp->rmac); + } + } + } else { + if (json) { + print_bgp_vrfs(bgp, vty, json, type); + } else { + vty_out(vty, "%4s %-5d %-16pI4 %-9u %-10u %-37s\n", + type, + bgp->vrf_id == VRF_UNKNOWN ? -1 + : (int)bgp->vrf_id, + &bgp->router_id, peers_cfg, peers_estb, name); + vty_out(vty, "%11s %-16u %-21pEA %-20s\n", " ", + bgp->l3vni, &bgp->rmac, + ifindex2ifname(bgp->l3vni_svi_ifindex, + bgp->vrf_id)); + } + } + + return CMD_SUCCESS; +} + +DEFPY (show_bgp_vrfs, show_bgp_vrfs_cmd, - "show [ip] bgp vrfs [json]", + "show [ip] bgp vrfs [<VRFNAME$vrf_name>] [json]", SHOW_STR IP_STR BGP_STR "Show BGP VRFs\n" + "Specific VRF name\n" JSON_STR) { - char buf[ETHER_ADDR_STRLEN]; struct list *inst = bm->bgp; struct listnode *node; struct bgp *bgp; bool uj = use_json(argc, argv); json_object *json = NULL; json_object *json_vrfs = NULL; + json_object *json_vrf = NULL; int count = 0; + const char *name = vrf_name; + const char *type; - if (uj) { + if (uj) json = json_object_new_object(); - json_vrfs = json_object_new_object(); + + if (name) { + if (strmatch(name, VRF_DEFAULT_NAME)) { + bgp = bgp_get_default(); + type = "DFLT"; + } else { + bgp = bgp_lookup_by_name(name); + type = "VRF"; + } + if (!bgp) { + if (uj) + vty_json(vty, json); + else + vty_out(vty, + "%% Specified BGP instance not found\n"); + + return CMD_WARNING; + } } + if (vrf_name) { + if (uj) + json_vrf = json_object_new_object(); + + show_bgp_vrfs_detail_common(vty, bgp, json_vrf, name, type, + true); + + if (uj) { + json_object_object_add(json, name, json_vrf); + vty_json(vty, json); + } + + return CMD_SUCCESS; + } + + if (uj) + json_vrfs = json_object_new_object(); + for (ALL_LIST_ELEMENTS_RO(inst, node, bgp)) { - const char *name, *type; - struct peer *peer; - struct listnode *node2, *nnode2; - int peers_cfg, peers_estb; - json_object *json_vrf = NULL; + const char *name; /* Skip Views. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW) @@ -10306,20 +10434,9 @@ DEFUN (show_bgp_vrfs, vty_out(vty, "%11s %-16s %-21s %-6s\n", " ", "L3-VNI", "RouterMAC", "Interface"); } - - peers_cfg = peers_estb = 0; if (uj) json_vrf = json_object_new_object(); - - for (ALL_LIST_ELEMENTS(bgp->peer, node2, nnode2, peer)) { - if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) - continue; - peers_cfg++; - if (peer_established(peer)) - peers_estb++; - } - if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { name = VRF_DEFAULT_NAME; type = "DFLT"; @@ -10328,49 +10445,16 @@ DEFUN (show_bgp_vrfs, type = "VRF"; } + show_bgp_vrfs_detail_common(vty, bgp, json_vrf, name, type, + false); - if (uj) { - int64_t vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN) - ? -1 - : (int64_t)bgp->vrf_id; - char buf[BUFSIZ] = {0}; - - json_object_string_add(json_vrf, "type", type); - json_object_int_add(json_vrf, "vrfId", vrf_id_ui); - json_object_string_addf(json_vrf, "routerId", "%pI4", - &bgp->router_id); - json_object_int_add(json_vrf, "numConfiguredPeers", - peers_cfg); - json_object_int_add(json_vrf, "numEstablishedPeers", - peers_estb); - - json_object_int_add(json_vrf, "l3vni", bgp->l3vni); - json_object_string_add( - json_vrf, "rmac", - prefix_mac2str(&bgp->rmac, buf, sizeof(buf))); - json_object_string_add(json_vrf, "interface", - ifindex2ifname(bgp->l3vni_svi_ifindex, - bgp->vrf_id)); + if (uj) json_object_object_add(json_vrfs, name, json_vrf); - } else { - vty_out(vty, "%4s %-5d %-16pI4 %-9u %-10u %-37s\n", - type, - bgp->vrf_id == VRF_UNKNOWN ? -1 - : (int)bgp->vrf_id, - &bgp->router_id, peers_cfg, peers_estb, name); - vty_out(vty,"%11s %-16u %-21s %-20s\n", " ", - bgp->l3vni, - prefix_mac2str(&bgp->rmac, buf, sizeof(buf)), - ifindex2ifname(bgp->l3vni_svi_ifindex, - bgp->vrf_id)); - } } if (uj) { json_object_object_add(json, "vrfs", json_vrfs); - json_object_int_add(json, "totalVrfs", count); - vty_json(vty, json); } else { if (count) diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index f6e7b444c6..8ce17ab49e 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -234,7 +234,6 @@ static int bgp_ifp_up(struct interface *ifp) struct connected *c; struct nbr_connected *nc; struct listnode *node, *nnode; - struct bgp *bgp_default = bgp_get_default(); struct bgp *bgp; bgp = ifp->vrf->info; @@ -257,14 +256,6 @@ static int bgp_ifp_up(struct interface *ifp) hook_call(bgp_vrf_status_changed, bgp, ifp); bgp_nht_ifp_up(ifp); - if (bgp_default && if_is_loopback(ifp)) { - vpn_leak_zebra_vrf_label_update(bgp, AFI_IP); - vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6); - vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP); - vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP6); - vpn_leak_postchange_all(); - } - return 0; } @@ -273,7 +264,6 @@ static int bgp_ifp_down(struct interface *ifp) struct connected *c; struct nbr_connected *nc; struct listnode *node, *nnode; - struct bgp *bgp_default = bgp_get_default(); struct bgp *bgp; struct peer *peer; @@ -313,14 +303,6 @@ static int bgp_ifp_down(struct interface *ifp) hook_call(bgp_vrf_status_changed, bgp, ifp); bgp_nht_ifp_down(ifp); - if (bgp_default && if_is_loopback(ifp)) { - vpn_leak_zebra_vrf_label_update(bgp, AFI_IP); - vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6); - vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP); - vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP6); - vpn_leak_postchange_all(); - } - return 0; } @@ -408,16 +390,10 @@ static int bgp_interface_address_add(ZAPI_CALLBACK_ARGS) static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS) { struct listnode *node, *nnode; - struct bgp_path_info *pi; - struct bgp_table *table; - struct bgp_dest *dest; struct connected *ifc; struct peer *peer; - struct bgp *bgp, *from_bgp, *bgp_default; - struct listnode *next; + struct bgp *bgp; struct prefix *addr; - afi_t afi; - safi_t safi; bgp = bgp_lookup_by_vrf_id(vrf_id); @@ -445,6 +421,9 @@ static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS) * we do not want the peering to bounce. */ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + afi_t afi; + safi_t safi; + if (addr->family == AF_INET) continue; @@ -460,44 +439,6 @@ static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS) } } - bgp_default = bgp_get_default(); - afi = family2afi(addr->family); - safi = SAFI_UNICAST; - - /* When the last IPv4 address was deleted, Linux removes all routes - * using the interface so that bgpd needs to re-send them. - */ - if (bgp_default && afi == AFI_IP) { - for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, from_bgp)) { - table = from_bgp->rib[afi][safi]; - if (!table) - continue; - - for (dest = bgp_table_top(table); dest; - dest = bgp_route_next(dest)) { - for (pi = bgp_dest_get_bgp_path_info(dest); pi; - pi = pi->next) { - if (pi->type == ZEBRA_ROUTE_BGP && - pi->attr && - pi->attr->nh_ifindex == - ifc->ifp->ifindex) { - SET_FLAG(pi->attr->nh_flag, - BGP_ATTR_NH_REFRESH); - } - } - } - - if (from_bgp->inst_type != BGP_INSTANCE_TYPE_VRF) - continue; - - vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, - bgp_default, from_bgp); - - vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi, - bgp_default, from_bgp); - } - } - connected_free(&ifc); return 0; @@ -1066,8 +1007,7 @@ bgp_path_info_to_ipv6_nexthop(struct bgp_path_info *path, ifindex_t *ifindex) || path->attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) { /* Check if route-map is set to prefer global over link-local */ - if (CHECK_FLAG(path->attr->nh_flag, - BGP_ATTR_NH_MP_PREFER_GLOBAL)) { + if (path->attr->mp_nexthop_prefer_global) { nexthop = &path->attr->mp_nexthop_global; if (IN6_IS_ADDR_LINKLOCAL(nexthop)) *ifindex = path->attr->nh_ifindex; @@ -1367,7 +1307,6 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, uint8_t distance; struct peer *peer; struct bgp_path_info *mpinfo; - struct bgp_path_info *bpi_ultimate; struct bgp *bgp_orig; uint32_t metric; struct attr local_attr; @@ -1416,9 +1355,13 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, peer = info->peer; - if (info->type == ZEBRA_ROUTE_BGP) { - bpi_ultimate = bgp_get_imported_bpi_ultimate(info); - peer = bpi_ultimate->peer; + if (info->type == ZEBRA_ROUTE_BGP + && info->sub_type == BGP_ROUTE_IMPORTED) { + + /* Obtain peer from parent */ + if (info->extra && info->extra->parent) + peer = ((struct bgp_path_info *)(info->extra->parent)) + ->peer; } tag = info->attr->tag; @@ -3250,7 +3193,6 @@ extern struct zebra_privs_t bgpd_privs; static int bgp_ifp_create(struct interface *ifp) { - struct bgp *bgp_default = bgp_get_default(); struct bgp *bgp; if (BGP_DEBUG(zebra, ZEBRA)) @@ -3265,17 +3207,6 @@ static int bgp_ifp_create(struct interface *ifp) bgp_update_interface_nbrs(bgp, ifp, ifp); hook_call(bgp_vrf_status_changed, bgp, ifp); - - if (bgp_default && - (if_is_loopback_exact(ifp) || - (if_is_vrf(ifp) && ifp->vrf->vrf_id != VRF_DEFAULT))) { - vpn_leak_zebra_vrf_label_update(bgp, AFI_IP); - vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6); - vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP); - vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP6); - vpn_leak_postchange_all(); - } - return 0; } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index a75bfdf746..8697912314 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1906,7 +1906,6 @@ struct bgp_nlri { #define BGP_ATTR_EXT_COMMUNITIES 16 #define BGP_ATTR_AS4_PATH 17 #define BGP_ATTR_AS4_AGGREGATOR 18 -#define BGP_ATTR_AS_PATHLIMIT 21 #define BGP_ATTR_PMSI_TUNNEL 22 #define BGP_ATTR_ENCAP 23 #define BGP_ATTR_IPV6_EXT_COMMUNITIES 25 diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 0c51ce2d21..7f97491630 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -4150,6 +4150,28 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. .. clicmd:: show bgp [afi] [safi] [all] [wide|json] +.. clicmd:: show bgp vrfs [<VRFNAME$vrf_name>] [json] + + The command displays all bgp vrf instances basic info like router-id, + configured and established neighbors, + evpn related basic info like l3vni, router-mac, vxlan-interface. + User can get that information as JSON format when ``json`` keyword + at the end of cli is presented. + + .. code-block:: frr + + torc-11# show bgp vrfs + Type Id routerId #PeersCfg #PeersEstb Name + L3-VNI RouterMAC Interface + DFLT 0 17.0.0.6 3 3 default + 0 00:00:00:00:00:00 unknown + VRF 21 17.0.0.6 0 0 sym_1 + 8888 34:11:12:22:22:01 vlan4034_l3 + VRF 32 17.0.0.6 0 0 sym_2 + 8889 34:11:12:22:22:01 vlan4035_l3 + + Total number of VRFs (including default): 3 + .. clicmd:: show bgp [<ipv4|ipv6> <unicast|multicast|vpn|labeled-unicast|flowspec> | l2vpn evpn] These commands display BGP routes for the specific routing table indicated by @@ -4199,7 +4221,7 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. The ``terse`` option can be used in combination with the remote-as, neighbor, failed and established filters, and with the ``wide`` option as well. -.. clicmd:: show bgp [afi] [safi] [neighbor [PEER] [routes|advertised-routes|received-routes] [json] +.. clicmd:: show bgp [afi] [safi] [neighbor [PEER] [routes|advertised-routes|received-routes] [detail] [json] This command shows information on a specific BGP peer of the relevant afi and safi selected. @@ -4214,6 +4236,13 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. The ``received-routes`` keyword displays all routes belonging to this address-family (prior to inbound policy) that were received by this peer. + If ``detail`` option is specified, the detailed version of all routes + will be displayed. The same format as ``show [ip] bgp [afi] [safi] PREFIX`` + will be used, but for the whole table of received, advertised or filtered + prefixes. + + If ``json`` option is specified, output is displayed in JSON format. + .. clicmd:: show bgp [<view|vrf> VIEWVRFNAME] [afi] [safi] neighbors PEER received prefix-filter [json] Display Address Prefix ORFs received from this peer. @@ -4306,7 +4335,7 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. If the ``json`` option is specified, output is displayed in JSON format. -.. clicmd:: show [ip] bgp [afi] [safi] [all] neighbors A.B.C.D [advertised-routes|received-routes|filtered-routes] [json|wide] +.. clicmd:: show [ip] bgp [afi] [safi] [all] neighbors A.B.C.D [advertised-routes|received-routes|filtered-routes] [detail] [json|wide] Display the routes advertised to a BGP neighbor or received routes from neighbor or filtered routes received from neighbor based on the @@ -4323,6 +4352,11 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`. if afi is specified, with ``all`` option, routes will be displayed for each SAFI in the selcted AFI + If ``detail`` option is specified, the detailed version of all routes + will be displayed. The same format as ``show [ip] bgp [afi] [safi] PREFIX`` + will be used, but for the whole table of received, advertised or filtered + prefixes. + If ``json`` option is specified, output is displayed in JSON format. .. clicmd:: show [ip] bgp [afi] [safi] [all] detail-routes @@ -4493,7 +4527,7 @@ Displaying Update Group Information Display Information about update-group events in FRR. Displaying Nexthop Information --------------------- +------------------------------ .. clicmd:: show [ip] bgp [<view|vrf> VIEWVRFNAME] nexthop ipv4 [A.B.C.D] [detail] [json] .. clicmd:: show [ip] bgp [<view|vrf> VIEWVRFNAME] nexthop ipv6 [X:X::X:X] [detail] [json] diff --git a/doc/user/evpn.rst b/doc/user/evpn.rst index c8052803cc..7c16e1f4ab 100644 --- a/doc/user/evpn.rst +++ b/doc/user/evpn.rst @@ -502,3 +502,11 @@ the creation of the VLAN subinterfaces and directly enslave "eth0" to "br10". ip link set eth0 master br10 This completes the necessary configuration for an L2VNI. + +Displaying EVPN information +--------------------------- + +.. clicmd:: show evpn mac vni (1-16777215) detail [json] + + Display detailed information about MAC addresses for + a specified VNI.
\ No newline at end of file @@ -564,24 +564,9 @@ size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz, return count; } -/* Get the VRF loopback interface, i.e. the loopback on the default VRF - * or the VRF interface. - */ -struct interface *if_get_vrf_loopback(vrf_id_t vrf_id) -{ - struct interface *ifp = NULL; - struct vrf *vrf = vrf_lookup_by_id(vrf_id); - - FOR_ALL_INTERFACES (vrf, ifp) - if (if_is_loopback(ifp)) - return ifp; - - return NULL; -} /* Get interface by name if given name interface doesn't exist create - * one. - */ + one. */ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, const char *vrf_name) { @@ -532,7 +532,6 @@ static inline bool if_address_is_local(const void *matchaddr, int family, struct vrf; extern struct interface *if_lookup_by_name_vrf(const char *name, struct vrf *vrf); extern struct interface *if_lookup_by_name(const char *ifname, vrf_id_t vrf_id); -extern struct interface *if_get_vrf_loopback(vrf_id_t vrf_id); extern struct interface *if_get_by_name(const char *ifname, vrf_id_t vrf_id, const char *vrf_name); diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 3310009c02..082862709d 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -711,6 +711,8 @@ static void on_ifjoin_prune_pending_timer(struct thread *t) pim_channel_del_oif( ch->upstream->channel_oil, ifp, PIM_OIF_FLAG_PROTO_STAR, __func__); + pim_channel_del_oif(ch->upstream->channel_oil, ifp, + PIM_OIF_FLAG_PROTO_PIM, __func__); if (!ch->upstream->channel_oil->installed) pim_upstream_mroute_add( ch->upstream->channel_oil, diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf index 375bbea9ff..46831bb711 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf @@ -4,8 +4,6 @@ hostname ce1 ! interface lo ip address 99.0.0.1/32 - ip address 5.1.0.1/24 - ip address 6.0.2.1/24 ! interface ce1-eth0 description to r1 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf index 90dd3c55b4..fb4d8cc9c4 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf @@ -4,8 +4,6 @@ hostname ce2 ! interface lo ip address 99.0.0.2/32 - ip address 5.1.0.1/24 - ip address 6.0.2.1/24 ! interface ce2-eth0 description to r3 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf index cf7396eb12..e316de5690 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf @@ -19,7 +19,6 @@ router bgp 5227 network 5.1.3.0/24 route-map rm-nh network 6.0.1.0/24 route-map rm-nh network 6.0.2.0/24 route-map rm-nh-same - network 6.0.3.0/24 route-map rm-nh-same neighbor 192.168.1.1 activate exit-address-family ! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf index df6ac47b08..77a1163a4b 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf @@ -4,7 +4,6 @@ hostname ce3 ! interface lo ip address 99.0.0.3/32 - ip address 6.0.3.1/24 ! interface ce3-eth0 description to r4 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf index 9a6ca08a0b..60d9e93108 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf @@ -19,7 +19,6 @@ router bgp 5228 vrf ce4-cust2 network 5.4.3.0/24 route-map rm-nh network 6.0.1.0/24 route-map rm-nh network 6.0.2.0/24 route-map rm-nh-same - network 6.0.3.0/24 route-map rm-nh-same neighbor 192.168.2.1 activate exit-address-family ! diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf index 0e3a736292..e55c9e779a 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf @@ -4,7 +4,6 @@ hostname ce4 ! interface ce4-cust2 ip address 99.0.0.4/32 - ip address 6.0.3.1/24 ! interface ce4-eth0 description to r4 diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py index b2bf5f5f63..5161d8471f 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py @@ -175,20 +175,6 @@ def ltemplatePreRouterStartHook(): "setup {0} vrf {0}-cust1, {0}-eth4. enabled mpls input.".format(rtr) ) # configure cust2 VRFs & MPLS - rtrs = ["r1"] - cmds = [ - "ip link add {0}-cust3 type vrf table 20", - "ip link set dev {0}-cust3 up", - "ip link add {0}-cust4 type vrf table 30", - "ip link set dev {0}-cust4 up", - "ip link add {0}-cust5 type vrf table 40", - "ip link set dev {0}-cust5 up", - ] - for rtr in rtrs: - for cmd in cmds: - cc.doCmd(tgen, rtr, cmd.format(rtr)) - logger.info("setup {0} vrf {0}-cust3 and{0}-cust4.".format(rtr)) - # configure cust2 VRFs & MPLS rtrs = ["r4"] cmds = [ "ip link add {0}-cust2 type vrf table 20", diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf index 24e9f95372..8d42cfc0d8 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf @@ -11,7 +11,6 @@ log file bgpd.log debugging #debug bgp vpn leak-from-vrf #debug bgp vpn label #debug bgp updates out -#debug bgp nht router bgp 5226 bgp router-id 1.1.1.1 @@ -40,11 +39,6 @@ router bgp 5227 vrf r1-cust1 neighbor 192.168.1.2 timers 3 10 address-family ipv4 unicast - network 10.2.3.4/32 - network 192.0.0.0/24 - - redistribute connected - neighbor 192.168.1.2 activate neighbor 192.168.1.2 next-hop-self @@ -57,47 +51,5 @@ router bgp 5227 vrf r1-cust1 exit-address-family -router bgp 5228 vrf r1-cust3 - bgp router-id 192.168.1.1 - - address-family ipv4 unicast - rd vpn export 10:13 - rt vpn import 52:100 - - import vpn - export vpn - exit-address-family - - -router bgp 5227 vrf r1-cust4 - no bgp network import-check - - bgp router-id 192.168.1.1 - - address-family ipv4 unicast - network 28.0.0.0/24 - - rd vpn export 10:14 - rt vpn export 52:100 - - import vpn - export vpn - exit-address-family - - -router bgp 5227 vrf r1-cust5 - bgp router-id 192.168.1.1 - - address-family ipv4 unicast - redistribute connected - - label vpn export 105 - rd vpn export 10:15 - rt vpn both 52:100 - - import vpn - export vpn - exit-address-family - ! end diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/staticd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/staticd.conf deleted file mode 100644 index 59430fdf99..0000000000 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/staticd.conf +++ /dev/null @@ -1,6 +0,0 @@ -hostname r1 -log file staticd.log -! -vrf r1-cust1 - ip route 192.0.0.0/24 192.168.1.2 -exit-vrf diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf index e81bc6b2ab..221bc7a839 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf @@ -4,9 +4,6 @@ hostname r1 password zebra #debug zebra packet -#debug zebra rib detailed -#debug zebra dplane detailed -#debug zebra nexthop detail interface lo ip address 1.1.1.1/32 @@ -21,14 +18,6 @@ interface r1-eth4 ip address 192.168.1.1/24 no link-detect -interface r1-cust1 - ip address 10.4.5.6/24 - no link-detect - -interface r1-cust5 - ip address 29.0.0.1/32 - no link-detect - ip forwarding diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py index 89369241a8..91a7adf997 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py @@ -81,24 +81,3 @@ if ret != False and found != None: "wait", "CE3->CE4 (loopback) ping", ) - luCommand( - "r1", - "ip vrf exec r1-cust1 ping 6.0.3.1 -I 10.4.5.6 -c 1", - " 0. packet loss", - "wait", - "R1(r1-cust1)->CE3/4 (loopback) ping", - ) - luCommand( - "r1", - "ip vrf exec r1-cust1 ping 6.0.3.1 -I 10.4.5.6 -c 1", - " 0. packet loss", - "pass", - "R1(r1-cust1)->CE3/4 (loopback) ping", - ) - luCommand( - "r1", - "ip vrf exec r1-cust5 ping 6.0.3.1 -I 29.0.0.1 -c 1", - " 0. packet loss", - "pass", - "R1(r1-cust5)->CE3/4 ( (loopback) ping", - ) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py index e9647898ab..75158b127e 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py @@ -72,53 +72,3 @@ luCommand( "wait", "CE4->PE4 ping", ) -ret = luCommand( - "r1", - "ip vrf exec r1-cust5 ping 29.0.0.1 -I 29.0.0.1 -c 1", - " 0. packet loss", - "pass", - "Ping its own IP. Check https://bugzilla.kernel.org/show_bug.cgi?id=203483 if it fails", -) -luCommand( - "r1", - "ip vrf exec r1-cust5 ping 192.168.1.1 -I 29.0.0.1 -c 1", - " 0. packet loss", - "pass", - "R1(r1-cust5)->R1(r1-cust1 - r1-eth4) ping", -) -luCommand( - "r1", - "ip vrf exec r1-cust5 ping 192.168.1.2 -I 29.0.0.1 -c 1", - " 0. packet loss", - "wait", - "R1(r1-cust5)->CE1 ping", -) -luCommand( - "r1", - "ip vrf exec r1-cust5 ping 192.168.1.2 -I 29.0.0.1 -c 1", - " 0. packet loss", - "pass", - "R1(r1-cust5)->CE1 ping", -) -luCommand( - "r1", - "ip vrf exec r1-cust5 ping 99.0.0.1 -I 29.0.0.1 -c 1", - " 0. packet loss", - "pass", - "R1(r1-cust5)->CE1 (loopback) ping", -) -luCommand( - "r1", - "ip vrf exec r1-cust5 ping 5.1.0.1 -I 29.0.0.1 -c 1", - " 0. packet loss", - "wait", - "R1(r1-cust5)->CE1 (loopback) ping", - time=30, -) -luCommand( - "r1", - "ip vrf exec r1-cust5 ping 5.1.0.1 -I 29.0.0.1 -c 1", - " 0. packet loss", - "pass", - "R1(r1-cust5)->CE1 (loopback) ping", -) diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py index 3242e3bd3a..1e2758c1c9 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py @@ -54,44 +54,15 @@ bgpribRequireUnicastRoutes("ce4", "ipv4", "ce4-cust2", "Cust 4 routes in ce1", w # # r1 vtysh -c "show bgp vrf r1-cust1 ipv4" # -want_r1_cust1_3_5_routes = [ +want_r1_cust1_routes = [ {"p": "5.1.0.0/24", "n": "99.0.0.1"}, {"p": "5.1.1.0/24", "n": "99.0.0.1"}, {"p": "6.0.1.0/24", "n": "99.0.0.1"}, {"p": "6.0.2.0/24", "n": "99.0.0.1"}, - {"p": "10.2.3.4/32", "n": "0.0.0.0", "bp": False}, - {"p": "10.4.5.0/24", "n": "0.0.0.0", "bp": True}, - {"p": "28.0.0.0/24", "n": "0.0.0.0", "bp": True}, - {"p": "29.0.0.1/32", "n": "0.0.0.0", "bp": True}, {"p": "99.0.0.1/32", "n": "192.168.1.2"}, - {"p": "192.0.0.0/24", "n": "0.0.0.0", "bp": True}, - {"p": "192.168.1.0/24", "n": "0.0.0.0", "bp": True}, ] bgpribRequireUnicastRoutes( - "r1", "ipv4", "r1-cust1", "Customer 1 routes in r1 vrf", want_r1_cust1_3_5_routes -) -bgpribRequireUnicastRoutes( - "r1", "ipv4", "r1-cust3", "Customer 3 routes in r1 vrf", want_r1_cust1_3_5_routes -) -bgpribRequireUnicastRoutes( - "r1", "ipv4", "r1-cust5", "Customer 5 routes in r1 vrf", want_r1_cust1_3_5_routes -) - -want_r1_cust4_routes = [ - {"p": "5.1.0.0/24", "n": "99.0.0.1", "exist": False}, - {"p": "5.1.1.0/24", "n": "99.0.0.1", "exist": False}, - {"p": "6.0.1.0/24", "n": "99.0.0.1", "exist": False}, - {"p": "6.0.2.0/24", "n": "99.0.0.1", "exist": False}, - {"p": "10.2.3.4/32", "n": "0.0.0.0", "exist": False}, - {"p": "10.4.5.0/24", "n": "0.0.0.0", "exist": False}, - {"p": "28.0.0.0/24", "n": "0.0.0.0", "bp": True}, - {"p": "29.0.0.1/32", "n": "0.0.0.0", "exist": False}, - {"p": "99.0.0.1/32", "n": "192.168.1.2", "exist": False}, - {"p": "192.0.0.0/24", "n": "0.0.0.0", "exist": False}, - {"p": "192.168.1.0/24", "n": "0.0.0.0", "exist": False}, -] -bgpribRequireUnicastRoutes( - "r1", "ipv4", "r1-cust4", "Customer 4 routes in r1 vrf", want_r1_cust4_routes + "r1", "ipv4", "r1-cust1", "Customer 1 routes in r1 vrf", want_r1_cust1_routes ) want_r3_cust1_routes = [ @@ -99,20 +70,10 @@ want_r3_cust1_routes = [ {"p": "5.1.1.0/24", "n": "99.0.0.2"}, {"p": "6.0.1.0/24", "n": "99.0.0.2"}, {"p": "6.0.2.0/24", "n": "99.0.0.2"}, - {"p": "10.2.3.4/32", "n": "0.0.0.0", "exist": False}, - {"p": "28.0.0.0/24", "n": "1.1.1.1", "bp": True}, - {"p": "29.0.0.1/32", "n": "1.1.1.1", "bp": True}, {"p": "99.0.0.2/32", "n": "192.168.1.2"}, - {"p": "192.0.0.0/24", "n": "1.1.1.1", "bp": True}, - {"p": "192.168.1.0/24", "n": "1.1.1.1", "bp": True}, ] bgpribRequireUnicastRoutes( - "r3", - "ipv4", - "r3-cust1", - "Customer 1 routes in r3 vrf", - want_r3_cust1_routes, - retry=30, + "r3", "ipv4", "r3-cust1", "Customer 1 routes in r3 vrf", want_r3_cust1_routes ) want_r4_cust1_routes = [ @@ -120,20 +81,10 @@ want_r4_cust1_routes = [ {"p": "5.1.3.0/24", "n": "99.0.0.3"}, {"p": "6.0.1.0/24", "n": "99.0.0.3"}, {"p": "6.0.2.0/24", "n": "99.0.0.3"}, - {"p": "10.2.3.4/32", "n": "0.0.0.0", "exist": False}, - {"p": "28.0.0.0/24", "n": "1.1.1.1", "bp": True}, - {"p": "29.0.0.1/32", "n": "1.1.1.1", "bp": True}, {"p": "99.0.0.3/32", "n": "192.168.1.2"}, - {"p": "192.0.0.0/24", "n": "1.1.1.1", "bp": True}, - {"p": "192.168.1.0/24", "n": "1.1.1.1", "bp": True}, ] bgpribRequireUnicastRoutes( - "r4", - "ipv4", - "r4-cust1", - "Customer 1 routes in r4 vrf", - want_r4_cust1_routes, - retry=30, + "r4", "ipv4", "r4-cust1", "Customer 1 routes in r4 vrf", want_r4_cust1_routes ) want_r4_cust2_routes = [ @@ -141,20 +92,10 @@ want_r4_cust2_routes = [ {"p": "5.4.3.0/24", "n": "99.0.0.4"}, {"p": "6.0.1.0/24", "n": "99.0.0.4"}, {"p": "6.0.2.0/24", "n": "99.0.0.4"}, - {"p": "10.2.3.4/32", "n": "0.0.0.0", "exist": False}, - {"p": "28.0.0.0/24", "n": "1.1.1.1", "bp": True}, - {"p": "29.0.0.1/32", "n": "1.1.1.1", "bp": True}, {"p": "99.0.0.4/32", "n": "192.168.2.2"}, - {"p": "192.0.0.0/24", "n": "1.1.1.1", "bp": True}, - {"p": "192.168.1.0/24", "n": "1.1.1.1", "bp": True}, ] bgpribRequireUnicastRoutes( - "r4", - "ipv4", - "r4-cust2", - "Customer 2 routes in r4 vrf", - want_r4_cust2_routes, - retry=30, + "r4", "ipv4", "r4-cust2", "Customer 2 routes in r4 vrf", want_r4_cust2_routes ) ######################################################################## @@ -726,7 +667,7 @@ bgpribRequireUnicastRoutes( luCommand( "ce1", 'vtysh -c "show bgp ipv4 uni"', - "18 routes and 19", + "12 routes and 12", "wait", "Local and remote routes", 10, @@ -748,7 +689,7 @@ bgpribRequireUnicastRoutes( luCommand( "ce2", 'vtysh -c "show bgp ipv4 uni"', - "18 routes and 22", + "12 routes and 15", "wait", "Local and remote routes", 10, @@ -780,7 +721,7 @@ luCommand("r4", 'vtysh -c "show ip route vrf r4-cust2"') luCommand( "ce3", 'vtysh -c "show bgp ipv4 uni"', - "18 routes and 19", + "12 routes and 13", "wait", "Local and remote routes", 10, @@ -802,7 +743,7 @@ bgpribRequireUnicastRoutes( luCommand( "ce4", 'vtysh -c "show bgp vrf ce4-cust2 ipv4 uni"', - "18 routes and 21", + "12 routes and 14", "wait", "Local and remote routes", 10, diff --git a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json index 2ce936b291..9f78447255 100644 --- a/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json +++ b/tests/topotests/bgp_srv6l3vpn_route_leak/pe1/results/vrf20_ipv4.json @@ -12,7 +12,7 @@ { "fib": true, "directlyConnected": true, - "interfaceName": "vrf10", + "interfaceName": "eth0", "vrf": "vrf10", "active": true } diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py new file mode 100644 index 0000000000..c51beca72a --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-1.py @@ -0,0 +1,407 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE 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. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify recursive import among Tenant VRFs. +2. Verify that dynamic import works fine between two different Tenant VRFs. + When next-hop IPs are same across all VRFs. + When next-hop IPs are different across all VRFs. +3. Verify that with multiple tenant VRFs, dynamic import works fine between + Tenant VRFs to default VRF. + When next-hop IPs and prefixes are same across all VRFs. + When next-hop IPs and prefixes are different across all VRFs. +""" + +import os +import sys +import time +import pytest +import platform +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_bgp_community_lists, + get_frr_ipv6_linklocal, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} +NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} +NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} +NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} +NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} + +PREFIX_LIST = { + "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], + "ipv6": ["11:11::1", "22:22::2", "22:22::22"], +} +PREFERRED_NEXT_HOP = "global" +VRF_LIST = ["RED", "BLUE", "GREEN"] +COMM_VAL_1 = "100:100" +COMM_VAL_2 = "500:500" +COMM_VAL_3 = "600:600" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_dynamic_import_recursive_import_tenant_vrf_p1(request): + """ + Verify recursive import among Tenant VRFs. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R2 for vrf RED and redistribute in " + "respective BGP instance" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF RED") + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step("Verify that R2 has installed redistributed routes in vrf RED only") + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED"}] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Import vrf RED's routes into vrf GREEN on R2") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}}) + + import_dict = { + "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R2, that it installs imported routes from vrf RED to vrf " + "GREEN's RIB/FIB pointing next-hop to vrf RED" + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}] + } + } + result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r2", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("On R3 import routes from vrf GREEN to vrf default") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}}) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R3, that it installs imported routes from vrf GREEN to " + "vrf default RIB/FIB pointing next-hop to vrf GREEN. " + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]} + } + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("On R4 import routes from vrf default to vrf BLUE") + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": "default"}}}}) + + import_dict = { + "r4": {"bgp": [{"vrf": "BLUE", "local_as": 4, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) + + step( + "Verify on R4, that it installs imported routes from vrf default to " + "vrf BLUE RIB/FIB pointing next-hop to vrf default." + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r4": { + "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"}] + } + } + result = verify_bgp_rib(tgen, addr_type, "r4", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r4", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for dut, vrf_name, vrf_import, as_num in zip( + ["r2", "r4"], ["GREEN", "BLUE"], ["RED", "default"], [2, 4] + ): + + for action, value in zip(["Delete", "Re-add"], [True, False]): + step("{} the import command on {} router".format(action, dut)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": {"import": {"vrf": vrf_import, "delete": value}} + } + } + ) + + import_dict = { + dut: { + "bgp": [ + {"vrf": vrf_name, "local_as": as_num, "address_family": temp} + ] + } + } + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for addr_type in ADDR_TYPES: + static_routes = { + "r4": { + "static_routes": [ + {"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"} + ] + } + } + if value: + result = verify_bgp_rib( + tgen, addr_type, "r4", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes["r4"]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, "r4", static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes["r4"]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, "r4", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r4", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py new file mode 100644 index 0000000000..bfeaaa17df --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-2.py @@ -0,0 +1,932 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE 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. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify recursive import among Tenant VRFs. +2. Verify that dynamic import works fine between two different Tenant VRFs. + When next-hop IPs are same across all VRFs. + When next-hop IPs are different across all VRFs. +3. Verify that with multiple tenant VRFs, dynamic import works fine between + Tenant VRFs to default VRF. + When next-hop IPs and prefixes are same across all VRFs. + When next-hop IPs and prefixes are different across all VRFs. +""" + +import os +import sys +import time +import pytest +import platform +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_bgp_community_lists, + get_frr_ipv6_linklocal, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} +NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} +NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} +NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} +NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} + +PREFIX_LIST = { + "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], + "ipv6": ["11:11::1", "22:22::2", "22:22::22"], +} +PREFERRED_NEXT_HOP = "global" +VRF_LIST = ["RED", "BLUE", "GREEN"] +COMM_VAL_1 = "100:100" +COMM_VAL_2 = "500:500" +COMM_VAL_3 = "600:600" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_dynamic_import_routes_between_two_tenant_vrf_p0(request): + """ + Verify that dynamic import works fine between two different Tenant VRFs. + + When next-hop IPs are same across all VRFs. + When next-hop IPs are different across all VRFs. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R3 for each vrf and redistribute in " + "respective BGP instance" + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step("Configure static route for VRF : {}".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step( + "Verify that R3 has installed redistributed routes in respective " + "vrfs: {}".format(vrf_name) + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Import from vrf GREEN+BLUE into vrf RED on R3") + + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on R1, that it installs all the routes(local+imported) in " + "vrf RED's RIB/FIB and doesn't get confuse with next-hop attribute, " + "as all vrfs on R1 are using same IP address for next-hop" + ) + + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + + next_hop_1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[0] + result = verify_bgp_rib( + tgen, addr_type, "r1", static_routes, next_hop=next_hop_1 + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r1", static_routes, next_hop=next_hop_1) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import vrf GREEN/BLUE/Both command from vrf RED's instance on" " R3") + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + import_dict = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB") + for dut in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in Route table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R3") + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Shut", "No shut"], [True, False]): + step( + "{} the neighborship between R1-R3 and R1-R2 for vrf GREEN, BLUE " + "and default".format(action) + ) + bgp_disable = {"r3": {"bgp": []}} + for vrf_name in ["GREEN", "BLUE", "default"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r3-link1": {"shutdown": value}} + }, + "r2": { + "dest_link": {"r3-link1": {"shutdown": value}} + }, + } + } + } + } + ) + + bgp_disable["r3"]["bgp"].append( + {"vrf": vrf_name, "local_as": 3, "address_family": temp} + ) + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers") + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] + ): + step("{} the neighborship between R1-R3 and R1-R2 for vrf RED".format(action)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3-link1": {"shutdown": value}}}, + "r2": {"dest_link": {"r3-link1": {"shutdown": value}}}, + } + } + } + } + ) + + bgp_disable = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1 and R2 {} all the routes from RED vrf's RIB and" + " FIB".format(status) + ) + for dut in ["r1", "r2"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import command from router R3 and configure the same on R2") + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + import_dict = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that once import commands are removed from R3, all imported " + "routes are withdrawn from RIB/FIB of vrf RED on R1/R2/R3" + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + + step( + "Configure static routes on R2 for each vrf and redistribute in " + "respective BGP instance" + ) + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step("Configure static route for VRF : {}".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + } + } + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify after import commands are re-configured on R2's vrf RED, all " + "those routes are installed again in vrf RED of R1,R2,R3" + ) + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step( + "Remove/add import vrf GREEN/BLUE/both command from vrf RED's " "instance on R2" + ) + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB") + for dut in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + + step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R2") + for vrf_name in ["BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + redist_dict = { + "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Shut", "No shut"], [True, False]): + step( + "{} the neighborship between R2-R3 for vrf GREEN, BLUE and default".format( + action + ) + ) + bgp_disable = {"r2": {"bgp": []}} + for vrf_name in ["GREEN", "BLUE", "default"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r2-link1": {"shutdown": value}} + } + } + } + } + } + ) + + bgp_disable["r2"]["bgp"].append( + {"vrf": vrf_name, "local_as": 2, "address_family": temp} + ) + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers") + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] + ): + step("{} the neighborship between R2-R3 for vrf RED".format(action)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r2": {"dest_link": {"r3-link1": {"shutdown": value}}} + } + } + } + } + ) + + bgp_disable = { + "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} + } + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1 and R2 {} all the routes from RED vrf's RIB and" + " FIB".format(status) + ) + for dut in ["r1", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + "vrf": "RED", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py new file mode 100644 index 0000000000..1d80a2a64a --- /dev/null +++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4-3.py @@ -0,0 +1,932 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2021 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, +# Inc. ("NetDEF") in this file. +# +# 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 VMWARE 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. +# + +""" +Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: +1. Verify recursive import among Tenant VRFs. +2. Verify that dynamic import works fine between two different Tenant VRFs. + When next-hop IPs are same across all VRFs. + When next-hop IPs are different across all VRFs. +3. Verify that with multiple tenant VRFs, dynamic import works fine between + Tenant VRFs to default VRF. + When next-hop IPs and prefixes are same across all VRFs. + When next-hop IPs and prefixes are different across all VRFs. +""" + +import os +import sys +import time +import pytest +import platform +from time import sleep + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) +sys.path.append(os.path.join(CWD, "../lib/")) + +# Required to instantiate the topology builder class. + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from lib.topotest import version_cmp + +from lib.common_config import ( + start_topology, + write_test_header, + check_address_types, + write_test_footer, + reset_config_on_routers, + verify_rib, + step, + create_route_maps, + create_static_routes, + create_prefix_lists, + create_bgp_community_lists, + get_frr_ipv6_linklocal, +) + +from lib.topolog import logger +from lib.bgp import ( + verify_bgp_convergence, + create_router_bgp, + verify_bgp_community, + verify_bgp_rib, +) +from lib.topojson import build_config_from_json + +pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] + +# Global variables +NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} +NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} +NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} +NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} +NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} +NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} + +NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} +NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} +NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} +NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} +NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} +NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} + +NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} +NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} + +PREFIX_LIST = { + "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], + "ipv6": ["11:11::1", "22:22::2", "22:22::22"], +} +PREFERRED_NEXT_HOP = "global" +VRF_LIST = ["RED", "BLUE", "GREEN"] +COMM_VAL_1 = "100:100" +COMM_VAL_2 = "500:500" +COMM_VAL_3 = "600:600" + + +def setup_module(mod): + """ + Sets up the pytest environment + + * `mod`: module name + """ + + testsuite_run_time = time.asctime(time.localtime(time.time())) + logger.info("Testsuite start time: {}".format(testsuite_run_time)) + logger.info("=" * 40) + + logger.info("Running setup_module to create topology") + + # This function initiates the topology build with Topogen... + json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD) + tgen = Topogen(json_file, mod.__name__) + global topo + topo = tgen.json_topo + # ... and here it calls Mininet initialization functions. + + # Starting topology, create tmp files which are loaded to routers + # to start daemons and then start routers + start_topology(tgen) + + # Run these tests for kernel version 4.19 or above + if version_cmp(platform.release(), "4.19") < 0: + error_msg = ( + "BGP vrf dynamic route leak tests will not run " + '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) + ) + pytest.skip(error_msg) + + # Creating configuration from JSON + build_config_from_json(tgen, topo) + + global BGP_CONVERGENCE + global ADDR_TYPES + ADDR_TYPES = check_address_types() + + BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) + assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( + BGP_CONVERGENCE + ) + + logger.info("Running setup_module() done") + + +def teardown_module(): + """Teardown the pytest environment""" + + logger.info("Running teardown_module to delete topology") + + tgen = get_topogen() + + # Stop toplogy and Remove tmp files + tgen.stop_topology() + + logger.info( + "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) + ) + logger.info("=" * 40) + + +##################################################### +# +# Testcases +# +##################################################### + + +def test_dynamic_import_routes_between_tenant_to_default_vrf_p0(request): + """ + Verify that with multiple tenant VRFs, dynamic import works fine between + Tenant VRFs to default VRF. + + When next-hop IPs and prefixes are same across all VRFs. + When next-hop IPs and prefixes are different across all VRFs. + """ + + tgen = get_topogen() + tc_name = request.node.name + write_test_header(tc_name) + reset_config_on_routers(tgen) + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + step( + "Configure static routes on R3 for each vrf and redistribute in " + "respective BGP instance" + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step("Configure static route for VRF : {}".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step( + "Verify that R3 has installed redistributed routes in respective " + "vrfs: {}".format(vrf_name) + ) + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Import all tenant vrfs(GREEN+BLUE+RED) in default vrf on R3") + + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + redist_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify on R3 that it installs all the routes(imported from tenant " + "VRFs) in default vrf. Additionally, verify that R1 & R2 also " + "receive these routes from R3 and install in default vrf, next-hop " + "pointing to R3" + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + for addr_type in ADDR_TYPES: + static_routes = { + "r3": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + } + ] + } + } + + for dut in ["r2", "r1"]: + next_hop_val = topo["routers"]["r3"]["links"]["{}-link4".format(dut)][ + addr_type + ].split("/")[0] + + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, next_hop=next_hop_val + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, next_hop=next_hop_val + ) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, "r3", static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Remove", "Add"], [True, False], ["withdraw", "re-install"] + ): + step( + "{} import vrf GREEN/BLUE/RED/all command from default vrf " + "instance on R3".format(action) + ) + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": {"import": {"vrf": vrf_name, "delete": value}} + } + } + ) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1,R2 & R3 {} imported routes from GREEN/BLUE/RED/all" + " in default vrf's RIB".format(status) + ) + for dut in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Shut", "No shut"], [True, False]): + step( + "{} the neighborship between R1-R3 and R1-R2 for vrf RED, GREEN " + "and BLUE".format(action) + ) + bgp_disable = {"r3": {"bgp": []}} + for vrf_name in ["RED", "GREEN", "BLUE"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r1": { + "dest_link": {"r3-link4": {"shutdown": value}} + }, + "r2": { + "dest_link": {"r3-link4": {"shutdown": value}} + }, + } + } + } + } + ) + + bgp_disable["r3"]["bgp"].append( + {"vrf": vrf_name, "local_as": 3, "address_family": temp} + ) + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that when peering is shutdown for tenant vrfs, it " + "doesn't impact the RIB/FIB of default vrf on router R1 and R2" + ) + for dut in ["r1", "r2"]: + step("Verify RIB/FIB for default vrf on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] + ): + step( + "{} the neighborship between R1-R3 and R2-R3 for default vrf".format(action) + ) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r1": {"dest_link": {"r3-link4": {"shutdown": value}}}, + "r2": {"dest_link": {"r3-link4": {"shutdown": value}}}, + } + } + } + } + ) + + bgp_disable = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1 and R2 {} all the routes from default vrf's RIB" + " and FIB".format(status) + ) + for dut in ["r1", "r2"]: + step("Verify RIB/FIB for default vrf on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import command from router R3 and configure the same on R2") + temp = {} + for vrf_name in VRF_LIST: + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that once import commands are removed from R3, all imported " + "routes are withdrawn from RIB/FIB of default vrf on R1/R2/R3" + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for default vrf on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): + step("Configure static route for VRF : {} on r2".format(vrf_name)) + for addr_type in ADDR_TYPES: + static_routes = { + "r2": { + "static_routes": [ + { + "network": [network[addr_type]], + "next_hop": "blackhole", + "vrf": vrf_name, + } + ] + } + } + + result = create_static_routes(tgen, static_routes) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Redistribute static route on BGP VRF : {}".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} + ) + + redist_dict = { + "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "redistribute": [{"redist_type": "static", "delete": True}] + } + } + } + ) + + redist_dict = { + "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} + } + + result = create_router_bgp(tgen, topo, redist_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify after import commands are re-configured on R2's vrf RED, all " + "those routes are installed again in default vrf of R1,R2,R3" + ) + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Remove import vrf RED/GREEN/BLUE/all one by one from default vrf" " on R2") + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} + ) + + import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1, R2 and R3 withdraws imported routes from default " + "vrf's RIB and FIB " + ) + for dut in ["r1", "r2", "r3"]: + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, ( + "Testcase {} : Failed \nError {}\n" + "Routes {} still in BGP table".format( + tc_name, result, static_routes[dut]["static_routes"][0]["network"] + ) + ) + + result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) + assert result is not True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + step("Add import vrf RED/GREEN/BLUE/all one by one from default vrf on R2") + for vrf_name in ["RED", "BLUE", "GREEN"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) + + import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} + + result = create_router_bgp(tgen, topo, import_dict) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + for dut in ["r1", "r2", "r3"]: + step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value in zip(["Shut", "No shut"], [True, False]): + step( + "{} the neighborship between R2-R3 for vrf GREEN, BLUE and RED".format( + action + ) + ) + bgp_disable = {"r2": {"bgp": []}} + for vrf_name in ["GREEN", "BLUE", "RED"]: + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r3": { + "dest_link": {"r2-link4": {"shutdown": value}} + } + } + } + } + } + ) + + bgp_disable["r2"]["bgp"].append( + {"vrf": vrf_name, "local_as": 2, "address_family": temp} + ) + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers") + for dut in ["r1", "r2", "r3"]: + step("Verify RIB/FIB for vrf RED on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + for action, value, status in zip( + ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] + ): + step("{} the neighborship between R2-R3 for default vrf".format(action)) + temp = {} + for addr_type in ADDR_TYPES: + temp.update( + { + addr_type: { + "unicast": { + "neighbor": { + "r3": {"dest_link": {"r2-link4": {"shutdown": value}}} + } + } + } + } + ) + + bgp_disable = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} + result = create_router_bgp(tgen, topo, bgp_disable) + assert result is True, "Testcase {} :Failed \n Error: {}".format( + tc_name, result + ) + + step( + "Verify that R1 and R2 {} all the routes from default vrfs RIB and" + " FIB".format(status) + ) + for dut in ["r1", "r3"]: + step("Verify RIB/FIB for default vrf on {}".format(dut)) + for addr_type in ADDR_TYPES: + static_routes = { + dut: { + "static_routes": [ + { + "network": [ + NETWORK1_1[addr_type], + NETWORK2_1[addr_type], + NETWORK3_1[addr_type], + ], + "next_hop": "blackhole", + } + ] + } + } + + if value: + result = verify_bgp_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + + result = verify_rib( + tgen, addr_type, dut, static_routes, expected=False + ) + assert ( + result is not True + ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( + tc_name, + result, + static_routes[dut]["static_routes"][0]["network"], + ) + else: + result = verify_bgp_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + result = verify_rib(tgen, addr_type, dut, static_routes) + assert result is True, "Testcase {} : Failed \n Error {}".format( + tc_name, result + ) + + write_test_footer(tc_name) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py deleted file mode 100644 index 97016caa75..0000000000 --- a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py +++ /dev/null @@ -1,1909 +0,0 @@ -#!/usr/bin/env python - -# -# Copyright (c) 2021 by VMware, Inc. ("VMware") -# Used Copyright (c) 2018 by Network Device Education Foundation, -# Inc. ("NetDEF") in this file. -# -# 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 VMWARE 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. -# - -""" -Following tests are covered to test BGP Multi-VRF Dynamic Route Leaking: -1. Verify recursive import among Tenant VRFs. -2. Verify that dynamic import works fine between two different Tenant VRFs. - When next-hop IPs are same across all VRFs. - When next-hop IPs are different across all VRFs. -3. Verify that with multiple tenant VRFs, dynamic import works fine between - Tenant VRFs to default VRF. - When next-hop IPs and prefixes are same across all VRFs. - When next-hop IPs and prefixes are different across all VRFs. -""" - -import os -import sys -import time -import pytest -import platform -from time import sleep - -# Save the Current Working Directory to find configuration files. -CWD = os.path.dirname(os.path.realpath(__file__)) -sys.path.append(os.path.join(CWD, "../")) -sys.path.append(os.path.join(CWD, "../lib/")) - -# Required to instantiate the topology builder class. - -# pylint: disable=C0413 -# Import topogen and topotest helpers -from lib.topogen import Topogen, get_topogen -from lib.topotest import version_cmp - -from lib.common_config import ( - start_topology, - write_test_header, - check_address_types, - write_test_footer, - reset_config_on_routers, - verify_rib, - step, - create_route_maps, - create_static_routes, - create_prefix_lists, - create_bgp_community_lists, - get_frr_ipv6_linklocal, -) - -from lib.topolog import logger -from lib.bgp import ( - verify_bgp_convergence, - create_router_bgp, - verify_bgp_community, - verify_bgp_rib, -) -from lib.topojson import build_config_from_json - -pytestmark = [pytest.mark.bgpd, pytest.mark.staticd] - -# Global variables -NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"} -NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"} -NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"} -NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"} -NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"} -NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"} - -NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"} -NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"} -NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"} -NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"} -NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"} -NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"} - -NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"} -NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"} - -PREFIX_LIST = { - "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"], - "ipv6": ["11:11::1", "22:22::2", "22:22::22"], -} -PREFERRED_NEXT_HOP = "global" -VRF_LIST = ["RED", "BLUE", "GREEN"] -COMM_VAL_1 = "100:100" -COMM_VAL_2 = "500:500" -COMM_VAL_3 = "600:600" - - -def setup_module(mod): - """ - Sets up the pytest environment - - * `mod`: module name - """ - - testsuite_run_time = time.asctime(time.localtime(time.time())) - logger.info("Testsuite start time: {}".format(testsuite_run_time)) - logger.info("=" * 40) - - logger.info("Running setup_module to create topology") - - # This function initiates the topology build with Topogen... - json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD) - tgen = Topogen(json_file, mod.__name__) - global topo - topo = tgen.json_topo - # ... and here it calls Mininet initialization functions. - - # Starting topology, create tmp files which are loaded to routers - # to start daemons and then start routers - start_topology(tgen) - - # Run these tests for kernel version 4.19 or above - if version_cmp(platform.release(), "4.19") < 0: - error_msg = ( - "BGP vrf dynamic route leak tests will not run " - '(have kernel "{}", but it requires >= 4.19)'.format(platform.release()) - ) - pytest.skip(error_msg) - - # Creating configuration from JSON - build_config_from_json(tgen, topo) - - global BGP_CONVERGENCE - global ADDR_TYPES - ADDR_TYPES = check_address_types() - - BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) - assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error: {}".format( - BGP_CONVERGENCE - ) - - logger.info("Running setup_module() done") - - -def teardown_module(): - """Teardown the pytest environment""" - - logger.info("Running teardown_module to delete topology") - - tgen = get_topogen() - - # Stop toplogy and Remove tmp files - tgen.stop_topology() - - logger.info( - "Testsuite end time: {}".format(time.asctime(time.localtime(time.time()))) - ) - logger.info("=" * 40) - - -##################################################### -# -# Testcases -# -##################################################### - - -def test_dynamic_import_recursive_import_tenant_vrf_p1(request): - """ - Verify recursive import among Tenant VRFs. - """ - - tgen = get_topogen() - tc_name = request.node.name - write_test_header(tc_name) - reset_config_on_routers(tgen) - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - step( - "Configure static routes on R2 for vrf RED and redistribute in " - "respective BGP instance" - ) - for addr_type in ADDR_TYPES: - static_routes = { - "r2": { - "static_routes": [ - { - "network": [NETWORK2_1[addr_type]], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - - result = create_static_routes(tgen, static_routes) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Redistribute static route on BGP VRF RED") - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} - ) - - redist_dict = { - "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - - step("Verify that R2 has installed redistributed routes in vrf RED only") - for addr_type in ADDR_TYPES: - static_routes = { - "r2": { - "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED"}] - } - } - result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, "r2", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("Import vrf RED's routes into vrf GREEN on R2") - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}}) - - import_dict = { - "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]} - } - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - - step( - "Verify on R2, that it installs imported routes from vrf RED to vrf " - "GREEN's RIB/FIB pointing next-hop to vrf RED" - ) - for addr_type in ADDR_TYPES: - static_routes = { - "r2": { - "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}] - } - } - result = verify_bgp_rib(tgen, addr_type, "r2", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, "r2", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("On R3 import routes from vrf GREEN to vrf default") - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}}) - - import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - - step( - "Verify on R3, that it installs imported routes from vrf GREEN to " - "vrf default RIB/FIB pointing next-hop to vrf GREEN. " - ) - for addr_type in ADDR_TYPES: - static_routes = { - "r2": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]} - } - result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, "r3", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("On R4 import routes from vrf default to vrf BLUE") - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": "default"}}}}) - - import_dict = { - "r4": {"bgp": [{"vrf": "BLUE", "local_as": 4, "address_family": temp}]} - } - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result) - - step( - "Verify on R4, that it installs imported routes from vrf default to " - "vrf BLUE RIB/FIB pointing next-hop to vrf default." - ) - for addr_type in ADDR_TYPES: - static_routes = { - "r4": { - "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"}] - } - } - result = verify_bgp_rib(tgen, addr_type, "r4", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, "r4", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for dut, vrf_name, vrf_import, as_num in zip( - ["r2", "r4"], ["GREEN", "BLUE"], ["RED", "default"], [2, 4] - ): - - for action, value in zip(["Delete", "Re-add"], [True, False]): - step("{} the import command on {} router".format(action, dut)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": {"import": {"vrf": vrf_import, "delete": value}} - } - } - ) - - import_dict = { - dut: { - "bgp": [ - {"vrf": vrf_name, "local_as": as_num, "address_family": temp} - ] - } - } - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - for addr_type in ADDR_TYPES: - static_routes = { - "r4": { - "static_routes": [ - {"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"} - ] - } - } - if value: - result = verify_bgp_rib( - tgen, addr_type, "r4", static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( - tc_name, - result, - static_routes["r4"]["static_routes"][0]["network"], - ) - - result = verify_rib( - tgen, addr_type, "r4", static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( - tc_name, - result, - static_routes["r4"]["static_routes"][0]["network"], - ) - else: - result = verify_bgp_rib(tgen, addr_type, "r4", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, "r4", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - write_test_footer(tc_name) - - -def test_dynamic_import_routes_between_two_tenant_vrf_p0(request): - """ - Verify that dynamic import works fine between two different Tenant VRFs. - - When next-hop IPs are same across all VRFs. - When next-hop IPs are different across all VRFs. - """ - - tgen = get_topogen() - tc_name = request.node.name - write_test_header(tc_name) - reset_config_on_routers(tgen) - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - step( - "Configure static routes on R3 for each vrf and redistribute in " - "respective BGP instance" - ) - - for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): - step("Configure static route for VRF : {}".format(vrf_name)) - for addr_type in ADDR_TYPES: - static_routes = { - "r3": { - "static_routes": [ - { - "network": [network[addr_type]], - "next_hop": "blackhole", - "vrf": vrf_name, - } - ] - } - } - - result = create_static_routes(tgen, static_routes) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Redistribute static route on BGP VRF : {}".format(vrf_name)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} - ) - - redist_dict = { - "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): - step( - "Verify that R3 has installed redistributed routes in respective " - "vrfs: {}".format(vrf_name) - ) - for addr_type in ADDR_TYPES: - static_routes = { - "r3": { - "static_routes": [ - { - "network": [network[addr_type]], - "next_hop": "blackhole", - "vrf": vrf_name, - } - ] - } - } - - result = verify_rib(tgen, addr_type, "r3", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("Import from vrf GREEN+BLUE into vrf RED on R3") - - for vrf_name in ["BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) - - import_dict = { - "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify on R1, that it installs all the routes(local+imported) in " - "vrf RED's RIB/FIB and doesn't get confuse with next-hop attribute, " - "as all vrfs on R1 are using same IP address for next-hop" - ) - - for addr_type in ADDR_TYPES: - static_routes = { - "r3": { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - - next_hop_1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[0] - result = verify_bgp_rib( - tgen, addr_type, "r1", static_routes, next_hop=next_hop_1 - ) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, "r1", static_routes, next_hop=next_hop_1) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("Remove import vrf GREEN/BLUE/Both command from vrf RED's instance on" " R3") - for vrf_name in ["BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} - ) - - import_dict = { - "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB") - for dut in ["r1", "r2", "r3"]: - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) - assert result is not True, ( - "Testcase {} : Failed \nError {}\n" - "Routes {} still in BGP table".format( - tc_name, result, static_routes[dut]["static_routes"][0]["network"] - ) - ) - - result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) - assert result is not True, ( - "Testcase {} : Failed \nError {}\n" - "Routes {} still in Route table".format( - tc_name, result, static_routes[dut]["static_routes"][0]["network"] - ) - ) - - step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R3") - for vrf_name in ["BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) - - import_dict = { - "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - for dut in ["r1", "r2", "r3"]: - step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for action, value in zip(["Shut", "No shut"], [True, False]): - step( - "{} the neighborship between R1-R3 and R1-R2 for vrf GREEN, BLUE " - "and default".format(action) - ) - bgp_disable = {"r3": {"bgp": []}} - for vrf_name in ["GREEN", "BLUE", "default"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "neighbor": { - "r1": { - "dest_link": {"r3-link1": {"shutdown": value}} - }, - "r2": { - "dest_link": {"r3-link1": {"shutdown": value}} - }, - } - } - } - } - ) - - bgp_disable["r3"]["bgp"].append( - {"vrf": vrf_name, "local_as": 3, "address_family": temp} - ) - result = create_router_bgp(tgen, topo, bgp_disable) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers") - for dut in ["r1", "r2", "r3"]: - step("Verify RIB/FIB for vrf RED on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for action, value, status in zip( - ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] - ): - step("{} the neighborship between R1-R3 and R1-R2 for vrf RED".format(action)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "neighbor": { - "r1": {"dest_link": {"r3-link1": {"shutdown": value}}}, - "r2": {"dest_link": {"r3-link1": {"shutdown": value}}}, - } - } - } - } - ) - - bgp_disable = { - "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} - } - result = create_router_bgp(tgen, topo, bgp_disable) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify that R1 and R2 {} all the routes from RED vrf's RIB and" - " FIB".format(status) - ) - for dut in ["r1", "r2"]: - step("Verify RIB/FIB for vrf RED on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - - if value: - result = verify_bgp_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - - result = verify_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed \nError {}\n" "Routes {} still in Route table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - else: - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("Remove import command from router R3 and configure the same on R2") - for vrf_name in ["BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} - ) - - import_dict = { - "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify that once import commands are removed from R3, all imported " - "routes are withdrawn from RIB/FIB of vrf RED on R1/R2/R3" - ) - - for dut in ["r1", "r2", "r3"]: - step("Verify RIB/FIB for vrf RED on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) - assert result is not True, ( - "Testcase {} : Failed \nError {}\n" - "Routes {} still in BGP table".format( - tc_name, result, static_routes[dut]["static_routes"][0]["network"] - ) - ) - - result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( - tc_name, result, static_routes[dut]["static_routes"][0]["network"] - ) - - step( - "Configure static routes on R2 for each vrf and redistribute in " - "respective BGP instance" - ) - for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): - step("Configure static route for VRF : {}".format(vrf_name)) - for addr_type in ADDR_TYPES: - static_routes = { - "r2": { - "static_routes": [ - { - "network": [network[addr_type]], - "next_hop": "blackhole", - "vrf": vrf_name, - } - ] - } - } - - result = create_static_routes(tgen, static_routes) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Redistribute static route on BGP VRF : {}".format(vrf_name)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} - ) - - redist_dict = { - "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "redistribute": [{"redist_type": "static", "delete": True}] - } - } - } - ) - - redist_dict = { - "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - for vrf_name in ["BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) - - import_dict = { - "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify after import commands are re-configured on R2's vrf RED, all " - "those routes are installed again in vrf RED of R1,R2,R3" - ) - for dut in ["r1", "r2", "r3"]: - step("Verify RIB/FIB for vrf RED on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step( - "Remove/add import vrf GREEN/BLUE/both command from vrf RED's " "instance on R2" - ) - for vrf_name in ["BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} - ) - - redist_dict = { - "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB") - for dut in ["r1", "r2", "r3"]: - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) - assert result is not True, ( - "Testcase {} : Failed \nError {}\n" - "Routes {} still in BGP table".format( - tc_name, result, static_routes[dut]["static_routes"][0]["network"] - ) - ) - - result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) - assert ( - result is not True - ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( - tc_name, result, static_routes[dut]["static_routes"][0]["network"] - ) - - step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R2") - for vrf_name in ["BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) - - redist_dict = { - "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - for dut in ["r1", "r2", "r3"]: - step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for action, value in zip(["Shut", "No shut"], [True, False]): - step( - "{} the neighborship between R2-R3 for vrf GREEN, BLUE and default".format( - action - ) - ) - bgp_disable = {"r2": {"bgp": []}} - for vrf_name in ["GREEN", "BLUE", "default"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "neighbor": { - "r3": { - "dest_link": {"r2-link1": {"shutdown": value}} - } - } - } - } - } - ) - - bgp_disable["r2"]["bgp"].append( - {"vrf": vrf_name, "local_as": 2, "address_family": temp} - ) - result = create_router_bgp(tgen, topo, bgp_disable) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers") - for dut in ["r1", "r2", "r3"]: - step("Verify RIB/FIB for vrf RED on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for action, value, status in zip( - ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] - ): - step("{} the neighborship between R2-R3 for vrf RED".format(action)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "neighbor": { - "r2": {"dest_link": {"r3-link1": {"shutdown": value}}} - } - } - } - } - ) - - bgp_disable = { - "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]} - } - result = create_router_bgp(tgen, topo, bgp_disable) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify that R1 and R2 {} all the routes from RED vrf's RIB and" - " FIB".format(status) - ) - for dut in ["r1", "r3"]: - step("Verify RIB/FIB for vrf RED on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - "vrf": "RED", - } - ] - } - } - - if value: - result = verify_bgp_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - - result = verify_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - else: - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - write_test_footer(tc_name) - - -def test_dynamic_import_routes_between_tenant_to_default_vrf_p0(request): - """ - Verify that with multiple tenant VRFs, dynamic import works fine between - Tenant VRFs to default VRF. - - When next-hop IPs and prefixes are same across all VRFs. - When next-hop IPs and prefixes are different across all VRFs. - """ - - tgen = get_topogen() - tc_name = request.node.name - write_test_header(tc_name) - reset_config_on_routers(tgen) - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - step( - "Configure static routes on R3 for each vrf and redistribute in " - "respective BGP instance" - ) - - for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): - step("Configure static route for VRF : {}".format(vrf_name)) - for addr_type in ADDR_TYPES: - static_routes = { - "r3": { - "static_routes": [ - { - "network": [network[addr_type]], - "next_hop": "blackhole", - "vrf": vrf_name, - } - ] - } - } - - result = create_static_routes(tgen, static_routes) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Redistribute static route on BGP VRF : {}".format(vrf_name)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} - ) - - redist_dict = { - "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): - step( - "Verify that R3 has installed redistributed routes in respective " - "vrfs: {}".format(vrf_name) - ) - for addr_type in ADDR_TYPES: - static_routes = { - "r3": { - "static_routes": [ - { - "network": [network[addr_type]], - "next_hop": "blackhole", - "vrf": vrf_name, - } - ] - } - } - - result = verify_rib(tgen, addr_type, "r3", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("Import all tenant vrfs(GREEN+BLUE+RED) in default vrf on R3") - - for vrf_name in ["RED", "BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) - - redist_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify on R3 that it installs all the routes(imported from tenant " - "VRFs) in default vrf. Additionally, verify that R1 & R2 also " - "receive these routes from R3 and install in default vrf, next-hop " - "pointing to R3" - ) - - for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): - for addr_type in ADDR_TYPES: - static_routes = { - "r3": { - "static_routes": [ - { - "network": [network[addr_type]], - "next_hop": "blackhole", - } - ] - } - } - - for dut in ["r2", "r1"]: - next_hop_val = topo["routers"]["r3"]["links"]["{}-link4".format(dut)][ - addr_type - ].split("/")[0] - - result = verify_bgp_rib( - tgen, addr_type, dut, static_routes, next_hop=next_hop_val - ) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib( - tgen, addr_type, dut, static_routes, next_hop=next_hop_val - ) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_bgp_rib(tgen, addr_type, "r3", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, "r3", static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for action, value, status in zip( - ["Remove", "Add"], [True, False], ["withdraw", "re-install"] - ): - step( - "{} import vrf GREEN/BLUE/RED/all command from default vrf " - "instance on R3".format(action) - ) - for vrf_name in ["RED", "BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": {"import": {"vrf": vrf_name, "delete": value}} - } - } - ) - - import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify that R1,R2 & R3 {} imported routes from GREEN/BLUE/RED/all" - " in default vrf's RIB".format(status) - ) - for dut in ["r1", "r2", "r3"]: - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - } - ] - } - } - - if value: - result = verify_bgp_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - - result = verify_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - else: - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for action, value in zip(["Shut", "No shut"], [True, False]): - step( - "{} the neighborship between R1-R3 and R1-R2 for vrf RED, GREEN " - "and BLUE".format(action) - ) - bgp_disable = {"r3": {"bgp": []}} - for vrf_name in ["RED", "GREEN", "BLUE"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "neighbor": { - "r1": { - "dest_link": {"r3-link4": {"shutdown": value}} - }, - "r2": { - "dest_link": {"r3-link4": {"shutdown": value}} - }, - } - } - } - } - ) - - bgp_disable["r3"]["bgp"].append( - {"vrf": vrf_name, "local_as": 3, "address_family": temp} - ) - result = create_router_bgp(tgen, topo, bgp_disable) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify that when peering is shutdown for tenant vrfs, it " - "doesn't impact the RIB/FIB of default vrf on router R1 and R2" - ) - for dut in ["r1", "r2"]: - step("Verify RIB/FIB for default vrf on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for action, value, status in zip( - ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] - ): - step( - "{} the neighborship between R1-R3 and R2-R3 for default vrf".format(action) - ) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "neighbor": { - "r1": {"dest_link": {"r3-link4": {"shutdown": value}}}, - "r2": {"dest_link": {"r3-link4": {"shutdown": value}}}, - } - } - } - } - ) - - bgp_disable = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} - result = create_router_bgp(tgen, topo, bgp_disable) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify that R1 and R2 {} all the routes from default vrf's RIB" - " and FIB".format(status) - ) - for dut in ["r1", "r2"]: - step("Verify RIB/FIB for default vrf on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - } - ] - } - } - - if value: - result = verify_bgp_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - - result = verify_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - else: - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("Remove import command from router R3 and configure the same on R2") - temp = {} - for vrf_name in VRF_LIST: - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} - ) - - import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}} - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify that once import commands are removed from R3, all imported " - "routes are withdrawn from RIB/FIB of default vrf on R1/R2/R3" - ) - - for dut in ["r1", "r2", "r3"]: - step("Verify RIB/FIB for default vrf on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) - assert result is not True, ( - "Testcase {} : Failed \nError {}\n" - "Routes {} still in BGP table".format( - tc_name, result, static_routes[dut]["static_routes"][0]["network"] - ) - ) - - result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) - assert result is not True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]): - step("Configure static route for VRF : {} on r2".format(vrf_name)) - for addr_type in ADDR_TYPES: - static_routes = { - "r2": { - "static_routes": [ - { - "network": [network[addr_type]], - "next_hop": "blackhole", - "vrf": vrf_name, - } - ] - } - } - - result = create_static_routes(tgen, static_routes) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Redistribute static route on BGP VRF : {}".format(vrf_name)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}} - ) - - redist_dict = { - "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "redistribute": [{"redist_type": "static", "delete": True}] - } - } - } - ) - - redist_dict = { - "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]} - } - - result = create_router_bgp(tgen, topo, redist_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - for vrf_name in ["RED", "BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) - - import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify after import commands are re-configured on R2's vrf RED, all " - "those routes are installed again in default vrf of R1,R2,R3" - ) - for dut in ["r1", "r2", "r3"]: - step("Verify RIB/FIB for vrf RED on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("Remove import vrf RED/GREEN/BLUE/all one by one from default vrf" " on R2") - for vrf_name in ["RED", "BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}} - ) - - import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify that R1, R2 and R3 withdraws imported routes from default " - "vrf's RIB and FIB " - ) - for dut in ["r1", "r2", "r3"]: - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False) - assert result is not True, ( - "Testcase {} : Failed \nError {}\n" - "Routes {} still in BGP table".format( - tc_name, result, static_routes[dut]["static_routes"][0]["network"] - ) - ) - - result = verify_rib(tgen, addr_type, dut, static_routes, expected=False) - assert result is not True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - step("Add import vrf RED/GREEN/BLUE/all one by one from default vrf on R2") - for vrf_name in ["RED", "BLUE", "GREEN"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}}) - - import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} - - result = create_router_bgp(tgen, topo, import_dict) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - for dut in ["r1", "r2", "r3"]: - step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - } - ] - } - } - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for action, value in zip(["Shut", "No shut"], [True, False]): - step( - "{} the neighborship between R2-R3 for vrf GREEN, BLUE and RED".format( - action - ) - ) - bgp_disable = {"r2": {"bgp": []}} - for vrf_name in ["GREEN", "BLUE", "RED"]: - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "neighbor": { - "r3": { - "dest_link": {"r2-link4": {"shutdown": value}} - } - } - } - } - } - ) - - bgp_disable["r2"]["bgp"].append( - {"vrf": vrf_name, "local_as": 2, "address_family": temp} - ) - result = create_router_bgp(tgen, topo, bgp_disable) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers") - for dut in ["r1", "r2", "r3"]: - step("Verify RIB/FIB for vrf RED on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - } - ] - } - } - - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - for action, value, status in zip( - ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"] - ): - step("{} the neighborship between R2-R3 for default vrf".format(action)) - temp = {} - for addr_type in ADDR_TYPES: - temp.update( - { - addr_type: { - "unicast": { - "neighbor": { - "r3": {"dest_link": {"r2-link4": {"shutdown": value}}} - } - } - } - } - ) - - bgp_disable = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}} - result = create_router_bgp(tgen, topo, bgp_disable) - assert result is True, "Testcase {} :Failed \n Error: {}".format( - tc_name, result - ) - - step( - "Verify that R1 and R2 {} all the routes from default vrfs RIB and" - " FIB".format(status) - ) - for dut in ["r1", "r3"]: - step("Verify RIB/FIB for default vrf on {}".format(dut)) - for addr_type in ADDR_TYPES: - static_routes = { - dut: { - "static_routes": [ - { - "network": [ - NETWORK1_1[addr_type], - NETWORK2_1[addr_type], - NETWORK3_1[addr_type], - ], - "next_hop": "blackhole", - } - ] - } - } - - if value: - result = verify_bgp_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - - result = verify_rib( - tgen, addr_type, dut, static_routes, expected=False - ) - assert ( - result is not True - ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format( - tc_name, - result, - static_routes[dut]["static_routes"][0]["network"], - ) - else: - result = verify_bgp_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - result = verify_rib(tgen, addr_type, dut, static_routes) - assert result is True, "Testcase {} : Failed \n Error {}".format( - tc_name, result - ) - - write_test_footer(tc_name) - - -if __name__ == "__main__": - args = ["-s"] + sys.argv[1:] - sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf index 0540a62096..03dfbf9322 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf +++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf @@ -1,11 +1,5 @@ hostname r1 - -#debug bgp vpn leak-to-vrf -#debug bgp vpn leak-from-vrf -#debug bgp nht - - router bgp 99 vrf DONNA no bgp ebgp-requires-policy address-family ipv4 unicast diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf index 731a00829d..35038557df 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf +++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf @@ -16,9 +16,3 @@ int dummy4 ip address 10.0.3.1/24 no shut ! -int EVA - no shut -! -int DONNA - no shut -! diff --git a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py index be07c85997..191a0b53ec 100644 --- a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py +++ b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py @@ -29,7 +29,6 @@ import os import sys from functools import partial import pytest -import time CWD = os.path.dirname(os.path.realpath(__file__)) sys.path.append(os.path.join(CWD, "../")) @@ -78,117 +77,7 @@ def teardown_module(mod): tgen.stop_topology() -def check_bgp_rib(router, vrf, in_fib): - if in_fib: - attr = [{"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]}] - else: - attr = [{"protocol": "bgp", "nexthops": []}] - - if vrf == "DONNA": - expect = { - "10.0.0.0/24": [ - { - "protocol": "connected", - } - ], - "10.0.1.0/24": attr, - "10.0.2.0/24": [{"protocol": "connected"}], - "10.0.3.0/24": attr, - } - else: - expect = { - "10.0.0.0/24": attr, - "10.0.1.0/24": [ - { - "protocol": "connected", - } - ], - "10.0.2.0/24": attr, - "10.0.3.0/24": [ - { - "protocol": "connected", - } - ], - } - - test_func = partial( - topotest.router_json_cmp, router, "show ip route vrf %s json" % vrf, expect - ) - return topotest.run_and_expect(test_func, None, count=10, wait=0.5) - - -def check_bgp_fib(router, vrf, in_rib): - # Check FIB - # DONNA - # 10.0.1.0/24 dev EVA proto bgp metric 20 - # 10.0.3.0/24 dev EVA proto bgp metric 20 - # EVA - # 10.0.0.0/24 dev DONNA proto bgp metric 20 - # 10.0.2.0/24 dev DONNA proto bgp metric 20 - - if vrf == "DONNA": - table = 1001 - nh_vrf = "EVA" - else: - table = 1002 - nh_vrf = "DONNA" - - negate = "" if in_rib else "! " - - cmd = "%sip route show table %s | grep %s" % (negate, table, nh_vrf) - result = False - retry = 5 - output = "" - while retry: - retry -= 1 - try: - output = router.cmd_raises(cmd) - result = True - break - except: - time.sleep(0.1) - - logger.info("VRF %s leaked FIB content %s: %s", vrf, cmd, output) - - return result, output - - -def check_bgp_ping(router, vrf): - if vrf == "DONNA": - cmd = "ip vrf exec DONNA ping -c1 10.0.1.1 -I 10.0.0.1" - else: - cmd = "ip vrf exec EVA ping -c1 10.0.0.1 -I 10.0.1.1" - - result = False - retry = 5 - output = "" - while retry: - retry -= 1 - try: - output = router.cmd_raises(cmd) - result = True - break - except: - time.sleep(0.1) - - return result, output - - -def check_bgp_ping_own_ip(router): - cmd = "ip vrf exec DONNA ping -c1 10.0.0.1 -I 10.0.0.1" - - output = "" - try: - output = router.cmd_raises(cmd) - result = True - except: - result = False - pass - - return result, output - - -def test_vrf_route_leak_test1(): +def test_vrf_route_leak(): logger.info("Ensure that routes are leaked back and forth") tgen = get_topogen() # Don't run this test if we have any failure. @@ -197,86 +86,53 @@ def test_vrf_route_leak_test1(): r1 = tgen.gears["r1"] - result, output = check_bgp_ping_own_ip(r1) - assert ( - result - ), "Ping from VRF fails - check https://bugzilla.kernel.org/show_bug.cgi?id=203483\n:{}".format( - output - ) - - for vrf in ["EVA", "DONNA"]: - result, diff = check_bgp_rib(r1, vrf, True) - assert result, "BGP RIB VRF {} check failed:\n{}".format(vrf, diff) - result, output = check_bgp_fib(r1, vrf, True) - assert result, "BGP FIB VRF {} check failed:\n{}".format(vrf, output) - result, output = check_bgp_ping(r1, vrf) - assert result, "Ping from VRF {} failed:\n{}".format(vrf, output) - + # Test DONNA VRF. + expect = { + "10.0.0.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.1.0/24": [ + {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + ], + "10.0.2.0/24": [{"protocol": "connected"}], + "10.0.3.0/24": [ + {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + ], + } -def test_vrf_route_leak_test2(): - logger.info( - "Ensure that leaked are still present after VRF iface IP address deletion" + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect ) - tgen = get_topogen() - # Don't run this test if we have any failure. - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - r1 = tgen.gears["r1"] - - logger.info("Adding and removing an IPv4 address to EVA and DONNA VRF ifaces") - r1.cmd("ip address add 1.1.1.1/32 dev EVA && ip address del 1.1.1.1/32 dev EVA") - r1.cmd("ip address add 2.2.2.2/32 dev DONNA && ip address del 2.2.2.2/32 dev DONNA") - - for vrf in ["EVA", "DONNA"]: - result, diff = check_bgp_rib(r1, vrf, True) - assert result, "BGP RIB VRF {} check failed:\n{}".format(vrf, diff) - result, output = check_bgp_fib(r1, vrf, True) - assert result, "BGP FIB VRF {} check failed:\n{}".format(vrf, output) - result, output = check_bgp_ping(r1, vrf) - assert result, "Ping from VRF {} failed:\n{}".format(vrf, output) - - -def test_vrf_route_leak_test3(): - logger.info("Ensure that setting down the VRF ifaces invalidates leaked routes") - tgen = get_topogen() - # Don't run this test if we have any failure. - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - r1 = tgen.gears["r1"] + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF DONNA check failed:\n{}".format(diff) + + # Test EVA VRF. + expect = { + "10.0.0.0/24": [ + {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + ], + "10.0.1.0/24": [ + { + "protocol": "connected", + } + ], + "10.0.2.0/24": [ + {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]} + ], + "10.0.3.0/24": [ + { + "protocol": "connected", + } + ], + } - logger.info("Setting down EVA and DONNA VRF ifaces") - r1.cmd("ip link set EVA down") - r1.cmd("ip link set DONNA down") - - for vrf in ["EVA", "DONNA"]: - result, diff = check_bgp_rib(r1, vrf, False) - assert result, "BGP RIB VRF {} check failed:\n{}".format(vrf, diff) - result, output = check_bgp_fib(r1, vrf, False) - assert result, "BGP FIB VRF {} check failed:\n{}".format(vrf, output) - - -def test_vrf_route_leak_test4(): - logger.info("Ensure that setting up the VRF ifaces validates leaked routes") - tgen = get_topogen() - # Don't run this test if we have any failure. - if tgen.routers_have_failure(): - pytest.skip(tgen.errors) - - r1 = tgen.gears["r1"] - - logger.info("Setting up EVA and DONNA VRF ifaces") - r1.cmd("ip link set EVA up") - r1.cmd("ip link set DONNA up") - - for vrf in ["EVA", "DONNA"]: - result, diff = check_bgp_rib(r1, vrf, True) - assert result, "BGP RIB VRF {} check failed:\n{}".format(vrf, diff) - result, output = check_bgp_fib(r1, vrf, True) - assert result, "BGP FIB VRF {} check failed:\n{}".format(vrf, output) - result, output = check_bgp_ping(r1, vrf) - assert result, "Ping from VRF {} failed:\n{}".format(vrf, output) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route vrf EVA json", expect + ) + result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5) + assert result, "BGP VRF EVA check failed:\n{}".format(diff) def test_memory_leak(): diff --git a/tests/topotests/lib/bgprib.py b/tests/topotests/lib/bgprib.py index 01439373c5..35a57d0a99 100644 --- a/tests/topotests/lib/bgprib.py +++ b/tests/topotests/lib/bgprib.py @@ -37,7 +37,6 @@ from lib.lutil import luCommand, luResult, LUtil import json import re -import time # gpz: get rib in json form and compare against desired routes class BgpRib: @@ -49,15 +48,7 @@ class BgpRib: for pfx in pfxtbl.keys(): if debug: self.log("trying pfx %s" % pfx) - if "exist" in want and want["exist"] == False: - if pfx == want["p"]: - if debug: - self.log("unexpected route: pfx=" + want["p"]) - return 0 - if debug: - self.log("unwant pfx=" + want["p"] + ", not " + pfx) - continue - elif pfx != want["p"]: + if pfx != want["p"]: if debug: self.log("want pfx=" + want["p"] + ", not " + pfx) continue @@ -84,67 +75,53 @@ class BgpRib: if debug: self.log("missing route: pfx=" + want["p"] + ", nh=" + want["n"]) return 0 - if "exist" in want and want["exist"] == False: - return 1 - return 0 - def RequireVpnRoutes(self, target, title, wantroutes, retry=0, wait=1, debug=0): + def RequireVpnRoutes(self, target, title, wantroutes, debug=0): import json logstr = "RequireVpnRoutes " + str(wantroutes) - retry += 1 - while retry: - retry -= 1 - # non json form for humans - luCommand( - target, - 'vtysh -c "show bgp ipv4 vpn"', - ".", - "None", - "Get VPN RIB (non-json)", - ) - ret = luCommand( - target, - 'vtysh -c "show bgp ipv4 vpn json"', - ".*", - "None", - "Get VPN RIB (json)", - ) - if re.search(r"^\s*$", ret): - # degenerate case: empty json means no routes - if len(wantroutes) > 0: - luResult(target, False, title, logstr) - return - luResult(target, True, title, logstr) - rib = json.loads(ret) - rds = rib["routes"]["routeDistinguishers"] - for want in wantroutes: - found = 0 - if debug: - self.log("want rd %s" % want["rd"]) - for rd in rds.keys(): - if rd != want["rd"]: - continue - if debug: - self.log("found rd %s" % rd) - table = rds[rd] - if self.routes_include_wanted(table, want, debug): - found = 1 - break - if not found: - if retry: - break - luResult(target, False, title, logstr) - return - if not found and retry: - time.sleep(wait) - continue + # non json form for humans + luCommand( + target, + 'vtysh -c "show bgp ipv4 vpn"', + ".", + "None", + "Get VPN RIB (non-json)", + ) + ret = luCommand( + target, + 'vtysh -c "show bgp ipv4 vpn json"', + ".*", + "None", + "Get VPN RIB (json)", + ) + if re.search(r"^\s*$", ret): + # degenerate case: empty json means no routes + if len(wantroutes) > 0: + luResult(target, False, title, logstr) + return luResult(target, True, title, logstr) - break + rib = json.loads(ret) + rds = rib["routes"]["routeDistinguishers"] + for want in wantroutes: + found = 0 + if debug: + self.log("want rd %s" % want["rd"]) + for rd in rds.keys(): + if rd != want["rd"]: + continue + if debug: + self.log("found rd %s" % rd) + table = rds[rd] + if self.routes_include_wanted(table, want, debug): + found = 1 + break + if not found: + luResult(target, False, title, logstr) + return + luResult(target, True, title, logstr) - def RequireUnicastRoutes( - self, target, afi, vrf, title, wantroutes, retry=0, wait=1, debug=0 - ): + def RequireUnicastRoutes(self, target, afi, vrf, title, wantroutes, debug=0): logstr = "RequireUnicastRoutes %s" % str(wantroutes) vrfstr = "" if vrf != "": @@ -153,62 +130,48 @@ class BgpRib: if (afi != "ipv4") and (afi != "ipv6"): self.log("ERROR invalid afi") - retry += 1 - while retry: - retry -= 1 - cmdstr = "show bgp %s %s unicast" % (vrfstr, afi) - # non json form for humans - cmd = 'vtysh -c "%s"' % cmdstr - luCommand( - target, cmd, ".", "None", "Get %s %s RIB (non-json)" % (vrfstr, afi) - ) - cmd = 'vtysh -c "%s json"' % cmdstr - ret = luCommand( - target, cmd, ".*", "None", "Get %s %s RIB (json)" % (vrfstr, afi) - ) - if re.search(r"^\s*$", ret): - # degenerate case: empty json means no routes - if len(wantroutes) > 0: - luResult(target, False, title, logstr) - return - luResult(target, True, title, logstr) - rib = json.loads(ret) - try: - table = rib["routes"] - # KeyError: 'routes' probably means missing/bad VRF - except KeyError as err: - if vrf != "": - errstr = "-script ERROR: check if wrong vrf (%s)" % (vrf) - else: - errstr = "-script ERROR: check if vrf missing" - if retry: - time.sleep(wait) - continue - luResult(target, False, title + errstr, logstr) + cmdstr = "show bgp %s %s unicast" % (vrfstr, afi) + # non json form for humans + cmd = 'vtysh -c "%s"' % cmdstr + luCommand(target, cmd, ".", "None", "Get %s %s RIB (non-json)" % (vrfstr, afi)) + cmd = 'vtysh -c "%s json"' % cmdstr + ret = luCommand( + target, cmd, ".*", "None", "Get %s %s RIB (json)" % (vrfstr, afi) + ) + if re.search(r"^\s*$", ret): + # degenerate case: empty json means no routes + if len(wantroutes) > 0: + luResult(target, False, title, logstr) return - # if debug: - # self.log("table=%s" % table) - for want in wantroutes: - if debug: - self.log("want=%s" % want) - if not self.routes_include_wanted(table, want, debug): - if retry: - time.sleep(wait) - continue - luResult(target, False, title, logstr) - return luResult(target, True, title, logstr) - break + rib = json.loads(ret) + try: + table = rib["routes"] + # KeyError: 'routes' probably means missing/bad VRF + except KeyError as err: + if vrf != "": + errstr = "-script ERROR: check if wrong vrf (%s)" % (vrf) + else: + errstr = "-script ERROR: check if vrf missing" + luResult(target, False, title + errstr, logstr) + return + # if debug: + # self.log("table=%s" % table) + for want in wantroutes: + if debug: + self.log("want=%s" % want) + if not self.routes_include_wanted(table, want, debug): + luResult(target, False, title, logstr) + return + luResult(target, True, title, logstr) BgpRib = BgpRib() -def bgpribRequireVpnRoutes(target, title, wantroutes, retry=0, wait=1, debug=0): - BgpRib.RequireVpnRoutes(target, title, wantroutes, retry, wait, debug) +def bgpribRequireVpnRoutes(target, title, wantroutes, debug=0): + BgpRib.RequireVpnRoutes(target, title, wantroutes, debug) -def bgpribRequireUnicastRoutes( - target, afi, vrf, title, wantroutes, retry=0, wait=1, debug=0 -): - BgpRib.RequireUnicastRoutes(target, afi, vrf, title, wantroutes, retry, wait, debug) +def bgpribRequireUnicastRoutes(target, afi, vrf, title, wantroutes, debug=0): + BgpRib.RequireUnicastRoutes(target, afi, vrf, title, wantroutes, debug) diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py index 5c1fa24a7b..62c21e9ce4 100644 --- a/tests/topotests/lib/lutil.py +++ b/tests/topotests/lib/lutil.py @@ -50,6 +50,7 @@ class lUtil: l_line = 0 l_dotall_experiment = False l_last_nl = None + l_wait_strict = 1 fout = "" fsum = "" @@ -189,7 +190,7 @@ Total %-4d %-4d %d\n\ self.log("unable to read: " + tstFile) sys.exit(1) - def command(self, target, command, regexp, op, result, returnJson, startt=None): + def command(self, target, command, regexp, op, result, returnJson, startt=None, force_result=False): global net if op == "jsoncmp_pass" or op == "jsoncmp_fail": returnJson = True @@ -292,7 +293,7 @@ Total %-4d %-4d %d\n\ 9, ) if startt != None: - if js != None or ret is not False: + if js != None or ret is not False or force_result is not False: delta = time.time() - startt self.result(target, success, "%s +%4.2f secs" % (result, delta)) elif op == "pass" or op == "fail": @@ -322,12 +323,23 @@ Total %-4d %-4d %d\n\ n = 0 startt = time.time() + if (op == "wait-strict") or ((op == "wait") and self.l_wait_strict): + strict = True + else: + strict = False + # Calculate the amount of `sleep`s we are going to peform. wait_count = int(math.ceil(wait / wait_time)) + 1 + force_result = False while wait_count > 0: n += 1 - found = self.command(target, command, regexp, op, result, returnJson, startt) + + # log a failure on last iteration if we don't get desired regexp + if strict and (wait_count == 1): + force_result = True + + found = self.command(target, command, regexp, op, result, returnJson, startt, force_result) if found is not False: break @@ -378,12 +390,14 @@ def luCommand( returnJson=False, wait_time=0.5, ): - if op != "wait": - return LUtil.command(target, command, regexp, op, result, returnJson) - else: + waitops = ["wait", "wait-strict", "wait-nostrict"] + + if op in waitops: return LUtil.wait( target, command, regexp, op, result, time, returnJson, wait_time ) + else: + return LUtil.command(target, command, regexp, op, result, returnJson) def luLast(usenl=False): @@ -454,6 +468,25 @@ def luShowFail(): if printed > 0: logger.error("See %s for details of errors" % LUtil.fout_name) +# +# Sets default wait type for luCommand(op="wait) (may be overridden by +# specifying luCommand(op="wait-strict") or luCommand(op="wait-nostrict")). +# +# "nostrict" is the historical default behavior, which is to ignore +# failures to match the specified regexp in the specified time. +# +# "strict" means that failure to match the specified regexp in the +# specified time yields an explicit, logged failure result +# +def luSetWaitType(waittype): + if waittype == "strict": + LUtil.l_wait_strict = 1 + else: + if waittype == "nostrict": + LUtil.l_wait_strict = 0 + else: + raise ValueError('waittype must be one of "strict" or "nostrict"') + # for testing if __name__ == "__main__": diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt index 6bafbbb556..86c089ab3b 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt @@ -5,5 +5,5 @@ B>* 10.0.3.0/24 [20/20] via 10.0.30.3, r1-eth2 (vrf neno), weight 1, XX:XX:XX O>* 10.0.4.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX O 10.0.20.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX C>* 10.0.20.0/24 is directly connected, r1-eth1, XX:XX:XX -B>* 10.0.30.0/24 [20/0] is directly connected, neno (vrf neno), weight 1, XX:XX:XX +B>* 10.0.30.0/24 [20/0] is directly connected, r1-eth2 (vrf neno), weight 1, XX:XX:XX O>* 10.0.40.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt index 3ed6b1b3a1..9681d8a04e 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt @@ -7,4 +7,4 @@ B>* 10.0.4.0/24 [20/20] via 10.0.40.4, r2-eth2 (vrf ray), weight 1, XX:XX:XX O 10.0.20.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX C>* 10.0.20.0/24 is directly connected, r2-eth1, XX:XX:XX O>* 10.0.30.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX -B>* 10.0.40.0/24 [20/0] is directly connected, ray (vrf ray), weight 1, XX:XX:XX +B>* 10.0.40.0/24 [20/0] is directly connected, r2-eth2 (vrf ray), weight 1, XX:XX:XX diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt index 4ad8441d85..ce9903ae71 100644 --- a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt +++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt @@ -1,9 +1,9 @@ VRF ray: B 10.0.1.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX -B 10.0.2.0/24 [20/0] is directly connected, lo (vrf default) inactive, weight 1, XX:XX:XX +B 10.0.2.0/24 [20/0] is directly connected, r2-eth0 (vrf default) inactive, weight 1, XX:XX:XX B>* 10.0.3.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX O>* 10.0.4.0/24 [110/20] via 10.0.40.4, r2-eth2, weight 1, XX:XX:XX -B 10.0.20.0/24 [20/0] is directly connected, lo (vrf default) inactive, weight 1, XX:XX:XX +B 10.0.20.0/24 [20/0] is directly connected, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX B>* 10.0.30.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX O 10.0.40.0/24 [110/10] is directly connected, r2-eth2, weight 1, XX:XX:XX C>* 10.0.40.0/24 is directly connected, r2-eth2, XX:XX:XX diff --git a/tests/topotests/pim_igmp_vrf/test_pim_vrf.py b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py index f845a4a6ee..64001deade 100755 --- a/tests/topotests/pim_igmp_vrf/test_pim_vrf.py +++ b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py @@ -166,6 +166,11 @@ def setup_module(module): tgen = Topogen(build_topo, module.__name__) tgen.start_topology() + # Required linux kernel version for this suite to run. + result = required_linux_kernel_version("4.19") + if result is not True: + pytest.skip("Kernel requirements are not met") + vrf_setup_cmds = [ "ip link add name blue type vrf table 11", "ip link add name red type vrf table 12", @@ -210,11 +215,6 @@ def test_ospf_convergence(): "Test for OSPFv2 convergence" tgen = get_topogen() - # Required linux kernel version for this suite to run. - result = required_linux_kernel_version("4.15") - if result is not True: - pytest.skip("Kernel requirements are not met") - # iproute2 needs to support VRFs for this suite to run. if not iproute2_is_vrf_capable(): pytest.skip("Installed iproute2 version does not support VRFs") diff --git a/zebra/connected.c b/zebra/connected.c index 57c7f1925b..c01be58e82 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -387,14 +387,10 @@ void connected_down(struct interface *ifp, struct connected *ifc) .ifindex = ifp->ifindex, .vrf_id = ifp->vrf->vrf_id, }; - struct zebra_vrf *zvrf, *zvrf_iter; - uint32_t count_ipv4 = 0; + struct zebra_vrf *zvrf; + uint32_t count = 0; struct listnode *cnode; struct connected *c; - struct route_table *table; - struct route_node *rn; - struct route_entry *re, *next; - struct vrf *vrf; zvrf = ifp->vrf->info; if (!zvrf) { @@ -460,14 +456,12 @@ void connected_down(struct interface *ifp, struct connected *ifc) prefix_copy(&cp, CONNECTED_PREFIX(c)); apply_mask(&cp); - if (CHECK_FLAG(c->conf, ZEBRA_IFC_DOWN)) - continue; + if (prefix_same(&p, &cp) && + !CHECK_FLAG(c->conf, ZEBRA_IFC_DOWN)) + count++; - if (prefix_same(&p, &cp)) + if (count >= 1) return; - - if (cp.family == AF_INET) - count_ipv4++; } /* @@ -480,60 +474,6 @@ void connected_down(struct interface *ifp, struct connected *ifc) rib_delete(afi, SAFI_MULTICAST, zvrf->vrf->vrf_id, ZEBRA_ROUTE_CONNECT, 0, 0, &p, NULL, &nh, 0, zvrf->table_id, 0, 0, false); - /* When the last IPv4 address of an interface is deleted, Linux removes - * all routes using this interface without any Netlink advertisement. - * The removed routes include those that only have this particular - * interface as a nexthop. Among those, remove the kernel one from the - * FRR RIB and reinstall the other that have been added from FRR. - */ - if (afi == AFI_IP && count_ipv4 == 0 && if_is_operative(ifp)) { - RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - zvrf_iter = vrf->info; - - if (!zvrf_iter) - continue; - - table = zvrf_iter->table[AFI_IP][SAFI_UNICAST]; - if (!table) - continue; - - for (rn = route_top(table); rn; - rn = srcdest_route_next(rn)) { - RNODE_FOREACH_RE_SAFE (rn, re, next) { - if (CHECK_FLAG(re->status, - ROUTE_ENTRY_REMOVED)) - continue; - if (re->nhe->ifp != ifp) - continue; - if (re->type == ZEBRA_ROUTE_KERNEL) - rib_delete( - afi, SAFI_UNICAST, - zvrf_iter->vrf->vrf_id, - re->type, 0, re->flags, - &rn->p, NULL, &nh, 0, - zvrf_iter->table_id, - re->metric, - re->distance, false); - else if (re->type != - ZEBRA_ROUTE_CONNECT) { - SET_FLAG(re->status, - ROUTE_ENTRY_CHANGED); - UNSET_FLAG( - re->status, - ROUTE_ENTRY_INSTALLED); - rib_add(afi, SAFI_UNICAST, - zvrf_iter->vrf->vrf_id, - re->type, 0, 0, &rn->p, - NULL, &nh, re->nhe_id, - zvrf_iter->table_id, - re->metric, 0, - re->distance, 0, false); - } - } - } - } - } - /* Schedule LSP forwarding entries for processing, if appropriate. */ if (zvrf->vrf->vrf_id == VRF_DEFAULT) { if (IS_ZEBRA_DEBUG_MPLS) diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index 8550d7e304..af75ddf742 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -604,7 +604,10 @@ static void fpm_read(struct thread *t) hdr, 0, false, ctx) != 1) { dplane_ctx_fini(&ctx); stream_pulldown(fnc->ibuf); - return; + /* + * Let's continue to read other messages + * Even if we ignore this one. + */ } break; default: diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 10725665e8..39ec003047 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -4389,7 +4389,7 @@ static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, "Tx %s family %s IF %s(%u) Neigh %pIA %s %s flags 0x%x state 0x%x %sext_flags 0x%x", nl_msg_type_to_str(cmd), nl_family_to_str(family), dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx), - ip, link_ip ? "Link " : "MAC ", buf2, flags, state, + ip, link_ip ? "Link" : "MAC", buf2, flags, state, ext ? "ext " : "", ext_flags); return netlink_neigh_update_msg_encode( diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 84dae7f2d6..739f6adce9 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -5070,8 +5070,8 @@ neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, ipaddr2str(link_ip, buf1, sizeof(buf1)); zlog_debug("init neigh ctx %s: ifp %s, %s %s, ip %pIA", dplane_op2str(op), ifp->name, - link_family == AF_ETHERNET ? "mac " : "link ", - buf1, ip); + link_family == AF_ETHERNET ? "mac" : "link", buf1, + ip); } ctx = dplane_ctx_alloc(); diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index 218184ad0d..2c953eef15 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -653,8 +653,13 @@ void zebra_evpn_print_mac(struct zebra_mac *mac, void *ctxt, json_object *json) json_object_int_add(json_mac, "vlan", vid); } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { json_object_string_add(json_mac, "type", "remote"); - json_object_string_addf(json_mac, "remoteVtep", "%pI4", - &mac->fwd_info.r_vtep_ip); + if (mac->es) + json_object_string_add(json_mac, "remoteEs", + mac->es->esi_str); + else + json_object_string_addf( + json_mac, "remoteVtep", "%pI4", + &mac->fwd_info.r_vtep_ip); } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) json_object_string_add(json_mac, "type", "auto"); @@ -937,8 +942,13 @@ void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt) "", mac->loc_seq, mac->rem_seq); } else { json_object_string_add(json_mac, "type", "remote"); - json_object_string_addf(json_mac, "remoteVtep", "%pI4", - &mac->fwd_info.r_vtep_ip); + if (mac->es) + json_object_string_add(json_mac, "remoteEs", + mac->es->esi_str); + else + json_object_string_addf( + json_mac, "remoteVtep", "%pI4", + &mac->fwd_info.r_vtep_ip); json_object_object_add(json_mac_hdr, buf1, json_mac); json_object_int_add(json_mac, "localSequence", mac->loc_seq); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index aac1c9b471..d099a28d4d 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -3511,7 +3511,26 @@ DEFUN (show_evpn_mac_vni, vni = strtoul(argv[4]->arg, NULL, 10); zvrf = zebra_vrf_get_evpn(); - zebra_vxlan_print_macs_vni(vty, zvrf, vni, uj); + zebra_vxlan_print_macs_vni(vty, zvrf, vni, uj, false); + return CMD_SUCCESS; +} + +DEFPY (show_evpn_mac_vni_detail, + show_evpn_mac_vni_detail_cmd, + "show evpn mac vni " CMD_VNI_RANGE " detail [json]", + SHOW_STR + "EVPN\n" + "MAC addresses\n" + "VXLAN Network Identifier\n" + "VNI number\n" + "Detailed Information On Each VNI MAC\n" + JSON_STR) +{ + struct zebra_vrf *zvrf; + bool uj = use_json(argc, argv); + + zvrf = zebra_vrf_get_evpn(); + zebra_vxlan_print_macs_vni(vty, zvrf, vni, uj, true); return CMD_SUCCESS; } @@ -4819,6 +4838,7 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_mac_vni_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_detail_cmd); + install_element(VIEW_NODE, &show_evpn_mac_vni_detail_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_mac_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_vtep_cmd); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index f1c7debe11..7e86d15b4b 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -2869,7 +2869,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, * Display MACs for a VNI (VTY command handler). */ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, bool use_json) + vni_t vni, bool use_json, bool detail) { struct zebra_evpn *zevpn; uint32_t num_macs; @@ -2902,17 +2902,28 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, wctx.json = json_mac; if (!use_json) { - vty_out(vty, - "Number of MACs (local and remote) known for this VNI: %u\n", - num_macs); - vty_out(vty, - "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n"); - vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC", "Type", - "Flags", "Intf/Remote ES/VTEP", "VLAN", "Seq #'s"); + if (detail) { + vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", + zevpn->vni, num_macs); + } else { + vty_out(vty, + "Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, + "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC", + "Type", "Flags", "Intf/Remote ES/VTEP", "VLAN", + "Seq #'s"); + } } else json_object_int_add(json, "numMacs", num_macs); - hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, &wctx); + if (detail) + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash_detail, + &wctx); + else + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, + &wctx); if (use_json) { json_object_object_add(json, "macs", json_mac); @@ -3511,6 +3522,12 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) json = json_object_new_object(); json_object_string_add(json, "advertiseGatewayMacip", zvrf->advertise_gw_macip ? "Yes" : "No"); + json_object_string_add(json, "advertiseSviMacip", + zvrf->advertise_svi_macip ? "Yes" + : "No"); + json_object_string_add(json, "advertiseSviMac", + zebra_evpn_mh_do_adv_svi_mac() ? "Yes" + : "No"); json_object_int_add(json, "numVnis", num_vnis); json_object_int_add(json, "numL2Vnis", num_l2vnis); json_object_int_add(json, "numL3Vnis", num_l3vnis); diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 121e5633d6..16c5bc0a1a 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -95,7 +95,7 @@ extern void zebra_vxlan_print_specific_rmac_l3vni(struct vty *vty, vni_t l3vni, struct ethaddr *rmac, bool use_json); extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, - vni_t vni, bool use_json); + vni_t vni, bool use_json, bool detail); extern void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, bool print_dup, |
