summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrey Aspelund <taspelund@nvidia.com>2023-05-08 03:54:25 +0000
committerTrey Aspelund <taspelund@nvidia.com>2023-05-30 15:20:35 +0000
commitbadc4857aa71c53de3594bf9f621f3630f20fdea (patch)
tree16887ecc98f1bc3249194edd0f1422ef9d5a418c
parent67b493a5b3acc0a6aeb5585b1ea4ea14d5650cf2 (diff)
bgpd: add EVPN reimport handler for martian change
Adds a generalized martian reimport function used for triggering a relearn/reimport of EVPN routes that were previously filtered/deleted as a result of a "self" check (either during import or by a martian change handler). The MAC-VRF SoO is the first consumer of this function, but can be expanded for use with Martian Tunnel-IPs, Interface-IPs, Interface-MACs, and RMACs. Signed-off-by: Trey Aspelund <taspelund@nvidia.com>
-rw-r--r--bgpd/bgp_attr.c15
-rw-r--r--bgpd/bgp_attr.h2
-rw-r--r--bgpd/bgp_evpn.c279
-rw-r--r--bgpd/bgp_evpn.h7
4 files changed, 237 insertions, 66 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index d5223a1e6e..fdf91b2574 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -5185,3 +5185,18 @@ enum bgp_attr_parse_ret bgp_attr_ignore(struct peer *peer, uint8_t type)
return withdraw ? BGP_ATTR_PARSE_WITHDRAW : BGP_ATTR_PARSE_PROCEED;
}
+
+bool route_matches_soo(struct bgp_path_info *pi, struct ecommunity *soo)
+{
+ struct attr *attr = pi->attr;
+ struct ecommunity *ecom;
+
+ if (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
+ return false;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return false;
+
+ return soo_in_ecom(ecom, soo);
+}
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 1c7e88a4f9..6cd34d301c 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -637,4 +637,6 @@ bgp_attr_set_vnc_subtlvs(struct attr *attr,
#endif
}
+extern bool route_matches_soo(struct bgp_path_info *pi, struct ecommunity *soo);
+
#endif /* _QUAGGA_BGP_ATTR_H */
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 63f0a61b7c..ef1817b479 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -41,6 +41,7 @@
#include "bgpd/bgp_nht.h"
#include "bgpd/bgp_trace.h"
#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_packet.h"
/*
* Definitions and external declarations.
@@ -2688,6 +2689,21 @@ int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
return 0;
}
+/* Update Type-2/3 Routes for L2VNI.
+ * Called by hash_iterate()
+ */
+static void update_routes_for_vni_hash(struct hash_bucket *bucket,
+ struct bgp *bgp)
+{
+ struct bgpevpn *vpn;
+
+ if (!bucket)
+ return;
+
+ vpn = (struct bgpevpn *)bucket->data;
+ update_routes_for_vni(bgp, vpn);
+}
+
/*
* Delete (and withdraw) local routes for specified VNI from the global
* table and per-VNI table. After this, remove all other routes from
@@ -3530,19 +3546,12 @@ static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
return 0;
}
-static bool route_matches_macvrf_soo(struct bgp_path_info *pi,
- const struct prefix_evpn *evp)
+static bool bgp_evpn_route_matches_macvrf_soo(struct bgp_path_info *pi,
+ const struct prefix_evpn *evp)
{
struct bgp *bgp_evpn = bgp_get_evpn();
- struct attr *attr = pi->attr;
- struct ecommunity *ecom, *macvrf_soo;
-
- if (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
- return false;
-
- ecom = attr->ecommunity;
- if (!ecom || !ecom->size)
- return false;
+ struct ecommunity *macvrf_soo;
+ bool ret = false;
if (!bgp_evpn->evpn_info)
return false;
@@ -3556,23 +3565,20 @@ static bool route_matches_macvrf_soo(struct bgp_path_info *pi,
return false;
macvrf_soo = bgp_evpn->evpn_info->soo;
+ ret = route_matches_soo(pi, macvrf_soo);
- /* Fail import check if route is carrying the MAC-VRF SoO */
- if (soo_in_ecom(ecom, macvrf_soo)) {
- if (bgp_debug_zebra(NULL)) {
- char *ecom_str;
+ if (ret && bgp_debug_zebra(NULL)) {
+ char *ecom_str;
- ecom_str = ecommunity_ecom2str(
- macvrf_soo, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
- zlog_debug(
- "import of evpn prefix %pFX skipped, local mac-vrf soo %s",
- evp, ecom_str);
- ecommunity_strfree(&ecom_str);
- }
- return true;
+ ecom_str = ecommunity_ecom2str(macvrf_soo,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ zlog_debug(
+ "import of evpn prefix %pFX skipped, local mac-vrf soo %s",
+ evp, ecom_str);
+ ecommunity_strfree(&ecom_str);
}
- return false;
+ return ret;
}
/* This API will scan evpn routes for checking attribute's rmac
@@ -3662,7 +3668,7 @@ int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
/* don't import hosts that are locally attached */
if (install && (bgp_evpn_skip_vrf_import_of_local_es(
bgp_vrf, evp, pi, install) ||
- route_matches_macvrf_soo(pi, evp)))
+ bgp_evpn_route_matches_macvrf_soo(pi, evp)))
return 0;
if (install)
@@ -3795,7 +3801,8 @@ static int install_uninstall_routes_for_vni(struct bgp *bgp,
continue;
if (install) {
- if (route_matches_macvrf_soo(pi, evp))
+ if (bgp_evpn_route_matches_macvrf_soo(
+ pi, evp))
continue;
ret = install_evpn_route_entry(bgp, vpn,
@@ -4027,7 +4034,7 @@ static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi,
/* Filter routes carrying a Site-of-Origin that matches our
* local MAC-VRF SoO.
*/
- if (import && route_matches_macvrf_soo(pi, evp))
+ if (import && bgp_evpn_route_matches_macvrf_soo(pi, evp))
return 0;
/* An EVPN route belongs to a VNI or a VRF or an ESI based on the RTs
@@ -5577,65 +5584,44 @@ void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn,
update_advertise_vni_routes(bgp, vpn);
}
-/*
+/* "mac-vrf soo" vty handler
* Handle change to the global MAC-VRF Site-of-Origin:
* - Unimport routes with new SoO from VNI/VRF
* - Import routes with old SoO into VNI/VRF
* - Update SoO on local VNI routes + re-advertise
*/
-static void bgp_evpn_update_vni_macvrf_soo(struct hash_bucket *bucket,
- struct bgp *bgp)
-{
- struct bgpevpn *vpn;
-
- if (bucket) {
- /* L2VNI */
- vpn = (struct bgpevpn *)bucket->data;
- if (is_vni_live(vpn)) {
- /* TODO: handle updates more gracefully */
- bgp_evpn_uninstall_routes(bgp, vpn);
- bgp_evpn_install_routes(bgp, vpn);
- update_routes_for_vni(bgp, vpn);
- }
- } else {
- /* L3VNI */
- if (is_l3vni_live(bgp)) {
- /* TODO: handle updates more gracefully */
- uninstall_routes_for_vrf(bgp);
- install_routes_for_vrf(bgp);
- }
- }
-}
-
-/* global "mac-vrf soo" vty handler */
void bgp_evpn_handle_global_macvrf_soo_change(struct bgp *bgp,
struct ecommunity *new_soo)
{
struct ecommunity *old_soo;
- struct listnode *node, *nnode;
- struct bgp *tmp_bgp_vrf;
old_soo = bgp->evpn_info->soo;
- /* cleanup and bail out if old/new soo are the same */
+ /* cleanup and bail out if old_soo == new_soo */
if (ecommunity_match(old_soo, new_soo)) {
ecommunity_free(&new_soo);
return;
}
- /* clear old soo + set new soo */
- ecommunity_free(&bgp->evpn_info->soo);
+ /* set new_soo */
bgp->evpn_info->soo = new_soo;
- /* walk L2VNIs */
+ /* Unimport routes matching the new_soo */
+ bgp_filter_evpn_routes_upon_martian_change(bgp, BGP_MARTIAN_SOO);
+
+ /* Reimport routes with old_soo and !new_soo.
+ */
+ bgp_reimport_evpn_routes_upon_martian_change(
+ bgp, BGP_MARTIAN_SOO, (void *)old_soo, (void *)new_soo);
+
+ /* Update locally originated routes for all L2VNIs */
hash_iterate(bgp->vnihash,
(void (*)(struct hash_bucket *,
- void *))bgp_evpn_update_vni_macvrf_soo,
+ void *))update_routes_for_vni_hash,
bgp);
- /* walk L3VNIs */
- for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, tmp_bgp_vrf))
- bgp_evpn_update_vni_macvrf_soo(NULL, tmp_bgp_vrf);
+ /* clear old_soo */
+ ecommunity_free(&old_soo);
}
/*
@@ -6205,7 +6191,84 @@ int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi,
return install_uninstall_evpn_route(bgp, afi, safi, p, pi, 0);
}
-/* Filter learned (!local) EVPN routes carrying attributes pointing to "self".
+/* Refresh previously-discarded EVPN routes carrying "self" MAC-VRF SoO.
+ * Walk global EVPN rib + import remote routes with old_soo && !new_soo.
+ */
+void bgp_reimport_evpn_routes_upon_macvrf_soo_change(struct bgp *bgp,
+ struct ecommunity *old_soo,
+ struct ecommunity *new_soo)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *rd_dest, *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* EVPN routes are a 2-level table: outer=prefix_rd, inner=prefix_evpn.
+ * A remote route could have any RD, so we need to walk them all.
+ */
+ for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
+ rd_dest = bgp_route_next(rd_dest)) {
+ table = bgp_dest_get_bgp_table_info(rd_dest);
+ if (!table)
+ continue;
+
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix *p;
+ struct prefix_evpn *evp;
+
+ p = bgp_dest_get_prefix(dest);
+ evp = (struct prefix_evpn *)p;
+
+ /* On export we only add MAC-VRF SoO to RT-2/3, so we
+ * can skip evaluation of other RTs.
+ */
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE &&
+ evp->prefix.route_type != BGP_EVPN_IMET_ROUTE)
+ continue;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ bool old_soo_fnd = false;
+ bool new_soo_fnd = false;
+
+ /* Only consider routes learned from peers */
+ if (!(pi->type == ZEBRA_ROUTE_BGP &&
+ pi->sub_type == BGP_ROUTE_NORMAL))
+ continue;
+
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+ continue;
+
+ old_soo_fnd = route_matches_soo(pi, old_soo);
+ new_soo_fnd = route_matches_soo(pi, new_soo);
+
+ if (old_soo_fnd && !new_soo_fnd) {
+ if (bgp_debug_update(pi->peer, p, NULL,
+ 1)) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(pi->attr,
+ attr_str, BUFSIZ);
+
+ zlog_debug(
+ "mac-vrf soo changed: evaluating reimport of prefix %pBD with attr %s",
+ dest, attr_str);
+ }
+
+ bgp_evpn_import_route(bgp, afi, safi, p,
+ pi);
+ }
+ }
+ }
+ }
+}
+
+/* Filter learned (!local) EVPN routes carrying "self" attributes.
* Walk the Global EVPN loc-rib unimporting martian routes from the appropriate
* L2VNIs (MAC-VRFs) / L3VNIs (IP-VRFs), and deleting them from the Global
* loc-rib when applicable (based on martian_type).
@@ -6234,9 +6297,11 @@ void bgp_filter_evpn_routes_upon_martian_change(
struct bgp_dest *rd_dest, *dest;
struct bgp_table *table;
struct bgp_path_info *pi;
+ struct ecommunity *macvrf_soo;
afi = AFI_L2VPN;
safi = SAFI_EVPN;
+ macvrf_soo = bgp->evpn_info->soo;
/* EVPN routes are a 2-level table: outer=prefix_rd, inner=prefix_evpn.
* A remote route could have any RD, so we need to walk them all.
@@ -6269,8 +6334,8 @@ void bgp_filter_evpn_routes_upon_martian_change(
pi->sub_type, pi->attr, dest);
break;
case BGP_MARTIAN_SOO:
- affected = route_matches_macvrf_soo(
- pi, (struct prefix_evpn *)p);
+ affected = route_matches_soo(
+ pi, macvrf_soo);
break;
case BGP_MARTIAN_IF_IP:
case BGP_MARTIAN_IF_MAC:
@@ -6318,6 +6383,88 @@ void bgp_filter_evpn_routes_upon_martian_change(
}
}
+/* Refresh previously-discarded EVPN routes carrying "self" attributes.
+ * This function is the handler for deleted martian entries, which is triggered
+ * by events occurring on the local system,
+ * e.g.
+ * - Del MAC-VRF Site-of-Origin
+ * + bgp_evpn_handle_global_macvrf_soo_change
+ * This will likely be extended in the future to cover these events too:
+ * - Del VTEP-IP
+ * + bgp_zebra_process_local_vni
+ * + bgp_zebra_process_local_l3vni
+ * - Del Interface IP
+ * + bgp_interface_address_delete
+ * - Del Interface MAC
+ * + bgp_ifp_down
+ * + bgp_ifp_destroy
+ * - Del RMAC
+ * + bgp_zebra_process_local_l3vni
+ */
+void bgp_reimport_evpn_routes_upon_martian_change(
+ struct bgp *bgp, enum bgp_martian_type martian_type, void *old_martian,
+ void *new_martian)
+{
+ struct listnode *node;
+ struct peer *peer;
+ safi_t safi;
+ afi_t afi;
+ struct ecommunity *old_soo, *new_soo;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Self-SoO routes are held in the global EVPN loc-rib, so we can
+ * reimport routes w/o triggering soft-reconfig/route-refresh.
+ */
+ if (martian_type == BGP_MARTIAN_SOO) {
+ old_soo = (struct ecommunity *)old_martian;
+ new_soo = (struct ecommunity *)new_martian;
+
+ /* If !old_soo, then we can skip the reimport because we
+ * wouldn't have filtered anything via the self-SoO import check
+ */
+ if (old_martian)
+ bgp_reimport_evpn_routes_upon_macvrf_soo_change(
+ bgp, old_soo, new_soo);
+
+ return;
+ }
+
+ /* Self-TIP/IP/MAC/RMAC routes are deleted from the global EVPN
+ * loc-rib, so we need to re-learn the routes via soft-reconfig/
+ * route-refresh.
+ */
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ continue;
+
+ if (peer->status != Established)
+ continue;
+
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SOFT_RECONFIG)) {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "Processing EVPN Martian/%s change on peer %s (inbound, soft-reconfig)",
+ bgp_martian_type2str(martian_type),
+ peer->host);
+
+ bgp_soft_reconfig_in(peer, afi, safi);
+ } else {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "Processing EVPN Martian/%s change on peer %s",
+ bgp_martian_type2str(martian_type),
+ peer->host);
+ bgp_route_refresh_send(peer, afi, safi, 0,
+ REFRESH_IMMEDIATE, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ }
+ }
+}
+
/*
* Handle del of a local MACIP.
*/
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index e52ad687c2..076248c9f7 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -158,6 +158,13 @@ extern int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi,
const struct prefix *p,
struct bgp_path_info *ri);
extern void
+bgp_reimport_evpn_routes_upon_macvrf_soo_change(struct bgp *bgp,
+ struct ecommunity *old_soo,
+ struct ecommunity *new_soo);
+extern void bgp_reimport_evpn_routes_upon_martian_change(
+ struct bgp *bgp, enum bgp_martian_type martian_type, void *old_martian,
+ void *new_martian);
+extern void
bgp_filter_evpn_routes_upon_martian_change(struct bgp *bgp,
enum bgp_martian_type martian_type);
extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni,