mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-02 13:23:44 +00:00
Merge pull request #11127 from louis-6wind/bgp-leak
bgpd: multiple fixes for route leaking
This commit is contained in:
commit
16aa1809e7
101
bgpd/bgp_attr.c
101
bgpd/bgp_attr.c
@ -825,55 +825,56 @@ 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_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_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)
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2254,6 +2255,12 @@ 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:
|
||||
@ -2271,6 +2278,12 @@ 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) {
|
||||
|
@ -170,6 +170,12 @@ 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;
|
||||
@ -220,9 +226,6 @@ 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;
|
||||
|
||||
|
@ -142,15 +142,21 @@ 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 = (bpi1->attr->mp_nexthop_prefer_global)
|
||||
addr1 = (CHECK_FLAG(
|
||||
bpi1->attr->nh_flag,
|
||||
BGP_ATTR_NH_MP_PREFER_GLOBAL))
|
||||
? bpi1->attr->mp_nexthop_global
|
||||
: bpi1->attr->mp_nexthop_local;
|
||||
addr2 = (bpi2->attr->mp_nexthop_prefer_global)
|
||||
addr2 = (CHECK_FLAG(
|
||||
bpi2->attr->nh_flag,
|
||||
BGP_ATTR_NH_MP_PREFER_GLOBAL))
|
||||
? bpi2->attr->mp_nexthop_global
|
||||
: bpi2->attr->mp_nexthop_local;
|
||||
|
||||
if (!bpi1->attr->mp_nexthop_prefer_global
|
||||
&& !bpi2->attr->mp_nexthop_prefer_global)
|
||||
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))
|
||||
compare = !bgp_interface_same(
|
||||
bpi1->peer->ifp,
|
||||
bpi2->peer->ifp);
|
||||
|
@ -1060,9 +1060,11 @@ 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;
|
||||
@ -1070,13 +1072,25 @@ static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn,
|
||||
bgp_nexthop = bgp_orig;
|
||||
|
||||
/*
|
||||
* No nexthop tracking for redistributed routes or for
|
||||
* No nexthop tracking for redistributed routes,
|
||||
* for static (i.e. coming from the bgp network statement 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
|
||||
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
|
||||
/*
|
||||
* TBD do we need to do anything about the
|
||||
* 'connected' parameter?
|
||||
@ -1266,6 +1280,7 @@ 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;
|
||||
}
|
||||
@ -1864,11 +1879,31 @@ 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);
|
||||
@ -1928,6 +1963,18 @@ 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
|
||||
*
|
||||
@ -1970,6 +2017,22 @@ 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
|
||||
*/
|
||||
@ -2051,22 +2114,6 @@ 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);
|
||||
|
@ -46,6 +46,7 @@
|
||||
#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;
|
||||
|
||||
@ -388,7 +389,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) {
|
||||
if (is_bgp_static_route && !CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE)) {
|
||||
SET_FLAG(bnc->flags, BGP_STATIC_ROUTE);
|
||||
|
||||
/* If we're toggling the type, re-register */
|
||||
@ -423,8 +424,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);
|
||||
@ -834,10 +835,13 @@ 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) {
|
||||
@ -858,25 +862,37 @@ 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 (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
|
||||
if (bnc_nhc)
|
||||
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 (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
|
||||
if (bnc_import) {
|
||||
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
|
||||
@ -989,7 +1005,8 @@ 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 (pi->attr->mp_nexthop_prefer_global)
|
||||
if (CHECK_FLAG(pi->attr->nh_flag,
|
||||
BGP_ATTR_NH_MP_PREFER_GLOBAL))
|
||||
p->u.prefix6 =
|
||||
pi->attr->mp_nexthop_global;
|
||||
else
|
||||
|
@ -8679,6 +8679,7 @@ 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);
|
||||
@ -8728,6 +8729,11 @@ 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;
|
||||
@ -9410,9 +9416,10 @@ 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)
|
||||
&& !attr->mp_nexthop_prefer_global)
|
||||
&attr->mp_nexthop_local) !=
|
||||
0) &&
|
||||
!CHECK_FLAG(attr->nh_flag,
|
||||
BGP_ATTR_NH_MP_PREFER_GLOBAL))
|
||||
json_object_boolean_true_add(
|
||||
json_nexthop_ll, "used");
|
||||
else
|
||||
@ -9424,10 +9431,11 @@ 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)
|
||||
&& !attr->mp_nexthop_prefer_global)
|
||||
|| (path->peer->conf_if)) {
|
||||
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 (path->peer->conf_if) {
|
||||
len = vty_out(vty, "%s",
|
||||
path->peer->conf_if);
|
||||
@ -10689,7 +10697,8 @@ 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 (!attr->mp_nexthop_prefer_global)
|
||||
if (!CHECK_FLAG(attr->nh_flag,
|
||||
BGP_ATTR_NH_MP_PREFER_GLOBAL))
|
||||
json_object_boolean_true_add(json_nexthop_ll,
|
||||
"used");
|
||||
else
|
||||
@ -10699,7 +10708,8 @@ 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),
|
||||
attr->mp_nexthop_prefer_global
|
||||
CHECK_FLAG(attr->nh_flag,
|
||||
BGP_ATTR_NH_MP_PREFER_GLOBAL)
|
||||
? "(prefer-global)"
|
||||
: "(used)");
|
||||
}
|
||||
|
@ -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 */
|
||||
path->attr->mp_nexthop_prefer_global = true;
|
||||
SET_FLAG(path->attr->nh_flag, BGP_ATTR_NH_MP_PREFER_GLOBAL);
|
||||
SET_FLAG(path->attr->rmap_change_flags,
|
||||
BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED);
|
||||
} else {
|
||||
path->attr->mp_nexthop_prefer_global = false;
|
||||
UNSET_FLAG(path->attr->nh_flag, BGP_ATTR_NH_MP_PREFER_GLOBAL);
|
||||
SET_FLAG(path->attr->rmap_change_flags,
|
||||
BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED);
|
||||
}
|
||||
|
@ -704,7 +704,8 @@ 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 (path->attr->mp_nexthop_prefer_global)
|
||||
if (CHECK_FLAG(path->attr->nh_flag,
|
||||
BGP_ATTR_NH_MP_PREFER_GLOBAL))
|
||||
return SNMP_INTEGER(2);
|
||||
else
|
||||
return SNMP_INTEGER(4);
|
||||
@ -718,7 +719,8 @@ 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 (path->attr->mp_nexthop_prefer_global)
|
||||
if (CHECK_FLAG(path->attr->nh_flag,
|
||||
BGP_ATTR_NH_MP_PREFER_GLOBAL))
|
||||
return SNMP_IP6ADDRESS(
|
||||
path->attr->mp_nexthop_global);
|
||||
else
|
||||
|
@ -234,6 +234,7 @@ 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;
|
||||
@ -256,6 +257,14 @@ 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;
|
||||
}
|
||||
|
||||
@ -264,6 +273,7 @@ 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;
|
||||
|
||||
@ -303,6 +313,14 @@ 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;
|
||||
}
|
||||
|
||||
@ -390,10 +408,16 @@ 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;
|
||||
struct bgp *bgp, *from_bgp, *bgp_default;
|
||||
struct listnode *next;
|
||||
struct prefix *addr;
|
||||
afi_t afi;
|
||||
safi_t safi;
|
||||
|
||||
bgp = bgp_lookup_by_vrf_id(vrf_id);
|
||||
|
||||
@ -421,9 +445,6 @@ 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;
|
||||
|
||||
@ -439,6 +460,44 @@ 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;
|
||||
@ -1007,7 +1066,8 @@ 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 (path->attr->mp_nexthop_prefer_global) {
|
||||
if (CHECK_FLAG(path->attr->nh_flag,
|
||||
BGP_ATTR_NH_MP_PREFER_GLOBAL)) {
|
||||
nexthop = &path->attr->mp_nexthop_global;
|
||||
if (IN6_IS_ADDR_LINKLOCAL(nexthop))
|
||||
*ifindex = path->attr->nh_ifindex;
|
||||
@ -1307,6 +1367,7 @@ 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;
|
||||
@ -1355,13 +1416,9 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
|
||||
|
||||
peer = info->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;
|
||||
if (info->type == ZEBRA_ROUTE_BGP) {
|
||||
bpi_ultimate = bgp_get_imported_bpi_ultimate(info);
|
||||
peer = bpi_ultimate->peer;
|
||||
}
|
||||
|
||||
tag = info->attr->tag;
|
||||
@ -3193,6 +3250,7 @@ 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))
|
||||
@ -3207,6 +3265,17 @@ 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;
|
||||
}
|
||||
|
||||
|
17
lib/if.c
17
lib/if.c
@ -564,9 +564,24 @@ 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)
|
||||
{
|
||||
|
1
lib/if.h
1
lib/if.h
@ -532,6 +532,7 @@ 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);
|
||||
|
||||
|
@ -4,6 +4,8 @@ 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
|
||||
|
@ -4,6 +4,8 @@ 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
|
||||
|
@ -19,6 +19,7 @@ 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
|
||||
!
|
||||
|
@ -4,6 +4,7 @@ hostname ce3
|
||||
!
|
||||
interface lo
|
||||
ip address 99.0.0.3/32
|
||||
ip address 6.0.3.1/24
|
||||
!
|
||||
interface ce3-eth0
|
||||
description to r4
|
||||
|
@ -19,6 +19,7 @@ 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
|
||||
!
|
||||
|
@ -4,6 +4,7 @@ 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
|
||||
|
@ -175,6 +175,20 @@ 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",
|
||||
|
@ -11,6 +11,7 @@ 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
|
||||
@ -39,6 +40,11 @@ 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
|
||||
|
||||
@ -51,5 +57,47 @@ 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
|
||||
|
6
tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/staticd.conf
Normal file
6
tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/staticd.conf
Normal file
@ -0,0 +1,6 @@
|
||||
hostname r1
|
||||
log file staticd.log
|
||||
!
|
||||
vrf r1-cust1
|
||||
ip route 192.0.0.0/24 192.168.1.2
|
||||
exit-vrf
|
@ -4,6 +4,9 @@ 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
|
||||
@ -18,6 +21,14 @@ 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
|
||||
|
||||
|
||||
|
@ -81,3 +81,24 @@ 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",
|
||||
)
|
||||
|
@ -72,3 +72,53 @@ 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",
|
||||
)
|
||||
|
@ -54,15 +54,44 @@ bgpribRequireUnicastRoutes("ce4", "ipv4", "ce4-cust2", "Cust 4 routes in ce1", w
|
||||
#
|
||||
# r1 vtysh -c "show bgp vrf r1-cust1 ipv4"
|
||||
#
|
||||
want_r1_cust1_routes = [
|
||||
want_r1_cust1_3_5_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_routes
|
||||
"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
|
||||
)
|
||||
|
||||
want_r3_cust1_routes = [
|
||||
@ -70,10 +99,20 @@ 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
|
||||
"r3",
|
||||
"ipv4",
|
||||
"r3-cust1",
|
||||
"Customer 1 routes in r3 vrf",
|
||||
want_r3_cust1_routes,
|
||||
retry=30,
|
||||
)
|
||||
|
||||
want_r4_cust1_routes = [
|
||||
@ -81,10 +120,20 @@ 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
|
||||
"r4",
|
||||
"ipv4",
|
||||
"r4-cust1",
|
||||
"Customer 1 routes in r4 vrf",
|
||||
want_r4_cust1_routes,
|
||||
retry=30,
|
||||
)
|
||||
|
||||
want_r4_cust2_routes = [
|
||||
@ -92,10 +141,20 @@ 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
|
||||
"r4",
|
||||
"ipv4",
|
||||
"r4-cust2",
|
||||
"Customer 2 routes in r4 vrf",
|
||||
want_r4_cust2_routes,
|
||||
retry=30,
|
||||
)
|
||||
|
||||
########################################################################
|
||||
@ -667,7 +726,7 @@ bgpribRequireUnicastRoutes(
|
||||
luCommand(
|
||||
"ce1",
|
||||
'vtysh -c "show bgp ipv4 uni"',
|
||||
"12 routes and 12",
|
||||
"18 routes and 19",
|
||||
"wait",
|
||||
"Local and remote routes",
|
||||
10,
|
||||
@ -689,7 +748,7 @@ bgpribRequireUnicastRoutes(
|
||||
luCommand(
|
||||
"ce2",
|
||||
'vtysh -c "show bgp ipv4 uni"',
|
||||
"12 routes and 15",
|
||||
"18 routes and 22",
|
||||
"wait",
|
||||
"Local and remote routes",
|
||||
10,
|
||||
@ -721,7 +780,7 @@ luCommand("r4", 'vtysh -c "show ip route vrf r4-cust2"')
|
||||
luCommand(
|
||||
"ce3",
|
||||
'vtysh -c "show bgp ipv4 uni"',
|
||||
"12 routes and 13",
|
||||
"18 routes and 19",
|
||||
"wait",
|
||||
"Local and remote routes",
|
||||
10,
|
||||
@ -743,7 +802,7 @@ bgpribRequireUnicastRoutes(
|
||||
luCommand(
|
||||
"ce4",
|
||||
'vtysh -c "show bgp vrf ce4-cust2 ipv4 uni"',
|
||||
"12 routes and 14",
|
||||
"18 routes and 21",
|
||||
"wait",
|
||||
"Local and remote routes",
|
||||
10,
|
||||
|
@ -1,5 +1,11 @@
|
||||
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
|
||||
|
@ -16,3 +16,9 @@ int dummy4
|
||||
ip address 10.0.3.1/24
|
||||
no shut
|
||||
!
|
||||
int EVA
|
||||
no shut
|
||||
!
|
||||
int DONNA
|
||||
no shut
|
||||
!
|
||||
|
@ -29,6 +29,7 @@ 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, "../"))
|
||||
@ -77,7 +78,117 @@ def teardown_module(mod):
|
||||
tgen.stop_topology()
|
||||
|
||||
|
||||
def test_vrf_route_leak():
|
||||
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():
|
||||
logger.info("Ensure that routes are leaked back and forth")
|
||||
tgen = get_topogen()
|
||||
# Don't run this test if we have any failure.
|
||||
@ -86,53 +197,86 @@ def test_vrf_route_leak():
|
||||
|
||||
r1 = tgen.gears["r1"]
|
||||
|
||||
# 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}]}
|
||||
],
|
||||
}
|
||||
|
||||
test_func = partial(
|
||||
topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect
|
||||
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
|
||||
)
|
||||
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",
|
||||
}
|
||||
],
|
||||
}
|
||||
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
|
||||
|
||||
def test_vrf_route_leak_test2():
|
||||
logger.info(
|
||||
"Ensure that leaked are still present after VRF iface IP address deletion"
|
||||
)
|
||||
result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||
assert result, "BGP VRF EVA check failed:\n{}".format(diff)
|
||||
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"]
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def test_memory_leak():
|
||||
|
@ -37,6 +37,7 @@
|
||||
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:
|
||||
@ -48,7 +49,15 @@ class BgpRib:
|
||||
for pfx in pfxtbl.keys():
|
||||
if debug:
|
||||
self.log("trying pfx %s" % pfx)
|
||||
if pfx != want["p"]:
|
||||
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 debug:
|
||||
self.log("want pfx=" + want["p"] + ", not " + pfx)
|
||||
continue
|
||||
@ -75,53 +84,67 @@ 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, debug=0):
|
||||
def RequireVpnRoutes(self, target, title, wantroutes, retry=0, wait=1, debug=0):
|
||||
import json
|
||||
|
||||
logstr = "RequireVpnRoutes " + str(wantroutes)
|
||||
# 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
|
||||
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("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)
|
||||
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
|
||||
luResult(target, True, title, logstr)
|
||||
break
|
||||
|
||||
def RequireUnicastRoutes(self, target, afi, vrf, title, wantroutes, debug=0):
|
||||
def RequireUnicastRoutes(
|
||||
self, target, afi, vrf, title, wantroutes, retry=0, wait=1, debug=0
|
||||
):
|
||||
logstr = "RequireUnicastRoutes %s" % str(wantroutes)
|
||||
vrfstr = ""
|
||||
if vrf != "":
|
||||
@ -130,48 +153,62 @@ class BgpRib:
|
||||
if (afi != "ipv4") and (afi != "ipv6"):
|
||||
self.log("ERROR invalid afi")
|
||||
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
break
|
||||
|
||||
|
||||
BgpRib = BgpRib()
|
||||
|
||||
|
||||
def bgpribRequireVpnRoutes(target, title, wantroutes, debug=0):
|
||||
BgpRib.RequireVpnRoutes(target, title, wantroutes, debug)
|
||||
def bgpribRequireVpnRoutes(target, title, wantroutes, retry=0, wait=1, debug=0):
|
||||
BgpRib.RequireVpnRoutes(target, title, wantroutes, retry, wait, debug)
|
||||
|
||||
|
||||
def bgpribRequireUnicastRoutes(target, afi, vrf, title, wantroutes, debug=0):
|
||||
BgpRib.RequireUnicastRoutes(target, afi, vrf, 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)
|
||||
|
@ -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, r1-eth2 (vrf neno), weight 1, XX:XX:XX
|
||||
B>* 10.0.30.0/24 [20/0] is directly connected, neno (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
|
||||
|
@ -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, r2-eth2 (vrf ray), weight 1, XX:XX:XX
|
||||
B>* 10.0.40.0/24 [20/0] is directly connected, ray (vrf ray), weight 1, XX:XX:XX
|
||||
|
@ -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, r2-eth0 (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.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, r2-eth1 (vrf default) inactive, 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.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
|
||||
|
@ -387,10 +387,14 @@ void connected_down(struct interface *ifp, struct connected *ifc)
|
||||
.ifindex = ifp->ifindex,
|
||||
.vrf_id = ifp->vrf->vrf_id,
|
||||
};
|
||||
struct zebra_vrf *zvrf;
|
||||
uint32_t count = 0;
|
||||
struct zebra_vrf *zvrf, *zvrf_iter;
|
||||
uint32_t count_ipv4 = 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) {
|
||||
@ -456,12 +460,14 @@ void connected_down(struct interface *ifp, struct connected *ifc)
|
||||
prefix_copy(&cp, CONNECTED_PREFIX(c));
|
||||
apply_mask(&cp);
|
||||
|
||||
if (prefix_same(&p, &cp) &&
|
||||
!CHECK_FLAG(c->conf, ZEBRA_IFC_DOWN))
|
||||
count++;
|
||||
if (CHECK_FLAG(c->conf, ZEBRA_IFC_DOWN))
|
||||
continue;
|
||||
|
||||
if (count >= 1)
|
||||
if (prefix_same(&p, &cp))
|
||||
return;
|
||||
|
||||
if (cp.family == AF_INET)
|
||||
count_ipv4++;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -474,6 +480,60 @@ 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)
|
||||
|
Loading…
Reference in New Issue
Block a user