mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-03 07:55:05 +00:00
bgpd: Implement ACCEPT_OWN extended community
TL;DR: rfc7611. Signed-off-by: Donatas Abraitis <donatas@opensourcerouting.org>
This commit is contained in:
parent
984eb32b58
commit
46dbf9d0c0
@ -23,6 +23,7 @@
|
||||
|
||||
#include "lib/json.h"
|
||||
#include "bgpd/bgp_route.h"
|
||||
#include "bgpd/bgp_attr.h"
|
||||
|
||||
/* Communities attribute. */
|
||||
struct community {
|
||||
@ -109,4 +110,30 @@ extern void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
|
||||
struct community *community);
|
||||
extern void bgp_aggr_community_remove(void *arg);
|
||||
|
||||
/* This implies that when propagating routes into a VRF, the ACCEPT_OWN
|
||||
* community SHOULD NOT be propagated.
|
||||
*/
|
||||
static inline void community_strip_accept_own(struct attr *attr)
|
||||
{
|
||||
struct community *old_com = bgp_attr_get_community(attr);
|
||||
struct community *new_com = NULL;
|
||||
uint32_t val = COMMUNITY_ACCEPT_OWN;
|
||||
|
||||
if (old_com && community_include(old_com, val)) {
|
||||
new_com = community_dup(old_com);
|
||||
val = htonl(val);
|
||||
community_del_val(new_com, &val);
|
||||
|
||||
if (!old_com->refcnt)
|
||||
community_free(&old_com);
|
||||
|
||||
if (!new_com->size) {
|
||||
community_free(&new_com);
|
||||
bgp_attr_set_community(attr, NULL);
|
||||
} else {
|
||||
bgp_attr_set_community(attr, new_com);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _QUAGGA_BGP_COMMUNITY_H */
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "bgpd/bgp_packet.h"
|
||||
#include "bgpd/bgp_vty.h"
|
||||
#include "bgpd/bgp_vpn.h"
|
||||
#include "bgpd/bgp_community.h"
|
||||
#include "bgpd/bgp_ecommunity.h"
|
||||
#include "bgpd/bgp_zebra.h"
|
||||
#include "bgpd/bgp_nexthop.h"
|
||||
@ -996,6 +997,9 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
|
||||
if (nexthop_self_flag)
|
||||
bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF);
|
||||
|
||||
if (CHECK_FLAG(source_bpi->flags, BGP_PATH_ACCEPT_OWN))
|
||||
bgp_path_info_set_flag(bn, bpi, BGP_PATH_ACCEPT_OWN);
|
||||
|
||||
if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi,
|
||||
source_bpi, bpi, bgp_orig, p,
|
||||
debug))
|
||||
@ -1036,6 +1040,9 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
|
||||
if (nexthop_self_flag)
|
||||
bgp_path_info_set_flag(bn, new, BGP_PATH_ANNC_NH_SELF);
|
||||
|
||||
if (CHECK_FLAG(source_bpi->flags, BGP_PATH_ACCEPT_OWN))
|
||||
bgp_path_info_set_flag(bn, new, BGP_PATH_ACCEPT_OWN);
|
||||
|
||||
bgp_path_info_extra_get(new);
|
||||
|
||||
/*
|
||||
@ -1217,6 +1224,8 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
|
||||
XFREE(MTYPE_ECOMMUNITY_STR, s);
|
||||
}
|
||||
|
||||
community_strip_accept_own(&static_attr);
|
||||
|
||||
/* Nexthop */
|
||||
/* if policy nexthop not set, use 0 */
|
||||
if (CHECK_FLAG(from_bgp->vpn_policy[afi].flags,
|
||||
@ -1362,7 +1371,7 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
|
||||
* because of loop checking.
|
||||
*/
|
||||
if (new_info)
|
||||
vpn_leak_to_vrf_update(from_bgp, new_info);
|
||||
vpn_leak_to_vrf_update(from_bgp, new_info, NULL);
|
||||
}
|
||||
|
||||
void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */
|
||||
@ -1518,10 +1527,40 @@ void vpn_leak_from_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||
struct bgp *from_bgp, /* from */
|
||||
struct bgp_path_info *path_vpn) /* route */
|
||||
static struct bgp *bgp_lookup_by_rd(struct bgp_path_info *bpi,
|
||||
struct prefix_rd *rd, afi_t afi)
|
||||
{
|
||||
struct listnode *node, *nnode;
|
||||
struct bgp *bgp;
|
||||
|
||||
if (!rd)
|
||||
return NULL;
|
||||
|
||||
/* If ACCEPT_OWN is not enabled for this path - return. */
|
||||
if (!CHECK_FLAG(bpi->flags, BGP_PATH_ACCEPT_OWN))
|
||||
return NULL;
|
||||
|
||||
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
|
||||
if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
|
||||
continue;
|
||||
|
||||
if (!CHECK_FLAG(bgp->vpn_policy[afi].flags,
|
||||
BGP_VPN_POLICY_TOVPN_RD_SET))
|
||||
continue;
|
||||
|
||||
/* Check if we have source VRF by RD value */
|
||||
if (memcmp(&bgp->vpn_policy[afi].tovpn_rd.val, rd->val,
|
||||
ECOMMUNITY_SIZE) == 0)
|
||||
return bgp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||
struct bgp *from_bgp, /* from */
|
||||
struct bgp_path_info *path_vpn,
|
||||
struct prefix_rd *prd)
|
||||
{
|
||||
const struct prefix *p = bgp_dest_get_prefix(path_vpn->net);
|
||||
afi_t afi = family2afi(p->family);
|
||||
@ -1558,9 +1597,22 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* A route MUST NOT ever be accepted back into its source VRF, even if
|
||||
* it carries one or more RTs that match that VRF.
|
||||
*/
|
||||
if (prd && memcmp(&prd->val, &to_bgp->vpn_policy[afi].tovpn_rd.val,
|
||||
ECOMMUNITY_SIZE) == 0) {
|
||||
if (debug)
|
||||
zlog_debug(
|
||||
"%s: skipping import, match RD (%pRD) of src VRF (%s) and the prefix (%pFX)",
|
||||
__func__, prd, to_bgp->name_pretty, p);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
zlog_debug("%s: updating %pFX to vrf %s", __func__, p,
|
||||
to_bgp->name_pretty);
|
||||
zlog_debug("%s: updating RD %pRD, %pFX to vrf %s", __func__,
|
||||
prd, p, to_bgp->name_pretty);
|
||||
|
||||
/* shallow copy */
|
||||
static_attr = *path_vpn->attr;
|
||||
@ -1585,6 +1637,8 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||
ecommunity_free(&old_ecom);
|
||||
}
|
||||
|
||||
community_strip_accept_own(&static_attr);
|
||||
|
||||
/*
|
||||
* Nexthop: stash and clear
|
||||
*
|
||||
@ -1711,9 +1765,16 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||
/*
|
||||
* 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;
|
||||
|
||||
@ -1723,8 +1784,9 @@ vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */
|
||||
struct bgp_path_info *path_vpn) /* route */
|
||||
bool vpn_leak_to_vrf_update(struct bgp *from_bgp,
|
||||
struct bgp_path_info *path_vpn,
|
||||
struct prefix_rd *prd)
|
||||
{
|
||||
struct listnode *mnode, *mnnode;
|
||||
struct bgp *bgp;
|
||||
@ -1741,7 +1803,7 @@ bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */
|
||||
if (!path_vpn->extra
|
||||
|| path_vpn->extra->bgp_orig != bgp) { /* no loop */
|
||||
leak_success |= vpn_leak_to_vrf_update_onevrf(
|
||||
bgp, from_bgp, path_vpn);
|
||||
bgp, from_bgp, path_vpn, prd);
|
||||
}
|
||||
}
|
||||
return leak_success;
|
||||
@ -1897,7 +1959,7 @@ void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *vpn_from,
|
||||
continue;
|
||||
|
||||
vpn_leak_to_vrf_update_onevrf(to_bgp, vpn_from,
|
||||
bpi);
|
||||
bpi, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,8 @@ extern void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
|
||||
afi_t afi);
|
||||
|
||||
extern bool vpn_leak_to_vrf_update(struct bgp *from_bgp,
|
||||
struct bgp_path_info *path_vpn);
|
||||
struct bgp_path_info *path_vpn,
|
||||
struct prefix_rd *prd);
|
||||
|
||||
extern void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp,
|
||||
struct bgp_path_info *path_vpn);
|
||||
|
@ -139,7 +139,8 @@ static int bgp_isvalid_nexthop_for_mpls(struct bgp_nexthop_cache *bnc,
|
||||
*/
|
||||
return (bgp_zebra_num_connects() == 0 ||
|
||||
(bnc && (bnc->nexthop_num > 0 &&
|
||||
(CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) ||
|
||||
(CHECK_FLAG(path->flags, BGP_PATH_ACCEPT_OWN) ||
|
||||
CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) ||
|
||||
bnc->bgp->srv6_enabled ||
|
||||
bgp_isvalid_nexthop_for_ebgp(bnc, path) ||
|
||||
bgp_isvalid_nexthop_for_mplsovergre(bnc, path)))));
|
||||
|
147
bgpd/bgp_route.c
147
bgpd/bgp_route.c
@ -103,10 +103,6 @@ DEFINE_HOOK(bgp_rpki_prefix_status,
|
||||
const struct prefix *prefix),
|
||||
(peer, attr, prefix));
|
||||
|
||||
/* Render dest to prefix_rd based on safi */
|
||||
static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
|
||||
safi_t safi);
|
||||
|
||||
/* Extern from bgp_dump.c */
|
||||
extern const char *bgp_origin_str[];
|
||||
extern const char *bgp_origin_long_str[];
|
||||
@ -881,6 +877,49 @@ static int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If a BGP speaker supports ACCEPT_OWN and is configured for the
|
||||
* extensions defined in this document, the following step is inserted
|
||||
* after the LOCAL_PREF comparison step in the BGP decision process:
|
||||
* When comparing a pair of routes for a BGP destination, the
|
||||
* route with the ACCEPT_OWN community attached is preferred over
|
||||
* the route that does not have the community.
|
||||
* This extra step MUST only be invoked during the best path selection
|
||||
* process of VPN-IP routes.
|
||||
*/
|
||||
if (safi == SAFI_MPLS_VPN &&
|
||||
(CHECK_FLAG(new->peer->af_flags[afi][safi], PEER_FLAG_ACCEPT_OWN) ||
|
||||
CHECK_FLAG(exist->peer->af_flags[afi][safi],
|
||||
PEER_FLAG_ACCEPT_OWN))) {
|
||||
bool new_accept_own = false;
|
||||
bool exist_accept_own = false;
|
||||
uint32_t accept_own = COMMUNITY_ACCEPT_OWN;
|
||||
|
||||
if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))
|
||||
new_accept_own = community_include(
|
||||
bgp_attr_get_community(newattr), accept_own);
|
||||
if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))
|
||||
exist_accept_own = community_include(
|
||||
bgp_attr_get_community(existattr), accept_own);
|
||||
|
||||
if (new_accept_own && !exist_accept_own) {
|
||||
*reason = bgp_path_selection_accept_own;
|
||||
if (debug)
|
||||
zlog_debug(
|
||||
"%s: %s wins over %s due to accept-own",
|
||||
pfx_buf, new_buf, exist_buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!new_accept_own && exist_accept_own) {
|
||||
*reason = bgp_path_selection_accept_own;
|
||||
if (debug)
|
||||
zlog_debug(
|
||||
"%s: %s loses to %s due to accept-own",
|
||||
pfx_buf, new_buf, exist_buf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 3. Local route check. We prefer:
|
||||
* - BGP_ROUTE_STATIC
|
||||
* - BGP_ROUTE_AGGREGATE
|
||||
@ -3883,6 +3922,60 @@ static void bgp_attr_add_no_export_community(struct attr *attr)
|
||||
bgp_attr_set_community(attr, new);
|
||||
}
|
||||
|
||||
static bool bgp_accept_own(struct peer *peer, afi_t afi, safi_t safi,
|
||||
struct attr *attr, const struct prefix *prefix,
|
||||
int *sub_type)
|
||||
{
|
||||
struct listnode *node, *nnode;
|
||||
struct bgp *bgp;
|
||||
bool accept_own_found = false;
|
||||
|
||||
if (safi != SAFI_MPLS_VPN)
|
||||
return false;
|
||||
|
||||
/* Processing of the ACCEPT_OWN community is enabled by configuration */
|
||||
if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ACCEPT_OWN))
|
||||
return false;
|
||||
|
||||
/* The route in question carries the ACCEPT_OWN community */
|
||||
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) {
|
||||
struct community *comm = bgp_attr_get_community(attr);
|
||||
|
||||
if (community_include(comm, COMMUNITY_ACCEPT_OWN))
|
||||
accept_own_found = true;
|
||||
}
|
||||
|
||||
/* The route in question is targeted to one or more destination VRFs
|
||||
* on the router (as determined by inspecting the Route Target(s)).
|
||||
*/
|
||||
for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
|
||||
if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
|
||||
continue;
|
||||
|
||||
if (accept_own_found &&
|
||||
ecommunity_include(
|
||||
bgp->vpn_policy[afi]
|
||||
.rtlist[BGP_VPN_POLICY_DIR_TOVPN],
|
||||
bgp_attr_get_ecommunity(attr))) {
|
||||
if (bgp_debug_update(peer, prefix, NULL, 1))
|
||||
zlog_debug(
|
||||
"%pBP prefix %pFX has ORIGINATOR_ID, but it's accepted due to ACCEPT_OWN",
|
||||
peer, prefix);
|
||||
|
||||
/* Treat this route as imported, because it's leaked
|
||||
* already from another VRF, and we got an updated
|
||||
* version from route-reflector with ACCEPT_OWN
|
||||
* community.
|
||||
*/
|
||||
*sub_type = BGP_ROUTE_IMPORTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||
struct attr *attr, afi_t afi, safi_t safi, int type,
|
||||
int sub_type, struct prefix_rd *prd, mpls_label_t *label,
|
||||
@ -3996,12 +4089,20 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||
}
|
||||
}
|
||||
|
||||
/* Route reflector originator ID check. */
|
||||
/* Route reflector originator ID check. If ACCEPT_OWN mechanism is
|
||||
* enabled, then take care of that too.
|
||||
*/
|
||||
bool accept_own = false;
|
||||
|
||||
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)
|
||||
&& IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) {
|
||||
peer->stat_pfx_originator_loop++;
|
||||
reason = "originator is us;";
|
||||
goto filtered;
|
||||
accept_own =
|
||||
bgp_accept_own(peer, afi, safi, attr, p, &sub_type);
|
||||
if (!accept_own) {
|
||||
peer->stat_pfx_originator_loop++;
|
||||
reason = "originator is us;";
|
||||
goto filtered;
|
||||
}
|
||||
}
|
||||
|
||||
/* Route reflector cluster ID check. */
|
||||
@ -4499,8 +4600,13 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||
bgp_path_info_unset_flag(dest, pi,
|
||||
BGP_PATH_VALID);
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
if (accept_own)
|
||||
bgp_path_info_set_flag(dest, pi,
|
||||
BGP_PATH_ACCEPT_OWN);
|
||||
|
||||
bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_BGP_VNC
|
||||
if (safi == SAFI_MPLS_VPN) {
|
||||
@ -4550,8 +4656,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||
}
|
||||
if ((SAFI_MPLS_VPN == safi)
|
||||
&& (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
|
||||
|
||||
leak_success = vpn_leak_to_vrf_update(bgp, pi);
|
||||
leak_success = vpn_leak_to_vrf_update(bgp, pi, prd);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_BGP_VNC
|
||||
@ -4659,8 +4764,12 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||
}
|
||||
bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID);
|
||||
}
|
||||
} else
|
||||
} else {
|
||||
if (accept_own)
|
||||
bgp_path_info_set_flag(dest, new, BGP_PATH_ACCEPT_OWN);
|
||||
|
||||
bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
|
||||
}
|
||||
|
||||
/* Addpath ID */
|
||||
new->addpath_rx_id = addpath_id;
|
||||
@ -4706,7 +4815,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
|
||||
}
|
||||
if ((SAFI_MPLS_VPN == safi)
|
||||
&& (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
|
||||
leak_success = vpn_leak_to_vrf_update(bgp, new);
|
||||
leak_success = vpn_leak_to_vrf_update(bgp, new, prd);
|
||||
}
|
||||
#ifdef ENABLE_BGP_VNC
|
||||
if (SAFI_MPLS_VPN == safi) {
|
||||
@ -6427,7 +6536,8 @@ static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p,
|
||||
|
||||
if (SAFI_MPLS_VPN == safi
|
||||
&& bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
|
||||
vpn_leak_to_vrf_update(bgp, pi);
|
||||
vpn_leak_to_vrf_update(bgp, pi,
|
||||
&bgp_static->prd);
|
||||
}
|
||||
#ifdef ENABLE_BGP_VNC
|
||||
rfapiProcessUpdate(pi->peer, NULL, p, &bgp_static->prd,
|
||||
@ -6467,7 +6577,7 @@ static void bgp_static_update_safi(struct bgp *bgp, const struct prefix *p,
|
||||
|
||||
if (SAFI_MPLS_VPN == safi
|
||||
&& bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
|
||||
vpn_leak_to_vrf_update(bgp, new);
|
||||
vpn_leak_to_vrf_update(bgp, new, &bgp_static->prd);
|
||||
}
|
||||
#ifdef ENABLE_BGP_VNC
|
||||
rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd, new->attr, afi,
|
||||
@ -8826,6 +8936,8 @@ const char *bgp_path_selection_reason2str(enum bgp_path_selection_reason reason)
|
||||
return "Weight";
|
||||
case bgp_path_selection_local_pref:
|
||||
return "Local Pref";
|
||||
case bgp_path_selection_accept_own:
|
||||
return "Accept Own";
|
||||
case bgp_path_selection_local_route:
|
||||
return "Local Route";
|
||||
case bgp_path_selection_confed_as_path:
|
||||
@ -11902,8 +12014,8 @@ static void bgp_show_path_info(const struct prefix_rd *pfx_rd,
|
||||
/*
|
||||
* Return rd based on safi
|
||||
*/
|
||||
static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
|
||||
safi_t safi)
|
||||
const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
|
||||
safi_t safi)
|
||||
{
|
||||
switch (safi) {
|
||||
case SAFI_MPLS_VPN:
|
||||
@ -11912,7 +12024,6 @@ static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
|
||||
return (struct prefix_rd *)(bgp_dest_get_prefix(dest));
|
||||
default:
|
||||
return NULL;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -289,7 +289,7 @@ struct bgp_path_info {
|
||||
int lock;
|
||||
|
||||
/* BGP information status. */
|
||||
uint16_t flags;
|
||||
uint32_t flags;
|
||||
#define BGP_PATH_IGP_CHANGED (1 << 0)
|
||||
#define BGP_PATH_DAMPED (1 << 1)
|
||||
#define BGP_PATH_HISTORY (1 << 2)
|
||||
@ -306,6 +306,7 @@ struct bgp_path_info {
|
||||
#define BGP_PATH_RIB_ATTR_CHG (1 << 13)
|
||||
#define BGP_PATH_ANNC_NH_SELF (1 << 14)
|
||||
#define BGP_PATH_LINK_BW_CHG (1 << 15)
|
||||
#define BGP_PATH_ACCEPT_OWN (1 << 16)
|
||||
|
||||
/* BGP route type. This can be static, RIP, OSPF, BGP etc. */
|
||||
uint8_t type;
|
||||
@ -852,4 +853,6 @@ extern void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr);
|
||||
const char *
|
||||
bgp_path_selection_reason2str(enum bgp_path_selection_reason reason);
|
||||
extern bool bgp_addpath_encode_rx(struct peer *peer, afi_t afi, safi_t safi);
|
||||
extern const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
|
||||
safi_t safi);
|
||||
#endif /* _QUAGGA_BGP_ROUTE_H */
|
||||
|
@ -63,6 +63,7 @@ enum bgp_path_selection_reason {
|
||||
bgp_path_selection_evpn_lower_ip,
|
||||
bgp_path_selection_weight,
|
||||
bgp_path_selection_local_pref,
|
||||
bgp_path_selection_accept_own,
|
||||
bgp_path_selection_local_route,
|
||||
bgp_path_selection_confed_as_path,
|
||||
bgp_path_selection_as_path,
|
||||
|
@ -8327,6 +8327,32 @@ ALIAS_HIDDEN(
|
||||
"Only give warning message when limit is exceeded\n"
|
||||
"Force checking all received routes not only accepted\n")
|
||||
|
||||
/* "neighbor accept-own" */
|
||||
DEFPY (neighbor_accept_own,
|
||||
neighbor_accept_own_cmd,
|
||||
"[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor accept-own",
|
||||
NO_STR
|
||||
NEIGHBOR_STR
|
||||
NEIGHBOR_ADDR_STR2
|
||||
"Enable handling of self-originated VPN routes containing ACCEPT_OWN community\n")
|
||||
{
|
||||
struct peer *peer;
|
||||
afi_t afi = bgp_node_afi(vty);
|
||||
safi_t safi = bgp_node_safi(vty);
|
||||
int ret;
|
||||
|
||||
peer = peer_and_group_lookup_vty(vty, neighbor);
|
||||
if (!peer)
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
|
||||
if (no)
|
||||
ret = peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ACCEPT_OWN);
|
||||
else
|
||||
ret = peer_af_flag_set(peer, afi, safi, PEER_FLAG_ACCEPT_OWN);
|
||||
|
||||
return bgp_vty_return(vty, ret);
|
||||
}
|
||||
|
||||
/* "neighbor soo" */
|
||||
DEFPY (neighbor_soo,
|
||||
neighbor_soo_cmd,
|
||||
@ -17362,6 +17388,10 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
|
||||
}
|
||||
}
|
||||
|
||||
/* accept-own */
|
||||
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ACCEPT_OWN))
|
||||
vty_out(vty, " neighbor %s accept-own\n", addr);
|
||||
|
||||
/* soo */
|
||||
if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOO)) {
|
||||
char *soo_str = ecommunity_ecom2str(
|
||||
@ -19571,6 +19601,10 @@ void bgp_vty_init(void)
|
||||
install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd);
|
||||
install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd);
|
||||
|
||||
/* neighbor accept-own */
|
||||
install_element(BGP_VPNV4_NODE, &neighbor_accept_own_cmd);
|
||||
install_element(BGP_VPNV6_NODE, &neighbor_accept_own_cmd);
|
||||
|
||||
/* "neighbor soo" */
|
||||
install_element(BGP_IPV4_NODE, &neighbor_soo_cmd);
|
||||
install_element(BGP_IPV4_NODE, &no_neighbor_soo_cmd);
|
||||
|
@ -4313,6 +4313,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = {
|
||||
{PEER_FLAG_WEIGHT, 0, peer_change_reset_in},
|
||||
{PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset},
|
||||
{PEER_FLAG_SOO, 0, peer_change_reset},
|
||||
{PEER_FLAG_ACCEPT_OWN, 0, peer_change_reset},
|
||||
{0, 0, 0}};
|
||||
|
||||
/* Proper action set. */
|
||||
|
@ -1462,6 +1462,7 @@ struct peer {
|
||||
#define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 29)
|
||||
#define PEER_FLAG_SOO (1ULL << 30)
|
||||
#define PEER_FLAG_ORR_GROUP (1ULL << 31) /* Optimal-Route-Reflection */
|
||||
#define PEER_FLAG_ACCEPT_OWN (1ULL << 32)
|
||||
|
||||
/* BGP Optimal Route Reflection Group name */
|
||||
char *orr_group_name[AFI_MAX][SAFI_MAX];
|
||||
|
@ -1670,6 +1670,23 @@ Configuring Peers
|
||||
turning on this command will allow BGP to install v4 routes with
|
||||
v6 nexthops if you do not have v4 configured on interfaces.
|
||||
|
||||
.. clicmd:: neighbor <A.B.C.D|X:X::X:X|WORD> accept-own
|
||||
|
||||
Enable handling of self-originated VPN routes containing ``accept-own`` community.
|
||||
|
||||
This feature allows you to handle self-originated VPN routes, which a BGP speaker
|
||||
receives from a route-reflector. A 'self-originated' route is one that was
|
||||
originally advertised by the speaker itself. As per :rfc:`4271`, a BGP speaker rejects
|
||||
advertisements that originated the speaker itself. However, the BGP ACCEPT_OWN
|
||||
mechanism enables a router to accept the prefixes it has advertised, when reflected
|
||||
from a route-reflector that modifies certain attributes of the prefix.
|
||||
|
||||
A special community called ``accept-own`` is attached to the prefix by the
|
||||
route-reflector, which is a signal to the receiving router to bypass the ORIGINATOR_ID
|
||||
and NEXTHOP/MP_REACH_NLRI check.
|
||||
|
||||
Default: disabled.
|
||||
|
||||
.. clicmd:: bgp fast-external-failover
|
||||
|
||||
This command causes bgp to take down ebgp peers immediately
|
||||
|
0
tests/topotests/bgp_accept_own/__init__.py
Normal file
0
tests/topotests/bgp_accept_own/__init__.py
Normal file
12
tests/topotests/bgp_accept_own/ce1/bgpd.conf
Normal file
12
tests/topotests/bgp_accept_own/ce1/bgpd.conf
Normal file
@ -0,0 +1,12 @@
|
||||
!
|
||||
debug bgp updates
|
||||
!
|
||||
router bgp 65010
|
||||
no bgp ebgp-requires-policy
|
||||
neighbor 192.168.1.2 remote-as external
|
||||
neighbor 192.168.1.2 timers 1 3
|
||||
neighbor 192.168.1.2 timers connect 1
|
||||
address-family ipv4 unicast
|
||||
redistribute connected
|
||||
exit-address-family
|
||||
!
|
9
tests/topotests/bgp_accept_own/ce1/zebra.conf
Normal file
9
tests/topotests/bgp_accept_own/ce1/zebra.conf
Normal file
@ -0,0 +1,9 @@
|
||||
!
|
||||
interface lo
|
||||
ip address 172.16.255.1/32
|
||||
!
|
||||
interface ce1-eth0
|
||||
ip address 192.168.1.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
12
tests/topotests/bgp_accept_own/ce2/bgpd.conf
Normal file
12
tests/topotests/bgp_accept_own/ce2/bgpd.conf
Normal file
@ -0,0 +1,12 @@
|
||||
!
|
||||
debug bgp updates
|
||||
!
|
||||
router bgp 65020
|
||||
no bgp ebgp-requires-policy
|
||||
neighbor 192.168.2.2 remote-as external
|
||||
neighbor 192.168.2.2 timers 1 3
|
||||
neighbor 192.168.2.2 timers connect 1
|
||||
address-family ipv4 unicast
|
||||
redistribute connected
|
||||
exit-address-family
|
||||
!
|
9
tests/topotests/bgp_accept_own/ce2/zebra.conf
Normal file
9
tests/topotests/bgp_accept_own/ce2/zebra.conf
Normal file
@ -0,0 +1,9 @@
|
||||
!
|
||||
interface lo
|
||||
ip address 172.16.255.2/32
|
||||
!
|
||||
interface ce2-eth0
|
||||
ip address 192.168.2.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
49
tests/topotests/bgp_accept_own/pe1/bgpd.conf
Normal file
49
tests/topotests/bgp_accept_own/pe1/bgpd.conf
Normal file
@ -0,0 +1,49 @@
|
||||
!
|
||||
debug bgp updates
|
||||
debug bgp vpn leak-from-vrf
|
||||
debug bgp vpn leak-to-vrf
|
||||
debug bgp nht
|
||||
!
|
||||
router bgp 65001
|
||||
bgp router-id 10.10.10.10
|
||||
no bgp ebgp-requires-policy
|
||||
no bgp default ipv4-unicast
|
||||
neighbor 10.10.10.101 remote-as internal
|
||||
neighbor 10.10.10.101 update-source 10.10.10.10
|
||||
neighbor 10.10.10.101 timers 1 3
|
||||
neighbor 10.10.10.101 timers connect 1
|
||||
address-family ipv4 vpn
|
||||
neighbor 10.10.10.101 activate
|
||||
neighbor 10.10.10.101 attribute-unchanged
|
||||
exit-address-family
|
||||
!
|
||||
router bgp 65001 vrf Customer
|
||||
bgp router-id 192.168.1.2
|
||||
no bgp ebgp-requires-policy
|
||||
neighbor 192.168.1.1 remote-as external
|
||||
neighbor 192.168.1.1 timers 1 3
|
||||
neighbor 192.168.1.1 timers connect 1
|
||||
address-family ipv4 unicast
|
||||
label vpn export 10
|
||||
rd vpn export 192.168.1.2:2
|
||||
rt vpn import 192.168.1.2:2
|
||||
rt vpn export 192.168.1.2:2
|
||||
export vpn
|
||||
import vpn
|
||||
exit-address-family
|
||||
!
|
||||
router bgp 65001 vrf Service
|
||||
bgp router-id 192.168.2.2
|
||||
no bgp ebgp-requires-policy
|
||||
neighbor 192.168.2.1 remote-as external
|
||||
neighbor 192.168.2.1 timers 1 3
|
||||
neighbor 192.168.2.1 timers connect 1
|
||||
address-family ipv4 unicast
|
||||
label vpn export 20
|
||||
rd vpn export 192.168.2.2:2
|
||||
rt vpn import 192.168.2.2:2
|
||||
rt vpn export 192.168.2.2:2
|
||||
export vpn
|
||||
import vpn
|
||||
exit-address-family
|
||||
!
|
12
tests/topotests/bgp_accept_own/pe1/ldpd.conf
Normal file
12
tests/topotests/bgp_accept_own/pe1/ldpd.conf
Normal file
@ -0,0 +1,12 @@
|
||||
mpls ldp
|
||||
router-id 10.10.10.10
|
||||
!
|
||||
address-family ipv4
|
||||
discovery transport-address 10.10.10.10
|
||||
!
|
||||
interface pe1-eth2
|
||||
exit
|
||||
!
|
||||
exit-address-family
|
||||
!
|
||||
exit
|
7
tests/topotests/bgp_accept_own/pe1/ospfd.conf
Normal file
7
tests/topotests/bgp_accept_own/pe1/ospfd.conf
Normal file
@ -0,0 +1,7 @@
|
||||
interface pe1-eth2
|
||||
ip ospf dead-interval 4
|
||||
ip ospf hello-interval 1
|
||||
!
|
||||
router ospf
|
||||
router-id 10.10.10.10
|
||||
network 0.0.0.0/0 area 0
|
15
tests/topotests/bgp_accept_own/pe1/zebra.conf
Normal file
15
tests/topotests/bgp_accept_own/pe1/zebra.conf
Normal file
@ -0,0 +1,15 @@
|
||||
!
|
||||
interface lo
|
||||
ip address 10.10.10.10/32
|
||||
!
|
||||
interface pe1-eth0 vrf Customer
|
||||
ip address 192.168.1.2/24
|
||||
!
|
||||
interface pe1-eth1 vrf Service
|
||||
ip address 192.168.2.2/24
|
||||
!
|
||||
interface pe1-eth2
|
||||
ip address 10.0.1.1/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
25
tests/topotests/bgp_accept_own/rr1/bgpd.conf
Normal file
25
tests/topotests/bgp_accept_own/rr1/bgpd.conf
Normal file
@ -0,0 +1,25 @@
|
||||
!
|
||||
debug bgp updates
|
||||
!
|
||||
router bgp 65001
|
||||
bgp router-id 10.10.10.101
|
||||
bgp route-reflector allow-outbound-policy
|
||||
no bgp ebgp-requires-policy
|
||||
no bgp default ipv4-unicast
|
||||
neighbor 10.10.10.10 remote-as internal
|
||||
neighbor 10.10.10.10 update-source 10.10.10.101
|
||||
neighbor 10.10.10.10 timers 1 3
|
||||
neighbor 10.10.10.10 timers connect 1
|
||||
address-family ipv4 vpn
|
||||
neighbor 10.10.10.10 activate
|
||||
neighbor 10.10.10.10 route-reflector-client
|
||||
neighbor 10.10.10.10 route-map pe1 out
|
||||
exit-address-family
|
||||
!
|
||||
route-map pe1 permit 10
|
||||
set extcommunity rt 192.168.1.2:2 192.168.2.2:2
|
||||
set community 65001:111 accept-own additive
|
||||
set ip next-hop unchanged
|
||||
route-map pe1 permit 20
|
||||
exit
|
||||
!
|
12
tests/topotests/bgp_accept_own/rr1/ldpd.conf
Normal file
12
tests/topotests/bgp_accept_own/rr1/ldpd.conf
Normal file
@ -0,0 +1,12 @@
|
||||
mpls ldp
|
||||
router-id 10.10.10.101
|
||||
!
|
||||
address-family ipv4
|
||||
discovery transport-address 10.10.10.101
|
||||
!
|
||||
interface rr1-eth0
|
||||
exit
|
||||
!
|
||||
exit-address-family
|
||||
!
|
||||
exit
|
7
tests/topotests/bgp_accept_own/rr1/ospfd.conf
Normal file
7
tests/topotests/bgp_accept_own/rr1/ospfd.conf
Normal file
@ -0,0 +1,7 @@
|
||||
interface rr1-eth0
|
||||
ip ospf dead-interval 4
|
||||
ip ospf hello-interval 1
|
||||
!
|
||||
router ospf
|
||||
router-id 10.10.10.101
|
||||
network 0.0.0.0/0 area 0
|
9
tests/topotests/bgp_accept_own/rr1/zebra.conf
Normal file
9
tests/topotests/bgp_accept_own/rr1/zebra.conf
Normal file
@ -0,0 +1,9 @@
|
||||
!
|
||||
interface lo
|
||||
ip address 10.10.10.101/32
|
||||
!
|
||||
interface rr1-eth0
|
||||
ip address 10.0.1.2/24
|
||||
!
|
||||
ip forwarding
|
||||
!
|
198
tests/topotests/bgp_accept_own/test_bgp_accept_own.py
Normal file
198
tests/topotests/bgp_accept_own/test_bgp_accept_own.py
Normal file
@ -0,0 +1,198 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# Copyright (c) 2022 by
|
||||
# Donatas Abraitis <donatas@opensourcerouting.org>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software
|
||||
# for any purpose with or without fee is hereby granted, provided
|
||||
# that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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.
|
||||
#
|
||||
|
||||
"""
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import pytest
|
||||
import functools
|
||||
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.append(os.path.join(CWD, "../"))
|
||||
|
||||
# pylint: disable=C0413
|
||||
from lib import topotest
|
||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||
from lib.common_config import step
|
||||
|
||||
pytestmark = [pytest.mark.bgpd]
|
||||
|
||||
|
||||
def build_topo(tgen):
|
||||
tgen.add_router("ce1")
|
||||
tgen.add_router("ce2")
|
||||
tgen.add_router("pe1")
|
||||
tgen.add_router("rr1")
|
||||
|
||||
switch = tgen.add_switch("s1")
|
||||
switch.add_link(tgen.gears["ce1"])
|
||||
switch.add_link(tgen.gears["pe1"])
|
||||
|
||||
switch = tgen.add_switch("s2")
|
||||
switch.add_link(tgen.gears["ce2"])
|
||||
switch.add_link(tgen.gears["pe1"])
|
||||
|
||||
switch = tgen.add_switch("s3")
|
||||
switch.add_link(tgen.gears["pe1"])
|
||||
switch.add_link(tgen.gears["rr1"])
|
||||
|
||||
|
||||
def setup_module(mod):
|
||||
tgen = Topogen(build_topo, mod.__name__)
|
||||
tgen.start_topology()
|
||||
|
||||
pe1 = tgen.gears["pe1"]
|
||||
rr1 = tgen.gears["rr1"]
|
||||
|
||||
pe1.run("ip link add Customer type vrf table 1001")
|
||||
pe1.run("ip link set up dev Customer")
|
||||
pe1.run("ip link set pe1-eth0 master Customer")
|
||||
pe1.run("ip link add Service type vrf table 1002")
|
||||
pe1.run("ip link set up dev Service")
|
||||
pe1.run("ip link set pe1-eth1 master Service")
|
||||
pe1.run("sysctl -w net.mpls.conf.pe1-eth2.input=1")
|
||||
rr1.run("sysctl -w net.mpls.conf.rr1-eth0.input=1")
|
||||
|
||||
router_list = tgen.routers()
|
||||
|
||||
for i, (rname, router) in enumerate(router_list.items(), 1):
|
||||
router.load_config(
|
||||
TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
|
||||
)
|
||||
router.load_config(
|
||||
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
|
||||
)
|
||||
router.load_config(
|
||||
TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
|
||||
)
|
||||
router.load_config(
|
||||
TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
|
||||
)
|
||||
|
||||
tgen.start_router()
|
||||
|
||||
|
||||
def teardown_module(mod):
|
||||
tgen = get_topogen()
|
||||
tgen.stop_topology()
|
||||
|
||||
|
||||
def test_bgp_accept_own():
|
||||
tgen = get_topogen()
|
||||
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
pe1 = tgen.gears["pe1"]
|
||||
ce2 = tgen.gears["ce2"]
|
||||
|
||||
step("Check if routes are not installed in PE1 from RR1 (due to ORIGINATOR_ID)")
|
||||
|
||||
def _bgp_check_received_routes_due_originator_id():
|
||||
output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json"))
|
||||
expected = {"peers": {"10.10.10.101": {"pfxRcd": 0, "pfxSnt": 4}}}
|
||||
return topotest.json_cmp(output, expected)
|
||||
|
||||
test_func = functools.partial(_bgp_check_received_routes_due_originator_id)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
||||
assert result is None, "Failed, received routes from RR1 regardless ORIGINATOR_ID"
|
||||
|
||||
step("Enable ACCEPT_OWN for RR1")
|
||||
|
||||
pe1.vtysh_cmd(
|
||||
"""
|
||||
configure terminal
|
||||
router bgp 65001
|
||||
address-family ipv4 vpn
|
||||
neighbor 10.10.10.101 accept-own
|
||||
"""
|
||||
)
|
||||
|
||||
step("Check if we received routes due to ACCEPT_OWN from RR1")
|
||||
|
||||
def _bgp_check_received_routes_with_modified_rts():
|
||||
output = json.loads(pe1.vtysh_cmd("show bgp ipv4 vpn summary json"))
|
||||
expected = {"peers": {"10.10.10.101": {"pfxRcd": 4, "pfxSnt": 4}}}
|
||||
return topotest.json_cmp(output, expected)
|
||||
|
||||
test_func = functools.partial(_bgp_check_received_routes_with_modified_rts)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
||||
assert (
|
||||
result is None
|
||||
), "Failed, didn't receive routes from RR1 with ACCEPT_OWN enabled"
|
||||
|
||||
step(
|
||||
"Check if 172.16.255.1/32 is imported into vrf Service due to modified RT list at RR1"
|
||||
)
|
||||
|
||||
def _bgp_check_received_routes_with_changed_rts():
|
||||
output = json.loads(
|
||||
pe1.vtysh_cmd("show bgp vrf Service ipv4 unicast 172.16.255.1/32 json")
|
||||
)
|
||||
expected = {
|
||||
"paths": [
|
||||
{
|
||||
"community": {
|
||||
"string": "65001:111"
|
||||
},
|
||||
"extendedCommunity": {
|
||||
"string": "RT:192.168.1.2:2 RT:192.168.2.2:2"
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
return topotest.json_cmp(output, expected)
|
||||
|
||||
test_func = functools.partial(_bgp_check_received_routes_with_changed_rts)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
||||
assert (
|
||||
result is None
|
||||
), "Failed, routes are not imported from RR1 with modified RT list"
|
||||
|
||||
step("Check if 172.16.255.1/32 is announced to CE2")
|
||||
|
||||
def _bgp_check_received_routes_from_pe():
|
||||
output = json.loads(ce2.vtysh_cmd("show ip route 172.16.255.1/32 json"))
|
||||
expected = {
|
||||
"172.16.255.1/32": [
|
||||
{
|
||||
"protocol": "bgp",
|
||||
"installed": True,
|
||||
"nexthops": [{"ip": "192.168.2.2"}],
|
||||
}
|
||||
]
|
||||
}
|
||||
return topotest.json_cmp(output, expected)
|
||||
|
||||
test_func = functools.partial(_bgp_check_received_routes_from_pe)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
|
||||
assert (
|
||||
result is None
|
||||
), "Failed, didn't receive 172.16.255.1/32 from PE1"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = ["-s"] + sys.argv[1:]
|
||||
sys.exit(pytest.main(args))
|
Loading…
Reference in New Issue
Block a user