mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-06 02:46:26 +00:00
bgpd: fix export prefixes when rt extcomm set by route-map
When exporting BGP prefixes, it is necessary to configure
the route target extended communities with the following
command:
> rt vpn export <RouteTarget>
But the customer may need to configure the route-target to
apply to bgp updates, solely based on a route-map criterium.
by using the below route-map configured like that:
> route-map vpn export <routemapname>
Fix this by allowing to export bgp updates based on the
presence of route-targets on either route-map or vpn
configured rt. the exportation process is stopped
if no route target is available in the ecommunity list.
Fixes: ddb5b4880b
("bgpd: vpn-vrf route leaking")
Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
parent
7243e9daa4
commit
e746985295
@ -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:
|
||||
|
@ -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 *,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -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);
|
||||
@ -1609,27 +1612,37 @@ 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]);
|
||||
if (debug)
|
||||
zlog_debug("%s: %s skipping: waiting for a non empty export rt list.",
|
||||
__func__, from_bgp->name_pretty);
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bgp_attr_set_ecommunity(&static_attr, new_ecom);
|
||||
|
||||
if (debug && bgp_attr_get_ecommunity(&static_attr)) {
|
||||
@ -1885,7 +1898,7 @@ 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;
|
||||
@ -2666,7 +2679,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) {
|
||||
|
@ -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) {
|
||||
@ -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;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user