Skip to content

Commit

Permalink
Merge pull request #14512 from pguibert6WIND/vpnv4_with_no_rt_export
Browse files Browse the repository at this point in the history
L3VPN exportation by using 'route-map vpn export' command instead of 'rt vpn export' command
  • Loading branch information
riw777 authored Nov 28, 2023
2 parents 9d90343 + fc1177f commit e854c43
Show file tree
Hide file tree
Showing 12 changed files with 382 additions and 110 deletions.
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.",
__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

0 comments on commit e854c43

Please sign in to comment.