mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-09 11:24:42 +00:00
Merge pull request #14512 from pguibert6WIND/vpnv4_with_no_rt_export
L3VPN exportation by using 'route-map vpn export' command instead of 'rt vpn export' command
This commit is contained in:
commit
e854c43b30
@ -1042,6 +1042,34 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt,
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ecommunity_has_route_target(struct ecommunity *ecom)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint8_t *pnt;
|
||||||
|
uint8_t type = 0;
|
||||||
|
uint8_t sub_type = 0;
|
||||||
|
|
||||||
|
if (!ecom)
|
||||||
|
return false;
|
||||||
|
for (i = 0; i < ecom->size; i++) {
|
||||||
|
/* Retrieve value field */
|
||||||
|
pnt = ecom->val + (i * ecom->unit_size);
|
||||||
|
|
||||||
|
/* High-order octet is the type */
|
||||||
|
type = *pnt++;
|
||||||
|
|
||||||
|
if (type == ECOMMUNITY_ENCODE_AS ||
|
||||||
|
type == ECOMMUNITY_ENCODE_IP ||
|
||||||
|
type == ECOMMUNITY_ENCODE_AS4) {
|
||||||
|
/* Low-order octet of type. */
|
||||||
|
sub_type = *pnt++;
|
||||||
|
if (sub_type == ECOMMUNITY_ROUTE_TARGET)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Convert extended community attribute to string.
|
/* Convert extended community attribute to string.
|
||||||
* Due to historical reason of industry standard implementation, there
|
* Due to historical reason of industry standard implementation, there
|
||||||
* are three types of format:
|
* are three types of format:
|
||||||
|
@ -341,6 +341,7 @@ extern struct ecommunity *ecommunity_str2com(const char *, int, int);
|
|||||||
extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
|
extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
|
||||||
int keyword_included);
|
int keyword_included);
|
||||||
extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
|
extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
|
||||||
|
extern bool ecommunity_has_route_target(struct ecommunity *ecom);
|
||||||
extern void ecommunity_strfree(char **s);
|
extern void ecommunity_strfree(char **s);
|
||||||
extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2);
|
extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2);
|
||||||
extern bool ecommunity_match(const struct ecommunity *,
|
extern bool ecommunity_match(const struct ecommunity *,
|
||||||
|
@ -300,7 +300,7 @@ void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vpn_leak_to_vpn_active(bgp, afi, NULL)) {
|
if (vpn_leak_to_vpn_active(bgp, afi, NULL, false)) {
|
||||||
label = bgp->vpn_policy[afi].tovpn_label;
|
label = bgp->vpn_policy[afi].tovpn_label;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1533,6 +1533,9 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
|
|||||||
struct bgp_dest *bn;
|
struct bgp_dest *bn;
|
||||||
const char *debugmsg;
|
const char *debugmsg;
|
||||||
int nexthop_self_flag = 0;
|
int nexthop_self_flag = 0;
|
||||||
|
struct ecommunity *old_ecom;
|
||||||
|
struct ecommunity *new_ecom = NULL;
|
||||||
|
struct ecommunity *rtlist_ecom;
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
zlog_debug("%s: from vrf %s", __func__, from_bgp->name_pretty);
|
zlog_debug("%s: from vrf %s", __func__, from_bgp->name_pretty);
|
||||||
@ -1560,7 +1563,7 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
|
|||||||
if (!is_route_injectable_into_vpn(path_vrf))
|
if (!is_route_injectable_into_vpn(path_vrf))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) {
|
if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg, false)) {
|
||||||
if (debug)
|
if (debug)
|
||||||
zlog_debug("%s: %s skipping: %s", __func__,
|
zlog_debug("%s: %s skipping: %s", __func__,
|
||||||
from_bgp->name, debugmsg);
|
from_bgp->name, debugmsg);
|
||||||
@ -1570,6 +1573,41 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
|
|||||||
/* shallow copy */
|
/* shallow copy */
|
||||||
static_attr = *path_vrf->attr;
|
static_attr = *path_vrf->attr;
|
||||||
|
|
||||||
|
if (debug && bgp_attr_get_ecommunity(&static_attr)) {
|
||||||
|
char *s = ecommunity_ecom2str(
|
||||||
|
bgp_attr_get_ecommunity(&static_attr),
|
||||||
|
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
|
||||||
|
|
||||||
|
zlog_debug("%s: post route map static_attr.ecommunity{%s}",
|
||||||
|
__func__, s);
|
||||||
|
XFREE(MTYPE_ECOMMUNITY_STR, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the vpn-policy rt-list
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Export with the 'from' instance's export RTs. */
|
||||||
|
/* If doing VRF-to-VRF leaking, strip existing RTs first. */
|
||||||
|
old_ecom = bgp_attr_get_ecommunity(&static_attr);
|
||||||
|
rtlist_ecom = from_bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN];
|
||||||
|
if (old_ecom) {
|
||||||
|
new_ecom = ecommunity_dup(old_ecom);
|
||||||
|
if (CHECK_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST],
|
||||||
|
BGP_CONFIG_VRF_TO_VRF_EXPORT))
|
||||||
|
ecommunity_strip_rts(new_ecom);
|
||||||
|
if (rtlist_ecom)
|
||||||
|
new_ecom = ecommunity_merge(new_ecom, rtlist_ecom);
|
||||||
|
if (!old_ecom->refcnt)
|
||||||
|
ecommunity_free(&old_ecom);
|
||||||
|
} else if (rtlist_ecom) {
|
||||||
|
new_ecom = ecommunity_dup(rtlist_ecom);
|
||||||
|
} else {
|
||||||
|
new_ecom = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bgp_attr_set_ecommunity(&static_attr, new_ecom);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* route map handling
|
* route map handling
|
||||||
*/
|
*/
|
||||||
@ -1586,52 +1624,24 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
|
|||||||
if (RMAP_DENYMATCH == ret) {
|
if (RMAP_DENYMATCH == ret) {
|
||||||
bgp_attr_flush(&static_attr); /* free any added parts */
|
bgp_attr_flush(&static_attr); /* free any added parts */
|
||||||
if (debug)
|
if (debug)
|
||||||
zlog_debug(
|
zlog_debug("%s: vrf %s route map \"%s\" says DENY, returning",
|
||||||
"%s: vrf %s route map \"%s\" says DENY, returning",
|
__func__, from_bgp->name_pretty,
|
||||||
__func__, from_bgp->name_pretty,
|
from_bgp->vpn_policy[afi]
|
||||||
from_bgp->vpn_policy[afi]
|
.rmap[BGP_VPN_POLICY_DIR_TOVPN]
|
||||||
.rmap[BGP_VPN_POLICY_DIR_TOVPN]
|
->name);
|
||||||
->name);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug && bgp_attr_get_ecommunity(&static_attr)) {
|
new_ecom = bgp_attr_get_ecommunity(&static_attr);
|
||||||
char *s = ecommunity_ecom2str(
|
if (!ecommunity_has_route_target(new_ecom)) {
|
||||||
bgp_attr_get_ecommunity(&static_attr),
|
ecommunity_free(&new_ecom);
|
||||||
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
|
if (debug)
|
||||||
|
zlog_debug("%s: %s skipping: waiting for a valid export rt list.",
|
||||||
zlog_debug("%s: post route map static_attr.ecommunity{%s}",
|
__func__, from_bgp->name_pretty);
|
||||||
__func__, s);
|
return;
|
||||||
XFREE(MTYPE_ECOMMUNITY_STR, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Add the vpn-policy rt-list
|
|
||||||
*/
|
|
||||||
struct ecommunity *old_ecom;
|
|
||||||
struct ecommunity *new_ecom;
|
|
||||||
|
|
||||||
/* Export with the 'from' instance's export RTs. */
|
|
||||||
/* If doing VRF-to-VRF leaking, strip existing RTs first. */
|
|
||||||
old_ecom = bgp_attr_get_ecommunity(&static_attr);
|
|
||||||
if (old_ecom) {
|
|
||||||
new_ecom = ecommunity_dup(old_ecom);
|
|
||||||
if (CHECK_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST],
|
|
||||||
BGP_CONFIG_VRF_TO_VRF_EXPORT))
|
|
||||||
ecommunity_strip_rts(new_ecom);
|
|
||||||
new_ecom = ecommunity_merge(
|
|
||||||
new_ecom, from_bgp->vpn_policy[afi]
|
|
||||||
.rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
|
|
||||||
if (!old_ecom->refcnt)
|
|
||||||
ecommunity_free(&old_ecom);
|
|
||||||
} else {
|
|
||||||
new_ecom = ecommunity_dup(
|
|
||||||
from_bgp->vpn_policy[afi]
|
|
||||||
.rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
|
|
||||||
}
|
|
||||||
bgp_attr_set_ecommunity(&static_attr, new_ecom);
|
|
||||||
|
|
||||||
if (debug && bgp_attr_get_ecommunity(&static_attr)) {
|
if (debug && bgp_attr_get_ecommunity(&static_attr)) {
|
||||||
char *s = ecommunity_ecom2str(
|
char *s = ecommunity_ecom2str(
|
||||||
bgp_attr_get_ecommunity(&static_attr),
|
bgp_attr_get_ecommunity(&static_attr),
|
||||||
@ -1885,20 +1895,20 @@ void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */
|
|||||||
if (!is_route_injectable_into_vpn(path_vrf))
|
if (!is_route_injectable_into_vpn(path_vrf))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) {
|
if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg, true)) {
|
||||||
if (debug)
|
if (debug)
|
||||||
zlog_debug("%s: skipping: %s", __func__, debugmsg);
|
zlog_debug("%s: skipping: %s", __func__, debugmsg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug)
|
bn = bgp_safi_node_lookup(to_bgp->rib[afi][safi], safi, p,
|
||||||
zlog_debug("%s: withdrawing (path_vrf=%p)", __func__, path_vrf);
|
&(from_bgp->vpn_policy[afi].tovpn_rd));
|
||||||
|
|
||||||
bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p,
|
|
||||||
&(from_bgp->vpn_policy[afi].tovpn_rd));
|
|
||||||
|
|
||||||
if (!bn)
|
if (!bn)
|
||||||
return;
|
return;
|
||||||
|
if (debug)
|
||||||
|
zlog_debug("%s: withdrawing (path_vrf=%p)", __func__, path_vrf);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* vrf -> vpn
|
* vrf -> vpn
|
||||||
* match original bpi imported from
|
* match original bpi imported from
|
||||||
@ -2666,7 +2676,7 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw,
|
|||||||
edir = BGP_VPN_POLICY_DIR_TOVPN;
|
edir = BGP_VPN_POLICY_DIR_TOVPN;
|
||||||
|
|
||||||
for (afi = 0; afi < AFI_MAX; ++afi) {
|
for (afi = 0; afi < AFI_MAX; ++afi) {
|
||||||
if (!vpn_leak_to_vpn_active(bgp, afi, NULL))
|
if (!vpn_leak_to_vpn_active(bgp, afi, NULL, false))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (withdraw) {
|
if (withdraw) {
|
||||||
|
@ -113,7 +113,8 @@ static inline bool is_bgp_vrf_mplsvpn(struct bgp *bgp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
|
static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
|
||||||
const char **pmsg)
|
const char **pmsg,
|
||||||
|
bool ignore_export_rt_list)
|
||||||
{
|
{
|
||||||
if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF
|
if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF
|
||||||
&& bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
|
&& bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
|
||||||
@ -133,8 +134,21 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is there an RT list set? */
|
/* Before performing withdrawal, VPN activation is checked; however,
|
||||||
if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) {
|
* when the route-map modifies the export route-target (RT) list, it
|
||||||
|
* becomes challenging to determine if VPN prefixes were previously
|
||||||
|
* present, or not. The 'ignore_export_rt_list' parameter will be
|
||||||
|
* used to force the withdraw operation by not checking the possible
|
||||||
|
* route-map changes.
|
||||||
|
* Of the 'ignore_export_rt_list' is set to false, check the following:
|
||||||
|
* - Is there an RT list set?
|
||||||
|
* - Is there a route-map that sets RT communities
|
||||||
|
*/
|
||||||
|
if (!ignore_export_rt_list &&
|
||||||
|
!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN] &&
|
||||||
|
(!bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN] ||
|
||||||
|
!bgp_route_map_has_extcommunity_rt(
|
||||||
|
bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]))) {
|
||||||
if (pmsg)
|
if (pmsg)
|
||||||
*pmsg = "rtlist tovpn not defined";
|
*pmsg = "rtlist tovpn not defined";
|
||||||
return 0;
|
return 0;
|
||||||
@ -246,8 +260,7 @@ static inline void vpn_leak_prechange(enum vpn_policy_direction direction,
|
|||||||
vpn_leak_to_vrf_withdraw_all(bgp_vrf, afi);
|
vpn_leak_to_vrf_withdraw_all(bgp_vrf, afi);
|
||||||
}
|
}
|
||||||
if ((direction == BGP_VPN_POLICY_DIR_TOVPN) &&
|
if ((direction == BGP_VPN_POLICY_DIR_TOVPN) &&
|
||||||
vpn_leak_to_vpn_active(bgp_vrf, afi, NULL)) {
|
vpn_leak_to_vpn_active(bgp_vrf, afi, NULL, true)) {
|
||||||
|
|
||||||
vpn_leak_from_vrf_withdraw_all(bgp_vpn, bgp_vrf, afi);
|
vpn_leak_from_vrf_withdraw_all(bgp_vpn, bgp_vrf, afi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4743,6 +4743,24 @@ static void bgp_route_map_delete(const char *rmap_name)
|
|||||||
route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED);
|
route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool bgp_route_map_has_extcommunity_rt(const struct route_map *map)
|
||||||
|
{
|
||||||
|
struct route_map_index *index = NULL;
|
||||||
|
struct route_map_rule *set = NULL;
|
||||||
|
|
||||||
|
assert(map);
|
||||||
|
|
||||||
|
for (index = map->head; index; index = index->next) {
|
||||||
|
for (set = index->set_list.head; set; set = set->next) {
|
||||||
|
if (set->cmd && set->cmd->str &&
|
||||||
|
(strmatch(set->cmd->str, "extcommunity rt") ||
|
||||||
|
strmatch(set->cmd->str, "extended-comm-list")))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void bgp_route_map_event(const char *rmap_name)
|
static void bgp_route_map_event(const char *rmap_name)
|
||||||
{
|
{
|
||||||
if (route_map_mark_updated(rmap_name) == 0)
|
if (route_map_mark_updated(rmap_name) == 0)
|
||||||
|
@ -1450,7 +1450,7 @@ int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(
|
|||||||
case NB_EV_ABORT:
|
case NB_EV_ABORT:
|
||||||
break;
|
break;
|
||||||
case NB_EV_APPLY:
|
case NB_EV_APPLY:
|
||||||
return lib_route_map_entry_match_destroy(args);
|
return lib_route_map_entry_set_destroy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NB_OK;
|
return NB_OK;
|
||||||
@ -1504,7 +1504,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(
|
|||||||
case NB_EV_ABORT:
|
case NB_EV_ABORT:
|
||||||
break;
|
break;
|
||||||
case NB_EV_APPLY:
|
case NB_EV_APPLY:
|
||||||
return lib_route_map_entry_match_destroy(args);
|
return lib_route_map_entry_set_destroy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NB_OK;
|
return NB_OK;
|
||||||
@ -1556,7 +1556,7 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy(
|
|||||||
case NB_EV_ABORT:
|
case NB_EV_ABORT:
|
||||||
break;
|
break;
|
||||||
case NB_EV_APPLY:
|
case NB_EV_APPLY:
|
||||||
return lib_route_map_entry_match_destroy(args);
|
return lib_route_map_entry_set_destroy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NB_OK;
|
return NB_OK;
|
||||||
@ -1611,7 +1611,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(
|
|||||||
case NB_EV_ABORT:
|
case NB_EV_ABORT:
|
||||||
break;
|
break;
|
||||||
case NB_EV_APPLY:
|
case NB_EV_APPLY:
|
||||||
return lib_route_map_entry_match_destroy(args);
|
return lib_route_map_entry_set_destroy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NB_OK;
|
return NB_OK;
|
||||||
@ -3043,7 +3043,7 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_destroy(
|
|||||||
case NB_EV_ABORT:
|
case NB_EV_ABORT:
|
||||||
break;
|
break;
|
||||||
case NB_EV_APPLY:
|
case NB_EV_APPLY:
|
||||||
return lib_route_map_entry_match_destroy(args);
|
return lib_route_map_entry_set_destroy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NB_OK;
|
return NB_OK;
|
||||||
|
@ -2446,6 +2446,8 @@ extern enum asnotation_mode bgp_get_asnotation(struct bgp *bgp);
|
|||||||
|
|
||||||
extern void bgp_route_map_terminate(void);
|
extern void bgp_route_map_terminate(void);
|
||||||
|
|
||||||
|
extern bool bgp_route_map_has_extcommunity_rt(const struct route_map *map);
|
||||||
|
|
||||||
extern int peer_cmp(struct peer *p1, struct peer *p2);
|
extern int peer_cmp(struct peer *p1, struct peer *p2);
|
||||||
|
|
||||||
extern int bgp_map_afi_safi_iana2int(iana_afi_t pkt_afi, iana_safi_t pkt_safi,
|
extern int bgp_map_afi_safi_iana2int(iana_afi_t pkt_afi, iana_safi_t pkt_safi,
|
||||||
|
@ -3048,7 +3048,33 @@ address-family:
|
|||||||
|
|
||||||
Specifies the route-target list to be attached to a route (export) or the
|
Specifies the route-target list to be attached to a route (export) or the
|
||||||
route-target list to match against (import) when exporting/importing between
|
route-target list to match against (import) when exporting/importing between
|
||||||
the current unicast VRF and VPN.
|
the current unicast VRF and VPN. The `rt vpn export RTLIST` command is not
|
||||||
|
mandatory and can be replaced or completed by the `set extcommunity rt`
|
||||||
|
command in the route-map attached with the `route-map vpn export`. The below
|
||||||
|
configuration illustrates how the route target is selected based on the
|
||||||
|
prefixes, and not solely on vrf criterium:
|
||||||
|
|
||||||
|
.. code-block:: frr
|
||||||
|
|
||||||
|
access-list acl1 permit 192.0.2.0/24
|
||||||
|
access-list acl2 permit 192.0.3.0/24
|
||||||
|
route-map rmap permit 10
|
||||||
|
match address acl1
|
||||||
|
set extcommunity ty 65001:10
|
||||||
|
!
|
||||||
|
route-map rmap permit 20
|
||||||
|
match address acl1
|
||||||
|
set extcommunity ty 65001:20
|
||||||
|
!
|
||||||
|
router bgp 65001 vrf vrf1
|
||||||
|
!
|
||||||
|
address-family ipv4 unicast
|
||||||
|
rd vpn export 65001:1
|
||||||
|
import vpn
|
||||||
|
export vpn
|
||||||
|
rt vpn import 65001:1
|
||||||
|
route-map vpn export rmap
|
||||||
|
|
||||||
|
|
||||||
The RTLIST is a space-separated list of route-targets, which are BGP
|
The RTLIST is a space-separated list of route-targets, which are BGP
|
||||||
extended community values as described in
|
extended community values as described in
|
||||||
|
@ -48,6 +48,10 @@ sys.path.append(os.path.join(CWD, "../"))
|
|||||||
# pylint: disable=C0413
|
# pylint: disable=C0413
|
||||||
# Import topogen and topotest helpers
|
# Import topogen and topotest helpers
|
||||||
from lib import topotest
|
from lib import topotest
|
||||||
|
from lib.bgpcheck import (
|
||||||
|
check_show_bgp_vpn_prefix_found,
|
||||||
|
check_show_bgp_vpn_prefix_not_found,
|
||||||
|
)
|
||||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
from lib.topolog import logger
|
from lib.topolog import logger
|
||||||
from lib.checkping import check_ping
|
from lib.checkping import check_ping
|
||||||
@ -259,62 +263,6 @@ def mpls_table_check_entry(router, out_label, out_nexthop):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def check_show_bgp_vpn_prefix_found(
|
|
||||||
router, ipversion, prefix, rd, label=None, nexthop=None
|
|
||||||
):
|
|
||||||
"""
|
|
||||||
Check if a given vpn prefix is present in the BGP RIB
|
|
||||||
* 'router': the router to check BGP VPN RIB
|
|
||||||
* 'ipversion': The ip version to check: ipv4 or ipv6
|
|
||||||
* 'prefix': the IP prefix to check
|
|
||||||
* 'rd': the route distinguisher to check
|
|
||||||
* 'label: the label to check
|
|
||||||
"""
|
|
||||||
output = json.loads(
|
|
||||||
router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
|
|
||||||
)
|
|
||||||
if label:
|
|
||||||
if nexthop:
|
|
||||||
expected = {
|
|
||||||
rd: {
|
|
||||||
"prefix": prefix,
|
|
||||||
"paths": [{"remoteLabel": label, "nexthops": [{"ip": nexthop}]}],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}}
|
|
||||||
else:
|
|
||||||
if nexthop:
|
|
||||||
expected = {
|
|
||||||
rd: {"prefix": prefix, "paths": [{"nexthops": [{"ip": nexthop}]}]}
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
expected = {rd: {"prefix": prefix}}
|
|
||||||
return topotest.json_cmp(output, expected)
|
|
||||||
|
|
||||||
|
|
||||||
def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None):
|
|
||||||
"""
|
|
||||||
Check if a given vpn prefix is not present in the BGP RIB
|
|
||||||
* 'router': the router to check BGP VPN RIB
|
|
||||||
* 'ipversion': The ip version to check: ipv4 or ipv6
|
|
||||||
* 'prefix': the IP prefix to check
|
|
||||||
* 'rd': the route distinguisher to check
|
|
||||||
* 'label: the label to check
|
|
||||||
"""
|
|
||||||
output = json.loads(
|
|
||||||
router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
|
|
||||||
)
|
|
||||||
if label:
|
|
||||||
expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}}
|
|
||||||
else:
|
|
||||||
expected = {rd: {"prefix": prefix}}
|
|
||||||
ret = topotest.json_cmp(output, expected)
|
|
||||||
if ret is None:
|
|
||||||
return "not good"
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def check_show_mpls_table_entry_label_not_found(router, inlabel):
|
def check_show_mpls_table_entry_label_not_found(router, inlabel):
|
||||||
output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
|
output = json.loads(router.vtysh_cmd("show mpls table {} json".format(inlabel)))
|
||||||
expected = {"inLabel": inlabel, "installed": True}
|
expected = {"inLabel": inlabel, "installed": True}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
bgp route-map delay-timer 1
|
||||||
router bgp 65500
|
router bgp 65500
|
||||||
bgp router-id 192.0.2.1
|
bgp router-id 192.0.2.1
|
||||||
no bgp ebgp-requires-policy
|
no bgp ebgp-requires-policy
|
||||||
|
@ -25,6 +25,10 @@ sys.path.append(os.path.join(CWD, "../"))
|
|||||||
# pylint: disable=C0413
|
# pylint: disable=C0413
|
||||||
# Import topogen and topotest helpers
|
# Import topogen and topotest helpers
|
||||||
from lib import topotest
|
from lib import topotest
|
||||||
|
from lib.bgpcheck import (
|
||||||
|
check_show_bgp_vpn_prefix_found,
|
||||||
|
check_show_bgp_vpn_prefix_not_found,
|
||||||
|
)
|
||||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||||
from lib.topolog import logger
|
from lib.topolog import logger
|
||||||
|
|
||||||
@ -214,6 +218,164 @@ def test_protocols_convergence():
|
|||||||
assert result is None, assertmsg
|
assert result is None, assertmsg
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_route_target_empty():
|
||||||
|
"""
|
||||||
|
Check that when removing 'rt vpn export' command, exported prefix is removed
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
logger.info("r1, Remove 'rt vpn export 52:100' command")
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nno rt vpn export 52:100\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
prefix = "172.31.0.1/32"
|
||||||
|
logger.info("r1, check that exported prefix {} is removed".format(prefix))
|
||||||
|
test_func = partial(
|
||||||
|
check_show_bgp_vpn_prefix_not_found,
|
||||||
|
router,
|
||||||
|
"ipv4",
|
||||||
|
prefix,
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "{}, vpnv4 update {} still present".format(router.name, prefix)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_route_target_with_routemap_with_export_route_target():
|
||||||
|
"""
|
||||||
|
Check that when removing 'rt vpn export' command, exported prefix is added back
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
logger.info("r1, configuring route target with route-map with export route target")
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nroute-map vpn export rmap\n"
|
||||||
|
)
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nroute-map rmap permit 1\nset extcommunity rt 52:100\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
prefix = "172.31.0.1/32"
|
||||||
|
logger.info("r1, check that exported prefix {} is added back".format(prefix))
|
||||||
|
test_func = partial(
|
||||||
|
check_show_bgp_vpn_prefix_found,
|
||||||
|
router,
|
||||||
|
"ipv4",
|
||||||
|
prefix,
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "{}, vpnv4 update {} still not present".format(router.name, prefix)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_route_target_with_routemap_without_export_route_target():
|
||||||
|
"""
|
||||||
|
Check that when removing 'set extcommunity rt' command, prefix is removed
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
logger.info("r1, removing 'set extcommunity rt 52:100.")
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nroute-map rmap permit 1\nno set extcommunity rt\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
prefix = "172.31.0.1/32"
|
||||||
|
logger.info("r1, check that exported prefix {} is removed".format(prefix))
|
||||||
|
test_func = partial(
|
||||||
|
check_show_bgp_vpn_prefix_not_found,
|
||||||
|
router,
|
||||||
|
"ipv4",
|
||||||
|
prefix,
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "{}, vpnv4 update {} still present".format(router.name, prefix)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_route_target_with_default_command():
|
||||||
|
"""
|
||||||
|
Add back route target with 'rt vpn export' command
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
logger.info("r1, detach route-map and re-add route target vpn export")
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nrouter bgp 65500 vrf vrf1\naddress-family ipv4 unicast\nrt vpn export 52:100\n"
|
||||||
|
)
|
||||||
|
prefix = "172.31.0.1/32"
|
||||||
|
logger.info("r1, check that exported prefix {} is added back".format(prefix))
|
||||||
|
test_func = partial(
|
||||||
|
check_show_bgp_vpn_prefix_found,
|
||||||
|
router,
|
||||||
|
"ipv4",
|
||||||
|
prefix,
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "{}, vpnv4 update {} still not present".format(router.name, prefix)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_suppress_route_target_with_route_map_command():
|
||||||
|
"""
|
||||||
|
Add back route target with 'rt vpn export' command
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
logger.info("r1, add an extended comm-list to delete 52:100")
|
||||||
|
|
||||||
|
router.vtysh_cmd("configure terminal\nbgp extcommunity-list 1 permit rt 52:100\n")
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nroute-map rmap permit 1\nset extended-comm-list 1 delete\n"
|
||||||
|
)
|
||||||
|
prefix = "172.31.0.1/32"
|
||||||
|
logger.info("r1, check that exported prefix {} is removed".format(prefix))
|
||||||
|
test_func = partial(
|
||||||
|
check_show_bgp_vpn_prefix_not_found,
|
||||||
|
router,
|
||||||
|
"ipv4",
|
||||||
|
prefix,
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "{}, vpnv4 update {} still present".format(router.name, prefix)
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_add_route_target_to_route_map_command():
|
||||||
|
"""
|
||||||
|
Add route target with route-map so that route is added back
|
||||||
|
"""
|
||||||
|
tgen = get_topogen()
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
router = tgen.gears["r1"]
|
||||||
|
logger.info("r1, add an additional set extcommunity 52:101")
|
||||||
|
router.vtysh_cmd(
|
||||||
|
"configure terminal\nroute-map rmap permit 1\nset extcommunity rt 52:101\n"
|
||||||
|
)
|
||||||
|
prefix = "172.31.0.1/32"
|
||||||
|
logger.info("r1, check that exported prefix {} is added back".format(prefix))
|
||||||
|
test_func = partial(
|
||||||
|
check_show_bgp_vpn_prefix_found,
|
||||||
|
router,
|
||||||
|
"ipv4",
|
||||||
|
prefix,
|
||||||
|
"444:1",
|
||||||
|
)
|
||||||
|
success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
|
||||||
|
assert success, "{}, vpnv4 update {} still not present".format(router.name, prefix)
|
||||||
|
|
||||||
|
|
||||||
def test_memory_leak():
|
def test_memory_leak():
|
||||||
"Run the memory leak test and report results."
|
"Run the memory leak test and report results."
|
||||||
tgen = get_topogen()
|
tgen = get_topogen()
|
||||||
|
63
tests/topotests/lib/bgpcheck.py
Normal file
63
tests/topotests/lib/bgpcheck.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
# Copyright 2023, 6wind
|
||||||
|
import json
|
||||||
|
|
||||||
|
from lib import topotest
|
||||||
|
|
||||||
|
|
||||||
|
def check_show_bgp_vpn_prefix_not_found(router, ipversion, prefix, rd, label=None):
|
||||||
|
"""
|
||||||
|
Check if a given vpn prefix is not present in the BGP RIB
|
||||||
|
* 'router': the router to check BGP VPN RIB
|
||||||
|
* 'ipversion': The ip version to check: ipv4 or ipv6
|
||||||
|
* 'prefix': the IP prefix to check
|
||||||
|
* 'rd': the route distinguisher to check
|
||||||
|
* 'label: the label to check
|
||||||
|
"""
|
||||||
|
output = json.loads(
|
||||||
|
router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
|
||||||
|
)
|
||||||
|
if label:
|
||||||
|
expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}}
|
||||||
|
else:
|
||||||
|
expected = {rd: {"prefix": prefix}}
|
||||||
|
ret = topotest.json_cmp(output, expected)
|
||||||
|
if ret is None:
|
||||||
|
return "not good"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def check_show_bgp_vpn_prefix_found(
|
||||||
|
router, ipversion, prefix, rd, label=None, nexthop=None
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Check if a given vpn prefix is present in the BGP RIB
|
||||||
|
* 'router': the router to check BGP VPN RIB
|
||||||
|
* 'ipversion': The ip version to check: ipv4 or ipv6
|
||||||
|
* 'prefix': the IP prefix to check
|
||||||
|
* 'rd': the route distinguisher to check
|
||||||
|
* 'label: the label to check
|
||||||
|
"""
|
||||||
|
output = json.loads(
|
||||||
|
router.vtysh_cmd("show bgp {} vpn {} json".format(ipversion, prefix))
|
||||||
|
)
|
||||||
|
if label:
|
||||||
|
if nexthop:
|
||||||
|
expected = {
|
||||||
|
rd: {
|
||||||
|
"prefix": prefix,
|
||||||
|
"paths": [{"remoteLabel": label, "nexthops": [{"ip": nexthop}]}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
expected = {rd: {"prefix": prefix, "paths": [{"remoteLabel": label}]}}
|
||||||
|
else:
|
||||||
|
if nexthop:
|
||||||
|
expected = {
|
||||||
|
rd: {"prefix": prefix, "paths": [{"nexthops": [{"ip": nexthop}]}]}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
expected = {rd: {"prefix": prefix}}
|
||||||
|
return topotest.json_cmp(output, expected)
|
Loading…
Reference in New Issue
Block a user