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:
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 *,
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;
}
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);
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);
/*
* 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)) {
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;
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) {
}
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) {
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;
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);
}
}
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)
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,