+// SPDX-License-Identifier: GPL-2.0-or-later
/* Zebra Nexthop Group Code.
* Copyright (C) 2019 Cumulus Networks, Inc.
* Donald Sharp
* Stephen Worley
- *
- * This file is part of FRR.
- *
- * FRR is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2, or (at your option) any
- * later version.
- *
- * FRR is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with FRR; see the file COPYING. If not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
*/
#include <zebra.h>
#include "zebra/interface.h"
#include "zebra/zapi_msg.h"
#include "zebra/rib.h"
+#include "zebra/zebra_vxlan.h"
DEFINE_MTYPE_STATIC(ZEBRA, NHG, "Nexthop Group Entry");
DEFINE_MTYPE_STATIC(ZEBRA, NHG_CONNECTED, "Nexthop Group Connected");
}
/*
- * When resolving a recursive nexthop, capture backup nexthop(s) also
- * so they can be conveyed through the dataplane to the FIB. We'll look
- * at the backups in the resolving nh 'nexthop' and its nhe, and copy them
- * into the route's resolved nh 'resolved' and its nhe 'nhe'.
+ * Downstream VNI and Single VXlan device check.
+ *
+ * If it has nexthop VNI labels at this point it must be D-VNI allocated
+ * and all the nexthops have to be on an SVD.
+ *
+ * If SVD is not available, mark as inactive.
+ */
+static bool nexthop_set_evpn_dvni_svd(vrf_id_t re_vrf_id,
+ struct nexthop *nexthop)
+{
+ if (!is_vrf_l3vni_svd_backed(re_vrf_id)) {
+ if (IS_ZEBRA_DEBUG_NHG_DETAIL) {
+ struct vrf *vrf = vrf_lookup_by_id(re_vrf_id);
+
+ zlog_debug(
+ "nexthop %pNHv D-VNI but route's vrf %s(%u) doesn't use SVD",
+ nexthop, VRF_LOGNAME(vrf), re_vrf_id);
+ }
+
+ return false;
+ }
+
+ nexthop->ifindex = get_l3vni_vxlan_ifindex(re_vrf_id);
+ nexthop->vrf_id = 0;
+
+ if (IS_ZEBRA_DEBUG_NHG_DETAIL)
+ zlog_debug("nexthop %pNHv using SVD", nexthop);
+
+ return true;
+}
+
+/*
+ * Given a nexthop we need to properly recursively resolve
+ * the route. As such, do a table lookup to find and match
+ * if at all possible. Set the nexthop->ifindex and resolved_id
+ * as appropriate
*/
static int resolve_backup_nexthops(const struct nexthop *nexthop,
const struct nhg_hash_entry *nhe,
* sure the nexthop's interface is known and is operational.
*/
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) {
+ /* DVNI/SVD Checks for EVPN routes */
+ if (nexthop->nh_label &&
+ nexthop->nh_label_type == ZEBRA_LSP_EVPN &&
+ !nexthop_set_evpn_dvni_svd(vrf_id, nexthop))
+ return 0;
+
ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id);
if (!ifp) {
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
return valid;
}
+/* Checks if the first nexthop is EVPN. If not, early return.
+ *
+ * This is used to determine if there is a mismatch between l3VNI
+ * of the route's vrf and the nexthops in use's VNI labels.
+ *
+ * If there is a mismatch, we keep the labels as these MUST be DVNI nexthops.
+ *
+ * IF there is no mismatch, we remove the labels and handle the routes as
+ * we have traditionally with evpn.
+ */
+static bool nexthop_list_set_evpn_dvni(struct route_entry *re,
+ struct nexthop_group *nhg)
+{
+ struct nexthop *nexthop;
+ vni_t re_vrf_vni;
+ vni_t nh_vni;
+ bool use_dvni = false;
+
+ nexthop = nhg->nexthop;
+
+ if (!nexthop->nh_label || nexthop->nh_label_type != ZEBRA_LSP_EVPN)
+ return false;
+
+ re_vrf_vni = get_l3vni_vni(re->vrf_id);
+
+ for (; nexthop; nexthop = nexthop->next) {
+ if (!nexthop->nh_label ||
+ nexthop->nh_label_type != ZEBRA_LSP_EVPN)
+ continue;
+
+ nh_vni = label2vni(&nexthop->nh_label->label[0]);
+
+ if (nh_vni != re_vrf_vni)
+ use_dvni = true;
+ }
+
+ /* Using traditional way, no VNI encap - remove labels */
+ if (!use_dvni) {
+ for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next)
+ nexthop_del_labels(nexthop);
+ }
+
+ return use_dvni;
+}
+
/*
* Process a list of nexthops, given an nhe, determining
* whether each one is ACTIVE/installable at this time.
uint32_t counter = 0;
struct nexthop *nexthop;
struct nexthop_group *nhg = &nhe->nhg;
+ bool vni_removed = false;
nexthop = nhg->nexthop;
/* Init recursive nh mtu */
re->nexthop_mtu = 0;
+ /* Handler for dvni evpn nexthops. Has to be done at nhg level */
+ vni_removed = !nexthop_list_set_evpn_dvni(re, nhg);
+
/* Process nexthops one-by-one */
for ( ; nexthop; nexthop = nexthop->next) {
counter++;
/* Check for changes to the nexthop - set ROUTE_ENTRY_CHANGED */
- if (prev_active != new_active || prev_index != nexthop->ifindex
- || ((nexthop->type >= NEXTHOP_TYPE_IFINDEX
- && nexthop->type < NEXTHOP_TYPE_IPV6)
- && prev_src.ipv4.s_addr
- != nexthop->rmap_src.ipv4.s_addr)
- || ((nexthop->type >= NEXTHOP_TYPE_IPV6
- && nexthop->type < NEXTHOP_TYPE_BLACKHOLE)
- && !(IPV6_ADDR_SAME(&prev_src.ipv6,
- &nexthop->rmap_src.ipv6)))
- || CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED))
+ if (prev_active != new_active ||
+ prev_index != nexthop->ifindex ||
+ ((nexthop->type >= NEXTHOP_TYPE_IFINDEX &&
+ nexthop->type < NEXTHOP_TYPE_IPV6) &&
+ prev_src.ipv4.s_addr != nexthop->rmap_src.ipv4.s_addr) ||
+ ((nexthop->type >= NEXTHOP_TYPE_IPV6 &&
+ nexthop->type < NEXTHOP_TYPE_BLACKHOLE) &&
+ !(IPV6_ADDR_SAME(&prev_src.ipv6,
+ &nexthop->rmap_src.ipv6))) ||
+ CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED) ||
+ vni_removed)
SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);
}