Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

L3VPN exportation by using 'route-map vpn export' command instead of 'rt vpn export' command #14512

Merged
merged 6 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions bgpd/bgp_ecommunity.c
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,34 @@ static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt,
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.
* Due to historical reason of industry standard implementation, there
* are three types of format:
Expand Down
1 change: 1 addition & 0 deletions bgpd/bgp_ecommunity.h
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ extern struct ecommunity *ecommunity_str2com(const char *, int, int);
extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
int keyword_included);
extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
extern bool ecommunity_has_route_target(struct ecommunity *ecom);
extern void ecommunity_strfree(char **s);
extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2);
extern bool ecommunity_match(const struct ecommunity *,
Expand Down
96 changes: 53 additions & 43 deletions bgpd/bgp_mplsvpn.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi)
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;
}

Expand Down Expand Up @@ -1533,6 +1533,9 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
struct bgp_dest *bn;
const char *debugmsg;
int nexthop_self_flag = 0;
struct ecommunity *old_ecom;
struct ecommunity *new_ecom = NULL;
struct ecommunity *rtlist_ecom;

if (debug)
zlog_debug("%s: from vrf %s", __func__, from_bgp->name_pretty);
Expand Down Expand Up @@ -1560,7 +1563,7 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
if (!is_route_injectable_into_vpn(path_vrf))
return;

if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) {
if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg, false)) {
if (debug)
zlog_debug("%s: %s skipping: %s", __func__,
from_bgp->name, debugmsg);
Expand All @@ -1570,32 +1573,6 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
/* shallow copy */
static_attr = *path_vrf->attr;

/*
* route map handling
*/
if (from_bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) {
struct bgp_path_info info;
route_map_result_t ret;

memset(&info, 0, sizeof(info));
info.peer = to_bgp->peer_self;
info.attr = &static_attr;
ret = route_map_apply(from_bgp->vpn_policy[afi]
.rmap[BGP_VPN_POLICY_DIR_TOVPN],
p, &info);
if (RMAP_DENYMATCH == ret) {
bgp_attr_flush(&static_attr); /* free any added parts */
if (debug)
zlog_debug(
"%s: vrf %s route map \"%s\" says DENY, returning",
__func__, from_bgp->name_pretty,
from_bgp->vpn_policy[afi]
.rmap[BGP_VPN_POLICY_DIR_TOVPN]
->name);
return;
}
}

if (debug && bgp_attr_get_ecommunity(&static_attr)) {
char *s = ecommunity_ecom2str(
bgp_attr_get_ecommunity(&static_attr),
Expand All @@ -1609,29 +1586,62 @@ void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
/*
* 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);
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);
new_ecom = ecommunity_merge(
new_ecom, from_bgp->vpn_policy[afi]
.rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
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 = ecommunity_dup(
from_bgp->vpn_policy[afi]
.rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
new_ecom = NULL;
}

bgp_attr_set_ecommunity(&static_attr, new_ecom);

/*
* route map handling
*/
if (from_bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) {
struct bgp_path_info info;
route_map_result_t ret;

memset(&info, 0, sizeof(info));
info.peer = to_bgp->peer_self;
info.attr = &static_attr;
ret = route_map_apply(from_bgp->vpn_policy[afi]
.rmap[BGP_VPN_POLICY_DIR_TOVPN],
p, &info);
if (RMAP_DENYMATCH == ret) {
bgp_attr_flush(&static_attr); /* free any added parts */
if (debug)
zlog_debug("%s: vrf %s route map \"%s\" says DENY, returning",
__func__, from_bgp->name_pretty,
from_bgp->vpn_policy[afi]
.rmap[BGP_VPN_POLICY_DIR_TOVPN]
->name);
return;
}
}

new_ecom = bgp_attr_get_ecommunity(&static_attr);
if (!ecommunity_has_route_target(new_ecom)) {
ecommunity_free(&new_ecom);
if (debug)
zlog_debug("%s: %s skipping: waiting for a valid export rt list.",
ton31337 marked this conversation as resolved.
Show resolved Hide resolved
__func__, from_bgp->name_pretty);
return;
}

if (debug && bgp_attr_get_ecommunity(&static_attr)) {
char *s = ecommunity_ecom2str(
bgp_attr_get_ecommunity(&static_attr),
Expand Down Expand Up @@ -1885,20 +1895,20 @@ void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */
if (!is_route_injectable_into_vpn(path_vrf))
return;

if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) {
if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg, true)) {
if (debug)
zlog_debug("%s: skipping: %s", __func__, debugmsg);
return;
}

if (debug)
zlog_debug("%s: withdrawing (path_vrf=%p)", __func__, path_vrf);

bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p,
&(from_bgp->vpn_policy[afi].tovpn_rd));
bn = bgp_safi_node_lookup(to_bgp->rib[afi][safi], safi, p,
&(from_bgp->vpn_policy[afi].tovpn_rd));

if (!bn)
return;
if (debug)
zlog_debug("%s: withdrawing (path_vrf=%p)", __func__, path_vrf);

/*
* vrf -> vpn
* match original bpi imported from
Expand Down Expand Up @@ -2666,7 +2676,7 @@ void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw,
edir = BGP_VPN_POLICY_DIR_TOVPN;

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;

if (withdraw) {
Expand Down
23 changes: 18 additions & 5 deletions bgpd/bgp_mplsvpn.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
const char **pmsg)
const char **pmsg,
bool ignore_export_rt_list)
{
if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF
&& bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
Expand All @@ -133,8 +134,21 @@ static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
return 0;
}

/* Is there an RT list set? */
if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) {
/* Before performing withdrawal, VPN activation is checked; however,
* 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)
*pmsg = "rtlist tovpn not defined";
return 0;
Expand Down Expand Up @@ -246,8 +260,7 @@ static inline void vpn_leak_prechange(enum vpn_policy_direction direction,
vpn_leak_to_vrf_withdraw_all(bgp_vrf, afi);
}
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);
}
}
Expand Down
18 changes: 18 additions & 0 deletions bgpd/bgp_routemap.c
Original file line number Diff line number Diff line change
Expand Up @@ -4743,6 +4743,24 @@ static void bgp_route_map_delete(const char *rmap_name)
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)
{
if (route_map_mark_updated(rmap_name) == 0)
Expand Down
10 changes: 5 additions & 5 deletions bgpd/bgp_routemap_nb_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1450,7 +1450,7 @@ int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
return lib_route_map_entry_match_destroy(args);
return lib_route_map_entry_set_destroy(args);
}

return NB_OK;
Expand Down Expand Up @@ -1504,7 +1504,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
return lib_route_map_entry_match_destroy(args);
return lib_route_map_entry_set_destroy(args);
}

return NB_OK;
Expand Down Expand Up @@ -1556,7 +1556,7 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_nt_destroy(
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
return lib_route_map_entry_match_destroy(args);
return lib_route_map_entry_set_destroy(args);
}

return NB_OK;
Expand Down Expand Up @@ -1611,7 +1611,7 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
return lib_route_map_entry_match_destroy(args);
return lib_route_map_entry_set_destroy(args);
}

return NB_OK;
Expand Down Expand Up @@ -3043,7 +3043,7 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_color_destroy(
case NB_EV_ABORT:
break;
case NB_EV_APPLY:
return lib_route_map_entry_match_destroy(args);
return lib_route_map_entry_set_destroy(args);
}

return NB_OK;
Expand Down
2 changes: 2 additions & 0 deletions bgpd/bgpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -2446,6 +2446,8 @@ extern enum asnotation_mode bgp_get_asnotation(struct bgp *bgp);

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 bgp_map_afi_safi_iana2int(iana_afi_t pkt_afi, iana_safi_t pkt_safi,
Expand Down
28 changes: 27 additions & 1 deletion doc/user/bgp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3048,7 +3048,33 @@ address-family:

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
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
extended community values as described in
Expand Down
Loading
Loading