summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.h9
-rw-r--r--bgpd/bgp_evpn.c233
-rw-r--r--bgpd/bgp_evpn.h1
-rw-r--r--bgpd/bgp_evpn_mh.c973
-rw-r--r--bgpd/bgp_evpn_mh.h58
-rw-r--r--bgpd/bgp_evpn_private.h9
-rw-r--r--bgpd/bgp_evpn_vty.c112
-rw-r--r--bgpd/bgp_memory.c3
-rw-r--r--bgpd/bgp_memory.h3
-rw-r--r--bgpd/bgp_mplsvpn_snmp.c25
-rw-r--r--bgpd/bgp_route.c183
-rw-r--r--bgpd/bgp_route.h32
-rw-r--r--bgpd/bgp_rpki.c6
-rw-r--r--bgpd/bgp_rpki.h33
-rw-r--r--bgpd/bgp_snmp.c14
-rw-r--r--bgpd/bgpd.h3
-rw-r--r--bgpd/subdir.am1
-rw-r--r--doc/developer/topotests.rst79
-rw-r--r--doc/developer/tracing.rst2
-rw-r--r--doc/user/bgp.rst6
-rw-r--r--doc/user/ospfd.rst21
-rw-r--r--doc/user/rpki.rst8
-rw-r--r--eigrpd/eigrp_snmp.c3
-rw-r--r--isisd/isis_snmp.c2
-rw-r--r--isisd/isis_tlvs.c126
-rw-r--r--ldpd/ldp_snmp.c46
-rw-r--r--lib/log.c4
-rw-r--r--lib/printf/vfprintf.c2
-rw-r--r--lib/smux.h3
-rw-r--r--lib/snmp.c17
-rw-r--r--lib/zclient.h2
-rw-r--r--ospf6d/ospf6_abr.c3
-rw-r--r--ospf6d/ospf6_asbr.c6
-rw-r--r--ospf6d/ospf6_flood.c13
-rw-r--r--ospf6d/ospf6_lsa.c16
-rw-r--r--ospfd/ospf_abr.c3
-rw-r--r--ospfd/ospf_interface.c45
-rw-r--r--ospfd/ospf_interface.h2
-rw-r--r--ospfd/ospf_lsa.c29
-rw-r--r--ospfd/ospf_lsa.h11
-rw-r--r--ospfd/ospf_snmp.c43
-rw-r--r--ospfd/ospf_vty.c77
-rw-r--r--ospfd/ospfd.c75
-rw-r--r--ospfd/ospfd.h12
-rw-r--r--pceplib/pcep_timers.c11
-rw-r--r--pimd/pim_cmd.c6
-rw-r--r--pimd/pim_nb_config.c1
-rw-r--r--ripd/rip_snmp.c9
-rw-r--r--tests/lib/test_table.c4
-rwxr-xr-xtests/topotests/conftest.py189
-rw-r--r--tests/topotests/lib/topotest.py137
-rwxr-xr-xtests/topotests/ospf_suppress_fa/__init__.py0
-rw-r--r--tests/topotests/ospf_suppress_fa/r1/ospfd.conf9
-rw-r--r--tests/topotests/ospf_suppress_fa/r1/zebra.conf4
-rw-r--r--tests/topotests/ospf_suppress_fa/r2/ospfd.conf16
-rw-r--r--tests/topotests/ospf_suppress_fa/r2/zebra.conf7
-rw-r--r--tests/topotests/ospf_suppress_fa/r3/ospfd.conf11
-rw-r--r--tests/topotests/ospf_suppress_fa/r3/zebra.conf8
-rw-r--r--tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot66
-rw-r--r--tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpgbin0 -> 33501 bytes
-rw-r--r--tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py181
-rw-r--r--tools/etc/frr/support_bundle_commands.conf25
-rw-r--r--tools/frr.service.in2
-rw-r--r--tools/frr@.service.in2
-rw-r--r--tools/frrcommon.sh.in13
-rw-r--r--yang/frr-pim.yang3
-rw-r--r--yang/subdir.am6
-rw-r--r--zebra/zapi_msg.c2
-rw-r--r--zebra/zebra_evpn_mh.c41
-rw-r--r--zebra/zebra_evpn_mh.h1
-rw-r--r--zebra/zebra_vxlan.c5
71 files changed, 2525 insertions, 588 deletions
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index 1b176f8716..a583581030 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -243,6 +243,15 @@ struct attr {
*/
#define ATTR_ES_PEER_ROUTER (1 << 4)
+ /* These two flags are only set on L3 routes installed in a
+ * VRF as a result of EVPN MAC-IP route
+ * XXX - while splitting up per-family attrs these need to be
+ * classified as non-EVPN
+ */
+#define ATTR_ES_L3_NHG_USE (1 << 5)
+#define ATTR_ES_L3_NHG_ACTIVE (1 << 6)
+#define ATTR_ES_L3_NHG (ATTR_ES_L3_NHG_USE | ATTR_ES_L3_NHG_ACTIVE)
+
/* route tag */
route_tag_t tag;
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index cebc1c4d22..2d4fea413a 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -65,11 +65,6 @@ DEFINE_QOBJ_TYPE(bgp_evpn_es);
* Static function declarations
*/
static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn);
-static void bgp_evpn_update_type2_route_entry(struct bgp *bgp,
- struct bgpevpn *vpn,
- struct bgp_dest *dest,
- struct bgp_path_info *local_pi,
- const char *caller);
static struct in_addr zero_vtep_ip;
/*
@@ -1602,8 +1597,8 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
}
}
- /* MAC-IP routes in the VNI route table are linked to the
- * destination ES
+ /* local MAC-IP routes in the VNI table are linked to
+ * the destination ES
*/
if (route_change && vpn_rt
&& (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE))
@@ -1669,6 +1664,18 @@ static void evpn_cleanup_local_non_best_route(struct bgp *bgp,
evpn_zebra_reinstall_best_route(bgp, vpn, dest);
}
+static inline bool bgp_evpn_route_add_l3_ecomm_ok(struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ esi_t *esi)
+{
+ return p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ && (is_evpn_prefix_ipaddr_v4(p)
+ || !IN6_IS_ADDR_LINKLOCAL(
+ &p->prefix.macip_addr.ip.ipaddr_v6))
+ && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)
+ && bgpevpn_get_l3vni(vpn) && bgp_evpn_es_add_l3_ecomm_ok(esi);
+}
+
/*
* Create or update EVPN route (of type based on prefix) for specified VNI
* and schedule for processing.
@@ -1738,12 +1745,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
* IPv4 or IPv6 global addresses and we're advertising L3VNI with
* these routes.
*/
- if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
- (is_evpn_prefix_ipaddr_v4(p) ||
- !IN6_IS_ADDR_LINKLOCAL(&p->prefix.macip_addr.ip.ipaddr_v6)) &&
- CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) &&
- bgpevpn_get_l3vni(vpn))
- add_l3_ecomm = 1;
+ add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok(
+ vpn, p, (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL);
/* Set up extended community. */
build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm);
@@ -1930,11 +1933,10 @@ static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
return 0;
}
-static void bgp_evpn_update_type2_route_entry(struct bgp *bgp,
- struct bgpevpn *vpn,
- struct bgp_dest *dest,
- struct bgp_path_info *local_pi,
- const char *caller)
+void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_dest *dest,
+ struct bgp_path_info *local_pi,
+ const char *caller)
{
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
@@ -1977,12 +1979,9 @@ static void bgp_evpn_update_type2_route_entry(struct bgp *bgp,
/* Add L3 VNI RTs and RMAC for non IPv6 link-local if
* using L3 VNI for type-2 routes also.
*/
- if ((is_evpn_prefix_ipaddr_v4(evp) ||
- !IN6_IS_ADDR_LINKLOCAL(
- &evp->prefix.macip_addr.ip.ipaddr_v6)) &&
- CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) &&
- bgpevpn_get_l3vni(vpn))
- add_l3_ecomm = 1;
+ add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok(
+ vpn, evp,
+ (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL);
/* Set up extended community. */
build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm);
@@ -2379,6 +2378,8 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
afi_t afi = 0;
safi_t safi = 0;
bool new_pi = false;
+ bool use_l3nhg = false;
+ bool is_l3nhg_active = false;
memset(pp, 0, sizeof(struct prefix));
ip_prefix_from_evpn_prefix(evp, pp);
@@ -2414,6 +2415,13 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
else
attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg,
+ &is_l3nhg_active, NULL);
+ if (use_l3nhg)
+ attr.es_flags |= ATTR_ES_L3_NHG_USE;
+ if (is_l3nhg_active)
+ attr.es_flags |= ATTR_ES_L3_NHG_ACTIVE;
+
/* Check if route entry is already present. */
for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
if (pi->extra
@@ -2454,6 +2462,9 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
/* as it is an importation, change nexthop */
bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF);
+ /* Link path to evpn nexthop */
+ bgp_evpn_path_nh_add(bgp_vrf, pi);
+
bgp_aggregate_increment(bgp_vrf, bgp_dest_get_prefix(dest), pi, afi,
safi);
@@ -2487,6 +2498,8 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
struct attr *attr_new;
int ret;
struct prefix_evpn ad_evp;
+ bool old_local_es = false;
+ bool new_local_es;
/* EAD prefix in the global table doesn't include the VTEP-IP so
* we need to create a different copy for the VNI
@@ -2509,6 +2522,7 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
/* Create an info */
pi = bgp_create_evpn_bgp_path_info(parent_pi, dest,
parent_pi->attr);
+ new_local_es = bgp_evpn_attr_is_local_es(pi->attr);
} else {
if (attrhash_cmp(pi->attr, parent_pi->attr)
&& !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
@@ -2527,17 +2541,29 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop))
SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED);
+ old_local_es = bgp_evpn_attr_is_local_es(pi->attr);
+ new_local_es = bgp_evpn_attr_is_local_es(attr_new);
+ /* If ESI is different or if its type has changed we
+ * need to reinstall the path in zebra
+ */
+ if ((old_local_es != new_local_es)
+ || memcmp(&pi->attr->esi, &attr_new->esi,
+ sizeof(attr_new->esi))) {
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("VNI %d path %pFX chg to %s es",
+ vpn->vni, &pi->net->p,
+ new_local_es ? "local"
+ : "non-local");
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED);
+ }
+
/* Unintern existing, set to new. */
bgp_attr_unintern(&pi->attr);
pi->attr = attr_new;
pi->uptime = bgp_clock();
}
- /* MAC-IP routes in the VNI table are linked to the destination ES */
- if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
- bgp_evpn_path_es_link(pi, vpn->vni,
- bgp_evpn_attr_get_esi(pi->attr));
-
/* Perform route selection and update zebra, if required. */
ret = evpn_route_select_install(bgp, vpn, dest);
@@ -2547,10 +2573,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
* from sync-path to remote-path)
*/
local_pi = bgp_evpn_route_get_local_path(bgp, dest);
- if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr))
+ if (local_pi && (old_local_es || new_local_es))
bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi,
- __func__);
-
+ __func__);
bgp_dest_unlock_node(dest);
return ret;
@@ -2619,6 +2644,9 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
/* Mark entry for deletion */
bgp_path_info_delete(dest, pi);
+ /* Unlink path to evpn nexthop */
+ bgp_evpn_path_nh_del(bgp_vrf, pi);
+
/* Perform route selection and update zebra, if required. */
bgp_process(bgp_vrf, dest, afi, safi);
@@ -2853,11 +2881,11 @@ static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf,
/* don't import hosts that are locally attached */
static inline bool
-bgp_evpn_skip_vrf_import_of_local_es(const struct prefix_evpn *evp,
+bgp_evpn_skip_vrf_import_of_local_es(struct bgp *bgp_vrf,
+ const struct prefix_evpn *evp,
struct bgp_path_info *pi, int install)
{
esi_t *esi;
- struct in_addr nh;
if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
esi = bgp_evpn_attr_get_esi(pi->attr);
@@ -2875,29 +2903,51 @@ bgp_evpn_skip_vrf_import_of_local_es(const struct prefix_evpn *evp,
}
return true;
}
+ }
+ return false;
+}
- /* Don't import routes with ES as destination if the nexthop
- * has not been advertised via the EAD-ES
- */
- if (pi->attr)
- nh = pi->attr->nexthop;
+/*
+ * Install or uninstall a mac-ip route in the provided vrf if
+ * there is a rt match
+ */
+int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
+ struct bgp_path_info *pi,
+ int install)
+{
+ int ret = 0;
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(pi->net);
+
+ /* Consider "valid" remote routes applicable for
+ * this VRF.
+ */
+ if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_NORMAL))
+ return 0;
+
+ if (is_route_matching_for_vrf(bgp_vrf, pi)) {
+ if (bgp_evpn_route_rmac_self_check(bgp_vrf, evp, pi))
+ return 0;
+
+ /* don't import hosts that are locally attached */
+ if (install
+ && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi,
+ install))
+ ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi);
else
- nh.s_addr = INADDR_ANY;
- if (install && !bgp_evpn_es_is_vtep_active(esi, nh)) {
- if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
- char esi_buf[ESI_STR_LEN];
+ ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp,
+ pi);
- zlog_debug(
- "vrf %s of evpn prefix %pFX skipped, nh %pI4 inactive in es %s",
- install ? "import" : "unimport", evp,
- &nh,
- esi_to_str(esi, esi_buf,
- sizeof(esi_buf)));
- }
- return true;
- }
+ if (ret)
+ flog_err(EC_BGP_EVPN_FAIL,
+ "Failed to %s EVPN %pFX route in VRF %s",
+ install ? "install" : "uninstall", evp,
+ vrf_id_to_name(bgp_vrf->vrf_id));
}
- return false;
+
+ return ret;
}
/*
@@ -2949,46 +2999,10 @@ static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install)
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
pi = pi->next) {
- /* Consider "valid" remote routes applicable for
- * this VRF.
- */
- if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
- && pi->type == ZEBRA_ROUTE_BGP
- && pi->sub_type == BGP_ROUTE_NORMAL))
- continue;
-
- /* don't import hosts that are locally attached
- */
- if (bgp_evpn_skip_vrf_import_of_local_es(
- evp, pi, install))
- continue;
-
- if (is_route_matching_for_vrf(bgp_vrf, pi)) {
- if (bgp_evpn_route_rmac_self_check(
- bgp_vrf, evp, pi))
- continue;
-
- if (install)
- ret = install_evpn_route_entry_in_vrf(
- bgp_vrf, evp, pi);
- else
- ret = uninstall_evpn_route_entry_in_vrf(
- bgp_vrf, evp, pi);
-
- if (ret) {
- flog_err(
- EC_BGP_EVPN_FAIL,
- "Failed to %s EVPN %pFX route in VRF %s",
- install ? "install"
- : "uninstall",
- evp,
- vrf_id_to_name(
- bgp_vrf->vrf_id));
- bgp_dest_unlock_node(rd_dest);
- bgp_dest_unlock_node(dest);
- return ret;
- }
- }
+ ret = bgp_evpn_route_entry_install_if_vrf_match(
+ bgp_vrf, pi, install);
+ if (ret)
+ return ret;
}
}
}
@@ -3169,14 +3183,13 @@ static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi,
|| is_evpn_prefix_ipaddr_v6(evp)))
return 0;
- /* don't import hosts that are locally attached */
- if (bgp_evpn_skip_vrf_import_of_local_es(evp, pi, install))
- return 0;
-
for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) {
int ret;
- if (install)
+ /* don't import hosts that are locally attached */
+ if (install
+ && !bgp_evpn_skip_vrf_import_of_local_es(bgp_vrf, evp, pi,
+ install))
ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi);
else
ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp,
@@ -3291,6 +3304,13 @@ static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi,
if (sub_type != ECOMMUNITY_ROUTE_TARGET)
continue;
+ /* non-local MAC-IP routes in the global route table are linked
+ * to the destination ES
+ */
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ bgp_evpn_path_es_link(pi, 0,
+ bgp_evpn_attr_get_esi(pi->attr));
+
/*
* macip routes (type-2) are imported into VNI and VRF tables.
* IMET route is imported into VNI table.
@@ -3370,6 +3390,18 @@ static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
true, true);
}
+void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import)
+{
+ struct bgp *bgp_evpn;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return;
+
+ install_uninstall_evpn_route(bgp_evpn, AFI_L2VPN, SAFI_EVPN,
+ &pi->net->p, pi, import);
+}
+
/* Import the pi into vrf routing tables */
void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import)
{
@@ -3723,7 +3755,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
if (attr) {
STREAM_GET(&attr->esi, pkt, sizeof(esi_t));
- if (bgp_evpn_is_esi_local(&attr->esi))
+ if (bgp_evpn_is_esi_local_and_non_bypass(&attr->esi))
attr->es_flags |= ATTR_ES_IS_LOCAL;
else
attr->es_flags &= ~ATTR_ES_IS_LOCAL;
@@ -5776,11 +5808,14 @@ void bgp_evpn_init(struct bgp *bgp)
/* Default BUM handling is to do head-end replication. */
bgp->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL;
+
+ bgp_evpn_nh_init(bgp);
}
void bgp_evpn_vrf_delete(struct bgp *bgp_vrf)
{
bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+ bgp_evpn_nh_finish(bgp_vrf);
}
/*
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index 29d3d2c62f..83a6dd84c8 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -206,5 +206,4 @@ extern void bgp_evpn_init(struct bgp *bgp);
extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx);
extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx);
extern void update_advertise_vrf_routes(struct bgp *bgp_vrf);
-
#endif /* _QUAGGA_BGP_EVPN_H */
diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c
index 826de21b9d..868238ebdd 100644
--- a/bgpd/bgp_evpn_mh.c
+++ b/bgpd/bgp_evpn_mh.c
@@ -64,17 +64,20 @@ static void bgp_evpn_es_vtep_del(struct bgp *bgp,
struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr);
static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es);
static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es);
-static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi);
+static struct bgp_evpn_es_evi *
+bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi);
static uint32_t bgp_evpn_es_get_active_vtep_cnt(struct bgp_evpn_es *es);
static void bgp_evpn_l3nhg_update_on_vtep_chg(struct bgp_evpn_es *es);
static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi);
static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller);
-static void
-bgp_evpn_es_path_update_on_vtep_chg(struct bgp_evpn_es_vtep *es_vtep,
- bool active);
+static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info);
+static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es,
+ bool is_local);
esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
static int bgp_evpn_run_consistency_checks(struct thread *t);
+static void bgp_evpn_path_nh_info_free(struct bgp_path_evpn_nh_info *nh_info);
+static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info);
/******************************************************************************
* per-ES (Ethernet Segment) routing table
@@ -1291,8 +1294,6 @@ static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp,
* removed.
*/
bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es);
- bgp_evpn_es_path_update_on_vtep_chg(es_vtep, new_active);
-
/* queue up the es for background consistency checks */
bgp_evpn_es_cons_checks_pend_add(es_vtep->es);
}
@@ -1368,59 +1369,60 @@ static void bgp_evpn_es_vtep_del(struct bgp *bgp,
bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr);
}
-bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh)
-{
- struct bgp_evpn_es *es;
- struct bgp_evpn_es_vtep *es_vtep;
- struct listnode *node = NULL;
- bool rc = false;
-
- if (!memcmp(esi, zero_esi, sizeof(*esi)) || !nh.s_addr)
- return true;
-
- es = bgp_evpn_es_find(esi);
- if (!es)
- return false;
-
- for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
- if (es_vtep->vtep_ip.s_addr == nh.s_addr) {
- if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE))
- rc = true;
- break;
- }
- }
- return rc;
-}
-
/********************** ES MAC-IP paths *************************************
- * MAC-IP routes in the VNI routing table are linked to the destination
- * ES for efficient updates on ES changes (such as VTEP add/del).
+ * 1. Local MAC-IP routes in the VNI routing table are linked to the
+ * destination ES (macip_evi_path_list) for efficient updates on ES oper
+ * state changes.
+ * 2. Non-local MAC-IP routes in the global routing table are linked to
+ * the detination for efficient updates on -
+ * a. VTEP add/del - this results in a L3NHG update.
+ * b. ES-VRF add/del - this may result in the host route being migrated to
+ * L3NHG or vice versa (flat multipath list).
****************************************************************************/
-void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info)
+static void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info)
{
bgp_evpn_path_es_unlink(es_info);
XFREE(MTYPE_BGP_EVPN_PATH_ES_INFO, es_info);
}
+void bgp_evpn_path_mh_info_free(struct bgp_path_mh_info *mh_info)
+{
+ if (mh_info->es_info)
+ bgp_evpn_path_es_info_free(mh_info->es_info);
+ if (mh_info->nh_info)
+ bgp_evpn_path_nh_info_free(mh_info->nh_info);
+ XFREE(MTYPE_BGP_EVPN_PATH_MH_INFO, mh_info);
+}
+
static struct bgp_path_es_info *
bgp_evpn_path_es_info_new(struct bgp_path_info *pi, vni_t vni)
{
struct bgp_path_info_extra *e;
+ struct bgp_path_mh_info *mh_info;
+ struct bgp_path_es_info *es_info;
e = bgp_path_info_extra_get(pi);
+ /* If mh_info doesn't exist allocate it */
+ mh_info = e->mh_info;
+ if (!mh_info)
+ e->mh_info = mh_info = XCALLOC(MTYPE_BGP_EVPN_PATH_MH_INFO,
+ sizeof(struct bgp_path_mh_info));
+
/* If es_info doesn't exist allocate it */
- if (!e->es_info) {
- e->es_info = XCALLOC(MTYPE_BGP_EVPN_PATH_ES_INFO,
- sizeof(struct bgp_path_es_info));
- e->es_info->pi = pi;
- e->es_info->vni = vni;
+ es_info = mh_info->es_info;
+ if (!es_info) {
+ mh_info->es_info = es_info =
+ XCALLOC(MTYPE_BGP_EVPN_PATH_ES_INFO,
+ sizeof(struct bgp_path_es_info));
+ es_info->vni = vni;
+ es_info->pi = pi;
}
- return e->es_info;
+ return es_info;
}
-void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info)
+static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info)
{
struct bgp_evpn_es *es = es_info->es;
struct bgp_path_info *pi;
@@ -1433,7 +1435,13 @@ void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info)
zlog_debug("vni %u path %pFX unlinked from es %s", es_info->vni,
&pi->net->p, es->esi_str);
- list_delete_node(es->macip_path_list, &es_info->es_listnode);
+ if (es_info->vni)
+ list_delete_node(es->macip_evi_path_list,
+ &es_info->es_listnode);
+ else
+ list_delete_node(es->macip_global_path_list,
+ &es_info->es_listnode);
+
es_info->es = NULL;
/* if there are no other references against the ES it
@@ -1450,9 +1458,11 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi)
{
struct bgp_path_es_info *es_info;
struct bgp_evpn_es *es;
- struct bgp *bgp_evpn = bgp_get_evpn();
+ struct bgp *bgp_evpn;
- es_info = pi->extra ? pi->extra->es_info : NULL;
+ es_info = (pi->extra && pi->extra->mh_info)
+ ? pi->extra->mh_info->es_info
+ : NULL;
/* if the esi is zero just unlink the path from the old es */
if (!esi || !memcmp(esi, zero_esi, sizeof(*esi))) {
if (es_info)
@@ -1460,6 +1470,7 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi)
return;
}
+ bgp_evpn = bgp_get_evpn();
if (!bgp_evpn)
return;
@@ -1486,43 +1497,59 @@ void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi)
/* link mac-ip path to the new destination ES */
es_info->es = es;
listnode_init(&es_info->es_listnode, es_info);
- listnode_add(es->macip_path_list, &es_info->es_listnode);
+ if (es_info->vni)
+ listnode_add(es->macip_evi_path_list, &es_info->es_listnode);
+ else
+ listnode_add(es->macip_global_path_list, &es_info->es_listnode);
}
+static bool bgp_evpn_is_macip_path(struct bgp_path_info *pi)
+{
+ struct prefix_evpn *evp;
+
+ /* Only MAC-IP routes need to be linked (MAC-only routes can be
+ * skipped) as these lists are maintained for managing
+ * host routes in the tenant VRF
+ */
+ evp = (struct prefix_evpn *)&pi->net->p;
+ return is_evpn_prefix_ipaddr_v4(evp) || is_evpn_prefix_ipaddr_v6(evp);
+}
+
+/* When a remote ES is added to a VRF, routes using that as
+ * a destination need to be migrated to a L3NHG or viceversa.
+ * This is done indirectly by re-attempting an install of the
+ * route in the associated VRFs. As a part of the VRF install use
+ * of l3 NHG is evaluated and this results in the
+ * attr.es_flag ATTR_ES_USE_L3_NHG being set or cleared.
+ */
static void
-bgp_evpn_es_path_update_on_vtep_chg(struct bgp_evpn_es_vtep *es_vtep,
- bool active)
+bgp_evpn_es_path_update_on_es_vrf_chg(struct bgp_evpn_es_vrf *es_vrf,
+ const char *reason)
{
struct listnode *node;
struct bgp_path_es_info *es_info;
struct bgp_path_info *pi;
- struct bgp_path_info *parent_pi;
- struct bgp_evpn_es *es = es_vtep->es;
+ struct bgp_evpn_es *es = es_vrf->es;
+
+ if (!bgp_mh_info->host_routes_use_l3nhg)
+ return;
if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
- zlog_debug("update paths linked to es %s on vtep chg",
- es->esi_str);
+ zlog_debug("update paths linked to es %s on es-vrf %s %s",
+ es->esi_str, es_vrf->bgp_vrf->name, reason);
- for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) {
+ for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) {
pi = es_info->pi;
- if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID))
- continue;
-
- if (pi->sub_type != BGP_ROUTE_IMPORTED)
- continue;
- parent_pi = pi->extra ? pi->extra->parent : NULL;
- if (!parent_pi || !parent_pi->attr)
- continue;
-
- if (es_vtep->vtep_ip.s_addr != parent_pi->attr->nexthop.s_addr)
+ if (!bgp_evpn_is_macip_path(pi))
continue;
if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
zlog_debug(
- "update path %pFX linked to es %s on vtep chg",
- &parent_pi->net->p, es->esi_str);
- bgp_evpn_import_route_in_vrfs(parent_pi, active ? 1 : 0);
+ "update path %pFX linked to es %s on vrf chg",
+ &pi->net->p, es->esi_str);
+ bgp_evpn_route_entry_install_if_vrf_match(es_vrf->bgp_vrf, pi,
+ 1);
}
}
@@ -1579,8 +1606,10 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi)
listset_app_node_mem(es->es_vrf_list);
/* Initialise the route list used for efficient event handling */
- es->macip_path_list = list_new();
- listset_app_node_mem(es->macip_path_list);
+ es->macip_evi_path_list = list_new();
+ listset_app_node_mem(es->macip_evi_path_list);
+ es->macip_global_path_list = list_new();
+ listset_app_node_mem(es->macip_global_path_list);
QOBJ_REG(es, bgp_evpn_es);
@@ -1594,7 +1623,8 @@ static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi)
static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller)
{
if ((es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE))
- || listcount(es->macip_path_list))
+ || listcount(es->macip_evi_path_list)
+ || listcount(es->macip_global_path_list))
return;
if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
@@ -1604,7 +1634,8 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller)
list_delete(&es->es_evi_list);
list_delete(&es->es_vrf_list);
list_delete(&es->es_vtep_list);
- list_delete(&es->macip_path_list);
+ list_delete(&es->macip_evi_path_list);
+ list_delete(&es->macip_global_path_list);
bgp_table_unlock(es->route_table);
/* remove the entry from various databases */
@@ -1615,15 +1646,25 @@ static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller)
XFREE(MTYPE_BGP_EVPN_ES, es);
}
+static inline bool bgp_evpn_is_es_local_and_non_bypass(struct bgp_evpn_es *es)
+{
+ return (es->flags & BGP_EVPNES_LOCAL)
+ && !(es->flags & BGP_EVPNES_BYPASS);
+}
+
/* init local info associated with the ES */
static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es)
{
char buf[BGP_EVPN_PREFIX_RD_LEN];
+ bool old_is_local;
+ bool is_local;
if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
return;
+ old_is_local = bgp_evpn_is_es_local_and_non_bypass(es);
SET_FLAG(es->flags, BGP_EVPNES_LOCAL);
+
listnode_init(&es->es_listnode, es);
listnode_add(bgp_mh_info->local_es_list, &es->es_listnode);
@@ -1633,16 +1674,28 @@ static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es)
es->prd.prefixlen = 64;
snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, es->rd_id);
(void)str2prefix_rd(buf, &es->prd);
+
+ is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (old_is_local != is_local)
+ bgp_evpn_mac_update_on_es_local_chg(es, is_local);
}
/* clear any local info associated with the ES */
-static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es)
+static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es, bool finish)
{
+ bool old_is_local;
+ bool is_local;
+
if (!CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
return;
+ old_is_local = bgp_evpn_is_es_local_and_non_bypass(es);
UNSET_FLAG(es->flags, BGP_EVPNES_LOCAL);
+ is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (!finish && (old_is_local != is_local))
+ bgp_evpn_mac_update_on_es_local_chg(es, is_local);
+
/* remove from the ES local list */
list_delete_node(bgp_mh_info->local_es_list, &es->es_listnode);
@@ -1664,10 +1717,127 @@ static void bgp_evpn_es_remote_info_re_eval(struct bgp_evpn_es *es)
}
}
-static inline bool bgp_evpn_local_es_is_active(struct bgp_evpn_es *es)
+/* If ES is present and local it needs to be active/oper-up for
+ * including L3 EC
+ */
+bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi)
{
- return (es->flags & BGP_EVPNES_OPER_UP)
- && !(es->flags & BGP_EVPNES_BYPASS);
+ struct bgp_evpn_es *es;
+
+ if (!esi || !bgp_mh_info->suppress_l3_ecomm_on_inactive_es)
+ return true;
+
+ es = bgp_evpn_es_find(esi);
+
+ return (!es || !(es->flags & BGP_EVPNES_LOCAL)
+ || bgp_evpn_local_es_is_active(es));
+}
+
+static bool bgp_evpn_is_valid_local_path(struct bgp_path_info *pi)
+{
+ return (CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_STATIC);
+}
+
+/* Update all local MAC-IP routes in the VNI routing table associated
+ * with the ES. When the ES is down the routes are advertised without
+ * the L3 extcomm
+ */
+static void bgp_evpn_mac_update_on_es_oper_chg(struct bgp_evpn_es *es)
+{
+ struct listnode *node;
+ struct bgp_path_es_info *es_info;
+ struct bgp_path_info *pi;
+ struct bgp *bgp;
+ struct bgpevpn *vpn;
+
+ if (!bgp_mh_info->suppress_l3_ecomm_on_inactive_es)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("update paths linked to es %s on oper chg",
+ es->esi_str);
+
+ bgp = bgp_get_evpn();
+ for (ALL_LIST_ELEMENTS_RO(es->macip_evi_path_list, node, es_info)) {
+ pi = es_info->pi;
+
+ if (!bgp_evpn_is_valid_local_path(pi))
+ continue;
+
+ if (!bgp_evpn_is_macip_path(pi))
+ continue;
+
+ vpn = bgp_evpn_lookup_vni(bgp, es_info->vni);
+ if (!vpn)
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "update path %d %pFX linked to es %s on oper chg",
+ es_info->vni, &pi->net->p, es->esi_str);
+
+ bgp_evpn_update_type2_route_entry(bgp, vpn, pi->net, pi,
+ __func__);
+ }
+}
+
+static bool bgp_evpn_is_valid_bgp_path(struct bgp_path_info *pi)
+{
+ return (CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_NORMAL);
+}
+
+/* If an ES is no longer local (or becomes local) we need to re-install
+ * paths using that ES as destination. This is needed as the criteria
+ * for best path selection has changed.
+ */
+static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es,
+ bool is_local)
+{
+ struct listnode *node;
+ struct bgp_path_es_info *es_info;
+ struct bgp_path_info *pi;
+ bool tmp_local;
+ struct attr *attr_new;
+ struct attr attr_tmp;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("update paths linked to es %s on chg to %s",
+ es->esi_str, is_local ? "local" : "non-local");
+
+ for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) {
+ pi = es_info->pi;
+
+ /* Consider "valid" remote routes */
+ if (!bgp_evpn_is_valid_bgp_path(pi))
+ continue;
+
+ if (!pi->attr)
+ continue;
+
+ tmp_local = !!(pi->attr->es_flags & ATTR_ES_IS_LOCAL);
+ if (tmp_local == is_local)
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "update path %pFX linked to es %s on chg to %s",
+ &pi->net->p, es->esi_str,
+ is_local ? "local" : "non-local");
+
+ attr_tmp = *pi->attr;
+ if (is_local)
+ attr_tmp.es_flags |= ATTR_ES_IS_LOCAL;
+ else
+ attr_tmp.es_flags &= ~ATTR_ES_IS_LOCAL;
+ attr_new = bgp_attr_intern(&attr_tmp);
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+ bgp_evpn_import_type2_route(pi, 1);
+ }
}
static void bgp_evpn_local_es_deactivate(struct bgp *bgp,
@@ -1699,6 +1869,8 @@ static void bgp_evpn_local_es_deactivate(struct bgp *bgp,
"%u failed to delete type-1 route for ESI %s",
bgp->vrf_id, es->esi_str);
}
+
+ bgp_evpn_mac_update_on_es_oper_chg(es);
}
/* Process ES link oper-down by withdrawing ES-EAD and ESR */
@@ -1746,6 +1918,8 @@ static void bgp_evpn_local_es_activate(struct bgp *bgp, struct bgp_evpn_es *es,
es->originator_ip);
(void)bgp_evpn_type1_route_update(bgp, es, NULL, &p);
}
+
+ bgp_evpn_mac_update_on_es_oper_chg(es);
}
/* Process ES link oper-up by generating ES-EAD and ESR */
@@ -1780,11 +1954,14 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp,
bool old_bypass = !!(es->flags & BGP_EVPNES_BYPASS);
bool old_active;
bool new_active;
+ bool old_is_local;
+ bool is_local;
if (bypass == old_bypass)
return;
old_active = bgp_evpn_local_es_is_active(es);
+ old_is_local = bgp_evpn_is_es_local_and_non_bypass(es);
if (bypass)
SET_FLAG(es->flags, BGP_EVPNES_BYPASS);
else
@@ -1801,6 +1978,10 @@ static void bgp_evpn_local_es_bypass_update(struct bgp *bgp,
else
bgp_evpn_local_es_deactivate(bgp, es);
}
+
+ is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (old_is_local != is_local)
+ bgp_evpn_mac_update_on_es_local_chg(es, is_local);
}
static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es)
@@ -1825,16 +2006,17 @@ static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es)
/* Clear local info associated with the ES and free it up if there is
* no remote reference
*/
- bgp_evpn_es_local_info_clear(es);
+ bgp_evpn_es_local_info_clear(es, false);
}
-bool bgp_evpn_is_esi_local(esi_t *esi)
+bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi)
{
struct bgp_evpn_es *es = NULL;
/* Lookup ESI hash - should exist. */
es = bgp_evpn_es_find(esi);
- return es ? !!(es->flags & BGP_EVPNES_LOCAL) : false;
+
+ return es && bgp_evpn_is_es_local_and_non_bypass(es);
}
int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi)
@@ -2114,7 +2296,9 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty,
json_object_int_add(json, "vrfCount",
listcount(es->es_vrf_list));
json_object_int_add(json, "macipPathCount",
- listcount(es->macip_path_list));
+ listcount(es->macip_evi_path_list));
+ json_object_int_add(json, "macipGlobalPathCount",
+ listcount(es->macip_global_path_list));
json_object_int_add(json, "inconsistentVniVtepCount",
es->incons_evi_vtep_cnt);
if (listcount(es->es_vtep_list)) {
@@ -2162,8 +2346,10 @@ static void bgp_evpn_es_show_entry_detail(struct vty *vty,
vty_out(vty, " Remote VNI Count: %d\n",
es->remote_es_evi_cnt);
vty_out(vty, " VRF Count: %d\n", listcount(es->es_vrf_list));
- vty_out(vty, " MACIP Path Count: %d\n",
- listcount(es->macip_path_list));
+ vty_out(vty, " MACIP EVI Path Count: %d\n",
+ listcount(es->macip_evi_path_list));
+ vty_out(vty, " MACIP Global Path Count: %d\n",
+ listcount(es->macip_global_path_list));
vty_out(vty, " Inconsistent VNI VTEP Count: %d\n",
es->incons_evi_vtep_cnt);
if (es->inconsistencies) {
@@ -2394,6 +2580,8 @@ static void bgp_evpn_l3nhg_deactivate(struct bgp_evpn_es_vrf *es_vrf)
es_vrf->nhg_id);
bgp_evpn_l3nhg_zebra_del(es_vrf);
es_vrf->flags &= ~BGP_EVPNES_VRF_NHG_ACTIVE;
+ /* MAC-IPs can now be installed via the L3NHG */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg-deactivate");
}
static void bgp_evpn_l3nhg_activate(struct bgp_evpn_es_vrf *es_vrf, bool update)
@@ -2412,6 +2600,8 @@ static void bgp_evpn_l3nhg_activate(struct bgp_evpn_es_vrf *es_vrf, bool update)
es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id,
es_vrf->nhg_id);
es_vrf->flags |= BGP_EVPNES_VRF_NHG_ACTIVE;
+ /* MAC-IPs can now be installed via the L3NHG */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg_activate");
}
bgp_evpn_l3nhg_zebra_add(es_vrf);
@@ -2488,6 +2678,11 @@ static struct bgp_evpn_es_vrf *bgp_evpn_es_vrf_create(struct bgp_evpn_es *es,
bgp_vrf->vrf_id, es_vrf->nhg_id, es_vrf->v6_nhg_id);
bgp_evpn_l3nhg_activate(es_vrf, false /* update */);
+ /* update paths in the VRF that may already be associated with
+ * this destination ES
+ */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-create");
+
return es_vrf;
}
@@ -2516,6 +2711,11 @@ static void bgp_evpn_es_vrf_delete(struct bgp_evpn_es_vrf *es_vrf)
/* remove from the VRF-ESI rb tree */
RB_REMOVE(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, es_vrf);
+ /* update paths in the VRF that may already be associated with
+ * this destination ES
+ */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-delete");
+
XFREE(MTYPE_BGP_EVPN_ES_VRF, es_vrf);
}
@@ -2598,22 +2798,56 @@ void bgp_evpn_es_evi_vrf_ref(struct bgpevpn *vpn)
bgp_evpn_es_vrf_ref(es_evi, vpn->bgp_vrf);
}
+/* 1. If ES-VRF is not present install the host route with the exploded/flat
+ * multi-path list.
+ * 2. If ES-VRF is present -
+ * - if L3NHG has not been activated for the ES-VRF (this could be because
+ * all the PEs attached to the VRF are down) do not install the route
+ * in zebra.
+ * - if L3NHG has been activated install the route via that L3NHG
+ */
+void bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi, bool *use_l3nhg,
+ bool *is_l3nhg_active,
+ struct bgp_evpn_es_vrf **es_vrf_p)
+{
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_vrf *es_vrf;
+
+ if (!bgp_mh_info->host_routes_use_l3nhg)
+ return;
+
+ es = bgp_evpn_es_find(esi);
+ if (!es)
+ return;
+
+ es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf);
+ if (!es_vrf)
+ return;
+
+ *use_l3nhg = true;
+ if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE)
+ *is_l3nhg_active = true;
+ if (es_vrf_p)
+ *es_vrf_p = es_vrf;
+}
+
/* returns false if legacy-exploded mp needs to be used for route install */
bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi,
uint32_t *nhg_p)
{
esi_t *esi;
- struct bgp_evpn_es *es;
- struct bgp_evpn_es_vrf *es_vrf;
+ struct bgp_evpn_es_vrf *es_vrf = NULL;
struct bgp_path_info *parent_pi;
struct bgp_node *rn;
struct prefix_evpn *evp;
struct bgp_path_info *mpinfo;
+ bool use_l3nhg = false;
+ bool is_l3nhg_active = false;
*nhg_p = 0;
- /* L3NHG support is disabled, use legacy-exploded multipath */
- if (!bgp_mh_info->host_routes_use_l3nhg)
+ /* we don't support NHG for routes leaked from another VRF yet */
+ if (pi->extra && pi->extra->bgp_orig)
return false;
parent_pi = get_route_parent_evpn(pi);
@@ -2633,15 +2867,17 @@ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi,
if (!memcmp(esi, zero_esi, sizeof(*esi)))
return false;
- /* if the ES-VRF is not setup or if the NHG has not been installed
- * we cannot install the route yet, return a 0-NHG to indicate
- * that
+ bgp_evpn_es_vrf_use_nhg(bgp_vrf, esi, &use_l3nhg, &is_l3nhg_active,
+ &es_vrf);
+
+ /* L3NHG support is disabled, use legacy-exploded multipath */
+ if (!use_l3nhg)
+ return false;
+
+ /* if the NHG has not been installed we cannot install the route yet,
+ * return a 0-NHG to indicate that
*/
- es = bgp_evpn_es_find(esi);
- if (!es)
- return true;
- es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf);
- if (!es_vrf || !(es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE))
+ if (!is_l3nhg_active)
return true;
/* this needs to be set the v6NHG if v6route */
@@ -2652,7 +2888,7 @@ bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi,
for (mpinfo = bgp_path_info_mpath_next(pi); mpinfo;
mpinfo = bgp_path_info_mpath_next(mpinfo)) {
- /* if any of the paths of have a different ESI we can't use
+ /* if any of the paths have a different ESI we can't use
* the NHG associated with the ES. fallback to legacy-exploded
* multipath
*/
@@ -2988,7 +3224,8 @@ static struct bgp_evpn_es_evi *bgp_evpn_es_evi_new(struct bgp_evpn_es *es,
/* remove the ES-EVI from the per-L2-VNI and per-ES tables and free
* up the memory.
*/
-static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi)
+static struct bgp_evpn_es_evi *
+bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi)
{
struct bgp_evpn_es *es = es_evi->es;
struct bgpevpn *vpn = es_evi->vpn;
@@ -2997,7 +3234,7 @@ static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi)
* reference
*/
if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE))
- return;
+ return es_evi;
bgp_evpn_es_vrf_deref(es_evi);
@@ -3012,6 +3249,8 @@ static void bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi)
/* remove from the VNI-ESI rb tree */
XFREE(MTYPE_BGP_EVPN_ES_EVI, es_evi);
+
+ return NULL;
}
/* init local info associated with the ES-EVI */
@@ -3028,17 +3267,18 @@ static void bgp_evpn_es_evi_local_info_set(struct bgp_evpn_es_evi *es_evi)
}
/* clear any local info associated with the ES-EVI */
-static void bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi)
+static struct bgp_evpn_es_evi *
+bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi)
{
struct bgpevpn *vpn = es_evi->vpn;
if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
- return;
+ return es_evi;
UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL);
list_delete_node(vpn->local_es_evi_list, &es_evi->l2vni_listnode);
- bgp_evpn_es_evi_free(es_evi);
+ return bgp_evpn_es_evi_free(es_evi);
}
/* eval remote info associated with the ES */
@@ -3068,14 +3308,15 @@ static void bgp_evpn_es_evi_remote_info_re_eval(struct bgp_evpn_es_evi *es_evi)
}
}
-static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi)
+static struct bgp_evpn_es_evi *
+bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi)
{
struct prefix_evpn p;
struct bgp_evpn_es *es = es_evi->es;
struct bgp *bgp;
if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
- return;
+ return es_evi;
if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
zlog_debug("del local es %s evi %u",
@@ -3109,8 +3350,7 @@ static void bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi)
}
}
- bgp_evpn_es_evi_local_info_clear(es_evi);
-
+ return bgp_evpn_es_evi_local_info_clear(es_evi);
}
int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni)
@@ -3326,6 +3566,30 @@ int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
return 0;
}
+/* If a VNI is being deleted we need to force del all remote VTEPs */
+static void bgp_evpn_remote_es_evi_flush(struct bgp_evpn_es_evi *es_evi)
+{
+ struct listnode *node = NULL;
+ struct listnode *nnode = NULL;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+ struct bgp *bgp;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return;
+
+ /* delete all VTEPs */
+ for (ALL_LIST_ELEMENTS(es_evi->es_evi_vtep_list, node, nnode,
+ evi_vtep)) {
+ evi_vtep->flags &= ~(BGP_EVPN_EVI_VTEP_EAD_PER_ES
+ | BGP_EVPN_EVI_VTEP_EAD_PER_EVI);
+ bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
+ bgp_evpn_es_evi_vtep_free(evi_vtep);
+ }
+ /* delete the EVI */
+ bgp_evpn_es_evi_remote_info_re_eval(es_evi);
+}
+
/* Initialize the ES tables maintained per-L2_VNI */
void bgp_evpn_vni_es_init(struct bgpevpn *vpn)
{
@@ -3345,7 +3609,9 @@ void bgp_evpn_vni_es_cleanup(struct bgpevpn *vpn)
RB_FOREACH_SAFE(es_evi, bgp_es_evi_rb_head,
&vpn->es_evi_rb_tree, es_evi_next) {
- bgp_evpn_local_es_evi_do_del(es_evi);
+ es_evi = bgp_evpn_local_es_evi_do_del(es_evi);
+ if (es_evi)
+ bgp_evpn_remote_es_evi_flush(es_evi);
}
list_delete(&vpn->local_es_evi_list);
@@ -3844,6 +4110,507 @@ static int bgp_evpn_run_consistency_checks(struct thread *t)
return 0;
}
+/*****************************************************************************
+ * EVPN-Nexthop and RMAC management: nexthops associated with Type-2 routes
+ * that have an ES as destination are consolidated by BGP into a per-VRF
+ * nh->rmac mapping which is sent to zebra. Zebra installs the nexthop
+ * as a remote neigh/fdb entry with a dummy (type-1) prefix referencing it.
+ *
+ * This handling is needed because Type-2 routes with ES as dest use NHG
+ * that is setup using EAD routes (i.e. such NHGs do not include the
+ * RMAC info).
+ ****************************************************************************/
+static void bgp_evpn_nh_zebra_update_send(struct bgp_evpn_nh *nh, bool add)
+{
+ struct stream *s;
+ struct bgp *bgp_vrf = nh->bgp_vrf;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp_vrf)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("No zebra instance, not %s remote nh %s",
+ add ? "adding" : "deleting", nh->nh_str);
+ return;
+ }
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(
+ s, add ? ZEBRA_EVPN_REMOTE_NH_ADD : ZEBRA_EVPN_REMOTE_NH_DEL,
+ bgp_vrf->vrf_id);
+ stream_putl(s, bgp_vrf->vrf_id);
+ stream_put(s, &nh->ip, sizeof(nh->ip));
+ if (add)
+ stream_put(s, &nh->rmac, sizeof(nh->rmac));
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) {
+ if (add)
+ zlog_debug("evpn vrf %s nh %s rmac %pEA add to zebra",
+ nh->bgp_vrf->name, nh->nh_str, &nh->rmac);
+ else if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s del to zebra",
+ nh->bgp_vrf->name, nh->nh_str);
+ }
+
+ zclient_send_message(zclient);
+}
+
+static void bgp_evpn_nh_zebra_update(struct bgp_evpn_nh *nh, bool add)
+{
+ if (add && !is_zero_mac(&nh->rmac)) {
+ nh->flags |= BGP_EVPN_NH_READY_FOR_ZEBRA;
+ bgp_evpn_nh_zebra_update_send(nh, true);
+ } else {
+ if (!(nh->flags & BGP_EVPN_NH_READY_FOR_ZEBRA))
+ return;
+ nh->flags &= ~BGP_EVPN_NH_READY_FOR_ZEBRA;
+ bgp_evpn_nh_zebra_update_send(nh, false);
+ }
+}
+
+static void *bgp_evpn_nh_alloc(void *p)
+{
+ struct bgp_evpn_nh *tmp_n = p;
+ struct bgp_evpn_nh *n;
+
+ n = XCALLOC(MTYPE_BGP_EVPN_NH, sizeof(struct bgp_evpn_nh));
+ *n = *tmp_n;
+
+ return ((void *)n);
+}
+
+static struct bgp_evpn_nh *bgp_evpn_nh_find(struct bgp *bgp_vrf,
+ struct ipaddr *ip)
+{
+ struct bgp_evpn_nh tmp;
+ struct bgp_evpn_nh *n;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.ip, ip, sizeof(struct ipaddr));
+ n = hash_lookup(bgp_vrf->evpn_nh_table, &tmp);
+
+ return n;
+}
+
+/* Add nexthop entry - implicitly created on first path reference */
+static struct bgp_evpn_nh *bgp_evpn_nh_add(struct bgp *bgp_vrf,
+ struct ipaddr *ip,
+ struct bgp_path_info *pi)
+{
+ struct bgp_evpn_nh tmp_n;
+ struct bgp_evpn_nh *n = NULL;
+
+ memset(&tmp_n, 0, sizeof(struct bgp_evpn_nh));
+ memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
+ n = hash_get(bgp_vrf->evpn_nh_table, &tmp_n, bgp_evpn_nh_alloc);
+ ipaddr2str(ip, n->nh_str, sizeof(n->nh_str));
+ n->bgp_vrf = bgp_vrf;
+
+ n->pi_list = list_new();
+ listset_app_node_mem(n->pi_list);
+
+ /* Setup ref_pi when the nh is created */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_VALID) && pi->attr) {
+ n->ref_pi = pi;
+ memcpy(&n->rmac, &pi->attr->rmac, ETH_ALEN);
+ }
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s rmac %pEA add", n->bgp_vrf->name,
+ n->nh_str, &n->rmac);
+ bgp_evpn_nh_zebra_update(n, true);
+ return n;
+}
+
+/* Delete nexthop entry if there are no paths referencing it */
+static void bgp_evpn_nh_del(struct bgp_evpn_nh *n)
+{
+ struct bgp_evpn_nh *tmp_n;
+ struct bgp *bgp_vrf = n->bgp_vrf;
+
+ if (listcount(n->pi_list))
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s del to zebra", bgp_vrf->name,
+ n->nh_str);
+
+ bgp_evpn_nh_zebra_update(n, false);
+ list_delete(&n->pi_list);
+ tmp_n = hash_release(bgp_vrf->evpn_nh_table, n);
+ XFREE(MTYPE_BGP_EVPN_NH, tmp_n);
+}
+
+static unsigned int bgp_evpn_nh_hash_keymake(const void *p)
+{
+ const struct bgp_evpn_nh *n = p;
+ const struct ipaddr *ip = &n->ip;
+
+ if (IS_IPADDR_V4(ip))
+ return jhash_1word(ip->ipaddr_v4.s_addr, 0);
+
+ return jhash2(ip->ipaddr_v6.s6_addr32,
+ array_size(ip->ipaddr_v6.s6_addr32), 0);
+}
+
+static bool bgp_evpn_nh_cmp(const void *p1, const void *p2)
+{
+ const struct bgp_evpn_nh *n1 = p1;
+ const struct bgp_evpn_nh *n2 = p2;
+
+ if (n1 == NULL && n2 == NULL)
+ return true;
+
+ if (n1 == NULL || n2 == NULL)
+ return false;
+
+ return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0);
+}
+
+void bgp_evpn_nh_init(struct bgp *bgp_vrf)
+{
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh init", bgp_vrf->name);
+ bgp_vrf->evpn_nh_table = hash_create(
+ bgp_evpn_nh_hash_keymake, bgp_evpn_nh_cmp, "BGP EVPN NH table");
+}
+
+static void bgp_evpn_nh_flush_entry(struct bgp_evpn_nh *nh)
+{
+ struct listnode *node;
+ struct listnode *nnode;
+ struct bgp_path_evpn_nh_info *nh_info;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s flush", nh->bgp_vrf->name,
+ nh->nh_str);
+
+ /* force flush paths */
+ for (ALL_LIST_ELEMENTS(nh->pi_list, node, nnode, nh_info))
+ bgp_evpn_path_nh_del(nh->bgp_vrf, nh_info->pi);
+}
+
+static void bgp_evpn_nh_flush_cb(struct hash_bucket *bucket, void *ctxt)
+{
+ struct bgp_evpn_nh *nh = (struct bgp_evpn_nh *)bucket->data;
+
+ bgp_evpn_nh_flush_entry(nh);
+}
+
+void bgp_evpn_nh_finish(struct bgp *bgp_vrf)
+{
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh finish", bgp_vrf->name);
+ hash_iterate(
+ bgp_vrf->evpn_nh_table,
+ (void (*)(struct hash_bucket *, void *))bgp_evpn_nh_flush_cb,
+ NULL);
+ hash_free(bgp_vrf->evpn_nh_table);
+ bgp_vrf->evpn_nh_table = NULL;
+}
+
+static void bgp_evpn_nh_update_ref_pi(struct bgp_evpn_nh *nh)
+{
+ struct listnode *node;
+ struct bgp_path_info *pi;
+ struct bgp_path_evpn_nh_info *nh_info;
+
+ if (nh->ref_pi)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(nh->pi_list, node, nh_info)) {
+ pi = nh_info->pi;
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID) || !pi->attr)
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s ref_pi update",
+ nh->bgp_vrf->name, nh->nh_str);
+ nh->ref_pi = pi;
+ /* If we have a new pi copy rmac from it and update
+ * zebra if the new rmac is different
+ */
+ if (memcmp(&nh->rmac, &nh->ref_pi->attr->rmac, ETH_ALEN)) {
+ memcpy(&nh->rmac, &nh->ref_pi->attr->rmac, ETH_ALEN);
+ bgp_evpn_nh_zebra_update(nh, true);
+ }
+ break;
+ }
+}
+
+static void bgp_evpn_nh_clear_ref_pi(struct bgp_evpn_nh *nh,
+ struct bgp_path_info *pi)
+{
+ if (nh->ref_pi != pi)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s ref_pi clear", nh->bgp_vrf->name,
+ nh->nh_str);
+ nh->ref_pi = NULL;
+ /* try to find another ref_pi */
+ bgp_evpn_nh_update_ref_pi(nh);
+ /* couldn't find one - clear the old rmac and notify zebra */
+ if (!nh->ref_pi) {
+ memset(&nh->rmac, 0, ETH_ALEN);
+ bgp_evpn_nh_zebra_update(nh, true);
+ }
+}
+
+static void bgp_evpn_path_nh_info_free(struct bgp_path_evpn_nh_info *nh_info)
+{
+ bgp_evpn_path_nh_unlink(nh_info);
+ XFREE(MTYPE_BGP_EVPN_PATH_NH_INFO, nh_info);
+}
+
+static struct bgp_path_evpn_nh_info *
+bgp_evpn_path_nh_info_new(struct bgp_path_info *pi)
+{
+ struct bgp_path_info_extra *e;
+ struct bgp_path_mh_info *mh_info;
+ struct bgp_path_evpn_nh_info *nh_info;
+
+ e = bgp_path_info_extra_get(pi);
+
+ /* If mh_info doesn't exist allocate it */
+ mh_info = e->mh_info;
+ if (!mh_info)
+ e->mh_info = mh_info = XCALLOC(MTYPE_BGP_EVPN_PATH_MH_INFO,
+ sizeof(struct bgp_path_mh_info));
+
+ /* If nh_info doesn't exist allocate it */
+ nh_info = mh_info->nh_info;
+ if (!nh_info) {
+ mh_info->nh_info = nh_info =
+ XCALLOC(MTYPE_BGP_EVPN_PATH_NH_INFO,
+ sizeof(struct bgp_path_evpn_nh_info));
+ nh_info->pi = pi;
+ }
+
+ return nh_info;
+}
+
+static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info)
+{
+ struct bgp_evpn_nh *nh = nh_info->nh;
+ struct bgp_path_info *pi;
+ char prefix_buf[PREFIX_STRLEN];
+
+ if (!nh)
+ return;
+
+ pi = nh_info->pi;
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("path %s unlinked from nh %s %s",
+ pi->net ? prefix2str(&pi->net->p, prefix_buf,
+ sizeof(prefix_buf))
+ : "",
+ nh->bgp_vrf->name, nh->nh_str);
+
+ list_delete_node(nh->pi_list, &nh_info->nh_listnode);
+
+ nh_info->nh = NULL;
+
+ /* check if the ref_pi need to be updated */
+ bgp_evpn_nh_clear_ref_pi(nh, pi);
+
+ /* if there are no other references against the nh it
+ * needs to be freed
+ */
+ bgp_evpn_nh_del(nh);
+
+ /* Note we don't free the path nh_info on unlink; it will be freed up
+ * along with the path.
+ */
+}
+
+static void bgp_evpn_path_nh_link(struct bgp *bgp_vrf, struct bgp_path_info *pi)
+{
+ struct bgp_path_evpn_nh_info *nh_info;
+ struct bgp_evpn_nh *nh;
+ struct ipaddr ip;
+
+ /* EVPN nexthop setup in bgp has been turned off */
+ if (!bgp_mh_info->bgp_evpn_nh_setup)
+ return;
+
+ if (!bgp_vrf->evpn_nh_table) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("path %pFX linked to vrf %s failed",
+ &pi->net->p, bgp_vrf->name);
+ return;
+ }
+
+ nh_info = (pi->extra && pi->extra->mh_info)
+ ? pi->extra->mh_info->nh_info
+ : NULL;
+
+ /* if NHG is not being used for this path we don't need to manage the
+ * nexthops in bgp (they are managed by zebra instead)
+ */
+ if (!(pi->attr->es_flags & ATTR_ES_L3_NHG_USE)) {
+ if (nh_info)
+ bgp_evpn_path_nh_unlink(nh_info);
+ return;
+ }
+
+ /* setup nh_info against the path if it doesn't aleady exist */
+ if (!nh_info)
+ nh_info = bgp_evpn_path_nh_info_new(pi);
+
+ /* find-create nh */
+ memset(&ip, 0, sizeof(ip));
+ if (pi->net->p.family == AF_INET6) {
+ SET_IPADDR_V6(&ip);
+ memcpy(&ip.ipaddr_v6, &pi->attr->mp_nexthop_global,
+ sizeof(ip.ipaddr_v6));
+ } else {
+ SET_IPADDR_V4(&ip);
+ memcpy(&ip.ipaddr_v4, &pi->attr->nexthop, sizeof(ip.ipaddr_v4));
+ }
+
+ nh = bgp_evpn_nh_find(bgp_vrf, &ip);
+ if (!nh)
+ nh = bgp_evpn_nh_add(bgp_vrf, &ip, pi);
+
+ /* dup check */
+ if (nh_info->nh == nh) {
+ /* Check if any of the paths are now valid */
+ bgp_evpn_nh_update_ref_pi(nh);
+ return;
+ }
+
+ /* unlink old nh if any */
+ bgp_evpn_path_nh_unlink(nh_info);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("path %pFX linked to nh %s %s", &pi->net->p,
+ nh->bgp_vrf->name, nh->nh_str);
+
+ /* link mac-ip path to the new nh */
+ nh_info->nh = nh;
+ listnode_init(&nh_info->nh_listnode, nh_info);
+ listnode_add(nh->pi_list, &nh_info->nh_listnode);
+ /* If a new valid path got linked to the nh see if can get the rmac
+ * from it
+ */
+ bgp_evpn_nh_update_ref_pi(nh);
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) {
+ if (!nh->ref_pi)
+ zlog_debug(
+ "path %pFX linked to nh %s %s with no valid pi",
+ &pi->net->p, nh->bgp_vrf->name, nh->nh_str);
+ }
+}
+
+void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi)
+{
+ struct bgp_path_evpn_nh_info *nh_info;
+
+ nh_info = (pi->extra && pi->extra->mh_info)
+ ? pi->extra->mh_info->nh_info
+ : NULL;
+
+ if (!nh_info)
+ return;
+
+ bgp_evpn_path_nh_unlink(nh_info);
+}
+
+void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi)
+{
+ bgp_evpn_path_nh_link(bgp_vrf, pi);
+}
+
+static void bgp_evpn_nh_show_entry(struct bgp_evpn_nh *nh, struct vty *vty,
+ json_object *json_array)
+{
+ json_object *json = NULL;
+ char mac_buf[ETHER_ADDR_STRLEN];
+ char prefix_buf[PREFIX_STRLEN];
+
+ if (json_array)
+ /* create a separate json object for each ES */
+ json = json_object_new_object();
+
+ prefix_mac2str(&nh->rmac, mac_buf, sizeof(mac_buf));
+ if (nh->ref_pi && nh->ref_pi->net)
+ prefix2str(&nh->ref_pi->net->p, prefix_buf, sizeof(prefix_buf));
+ else
+ prefix_buf[0] = '\0';
+ if (json) {
+ json_object_string_add(json, "vrf", nh->bgp_vrf->name);
+ json_object_string_add(json, "ip", nh->nh_str);
+ json_object_string_add(json, "rmac", mac_buf);
+ json_object_string_add(json, "basePath", prefix_buf);
+ json_object_int_add(json, "pathCount", listcount(nh->pi_list));
+ } else {
+ vty_out(vty, "%-15s %-15s %-17s %-10d %s\n", nh->bgp_vrf->name,
+ nh->nh_str, mac_buf, listcount(nh->pi_list),
+ prefix_buf);
+ }
+
+ /* add ES to the json array */
+ if (json_array)
+ json_object_array_add(json_array, json);
+}
+
+struct nh_show_ctx {
+ struct vty *vty;
+ json_object *json;
+};
+
+static void bgp_evpn_nh_show_hash_cb(struct hash_bucket *bucket, void *ctxt)
+{
+ struct bgp_evpn_nh *nh = (struct bgp_evpn_nh *)bucket->data;
+ struct nh_show_ctx *wctx = (struct nh_show_ctx *)ctxt;
+
+ bgp_evpn_nh_show_entry(nh, wctx->vty, wctx->json);
+}
+
+/* Display all evpn nexthops */
+void bgp_evpn_nh_show(struct vty *vty, bool uj)
+{
+ json_object *json_array = NULL;
+ struct bgp *bgp_vrf;
+ struct listnode *node;
+ struct nh_show_ctx wctx;
+
+ if (uj) {
+ /* create an array of nexthops */
+ json_array = json_object_new_array();
+ } else {
+ vty_out(vty, "%-15s %-15s %-17s %-10s %s\n", "VRF", "IP",
+ "RMAC", "#Paths", "Base Path");
+ }
+
+ wctx.vty = vty;
+ wctx.json = json_array;
+
+ /* walk through all vrfs */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ hash_iterate(bgp_vrf->evpn_nh_table,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_nh_show_hash_cb,
+ &wctx);
+ }
+
+ /* print the array of json-ESs */
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json_array, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json_array);
+ }
+}
+
/*****************************************************************************/
void bgp_evpn_mh_init(void)
{
@@ -3866,6 +4633,8 @@ void bgp_evpn_mh_init(void)
bgp_mh_info->consistency_checking = true;
bgp_mh_info->install_l3nhg = false;
bgp_mh_info->host_routes_use_l3nhg = BGP_EVPN_MH_USE_ES_L3NHG_DEF;
+ bgp_mh_info->suppress_l3_ecomm_on_inactive_es = true;
+ bgp_mh_info->bgp_evpn_nh_setup = true;
memset(&zero_esi_buf, 0, sizeof(esi_t));
}
@@ -3880,7 +4649,7 @@ void bgp_evpn_mh_finish(void)
RB_FOREACH_SAFE (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree,
es_next) {
- bgp_evpn_es_local_info_clear(es);
+ bgp_evpn_es_local_info_clear(es, true);
}
if (bgp_mh_info->t_cons_check)
thread_cancel(&bgp_mh_info->t_cons_check);
diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h
index 8c66e391b6..c96de86871 100644
--- a/bgpd/bgp_evpn_mh.h
+++ b/bgpd/bgp_evpn_mh.h
@@ -105,8 +105,17 @@ struct bgp_evpn_es {
/* List of MAC-IP VNI paths using this ES as destination -
* element is bgp_path_info_extra->es_info
+ * Note: Only local/zebra-added MACIP paths in the VNI
+ * routing table are linked to this list
*/
- struct list *macip_path_list;
+ struct list *macip_evi_path_list;
+
+ /* List of MAC-IP paths in the global routing table using this
+ * ES as destination - data is bgp_path_info_extra->es_info
+ * Note: Only non-local/imported MACIP paths in the global
+ * routing table are linked to this list
+ */
+ struct list *macip_global_path_list;
/* Number of remote VNIs referencing this ES */
uint32_t remote_es_evi_cnt;
@@ -241,6 +250,26 @@ struct bgp_evpn_es_evi_vtep {
struct bgp_evpn_es_vtep *es_vtep;
};
+/* A nexthop is created when a path (imported from an EVPN type-2 route)
+ * is added to the VRF route table using that nexthop.
+ * It is added on first pi reference and removed on last pi deref.
+ */
+struct bgp_evpn_nh {
+ /* backpointer to the VRF */
+ struct bgp *bgp_vrf;
+ /* nexthop/VTEP IP */
+ struct ipaddr ip;
+ /* description for easy logging */
+ char nh_str[INET6_ADDRSTRLEN];
+ struct ethaddr rmac;
+ /* pi from which we are pulling the nh RMAC */
+ struct bgp_path_info *ref_pi;
+ /* List of VRF paths using this nexthop */
+ struct list *pi_list;
+ uint8_t flags;
+#define BGP_EVPN_NH_READY_FOR_ZEBRA (1 << 0)
+};
+
/* multihoming information stored in bgp_master */
#define bgp_mh_info (bm->mh_info)
struct bgp_evpn_mh_info {
@@ -273,6 +302,12 @@ struct bgp_evpn_mh_info {
/* Skip EAD-EVI advertisements by turning off this knob */
bool ead_evi_tx;
#define BGP_EVPN_MH_EAD_EVI_TX_DEF true
+ /* If the Local ES is inactive we advertise the MAC-IP without the
+ * L3 ecomm
+ */
+ bool suppress_l3_ecomm_on_inactive_es;
+ /* Setup EVPN PE nexthops and their RMAC in bgpd */
+ bool bgp_evpn_nh_setup;
};
/****************************************************************************/
@@ -330,6 +365,12 @@ static inline uint32_t bgp_evpn_attr_get_df_pref(struct attr *attr)
return (attr) ? attr->df_pref : 0;
}
+static inline bool bgp_evpn_local_es_is_active(struct bgp_evpn_es *es)
+{
+ return (es->flags & BGP_EVPNES_OPER_UP)
+ && !(es->flags & BGP_EVPNES_BYPASS);
+}
+
/****************************************************************************/
extern int bgp_evpn_es_route_install_uninstall(struct bgp *bgp,
struct bgp_evpn_es *es, afi_t afi, safi_t safi,
@@ -362,21 +403,28 @@ void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni,
bool uj, bool detail);
void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail);
struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi);
-extern bool bgp_evpn_is_esi_local(esi_t *esi);
extern void bgp_evpn_vrf_es_init(struct bgp *bgp_vrf);
+extern bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi);
extern void bgp_evpn_es_vrf_deref(struct bgp_evpn_es_evi *es_evi);
extern void bgp_evpn_es_vrf_ref(struct bgp_evpn_es_evi *es_evi,
struct bgp *bgp_vrf);
-extern void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info);
-extern void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info);
+extern void bgp_evpn_path_mh_info_free(struct bgp_path_mh_info *mh_info);
extern void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni,
esi_t *esi);
-extern bool bgp_evpn_es_is_vtep_active(esi_t *esi, struct in_addr nh);
extern bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf,
struct bgp_path_info *pi, uint32_t *nhg_p);
extern void bgp_evpn_es_vrf_show(struct vty *vty, bool uj,
struct bgp_evpn_es *es);
extern void bgp_evpn_es_vrf_show_esi(struct vty *vty, esi_t *esi, bool uj);
extern void bgp_evpn_switch_ead_evi_rx(void);
+extern bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi);
+extern void bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi,
+ bool *use_l3nhg, bool *is_l3nhg_active,
+ struct bgp_evpn_es_vrf **es_vrf_p);
+extern void bgp_evpn_nh_init(struct bgp *bgp_vrf);
+extern void bgp_evpn_nh_finish(struct bgp *bgp_vrf);
+extern void bgp_evpn_nh_show(struct vty *vty, bool uj);
+extern void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi);
+extern void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi);
#endif /* _FRR_BGP_EVPN_MH_H */
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index ff4970af41..debed9f68b 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -631,4 +631,13 @@ bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi,
const struct prefix_evpn *evp,
struct prefix_rd *prd);
extern void bgp_evpn_import_route_in_vrfs(struct bgp_path_info *pi, int import);
+extern void bgp_evpn_update_type2_route_entry(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ struct bgp_node *rn,
+ struct bgp_path_info *local_pi,
+ const char *caller);
+extern int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
+ struct bgp_path_info *pi,
+ int install);
+extern void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import);
#endif /* _BGP_EVPN_PRIVATE_H */
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index b101589a79..5a0258f3bf 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -688,7 +688,8 @@ static void show_esi_routes(struct bgp *bgp,
/* Display all MAC-IP VNI routes linked to an ES */
static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi,
- json_object *json, int detail)
+ json_object *json, int detail,
+ bool global_table)
{
struct bgp_node *rn;
struct bgp_path_info *pi;
@@ -709,11 +710,17 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi,
json_paths = json_object_new_array();
RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
+ struct list *es_list;
if (esi && memcmp(esi, &es->esi, sizeof(*esi)))
continue;
- for (ALL_LIST_ELEMENTS_RO(es->macip_path_list, node, es_info)) {
+ if (global_table)
+ es_list = es->macip_global_path_list;
+ else
+ es_list = es->macip_evi_path_list;
+
+ for (ALL_LIST_ELEMENTS_RO(es_list, node, es_info)) {
json_object *json_path = NULL;
pi = es_info->pi;
@@ -734,9 +741,9 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi,
json_path = json_object_new_array();
if (detail)
- route_vty_out_detail(vty, bgp, rn, pi,
- AFI_L2VPN, SAFI_EVPN,
- json_path);
+ route_vty_out_detail(
+ vty, bgp, rn, pi, AFI_L2VPN, SAFI_EVPN,
+ RPKI_NOT_BEING_USED, json_path);
else
route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN,
json_path, false);
@@ -758,6 +765,18 @@ static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi,
}
}
+static void bgp_evpn_show_routes_mac_ip_evi_es(struct vty *vty, esi_t *esi,
+ json_object *json, int detail)
+{
+ return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false);
+}
+
+static void bgp_evpn_show_routes_mac_ip_global_es(struct vty *vty, esi_t *esi,
+ json_object *json, int detail)
+{
+ return bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true);
+}
+
static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
struct vty *vty, struct in_addr vtep_ip,
json_object *json, int detail)
@@ -823,6 +842,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
if (detail)
route_vty_out_detail(vty, bgp, dest, pi,
AFI_L2VPN, SAFI_EVPN,
+ RPKI_NOT_BEING_USED,
json_path);
else
route_vty_out(vty, p, pi, 0, SAFI_EVPN,
@@ -2367,7 +2387,8 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp,
if (json)
json_path = json_object_new_array();
- route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path);
+ route_vty_out_detail(vty, bgp, dest, pi, afi, safi,
+ RPKI_NOT_BEING_USED, json_path);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2436,7 +2457,8 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
if (json)
json_path = json_object_new_array();
- route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path);
+ route_vty_out_detail(vty, bgp, dest, pi, afi, safi,
+ RPKI_NOT_BEING_USED, json_path);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2541,7 +2563,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp,
if (json)
json_path = json_object_new_array();
- route_vty_out_detail(vty, bgp, dest, pi, afi, safi, json_path);
+ route_vty_out_detail(vty, bgp, dest, pi, afi, safi,
+ RPKI_NOT_BEING_USED, json_path);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2651,7 +2674,7 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp,
json_path = json_object_new_array();
route_vty_out_detail(vty, bgp, dest, pi, afi, safi,
- json_path);
+ RPKI_NOT_BEING_USED, json_path);
if (json)
json_object_array_add(json_paths, json_path);
@@ -2820,7 +2843,8 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
if (detail) {
route_vty_out_detail(
vty, bgp, dest, pi, AFI_L2VPN,
- SAFI_EVPN, json_path);
+ SAFI_EVPN, RPKI_NOT_BEING_USED,
+ json_path);
} else
route_vty_out(vty, p, pi, 0, SAFI_EVPN,
json_path, false);
@@ -4100,6 +4124,21 @@ DEFPY(show_bgp_l2vpn_evpn_es_vrf, show_bgp_l2vpn_evpn_es_vrf_cmd,
return CMD_SUCCESS;
}
+DEFPY(show_bgp_l2vpn_evpn_nh,
+ show_bgp_l2vpn_evpn_nh_cmd,
+ "show bgp l2vpn evpn next-hops [json$uj]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Nexthops\n"
+ JSON_STR)
+{
+ bgp_evpn_nh_show(vty, uj);
+
+ return CMD_SUCCESS;
+}
+
/*
* Display EVPN neighbor summary.
*/
@@ -4658,12 +4697,49 @@ DEFUN(show_bgp_l2vpn_evpn_route_vni_all,
}
DEFPY_HIDDEN(
- show_bgp_l2vpn_evpn_route_mac_ip_es,
- show_bgp_l2vpn_evpn_route_mac_ip_es_cmd,
- "show bgp l2vpn evpn route mac-ip-es [NAME$esi_str|detail$detail] [json$uj]",
+ show_bgp_l2vpn_evpn_route_mac_ip_evi_es,
+ show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd,
+ "show bgp l2vpn evpn route mac-ip-evi-es [NAME$esi_str|detail$detail] [json$uj]",
+ SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "MAC IP routes in the EVI tables linked to the ES\n"
+ "ES ID\n"
+ "Detailed information\n" JSON_STR)
+{
+ esi_t esi;
+ esi_t *esi_p;
+ json_object *json = NULL;
+
+ if (esi_str) {
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%%Malformed ESI\n");
+ return CMD_WARNING;
+ }
+ esi_p = &esi;
+ } else {
+ esi_p = NULL;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+ bgp_evpn_show_routes_mac_ip_evi_es(vty, esi_p, json, !!detail);
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY_HIDDEN(
+ show_bgp_l2vpn_evpn_route_mac_ip_global_es,
+ show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd,
+ "show bgp l2vpn evpn route mac-ip-global-es [NAME$esi_str|detail$detail] [json$uj]",
SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR
"EVPN route information\n"
- "MAC IP routes linked to the ES\n"
+ "MAC IP routes in the global table linked to the ES\n"
"ES ID\n"
"Detailed information\n" JSON_STR)
{
@@ -4683,7 +4759,7 @@ DEFPY_HIDDEN(
if (uj)
json = json_object_new_object();
- bgp_evpn_show_routes_mac_ip_es(vty, esi_p, json, !!detail);
+ bgp_evpn_show_routes_mac_ip_global_es(vty, esi_p, json, !!detail);
if (uj) {
vty_out(vty, "%s\n",
json_object_to_json_string_ext(
@@ -5957,6 +6033,7 @@ void bgp_ethernetvpn_init(void)
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_evi_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
@@ -5968,7 +6045,10 @@ void bgp_ethernetvpn_init(void)
&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd);
- install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_mac_ip_es_cmd);
+ install_element(VIEW_NODE,
+ &show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd);
+ install_element(VIEW_NODE,
+ &show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd);
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index 9a4eb5d3bd..36bdc05eb7 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -119,7 +119,10 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value");
DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information");
DEFINE_MTYPE(BGPD, BGP_EVPN_MH_INFO, "BGP EVPN MH Information");
DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP");
+DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_MH_INFO, "BGP EVPN PATH MH Information");
DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_ES_INFO, "BGP EVPN PATH ES Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_NH_INFO, "BGP EVPN PATH NH Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_NH, "BGP EVPN Nexthop");
DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI_VTEP, "BGP EVPN ES-EVI VTEP");
DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI Information");
DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI, "BGP EVPN ES-per-EVI Information");
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
index 7b839f1d4c..29923424e3 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -118,6 +118,9 @@ DECLARE_MTYPE(BGP_EVPN_ES_EVI);
DECLARE_MTYPE(BGP_EVPN_ES_VRF);
DECLARE_MTYPE(BGP_EVPN_ES_VTEP);
DECLARE_MTYPE(BGP_EVPN_PATH_ES_INFO);
+DECLARE_MTYPE(BGP_EVPN_PATH_MH_INFO);
+DECLARE_MTYPE(BGP_EVPN_PATH_NH_INFO);
+DECLARE_MTYPE(BGP_EVPN_NH);
DECLARE_MTYPE(BGP_EVPN_ES_EVI_VTEP);
DECLARE_MTYPE(BGP_EVPN);
diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c
index b74cf37ac7..6f75856d54 100644
--- a/bgpd/bgp_mplsvpn_snmp.c
+++ b/bgpd/bgp_mplsvpn_snmp.c
@@ -1478,10 +1478,16 @@ static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[],
oid_copy_str(&name[namelen], (*l3vpn_bgp)->name,
vrf_name_len);
oid_index = namelen + vrf_name_len;
- name[oid_index++] =
- v4 ? INETADDRESSTYPEIPV4 : INETADDRESSTYPEIPV6;
- oid_copy_addr(&name[oid_index], &p->u.prefix4,
- addr_len);
+ if (v4) {
+ name[oid_index++] = INETADDRESSTYPEIPV4;
+ oid_copy_in_addr(&name[oid_index],
+ &p->u.prefix4);
+ } else {
+ name[oid_index++] = INETADDRESSTYPEIPV6;
+ oid_copy_in6_addr(&name[oid_index],
+ &p->u.prefix6);
+ }
+
oid_index += addr_len;
name[oid_index++] = p->prefixlen;
name[oid_index++] = *policy >> 8;
@@ -1493,9 +1499,8 @@ static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[],
INETADDRESSTYPEUNKNOWN;
else {
name[oid_index++] = INETADDRESSTYPEIPV4;
- oid_copy_addr(&name[oid_index],
- &attr->nexthop,
- sizeof(struct in_addr));
+ oid_copy_in_addr(&name[oid_index],
+ &attr->nexthop);
oid_index += sizeof(struct in_addr);
}
} else {
@@ -1505,11 +1510,9 @@ static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[],
INETADDRESSTYPEUNKNOWN;
else {
name[oid_index++] = INETADDRESSTYPEIPV6;
- oid_copy_addr(
+ oid_copy_in6_addr(
&name[oid_index],
- (struct in_addr *)&attr
- ->mp_nexthop_global,
- sizeof(struct in6_addr));
+ &attr->mp_nexthop_global);
oid_index += sizeof(struct in6_addr);
}
}
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 124a477248..a6bfb519d8 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -71,6 +71,7 @@
#include "bgpd/bgp_mac.h"
#include "bgpd/bgp_network.h"
#include "bgpd/bgp_trace.h"
+#include "bgpd/bgp_rpki.h"
#ifdef ENABLE_BGP_VNC
#include "bgpd/rfapi/rfapi_backend.h"
@@ -250,8 +251,8 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra)
if (e->aggr_suppressors)
list_delete(&e->aggr_suppressors);
- if (e->es_info)
- bgp_evpn_path_es_info_free(e->es_info);
+ if (e->mh_info)
+ bgp_evpn_path_mh_info_free(e->mh_info);
if ((*extra)->bgp_fs_iprule)
list_delete(&((*extra)->bgp_fs_iprule));
@@ -7551,18 +7552,20 @@ static const char *bgp_origin2str(uint8_t origin)
return "n/a";
}
-static const char *bgp_rpki_validation2str(int v_state)
+static const char *bgp_rpki_validation2str(enum rpki_states v_state)
{
switch (v_state) {
- case 1:
+ case RPKI_NOT_BEING_USED:
+ return "not used";
+ case RPKI_VALID:
return "valid";
- case 2:
+ case RPKI_NOTFOUND:
return "not found";
- case 3:
+ case RPKI_INVALID:
return "invalid";
- default:
- break;
}
+
+ assert(!"We should never get here this is a dev escape");
return "ERROR";
}
@@ -8856,15 +8859,17 @@ void route_vty_out(struct vty *vty, const struct prefix *p,
if (safi == SAFI_EVPN) {
struct bgp_path_es_info *path_es_info = NULL;
- if (path->extra)
- path_es_info = path->extra->es_info;
-
if (bgp_evpn_is_esi_valid(&attr->esi)) {
/* XXX - add these params to the json out */
vty_out(vty, "%*s", 20, " ");
vty_out(vty, "ESI:%s",
esi_to_str(&attr->esi, esi_buf,
sizeof(esi_buf)));
+
+ if (path->extra && path->extra->mh_info)
+ path_es_info =
+ path->extra->mh_info->es_info;
+
if (path_es_info && path_es_info->es)
vty_out(vty, " VNI: %u",
path_es_info->vni);
@@ -9547,9 +9552,10 @@ static void route_vty_out_detail_es_info(struct vty *vty,
}
}
-void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
- struct bgp_dest *bn, struct bgp_path_info *path,
- afi_t afi, safi_t safi, json_object *json_paths)
+void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
+ struct bgp_path_info *path, afi_t afi, safi_t safi,
+ enum rpki_states rpki_curr_state,
+ json_object *json_paths)
{
char buf[INET6_ADDRSTRLEN];
char buf1[BUFSIZ];
@@ -9580,7 +9586,6 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
int i;
char *nexthop_hostname =
bgp_nexthop_hostname(path->peer, path->nexthop);
- int rpki_validation_state = 0;
if (json_paths) {
json_path = json_object_new_object();
@@ -9626,12 +9631,20 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
buf1, sizeof(buf1));
if (is_pi_family_evpn(parent_ri)) {
vty_out(vty,
- " Imported from %s:%pFX, VNI %s\n",
+ " Imported from %s:%pFX, VNI %s",
buf1,
(struct prefix_evpn *)
bgp_dest_get_prefix(
dest),
tag_buf);
+ if (attr->es_flags & ATTR_ES_L3_NHG)
+ vty_out(vty, ", L3NHG %s",
+ (attr->es_flags
+ & ATTR_ES_L3_NHG_ACTIVE)
+ ? "active"
+ : "inactive");
+ vty_out(vty, "\n");
+
} else
vty_out(vty,
" Imported from %s:%pFX\n",
@@ -10179,18 +10192,14 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
}
}
- const struct prefix *p = bgp_dest_get_prefix(bn);
- if (p->family == AF_INET || p->family == AF_INET6)
- rpki_validation_state = hook_call(bgp_rpki_prefix_status,
- path->peer, path->attr, p);
- if (rpki_validation_state) {
+ if (rpki_curr_state != RPKI_NOT_BEING_USED) {
if (json_paths)
json_object_string_add(
json_path, "rpkiValidationState",
- bgp_rpki_validation2str(rpki_validation_state));
+ bgp_rpki_validation2str(rpki_curr_state));
else
- vty_out(vty, ", validation-state: %s",
- bgp_rpki_validation2str(rpki_validation_state));
+ vty_out(vty, ", rpki validation-state: %s",
+ bgp_rpki_validation2str(rpki_curr_state));
}
if (json_bestpath)
@@ -10510,7 +10519,8 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
struct bgp_table *table, enum bgp_show_type type,
void *output_arg, char *rd, int is_last,
unsigned long *output_cum, unsigned long *total_cum,
- unsigned long *json_header_depth, uint8_t show_flags)
+ unsigned long *json_header_depth, uint8_t show_flags,
+ enum rpki_states rpki_target_state)
{
struct bgp_path_info *pi;
struct bgp_dest *dest;
@@ -10559,6 +10569,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
/* Start processing of routes. */
for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+ enum rpki_states rpki_curr_state = RPKI_NOT_BEING_USED;
pi = bgp_dest_get_bgp_path_info(dest);
if (pi == NULL)
@@ -10572,6 +10583,18 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
for (; pi; pi = pi->next) {
total_count++;
+
+ if (type == bgp_show_type_rpki) {
+ if (dest_p->family == AF_INET
+ || dest_p->family == AF_INET6)
+ rpki_curr_state = hook_call(
+ bgp_rpki_prefix_status,
+ pi->peer, pi->attr, dest_p);
+ if (rpki_target_state != RPKI_NOT_BEING_USED
+ && rpki_curr_state != rpki_target_state)
+ continue;
+ }
+
if (type == bgp_show_type_flap_statistics
|| type == bgp_show_type_flap_neighbor
|| type == bgp_show_type_dampend_paths
@@ -10881,7 +10904,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
bgp_show_table(vty, bgp, safi, itable, type, output_arg,
rd, next == NULL, &output_cum,
&total_cum, &json_header_depth,
- show_flags);
+ show_flags, RPKI_NOT_BEING_USED);
if (next == NULL)
show_msg = false;
}
@@ -10899,7 +10922,7 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
}
static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
enum bgp_show_type type, void *output_arg,
- uint8_t show_flags)
+ uint8_t show_flags, enum rpki_states rpki_target_state)
{
struct bgp_table *table;
unsigned long json_header_depth = 0;
@@ -10934,7 +10957,8 @@ static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
safi = SAFI_UNICAST;
return bgp_show_table(vty, bgp, safi, table, type, output_arg, NULL, 1,
- NULL, NULL, &json_header_depth, show_flags);
+ NULL, NULL, &json_header_depth, show_flags,
+ rpki_target_state);
}
static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi,
@@ -10968,7 +10992,7 @@ static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi,
: bgp->name);
}
bgp_show(vty, bgp, afi, safi, bgp_show_type_normal, NULL,
- show_flags);
+ show_flags, RPKI_NOT_BEING_USED);
}
if (use_json)
@@ -11187,15 +11211,25 @@ static void bgp_show_path_info(struct prefix_rd *pfx_rd,
struct bgp_dest *bgp_node, struct vty *vty,
struct bgp *bgp, afi_t afi, safi_t safi,
json_object *json, enum bgp_path_type pathtype,
- int *display)
+ int *display, enum rpki_states rpki_target_state)
{
struct bgp_path_info *pi;
int header = 1;
char rdbuf[RD_ADDRSTRLEN];
json_object *json_header = NULL;
json_object *json_paths = NULL;
+ const struct prefix *p = bgp_dest_get_prefix(bgp_node);
for (pi = bgp_dest_get_bgp_path_info(bgp_node); pi; pi = pi->next) {
+ enum rpki_states rpki_curr_state = RPKI_NOT_BEING_USED;
+
+ if (p->family == AF_INET || p->family == AF_INET6)
+ rpki_curr_state = hook_call(bgp_rpki_prefix_status,
+ pi->peer, pi->attr, p);
+
+ if (rpki_target_state != RPKI_NOT_BEING_USED
+ && rpki_curr_state != rpki_target_state)
+ continue;
if (json && !json_paths) {
/* Instantiate json_paths only if path is valid */
@@ -11221,9 +11255,8 @@ static void bgp_show_path_info(struct prefix_rd *pfx_rd,
|| (pathtype == BGP_PATH_SHOW_MULTIPATH
&& (CHECK_FLAG(pi->flags, BGP_PATH_MULTIPATH)
|| CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))))
- route_vty_out_detail(vty, bgp, bgp_node,
- pi, AFI_IP, safi,
- json_paths);
+ route_vty_out_detail(vty, bgp, bgp_node, pi, AFI_IP,
+ safi, rpki_curr_state, json_paths);
}
if (json && json_paths) {
@@ -11238,6 +11271,7 @@ static void bgp_show_path_info(struct prefix_rd *pfx_rd,
static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
struct bgp_table *rib, const char *ip_str,
afi_t afi, safi_t safi,
+ enum rpki_states rpki_target_state,
struct prefix_rd *prd, int prefix_check,
enum bgp_path_type pathtype, bool use_json)
{
@@ -11285,7 +11319,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty,
bgp, afi, safi, json, pathtype,
- &display);
+ &display, rpki_target_state);
bgp_dest_unlock_node(rm);
}
@@ -11344,7 +11378,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty,
bgp, afi, safi, json, pathtype,
- &display);
+ &display, rpki_target_state);
bgp_dest_unlock_node(rm);
}
@@ -11371,7 +11405,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
|| dest_p->prefixlen == match.prefixlen) {
bgp_show_path_info(NULL, dest, vty, bgp, afi,
safi, json, pathtype,
- &display);
+ &display, rpki_target_state);
}
bgp_dest_unlock_node(dest);
@@ -11397,7 +11431,7 @@ static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str,
afi_t afi, safi_t safi, struct prefix_rd *prd,
int prefix_check, enum bgp_path_type pathtype,
- bool use_json)
+ enum rpki_states rpki_target_state, bool use_json)
{
if (!bgp) {
bgp = bgp_get_default();
@@ -11415,8 +11449,8 @@ static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str,
safi = SAFI_UNICAST;
return bgp_show_route_in_table(vty, bgp, bgp->rib[afi][safi], ip_str,
- afi, safi, prd, prefix_check, pathtype,
- use_json);
+ afi, safi, rpki_target_state, prd,
+ prefix_check, pathtype, use_json);
}
static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc,
@@ -11458,9 +11492,9 @@ static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc,
}
ret = bgp_show(vty, bgp, afi, safi,
- (exact ? bgp_show_type_lcommunity_exact
- : bgp_show_type_lcommunity),
- lcom, show_flags);
+ (exact ? bgp_show_type_lcommunity_exact
+ : bgp_show_type_lcommunity),
+ lcom, show_flags, RPKI_NOT_BEING_USED);
lcommunity_free(&lcom);
return ret;
@@ -11488,7 +11522,7 @@ static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp,
return bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_lcommunity_list_exact
: bgp_show_type_lcommunity_list),
- list, show_flags);
+ list, show_flags, RPKI_NOT_BEING_USED);
}
DEFUN (show_ip_bgp_large_community_list,
@@ -11570,7 +11604,8 @@ DEFUN (show_ip_bgp_large_community,
exact_match, afi, safi, uj);
} else
return bgp_show(vty, bgp, afi, safi,
- bgp_show_type_lcommunity_all, NULL, show_flags);
+ bgp_show_type_lcommunity_all, NULL, show_flags,
+ RPKI_NOT_BEING_USED);
}
static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi,
@@ -11818,6 +11853,7 @@ DEFPY (show_ip_bgp_json,
|accept-own|accept-own-nexthop|route-filter-v6\
|route-filter-v4|route-filter-translated-v6\
|route-filter-translated-v4] [exact-match]\
+ |rpki <invalid|valid|notfound>\
] [json$uj | wide$wide]",
SHOW_STR
IP_STR
@@ -11847,6 +11883,10 @@ DEFPY (show_ip_bgp_json,
"RT translated VPNv6 route filtering (well-known community)\n"
"RT translated VPNv4 route filtering (well-known community)\n"
"Exact match of the communities\n"
+ "RPKI route types\n"
+ "A valid path as determined by rpki\n"
+ "A invalid path as determined by rpki\n"
+ "A path that has no rpki data\n"
JSON_STR
"Increase table width for longer prefixes\n")
{
@@ -11859,7 +11899,7 @@ DEFPY (show_ip_bgp_json,
char *community = NULL;
bool first = true;
uint8_t show_flags = 0;
-
+ enum rpki_states rpki_target_state = RPKI_NOT_BEING_USED;
if (uj) {
argc--;
@@ -11916,6 +11956,14 @@ DEFPY (show_ip_bgp_json,
sh_type = bgp_show_type_community_all;
}
+ if (argv_find(argv, argc, "rpki", &idx)) {
+ sh_type = bgp_show_type_rpki;
+ if (argv_find(argv, argc, "valid", &idx))
+ rpki_target_state = RPKI_VALID;
+ else if (argv_find(argv, argc, "invalid", &idx))
+ rpki_target_state = RPKI_INVALID;
+ }
+
if (!all) {
/* show bgp: AFI_IP6, show ip bgp: AFI_IP */
if (community)
@@ -11924,7 +11972,7 @@ DEFPY (show_ip_bgp_json,
show_flags);
else
return bgp_show(vty, bgp, afi, safi, sh_type, NULL,
- show_flags);
+ show_flags, rpki_target_state);
} else {
/* show <ip> bgp ipv4 all: AFI_IP, show <ip> bgp ipv6 all:
* AFI_IP6 */
@@ -11961,7 +12009,8 @@ DEFPY (show_ip_bgp_json,
safi, show_flags);
else
bgp_show(vty, bgp, afi, safi, sh_type,
- NULL, show_flags);
+ NULL, show_flags,
+ rpki_target_state);
if (uj)
vty_out(vty, "}\n");
}
@@ -11992,7 +12041,8 @@ DEFPY (show_ip_bgp_json,
safi, show_flags);
else
bgp_show(vty, bgp, afi, safi, sh_type,
- NULL, show_flags);
+ NULL, show_flags,
+ rpki_target_state);
if (uj)
vty_out(vty, "}\n");
}
@@ -12005,7 +12055,7 @@ DEFPY (show_ip_bgp_json,
DEFUN (show_ip_bgp_route,
show_ip_bgp_route_cmd,
- "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [json]",
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [rpki <valid|invalid|notfound>] [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -12018,6 +12068,10 @@ DEFUN (show_ip_bgp_route,
"IPv6 prefix\n"
"Display only the bestpath\n"
"Display only multipaths\n"
+ "Display only paths that match the specified rpki state\n"
+ "A valid path as determined by rpki\n"
+ "A invalid path as determined by rpki\n"
+ "A path that has no rpki data\n"
JSON_STR)
{
int prefix_check = 0;
@@ -12074,7 +12128,7 @@ DEFUN (show_ip_bgp_route,
path_type = BGP_PATH_SHOW_ALL;
return bgp_show_route(vty, bgp, prefix, afi, safi, NULL, prefix_check,
- path_type, uj);
+ path_type, RPKI_NOT_BEING_USED, uj);
}
DEFUN (show_ip_bgp_regexp,
@@ -12169,7 +12223,8 @@ static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
return CMD_WARNING;
}
- rc = bgp_show(vty, bgp, afi, safi, type, regex, show_flags);
+ rc = bgp_show(vty, bgp, afi, safi, type, regex, show_flags,
+ RPKI_NOT_BEING_USED);
bgp_regex_free(regex);
return rc;
}
@@ -12188,7 +12243,8 @@ static int bgp_show_prefix_list(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- return bgp_show(vty, bgp, afi, safi, type, plist, show_flags);
+ return bgp_show(vty, bgp, afi, safi, type, plist, show_flags,
+ RPKI_NOT_BEING_USED);
}
static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp,
@@ -12205,7 +12261,8 @@ static int bgp_show_filter_list(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- return bgp_show(vty, bgp, afi, safi, type, as_list, show_flags);
+ return bgp_show(vty, bgp, afi, safi, type, as_list, show_flags,
+ RPKI_NOT_BEING_USED);
}
static int bgp_show_route_map(struct vty *vty, struct bgp *bgp,
@@ -12221,7 +12278,8 @@ static int bgp_show_route_map(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- return bgp_show(vty, bgp, afi, safi, type, rmap, show_flags);
+ return bgp_show(vty, bgp, afi, safi, type, rmap, show_flags,
+ RPKI_NOT_BEING_USED);
}
static int bgp_show_community(struct vty *vty, struct bgp *bgp,
@@ -12240,7 +12298,7 @@ static int bgp_show_community(struct vty *vty, struct bgp *bgp,
ret = bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_community_exact
: bgp_show_type_community),
- com, show_flags);
+ com, show_flags, RPKI_NOT_BEING_USED);
community_free(&com);
return ret;
@@ -12262,7 +12320,7 @@ static int bgp_show_community_list(struct vty *vty, struct bgp *bgp,
return bgp_show(vty, bgp, afi, safi,
(exact ? bgp_show_type_community_list_exact
: bgp_show_type_community_list),
- list, show_flags);
+ list, show_flags, RPKI_NOT_BEING_USED);
}
static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
@@ -12281,7 +12339,8 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
return CMD_WARNING;
}
- ret = bgp_show(vty, bgp, afi, safi, type, p, show_flags);
+ ret = bgp_show(vty, bgp, afi, safi, type, p, show_flags,
+ RPKI_NOT_BEING_USED);
prefix_free(&p);
return ret;
}
@@ -13003,7 +13062,7 @@ DEFUN (show_bgp_l2vpn_evpn_route_prefix,
}
return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL,
prefix_check, BGP_PATH_SHOW_ALL,
- use_json(argc, argv));
+ RPKI_NOT_BEING_USED, use_json(argc, argv));
}
static void show_adj_route_header(struct vty *vty, struct bgp *bgp,
@@ -13723,7 +13782,8 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer,
if (safi == SAFI_LABELED_UNICAST)
safi = SAFI_UNICAST;
- return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, show_flags);
+ return bgp_show(vty, peer->bgp, afi, safi, type, &peer->su, show_flags,
+ RPKI_NOT_BEING_USED);
}
DEFUN (show_ip_bgp_flowspec_routes_detailed,
@@ -13756,7 +13816,7 @@ DEFUN (show_ip_bgp_flowspec_routes_detailed,
return CMD_WARNING;
return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL,
- show_flags);
+ show_flags, RPKI_NOT_BEING_USED);
}
DEFUN (show_ip_bgp_neighbor_routes,
@@ -13852,7 +13912,8 @@ DEFUN (show_bgp_afi_vpn_rd_route,
}
return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd,
- 0, BGP_PATH_SHOW_ALL, use_json(argc, argv));
+ 0, BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED,
+ use_json(argc, argv));
}
static struct bgp_distance *bgp_distance_new(void)
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 1dec99f085..0a4fd026e4 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -28,6 +28,7 @@
#include "nexthop.h"
#include "bgp_table.h"
#include "bgp_addpath_types.h"
+#include "bgp_rpki.h"
struct bgp_nexthop_cache;
struct bgp_route_evpn;
@@ -56,6 +57,7 @@ enum bgp_show_type {
bgp_show_type_dampend_paths,
bgp_show_type_damp_neighbor,
bgp_show_type_detail,
+ bgp_show_type_rpki,
};
enum bgp_show_adj_route_type {
@@ -102,7 +104,9 @@ enum bgp_show_adj_route_type {
#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE -15
#define BGP_NLRI_PARSE_ERROR -32
-/* MAC-IP/type-2 path_info in the VNI routing table is linked to the
+/* 1. local MAC-IP/type-2 paths in the VNI routing table are linked to the
+ * destination ES
+ * 2. remote MAC-IP paths in the global routing table are linked to the
* destination ES
*/
struct bgp_path_es_info {
@@ -113,6 +117,27 @@ struct bgp_path_es_info {
struct bgp_evpn_es *es;
/* memory used for linking the path to the destination ES */
struct listnode es_listnode;
+ uint8_t flags;
+/* Path is linked to the VNI list */
+#define BGP_EVPN_PATH_ES_INFO_VNI_LIST (1 << 0)
+/* Path is linked to the global list */
+#define BGP_EVPN_PATH_ES_INFO_GLOBAL_LIST (1 << 1)
+};
+
+/* IP paths imported into the VRF from an EVPN route source
+ * are linked to the nexthop/VTEP IP
+ */
+struct bgp_path_evpn_nh_info {
+ /* back pointer to the route */
+ struct bgp_path_info *pi;
+ struct bgp_evpn_nh *nh;
+ /* memory used for linking the path to the nexthop */
+ struct listnode nh_listnode;
+};
+
+struct bgp_path_mh_info {
+ struct bgp_path_es_info *es_info;
+ struct bgp_path_evpn_nh_info *nh_info;
};
/* Ancillary information to struct bgp_path_info,
@@ -202,7 +227,7 @@ struct bgp_path_info_extra {
/* presence of FS pbr iprule based entry */
struct list *bgp_fs_iprule;
/* Destination Ethernet Segment links for EVPN MH */
- struct bgp_path_es_info *es_info;
+ struct bgp_path_mh_info *mh_info;
};
struct bgp_path_info {
@@ -740,7 +765,8 @@ extern void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
extern void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
struct bgp_dest *bn,
struct bgp_path_info *path, afi_t afi,
- safi_t safi, json_object *json_paths);
+ safi_t safi, enum rpki_states,
+ json_object *json_paths);
extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
struct bgp_table *table, struct prefix_rd *prd,
enum bgp_show_type type, void *output_arg,
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
index 9344384956..3ef0137ba6 100644
--- a/bgpd/bgp_rpki.c
+++ b/bgpd/bgp_rpki.c
@@ -47,6 +47,8 @@
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_rpki.h"
+
#include "lib/network.h"
#include "lib/thread.h"
#ifndef VTYSH_EXTRACT_PL
@@ -63,10 +65,6 @@
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server");
DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group");
-#define RPKI_VALID 1
-#define RPKI_NOTFOUND 2
-#define RPKI_INVALID 3
-
#define POLLING_PERIOD_DEFAULT 3600
#define EXPIRE_INTERVAL_DEFAULT 7200
#define RETRY_INTERVAL_DEFAULT 600
diff --git a/bgpd/bgp_rpki.h b/bgpd/bgp_rpki.h
new file mode 100644
index 0000000000..4dd4b4a2b2
--- /dev/null
+++ b/bgpd/bgp_rpki.h
@@ -0,0 +1,33 @@
+/*
+ * bgp_rpki code
+ * Copyright (C) 2021 NVIDIA Corporation and Mellanox Technologies, LTD
+ * All Rights Reserved
+ * Donald Sharp
+ *
+ * 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 this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __BGP_RPKI_H__
+#define __BGP_RPKI_H__
+
+enum rpki_states {
+ RPKI_NOT_BEING_USED,
+ RPKI_VALID,
+ RPKI_NOTFOUND,
+ RPKI_INVALID
+};
+
+#endif
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
index bc26314b50..3afdbea908 100644
--- a/bgpd/bgp_snmp.c
+++ b/bgpd/bgp_snmp.c
@@ -435,7 +435,7 @@ static struct peer *bgpPeerTable_lookup(struct variable *v, oid name[],
if (peer == NULL)
return NULL;
- oid_copy_addr(name + namelen, addr, sizeof(struct in_addr));
+ oid_copy_in_addr(name + namelen, addr);
*length = sizeof(struct in_addr) + namelen;
return peer;
@@ -767,14 +767,12 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[],
v->namelen + BGP_PATHATTR_ENTRY_OFFSET;
offset = name + v->namelen;
- oid_copy_addr(offset, &rn_p->u.prefix4,
- IN_ADDR_SIZE);
+ oid_copy_in_addr(offset, &rn_p->u.prefix4);
offset += IN_ADDR_SIZE;
*offset = rn_p->prefixlen;
offset++;
- oid_copy_addr(offset,
- &min->peer->su.sin.sin_addr,
- IN_ADDR_SIZE);
+ oid_copy_in_addr(offset,
+ &min->peer->su.sin.sin_addr);
addr->prefix = rn_p->u.prefix4;
addr->prefixlen = rn_p->prefixlen;
@@ -868,7 +866,7 @@ static int bgpTrapEstablished(struct peer *peer)
if (ret == 0)
return 0;
- oid_copy_addr(index, &addr, IN_ADDR_SIZE);
+ oid_copy_in_addr(index, &addr);
smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid,
array_size(bgp_trap_oid), bgp_oid,
@@ -887,7 +885,7 @@ static int bgpTrapBackwardTransition(struct peer *peer)
if (ret == 0)
return 0;
- oid_copy_addr(index, &addr, IN_ADDR_SIZE);
+ oid_copy_in_addr(index, &addr);
smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid,
array_size(bgp_trap_oid), bgp_oid,
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 6270542178..43d0a3b2d2 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -664,6 +664,9 @@ struct bgp {
/* RB tree of ES-VRFs */
struct bgp_es_vrf_rb_head es_vrf_rb_tree;
+ /* Hash table of EVPN nexthops maintained per-tenant-VRF */
+ struct hash *evpn_nh_table;
+
/* vrf flags */
uint32_t vrf_flags;
#define BGP_VRF_AUTO (1 << 0)
diff --git a/bgpd/subdir.am b/bgpd/subdir.am
index 3991f7d1ed..0ca43fd308 100644
--- a/bgpd/subdir.am
+++ b/bgpd/subdir.am
@@ -176,6 +176,7 @@ noinst_HEADERS += \
bgpd/bgp_pbr.h \
bgpd/bgp_rd.h \
bgpd/bgp_regex.h \
+ bgpd/bgp_rpki.h \
bgpd/bgp_route.h \
bgpd/bgp_script.h \
bgpd/bgp_table.h \
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index 4f58ee335b..2a6d2dda34 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -233,6 +233,85 @@ for ``master`` branch:
and create ``frr`` user and ``frrvty`` group as shown above.
+Debugging Topotest Failures
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+For the below debugging options which launch programs, if the topotest is run
+within screen_ or tmux_, ``gdb``, the shell or ``vtysh`` will be launched using
+that windowing program, otherwise mininet's ``xterm`` functionality will be used
+to launch the given program.
+
+If you wish to force the use of ``xterm`` rather than ``tmux`` or ``screen``, or
+wish to use ``gnome-terminal`` instead of ``xterm``, set the environment
+variable ``FRR_TOPO_TERMINAL`` to either ``xterm`` or ``gnome-terminal``.
+
+.. _screen: https://www.gnu.org/software/screen/
+.. _tmux: https://github.com/tmux/tmux/wiki
+
+Spawning ``vtysh`` or Shells on Routers
+"""""""""""""""""""""""""""""""""""""""
+
+Topotest can automatically launch a shell or ``vtysh`` for any or all routers in
+a test. This is enabled by specifying 1 of 2 CLI arguments ``--shell`` or
+``--vtysh``. Both of these options can be set to a single router value, multiple
+comma-seperated values, or ``all``.
+
+When either of these options are specified topotest will pause after each test
+to allow for inspection of the router state.
+
+Here's an example of launching ``vtysh`` on routers ``rt1`` and ``rt2``.
+
+.. code:: shell
+
+ pytest --vtysh=rt1,rt2 all-protocol-startup
+
+Spawning Mininet CLI, ``vtysh`` or Shells on Routers on Test Failure
+""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+
+Similar to the previous section one can have ``vtysh`` or a shell launched on
+routers, but in this case only when a test fails. To launch the given process on
+each router after a test failure specify one of ``--shell-on-error`` or
+``--vtysh-on-error``.
+
+
+Here's an example of having ``vtysh`` launched on test failure.
+
+.. code:: shell
+
+ pytest --vtysh-on-error all-protocol-startup
+
+
+Additionally, one can have the mininet CLI invoked on test failures by
+specifying the ``--mininet-on-error`` CLI option as shown in the example below.
+
+.. code:: shell
+
+ pytest --mininet-on-error all-protocol-startup
+
+Debugging with GDB
+""""""""""""""""""
+
+Topotest can automatically launch any daemon with ``gdb``, possibly setting
+breakpoints for any test run. This is enabled by specifying 1 or 2 CLI arguments
+``--gdb-routers`` and ``--gdb-daemons``. Additionally ``--gdb-breakpoints`` can
+be used to automatically set breakpoints in the launched ``gdb`` processes.
+
+Each of these options can be set to a single value, multiple comma-seperated
+values, or ``all``. If ``--gdb-routers`` is empty but ``--gdb_daemons`` is set
+then the given daemons will be launched in ``gdb`` on all routers in the test.
+Likewise if ``--gdb_routers`` is set, but ``--gdb_daemons`` is empty then all
+daemons on the given routers will be launched in ``gdb``.
+
+Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router
+``r1`` with a breakpoint set on ``nb_config_diff``
+
+.. code:: shell
+
+ pytest --gdb-routers=r1 \
+ --gdb-daemons=bgpd,zebra \
+ --gdb-breakpoints=nb_config_diff \
+ all-protocol-startup
+
.. _topotests_docker:
Running Tests with Docker
diff --git a/doc/developer/tracing.rst b/doc/developer/tracing.rst
index ae4d621a8e..63b04585f1 100644
--- a/doc/developer/tracing.rst
+++ b/doc/developer/tracing.rst
@@ -396,7 +396,7 @@ modifying ``frr.service`` like so:
--- a/frr.service
+++ b/frr.service
@@ -7,6 +7,7 @@ Before=network.target
- OnFailure=heartbeat-failed@%n.service
+ OnFailure=heartbeat-failed@%n
[Service]
+Environment="LD_PRELOAD=liblttng-ust-fork.so"
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 4433dc9e21..61ed4d3e09 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1420,6 +1420,12 @@ Configuring Peers
This command is deprecated and may be removed in a future release. Its use
should be avoided.
+.. clicmd:: neighbor PEER interface remote-as <internal|external|ASN>
+
+ Configure an unnumbered BGP peer. ``PEER`` should be an interface name. The
+ session will be established via IPv6 link locals. Use ``internal`` for iBGP
+ and ``external`` for eBGP sessions, or specify an ASN if you wish.
+
.. clicmd:: neighbor PEER next-hop-self [all]
This command specifies an announced route's nexthop as being equivalent to
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 64ce85503e..af9a7844a2 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -390,6 +390,27 @@ Areas
Prevents an *ospfd* ABR from injecting inter-area
summaries into the specified stub area.
+.. clicmd:: area A.B.C.D nssa
+
+.. clicmd:: area (0-4294967295) nssa
+
+ Configure the area to be a NSSA (Not-So-Stubby Area). This is an area that
+ allows OSPF to import external routes into a stub area via a new LSA type
+ (type 7). An NSSA autonomous system boundary router (ASBR) will generate this
+ type of LSA. The area border router (ABR) translates the LSA type 7 into LSA
+ type 5, which is propagated into the OSPF domain. NSSA areas are defined in
+ RFC 3101.
+
+.. clicmd:: area A.B.C.D nssa suppress-fa
+
+.. clicmd:: area (0-4294967295) nssa suppress-fa
+
+ Configure the router to set the forwarding address to 0.0.0.0 in all LSA type 5
+ translated from LSA type 7. The router needs to be elected the translator of the
+ area for this command to take effect. This feature causes routers that are
+ configured not to advertise forwarding addresses into the backbone to direct
+ forwarded traffic to the NSSA ABR translator.
+
.. clicmd:: area A.B.C.D default-cost (0-16777215)
diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst
index 01705f607c..d496d437d3 100644
--- a/doc/user/rpki.rst
+++ b/doc/user/rpki.rst
@@ -206,6 +206,14 @@ Displaying RPKI
Display all configured cache servers, whether active or not.
+.. clicmd:: show bgp [afi] [safi] <A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> rpki <valid|invalid|notfound>
+
+ Display for the specified prefix or address the bgp paths that match the given rpki state.
+
+.. clicmd:: show bgp [afi] [safi] rpki <valid|invalid|notfound>
+
+ Display all prefixes that match the given rpki state.
+
RPKI Configuration Example
--------------------------
diff --git a/eigrpd/eigrp_snmp.c b/eigrpd/eigrp_snmp.c
index 3b232be386..9ada292feb 100644
--- a/eigrpd/eigrp_snmp.c
+++ b/eigrpd/eigrp_snmp.c
@@ -588,8 +588,7 @@ static struct eigrp_neighbor *eigrpNbrLookup(struct variable *v, oid *name,
if (nbr) {
*length = v->namelen + IN_ADDR_SIZE + 1;
- oid_copy_addr(name + v->namelen, nbr_addr,
- IN_ADDR_SIZE);
+ oid_copy_in_addr(name + v->namelen, nbr_addr);
name[v->namelen + IN_ADDR_SIZE] = *ifindex;
return nbr;
}
diff --git a/isisd/isis_snmp.c b/isisd/isis_snmp.c
index 522026dde4..fe6a2f4052 100644
--- a/isisd/isis_snmp.c
+++ b/isisd/isis_snmp.c
@@ -1037,6 +1037,8 @@ static int isis_snmp_circuit_level_lookup_next(
break;
}
+ assert(oid_idx != NULL);
+
/* We have to check level specified by index */
if (oid_idx[1] < IS_LEVEL_1) {
level = IS_LEVEL_1;
diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c
index 47fd684eb3..2bac8e7fd5 100644
--- a/isisd/isis_tlvs.c
+++ b/isisd/isis_tlvs.c
@@ -51,7 +51,8 @@ DEFINE_MTYPE_STATIC(ISISD, ISIS_MT_ITEM_LIST, "ISIS MT Item Lists");
typedef int (*unpack_tlv_func)(enum isis_tlv_context context, uint8_t tlv_type,
uint8_t tlv_len, struct stream *s,
struct sbuf *log, void *dest, int indent);
-typedef int (*pack_item_func)(struct isis_item *item, struct stream *s);
+typedef int (*pack_item_func)(struct isis_item *item, struct stream *s,
+ size_t *min_length);
typedef void (*free_item_func)(struct isis_item *i);
typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s,
struct sbuf *log, void *dest, int indent);
@@ -367,12 +368,14 @@ static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts)
}
static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts,
- struct stream *s)
+ struct stream *s, size_t *min_len)
{
uint8_t size;
- if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE)
+ if (STREAM_WRITEABLE(s) < ISIS_SUBTLV_MAX_SIZE) {
+ *min_len = ISIS_SUBTLV_MAX_SIZE;
return 1;
+ }
if (IS_SUBTLV(exts, EXT_ADM_GRP)) {
stream_putc(s, ISIS_SUBTLV_ADMIN_GRP);
@@ -828,14 +831,17 @@ static void free_item_prefix_sid(struct isis_item *i)
XFREE(MTYPE_ISIS_SUBTLV, i);
}
-static int pack_item_prefix_sid(struct isis_item *i, struct stream *s)
+static int pack_item_prefix_sid(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i;
uint8_t size = (sid->flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6;
- if (STREAM_WRITEABLE(s) < size)
+ if (STREAM_WRITEABLE(s) < size) {
+ *min_len = size;
return 1;
+ }
stream_putc(s, sid->flags);
stream_putc(s, sid->algorithm);
@@ -1120,12 +1126,15 @@ static void free_item_area_address(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, i);
}
-static int pack_item_area_address(struct isis_item *i, struct stream *s)
+static int pack_item_area_address(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_area_address *addr = (struct isis_area_address *)i;
- if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len)
+ if (STREAM_WRITEABLE(s) < (unsigned)1 + addr->len) {
+ *min_len = (unsigned)1 + addr->len;
return 1;
+ }
stream_putc(s, addr->len);
stream_put(s, addr->addr, addr->len);
return 0;
@@ -1199,12 +1208,15 @@ static void free_item_oldstyle_reach(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, i);
}
-static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s)
+static int pack_item_oldstyle_reach(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i;
- if (STREAM_WRITEABLE(s) < 11)
+ if (STREAM_WRITEABLE(s) < 11) {
+ *min_len = 11;
return 1;
+ }
stream_putc(s, r->metric);
stream_putc(s, 0x80); /* delay metric - unsupported */
@@ -1268,12 +1280,15 @@ static void free_item_lan_neighbor(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, i);
}
-static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s)
+static int pack_item_lan_neighbor(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i;
- if (STREAM_WRITEABLE(s) < 6)
+ if (STREAM_WRITEABLE(s) < 6) {
+ *min_len = 6;
return 1;
+ }
stream_put(s, n->mac, 6);
@@ -1333,12 +1348,15 @@ static void free_item_lsp_entry(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, i);
}
-static int pack_item_lsp_entry(struct isis_item *i, struct stream *s)
+static int pack_item_lsp_entry(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_lsp_entry *e = (struct isis_lsp_entry *)i;
- if (STREAM_WRITEABLE(s) < 16)
+ if (STREAM_WRITEABLE(s) < 16) {
+ *min_len = 16;
return 1;
+ }
stream_putw(s, e->rem_lifetime);
stream_put(s, e->id, 8);
@@ -1413,14 +1431,17 @@ static void free_item_extended_reach(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, item);
}
-static int pack_item_extended_reach(struct isis_item *i, struct stream *s)
+static int pack_item_extended_reach(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_extended_reach *r = (struct isis_extended_reach *)i;
size_t len;
size_t len_pos;
- if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE)
+ if (STREAM_WRITEABLE(s) < 11 + ISIS_SUBTLV_MAX_SIZE) {
+ *min_len = 11 + ISIS_SUBTLV_MAX_SIZE;
return 1;
+ }
stream_put(s, r->id, sizeof(r->id));
stream_put3(s, r->metric);
@@ -1428,7 +1449,7 @@ static int pack_item_extended_reach(struct isis_item *i, struct stream *s)
/* Real length will be adjust after adding subTLVs */
stream_putc(s, 11);
if (r->subtlvs)
- pack_item_ext_subtlvs(r->subtlvs, s);
+ pack_item_ext_subtlvs(r->subtlvs, s, min_len);
/* Adjust length */
len = stream_get_endp(s) - len_pos - 1;
stream_putc_at(s, len_pos, len);
@@ -1521,12 +1542,15 @@ static void free_item_oldstyle_ip_reach(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, i);
}
-static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s)
+static int pack_item_oldstyle_ip_reach(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i;
- if (STREAM_WRITEABLE(s) < 12)
+ if (STREAM_WRITEABLE(s) < 12) {
+ *min_len = 12;
return 1;
+ }
stream_putc(s, r->metric);
stream_putc(s, 0x80); /* delay metric - unsupported */
@@ -1676,12 +1700,15 @@ static void free_item_ipv4_address(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, i);
}
-static int pack_item_ipv4_address(struct isis_item *i, struct stream *s)
+static int pack_item_ipv4_address(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_ipv4_address *a = (struct isis_ipv4_address *)i;
- if (STREAM_WRITEABLE(s) < 4)
+ if (STREAM_WRITEABLE(s) < 4) {
+ *min_len = 4;
return 1;
+ }
stream_put(s, &a->addr, 4);
@@ -1737,12 +1764,15 @@ static void free_item_ipv6_address(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, i);
}
-static int pack_item_ipv6_address(struct isis_item *i, struct stream *s)
+static int pack_item_ipv6_address(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_ipv6_address *a = (struct isis_ipv6_address *)i;
- if (STREAM_WRITEABLE(s) < 16)
+ if (STREAM_WRITEABLE(s) < 16) {
+ *min_len = 16;
return 1;
+ }
stream_put(s, &a->addr, 16);
@@ -1801,12 +1831,15 @@ static void free_item_mt_router_info(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, i);
}
-static int pack_item_mt_router_info(struct isis_item *i, struct stream *s)
+static int pack_item_mt_router_info(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_mt_router_info *info = (struct isis_mt_router_info *)i;
- if (STREAM_WRITEABLE(s) < 2)
+ if (STREAM_WRITEABLE(s) < 2) {
+ *min_len = 2;
return 1;
+ }
uint16_t entry = info->mtid;
@@ -1961,13 +1994,16 @@ static void free_item_extended_ip_reach(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, item);
}
-static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s)
+static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i;
uint8_t control;
- if (STREAM_WRITEABLE(s) < 5)
+ if (STREAM_WRITEABLE(s) < 5) {
+ *min_len = 5;
return 1;
+ }
stream_putl(s, r->metric);
control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0;
@@ -1976,8 +2012,10 @@ static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s)
stream_putc(s, control);
- if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen))
+ if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) {
+ *min_len = 5 + (unsigned)PSIZE(r->prefix.prefixlen);
return 1;
+ }
stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen));
if (r->subtlvs)
@@ -2443,13 +2481,16 @@ static void free_item_ipv6_reach(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, item);
}
-static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s)
+static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i;
uint8_t control;
- if (STREAM_WRITEABLE(s) < 6)
+ if (STREAM_WRITEABLE(s) < 6 + (unsigned)PSIZE(r->prefix.prefixlen)) {
+ *min_len = 6 + (unsigned)PSIZE(r->prefix.prefixlen);
return 1;
+ }
stream_putl(s, r->metric);
control = r->down ? ISIS_IPV6_REACH_DOWN : 0;
@@ -2459,8 +2500,6 @@ static int pack_item_ipv6_reach(struct isis_item *i, struct stream *s)
stream_putc(s, control);
stream_putc(s, r->prefix.prefixlen);
- if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen))
- return 1;
stream_put(s, &r->prefix.prefix.s6_addr, PSIZE(r->prefix.prefixlen));
if (r->subtlvs)
@@ -2908,23 +2947,30 @@ static void free_item_auth(struct isis_item *i)
XFREE(MTYPE_ISIS_TLV, i);
}
-static int pack_item_auth(struct isis_item *i, struct stream *s)
+static int pack_item_auth(struct isis_item *i, struct stream *s,
+ size_t *min_len)
{
struct isis_auth *auth = (struct isis_auth *)i;
- if (STREAM_WRITEABLE(s) < 1)
+ if (STREAM_WRITEABLE(s) < 1) {
+ *min_len = 1;
return 1;
+ }
stream_putc(s, auth->type);
switch (auth->type) {
case ISIS_PASSWD_TYPE_CLEARTXT:
- if (STREAM_WRITEABLE(s) < auth->length)
+ if (STREAM_WRITEABLE(s) < auth->length) {
+ *min_len = 1 + auth->length;
return 1;
+ }
stream_put(s, auth->passwd, auth->length);
break;
case ISIS_PASSWD_TYPE_HMAC_MD5:
- if (STREAM_WRITEABLE(s) < 16)
+ if (STREAM_WRITEABLE(s) < 16) {
+ *min_len = 1 + 16;
return 1;
+ }
auth->offset = stream_get_endp(s);
stream_put(s, NULL, 16);
break;
@@ -3159,14 +3205,14 @@ static void free_items(enum isis_tlv_context context, enum isis_tlv_type type,
}
static int pack_item(enum isis_tlv_context context, enum isis_tlv_type type,
- struct isis_item *i, struct stream *s,
+ struct isis_item *i, struct stream *s, size_t *min_len,
struct isis_tlvs **fragment_tlvs,
const struct pack_order_entry *pe, uint16_t mtid)
{
const struct tlv_ops *ops = tlv_table[context][type];
if (ops && ops->pack_item) {
- return ops->pack_item(i, s);
+ return ops->pack_item(i, s, min_len);
}
assert(!"Unknown item tlv type!");
@@ -3200,6 +3246,7 @@ static int pack_items_(uint16_t mtid, enum isis_tlv_context context,
size_t len_pos, last_len, len;
struct isis_item *item = NULL;
int rv;
+ size_t min_len = 0;
if (!items->head)
return 0;
@@ -3227,7 +3274,8 @@ top:
last_len = len = 0;
for (item = item ? item : items->head; item; item = item->next) {
- rv = pack_item(context, type, item, s, fragment_tlvs, pe, mtid);
+ rv = pack_item(context, type, item, s, &min_len, fragment_tlvs,
+ pe, mtid);
if (rv)
goto too_long;
@@ -3271,6 +3319,8 @@ too_long:
if (!fragment_tlvs)
return 1;
stream_reset(s);
+ if (STREAM_WRITEABLE(s) < min_len)
+ return 1;
*fragment_tlvs = new_fragment(new_fragment_arg);
goto top;
}
diff --git a/ldpd/ldp_snmp.c b/ldpd/ldp_snmp.c
index 3f59d18aa8..9fb4e46515 100644
--- a/ldpd/ldp_snmp.c
+++ b/ldpd/ldp_snmp.c
@@ -301,8 +301,7 @@ static uint8_t *ldpEntityTable(struct variable *v, oid name[], size_t *length,
/* Append index */
*length = LDP_ENTITY_TOTAL_LEN;
- oid_copy_addr(name + v->namelen, &entityLdpId,
- IN_ADDR_SIZE);
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
name[v->namelen + 4] = 0;
name[v->namelen + 5] = 0;
name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX;
@@ -402,8 +401,7 @@ static uint8_t *ldpEntityStatsTable(struct variable *v, oid name[],
/* Append index */
*length = LDP_ENTITY_TOTAL_LEN;
- oid_copy_addr(name + v->namelen, &entityLdpId,
- IN_ADDR_SIZE);
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
name[v->namelen + 4] = 0;
name[v->namelen + 5] = 0;
name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX;
@@ -640,13 +638,11 @@ static uint8_t *ldpHelloAdjacencyTable(struct variable *v, oid name[], size_t *l
struct in_addr peerLdpId = ctl_adj->id;
- oid_copy_addr(name + v->namelen, &entityLdpId,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
name[v->namelen + 4] = 0;
name[v->namelen + 5] = 0;
name[v->namelen + 6] = LDP_DEFAULT_ENTITY_INDEX;
- oid_copy_addr(name + v->namelen + 7, &peerLdpId,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen + 7, &peerLdpId);
name[v->namelen + 11] = 0;
name[v->namelen + 12] = 0;
name[v->namelen + 13] = adjacencyIndex;
@@ -804,14 +800,12 @@ static uint8_t *ldpPeerTable(struct variable *v, oid name[], size_t *length,
memcpy(name, v->name, v->namelen * sizeof(oid));
/* Append index */
- oid_copy_addr(name + v->namelen, &entityLdpId,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
name[v->namelen + 4] = 0;
name[v->namelen + 5] = 0;
name[v->namelen + 6] = entityIndex;
- oid_copy_addr(name + v->namelen + 7, &peerLdpId,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen + 7, &peerLdpId);
name[v->namelen + 11] = 0;
name[v->namelen + 12] = 0;
@@ -875,14 +869,12 @@ static uint8_t *ldpSessionTable(struct variable *v, oid name[], size_t *length,
memcpy(name, v->name, v->namelen * sizeof(oid));
/* Append index */
- oid_copy_addr(name + v->namelen, &entityLdpId,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
name[v->namelen + 4] = 0;
name[v->namelen + 5] = 0;
name[v->namelen + 6] = entityIndex;
- oid_copy_addr(name + v->namelen + 7, &peerLdpId,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen + 7, &peerLdpId);
name[v->namelen + 11] = 0;
name[v->namelen + 12] = 0;
@@ -961,13 +953,11 @@ static uint8_t *ldpSessionStatsTable(struct variable *v, oid name[],
memcpy(name, v->name, v->namelen * sizeof(oid));
/* Append index */
- oid_copy_addr(name + v->namelen, &entityLdpId,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen, &entityLdpId);
name[v->namelen + 4] = 0;
name[v->namelen + 5] = 0;
name[v->namelen + 6] = entityIndex;
- oid_copy_addr(name + v->namelen + 7, &peerLdpId,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen + 7, &peerLdpId);
name[v->namelen + 11] = 0;
name[v->namelen + 12] = 0;
@@ -1147,15 +1137,15 @@ ldpTrapSession(struct nbr * nbr, unsigned int sptrap)
entityIndex = LDP_DEFAULT_ENTITY_INDEX;
peerLdpId = ctl_nbr->id;
- oid_copy_addr(index, &entityLdpId, sizeof(struct in_addr));
- index[4] = 0;
- index[5] = 0;
- index[6] = entityIndex;
- oid_copy_addr(&index[7], &peerLdpId, sizeof(struct in_addr));
- index[11] = 0;
- index[12] = 0;
+ oid_copy_in_addr(index, &entityLdpId);
+ index[4] = 0;
+ index[5] = 0;
+ index[6] = entityIndex;
+ oid_copy_in_addr(&index[7], &peerLdpId);
+ index[11] = 0;
+ index[12] = 0;
- index[LDP_PEER_ENTRY_MAX_IDX_LEN] = 0;
+ index[LDP_PEER_ENTRY_MAX_IDX_LEN] = 0;
smux_trap(ldpe_variables, array_size(ldpe_variables), ldp_trap_oid,
array_size(ldp_trap_oid), ldp_oid,
diff --git a/lib/log.c b/lib/log.c
index b86d3022b4..e078d8e2a7 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -462,7 +462,9 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_NHG_DEL),
DESC_ENTRY(ZEBRA_NHG_NOTIFY_OWNER),
DESC_ENTRY(ZEBRA_ROUTE_NOTIFY_REQUEST),
- DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY)};
+ DESC_ENTRY(ZEBRA_CLIENT_CLOSE_NOTIFY),
+ DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_ADD),
+ DESC_ENTRY(ZEBRA_EVPN_REMOTE_NH_DEL)};
#undef DESC_ENTRY
static const struct zebra_desc_table unknown = {0, "unknown", '?'};
diff --git a/lib/printf/vfprintf.c b/lib/printf/vfprintf.c
index a0634cde4b..8c7a8a58c4 100644
--- a/lib/printf/vfprintf.c
+++ b/lib/printf/vfprintf.c
@@ -138,7 +138,7 @@ __wcsconv(wchar_t *wcsarg, int prec)
* write a uintmax_t in octal (plus one byte).
*/
#if UINTMAX_MAX <= UINT64_MAX
-#define BUF 64
+#define BUF 80
#else
#error "BUF must be large enough to format a uintmax_t"
#endif
diff --git a/lib/smux.h b/lib/smux.h
index c063833e41..74447341d8 100644
--- a/lib/smux.h
+++ b/lib/smux.h
@@ -152,7 +152,8 @@ extern void oid2in_addr(oid[], int, struct in_addr *);
extern void oid2in6_addr(oid oid[], struct in6_addr *addr);
extern void oid2int(oid oid[], int *dest);
extern void *oid_copy(void *, const void *, size_t);
-extern void oid_copy_addr(oid[], const struct in_addr *, int);
+extern void oid_copy_in_addr(oid[], const struct in_addr *);
+extern void oid_copy_in6_addr(oid[], const struct in6_addr *);
extern void oid_copy_int(oid oid[], int *val);
extern void oid2string(oid oid[], int len, char *string);
extern void oid_copy_str(oid oid[], const char *string, int len);
diff --git a/lib/snmp.c b/lib/snmp.c
index 17a4ed4a1d..23d3f38b31 100644
--- a/lib/snmp.c
+++ b/lib/snmp.c
@@ -88,13 +88,24 @@ void oid2int(oid oid[], int *dest)
*dest = ntohl(network_dest);
}
-void oid_copy_addr(oid oid[], const struct in_addr *addr, int len)
+void oid_copy_in_addr(oid oid[], const struct in_addr *addr)
{
int i;
const uint8_t *pnt;
+ int len = sizeof(struct in_addr);
- if (len == 0)
- return;
+ pnt = (uint8_t *)addr;
+
+ for (i = 0; i < len; i++)
+ oid[i] = *pnt++;
+}
+
+
+void oid_copy_in6_addr(oid oid[], const struct in6_addr *addr)
+{
+ int i;
+ const uint8_t *pnt;
+ int len = sizeof(struct in6_addr);
pnt = (uint8_t *)addr;
diff --git a/lib/zclient.h b/lib/zclient.h
index 43197534a8..5b2298c42d 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -213,6 +213,8 @@ typedef enum {
ZEBRA_NHG_ADD,
ZEBRA_NHG_DEL,
ZEBRA_NHG_NOTIFY_OWNER,
+ ZEBRA_EVPN_REMOTE_NH_ADD,
+ ZEBRA_EVPN_REMOTE_NH_DEL,
ZEBRA_ERROR,
ZEBRA_CLIENT_CAPABILITIES,
ZEBRA_OPAQUE_MESSAGE,
diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c
index f6a246500b..2393cd6713 100644
--- a/ospf6d/ospf6_abr.c
+++ b/ospf6d/ospf6_abr.c
@@ -1209,7 +1209,8 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
*/
if (old_entry_updated == false) {
if ((old == NULL) || (old->type != route->type)
- || (old->path.type != route->path.type))
+ || (old->path.type != route->path.type)
+ || (old->path.cost != route->path.cost))
add_route = true;
}
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index 3497b26656..7894924a8e 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -1096,8 +1096,10 @@ void ospf6_asbr_send_externals_to_area(struct ospf6_area *oa)
for (ALL_LSDB(oa->ospf6->lsdb, lsa, lsanext)) {
if (ntohs(lsa->header->type) == OSPF6_LSTYPE_AS_EXTERNAL) {
- zlog_debug("%s: Flooding AS-External LSA %s",
- __func__, lsa->name);
+ if (IS_OSPF6_DEBUG_ASBR)
+ zlog_debug("%s: Flooding AS-External LSA %s",
+ __func__, lsa->name);
+
ospf6_flood_area(NULL, lsa, oa);
}
}
diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c
index 2d896546fa..5f4815fec1 100644
--- a/ospf6d/ospf6_flood.c
+++ b/ospf6d/ospf6_flood.c
@@ -463,6 +463,19 @@ static void ospf6_flood_process(struct ospf6_neighbor *from,
struct ospf6_area *oa;
for (ALL_LIST_ELEMENTS(process->area_list, node, nnode, oa)) {
+
+ /* If unknown LSA and U-bit clear, treat as link local
+ * flooding scope
+ */
+ if (!OSPF6_LSA_IS_KNOWN(lsa->header->type)
+ && !(ntohs(lsa->header->type) & OSPF6_LSTYPE_UBIT_MASK)
+ && (oa != OSPF6_INTERFACE(lsa->lsdb->data)->area)) {
+
+ if (IS_OSPF6_DEBUG_FLOODING)
+ zlog_debug("Unknown LSA, do not flood");
+ continue;
+ }
+
if (OSPF6_LSA_SCOPE(lsa->header->type) == OSPF6_SCOPE_AREA
&& oa != OSPF6_AREA(lsa->lsdb->data))
continue;
diff --git a/ospf6d/ospf6_lsa.c b/ospf6d/ospf6_lsa.c
index f2a933d878..b4f0c30f12 100644
--- a/ospf6d/ospf6_lsa.c
+++ b/ospf6d/ospf6_lsa.c
@@ -853,11 +853,12 @@ int ospf6_lsa_refresh(struct thread *thread)
void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6)
{
- struct listnode *node;
+ struct listnode *node, *nnode;
struct ospf6_area *oa;
struct ospf6_lsa *lsa;
const struct route_node *end = NULL;
uint32_t type, adv_router;
+ struct ospf6_interface *oi;
ospf6->inst_shutdown = 1;
@@ -872,6 +873,19 @@ void ospf6_flush_self_originated_lsas_now(struct ospf6 *ospf6)
lsa = ospf6_lsdb_next(end, lsa);
}
+
+ for (ALL_LIST_ELEMENTS(oa->if_list, node, nnode, oi)) {
+ end = ospf6_lsdb_head(oi->lsdb_self, 0, 0,
+ ospf6->router_id, &lsa);
+ while (lsa) {
+ /* RFC 2328 (14.1): Set MAXAGE */
+ lsa->header->age = htons(OSPF_LSA_MAXAGE);
+ /* Flood MAXAGE LSA*/
+ ospf6_flood(NULL, lsa);
+
+ lsa = ospf6_lsdb_next(end, lsa);
+ }
+ }
}
type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c
index f3c4798906..b5c97eda3c 100644
--- a/ospfd/ospf_abr.c
+++ b/ospfd/ospf_abr.c
@@ -675,7 +675,8 @@ static int ospf_abr_translate_nssa(struct ospf_area *area, struct ospf_lsa *lsa)
* originate translated LSA
*/
- if (ospf_translated_nssa_originate(area->ospf, lsa) == NULL) {
+ if (ospf_translated_nssa_originate(area->ospf, lsa, old)
+ == NULL) {
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
"ospf_abr_translate_nssa(): Could not translate Type-7 for %pI4 to Type-5",
diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c
index d494f0fbce..6829c4a347 100644
--- a/ospfd/ospf_interface.c
+++ b/ospfd/ospf_interface.c
@@ -1272,12 +1272,27 @@ void ospf_if_interface(struct interface *ifp)
hook_call(ospf_if_update, ifp);
}
-static int ospf_ifp_create(struct interface *ifp)
+uint32_t ospf_if_count_area_params(struct interface *ifp)
{
- struct ospf *ospf = NULL;
struct ospf_if_params *params;
struct route_node *rn;
uint32_t count = 0;
+
+ params = IF_DEF_PARAMS(ifp);
+ if (OSPF_IF_PARAM_CONFIGURED(params, if_area))
+ count++;
+
+ for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn))
+ if ((params = rn->info)
+ && OSPF_IF_PARAM_CONFIGURED(params, if_area))
+ count++;
+
+ return count;
+}
+
+static int ospf_ifp_create(struct interface *ifp)
+{
+ struct ospf *ospf = NULL;
struct ospf_if_info *oii;
if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
@@ -1303,18 +1318,8 @@ static int ospf_ifp_create(struct interface *ifp)
if (!ospf)
return 0;
- params = IF_DEF_PARAMS(ifp);
- if (OSPF_IF_PARAM_CONFIGURED(params, if_area))
- count++;
-
- for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn))
- if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area))
- count++;
-
- if (count > 0) {
- ospf->if_ospf_cli_count += count;
+ if (ospf_if_count_area_params(ifp) > 0)
ospf_interface_area_set(ospf, ifp);
- }
ospf_if_recalculate_output_cost(ifp);
@@ -1382,9 +1387,7 @@ static int ospf_ifp_down(struct interface *ifp)
static int ospf_ifp_destroy(struct interface *ifp)
{
struct ospf *ospf;
- struct ospf_if_params *params;
struct route_node *rn;
- uint32_t count = 0;
if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE))
zlog_debug(
@@ -1397,18 +1400,8 @@ static int ospf_ifp_destroy(struct interface *ifp)
ospf = ospf_lookup_by_vrf_id(ifp->vrf_id);
if (ospf) {
- params = IF_DEF_PARAMS(ifp);
- if (OSPF_IF_PARAM_CONFIGURED(params, if_area))
- count++;
-
- for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn))
- if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area))
- count++;
-
- if (count > 0) {
- ospf->if_ospf_cli_count -= count;
+ if (ospf_if_count_area_params(ifp) > 0)
ospf_interface_area_unset(ospf, ifp);
- }
}
for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn))
diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h
index a9534f543d..e2d7327381 100644
--- a/ospfd/ospf_interface.h
+++ b/ospfd/ospf_interface.h
@@ -338,6 +338,8 @@ extern void ospf_if_set_multicast(struct ospf_interface *);
extern void ospf_if_interface(struct interface *ifp);
+extern uint32_t ospf_if_count_area_params(struct interface *ifp);
+
DECLARE_HOOK(ospf_vl_add, (struct ospf_vl_data * vd), (vd));
DECLARE_HOOK(ospf_vl_delete, (struct ospf_vl_data * vd), (vd));
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index 6bde5467b2..cb1c565d37 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -1765,7 +1765,14 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf,
/* copy over Type-7 data to new */
extnew->e[0].tos = ext->e[0].tos;
extnew->e[0].route_tag = ext->e[0].route_tag;
- extnew->e[0].fwd_addr.s_addr = ext->e[0].fwd_addr.s_addr;
+ if (type7->area->suppress_fa) {
+ extnew->e[0].fwd_addr.s_addr = 0;
+ if (IS_DEBUG_OSPF_NSSA)
+ zlog_debug(
+ "ospf_lsa_translated_nssa_new(): Suppress forwarding address for %pI4",
+ &ei.p.prefix);
+ } else
+ extnew->e[0].fwd_addr.s_addr = ext->e[0].fwd_addr.s_addr;
new->data->ls_seqnum = type7->data->ls_seqnum;
/* add translated flag, checksum and lock new lsa */
@@ -1777,7 +1784,8 @@ static struct ospf_lsa *ospf_lsa_translated_nssa_new(struct ospf *ospf,
/* Originate Translated Type-5 for supplied Type-7 NSSA LSA */
struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
- struct ospf_lsa *type7)
+ struct ospf_lsa *type7,
+ struct ospf_lsa *type5)
{
struct ospf_lsa *new;
struct as_external_lsa *extnew;
@@ -1796,6 +1804,10 @@ struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
extnew = (struct as_external_lsa *)new->data;
+ /* Update LSA sequence number from translated Type-5 LSA */
+ if (type5)
+ new->data->ls_seqnum = lsa_seqnum_increment(type5);
+
if ((new = ospf_lsa_install(ospf, NULL, new)) == NULL) {
flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
"%s: Could not install LSA id %pI4", __func__,
@@ -1823,6 +1835,8 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
struct ospf_lsa *type5)
{
struct ospf_lsa *new = NULL;
+ struct as_external_lsa *extold = NULL;
+ uint32_t ls_seqnum = 0;
/* Sanity checks. */
assert(type7 || type5);
@@ -1887,6 +1901,12 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
return NULL;
}
+ extold = (struct as_external_lsa *)type5->data;
+ if (type7->area->suppress_fa == 1) {
+ if (extold->e[0].fwd_addr.s_addr == 0)
+ ls_seqnum = ntohl(type5->data->ls_seqnum);
+ }
+
/* Delete LSA from neighbor retransmit-list. */
ospf_ls_retransmit_delete_nbr_as(ospf, type5);
@@ -1899,6 +1919,11 @@ struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
return NULL;
}
+ if (type7->area->suppress_fa == 1) {
+ if (extold->e[0].fwd_addr.s_addr == 0)
+ new->data->ls_seqnum = htonl(ls_seqnum + 1);
+ }
+
if (!(new = ospf_lsa_install(ospf, NULL, new))) {
flog_warn(
EC_OSPF_LSA_INSTALL_FAILURE,
diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h
index f2a0d36e7e..3c1f94e628 100644
--- a/ospfd/ospf_lsa.h
+++ b/ospfd/ospf_lsa.h
@@ -341,11 +341,12 @@ extern char link_info_set(struct stream **s, struct in_addr id,
extern struct in_addr ospf_get_nssa_ip(struct ospf_area *);
extern int ospf_translated_nssa_compare(struct ospf_lsa *, struct ospf_lsa *);
-extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *,
- struct ospf_lsa *,
- struct ospf_lsa *);
-extern struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *,
- struct ospf_lsa *);
+extern struct ospf_lsa *ospf_translated_nssa_refresh(struct ospf *ospf,
+ struct ospf_lsa *type7,
+ struct ospf_lsa *type5);
+extern struct ospf_lsa *ospf_translated_nssa_originate(struct ospf *ospf,
+ struct ospf_lsa *type7,
+ struct ospf_lsa *type5);
extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id,
int type);
#endif /* _ZEBRA_OSPF_LSA_H */
diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c
index 8418bbf2b9..43b998ac5b 100644
--- a/ospfd/ospf_snmp.c
+++ b/ospfd/ospf_snmp.c
@@ -674,7 +674,7 @@ static struct ospf_area *ospfAreaLookup(struct variable *v, oid name[],
if (area == NULL)
return NULL;
- oid_copy_addr(name + v->namelen, addr, sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen, addr);
*length = sizeof(struct in_addr) + v->namelen;
return area;
@@ -800,7 +800,7 @@ static struct ospf_area *ospfStubAreaLookup(struct variable *v, oid name[],
if (area == NULL)
return NULL;
- oid_copy_addr(name + v->namelen, addr, sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen, addr);
/* Set TOS 0. */
name[v->namelen + sizeof(struct in_addr)] = 0;
*length = v->namelen + sizeof(struct in_addr) + 1;
@@ -1008,15 +1008,14 @@ static struct ospf_lsa *ospfLsdbLookup(struct variable *v, oid *name,
/* Fill in value. */
offset = name + v->namelen;
- oid_copy_addr(offset, area_id, IN_ADDR_SIZE);
+ oid_copy_in_addr(offset, area_id);
offset += IN_ADDR_SIZE;
*offset = lsa->data->type;
offset++;
- oid_copy_addr(offset, &lsa->data->id,
- IN_ADDR_SIZE);
+ oid_copy_in_addr(offset, &lsa->data->id);
offset += IN_ADDR_SIZE;
- oid_copy_addr(offset, &lsa->data->adv_router,
- IN_ADDR_SIZE);
+ oid_copy_in_addr(offset,
+ &lsa->data->adv_router);
return lsa;
}
@@ -1170,9 +1169,9 @@ static struct ospf_area_range *ospfAreaRangeLookup(struct variable *v,
/* Fill in value. */
offset = name + v->namelen;
- oid_copy_addr(offset, area_id, IN_ADDR_SIZE);
+ oid_copy_in_addr(offset, area_id);
offset += IN_ADDR_SIZE;
- oid_copy_addr(offset, range_net, IN_ADDR_SIZE);
+ oid_copy_in_addr(offset, range_net);
return range;
}
@@ -1573,7 +1572,7 @@ static struct ospf_interface *ospfIfLookup(struct variable *v, oid *name,
if (oi) {
*length = v->namelen + IN_ADDR_SIZE + 1;
offset = name + v->namelen;
- oid_copy_addr(offset, ifaddr, IN_ADDR_SIZE);
+ oid_copy_in_addr(offset, ifaddr);
offset += IN_ADDR_SIZE;
*offset = *ifindex;
return oi;
@@ -1717,7 +1716,7 @@ static struct ospf_interface *ospfIfMetricLookup(struct variable *v, oid *name,
if (oi) {
*length = v->namelen + IN_ADDR_SIZE + 1 + 1;
offset = name + v->namelen;
- oid_copy_addr(offset, ifaddr, IN_ADDR_SIZE);
+ oid_copy_in_addr(offset, ifaddr);
offset += IN_ADDR_SIZE;
*offset = *ifindex;
offset++;
@@ -1906,9 +1905,9 @@ ospfVirtIfLookup(struct variable *v, oid *name, size_t *length,
if (vl_data) {
*length = v->namelen + IN_ADDR_SIZE + IN_ADDR_SIZE;
- oid_copy_addr(name + v->namelen, area_id, IN_ADDR_SIZE);
- oid_copy_addr(name + v->namelen + IN_ADDR_SIZE,
- neighbor, IN_ADDR_SIZE);
+ oid_copy_in_addr(name + v->namelen, area_id);
+ oid_copy_in_addr(name + v->namelen + IN_ADDR_SIZE,
+ neighbor);
return vl_data;
}
}
@@ -2083,8 +2082,7 @@ static struct ospf_neighbor *ospfNbrLookup(struct variable *v, oid *name,
if (nbr) {
*length = v->namelen + IN_ADDR_SIZE + 1;
- oid_copy_addr(name + v->namelen, nbr_addr,
- IN_ADDR_SIZE);
+ oid_copy_in_addr(name + v->namelen, nbr_addr);
name[v->namelen + IN_ADDR_SIZE] = *ifindex;
return nbr;
}
@@ -2307,10 +2305,9 @@ static struct ospf_lsa *ospfExtLsdbLookup(struct variable *v, oid *name,
*offset = OSPF_AS_EXTERNAL_LSA;
offset++;
- oid_copy_addr(offset, &lsa->data->id, IN_ADDR_SIZE);
+ oid_copy_in_addr(offset, &lsa->data->id);
offset += IN_ADDR_SIZE;
- oid_copy_addr(offset, &lsa->data->adv_router,
- IN_ADDR_SIZE);
+ oid_copy_in_addr(offset, &lsa->data->adv_router);
return lsa;
}
@@ -2440,7 +2437,7 @@ static void ospfTrapNbrStateChange(struct ospf_neighbor *on)
zlog_info("%s: trap sent: %pI4 now %s", __func__,
&on->address.u.prefix4, msgbuf);
- oid_copy_addr(index, &(on->address.u.prefix4), IN_ADDR_SIZE);
+ oid_copy_in_addr(index, &(on->address.u.prefix4));
index[IN_ADDR_SIZE] = 0;
smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid,
@@ -2455,7 +2452,7 @@ static void ospfTrapVirtNbrStateChange(struct ospf_neighbor *on)
zlog_info("ospfTrapVirtNbrStateChange trap sent");
- oid_copy_addr(index, &(on->address.u.prefix4), IN_ADDR_SIZE);
+ oid_copy_in_addr(index, &(on->address.u.prefix4));
index[IN_ADDR_SIZE] = 0;
smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid,
@@ -2499,7 +2496,7 @@ static void ospfTrapIfStateChange(struct ospf_interface *oi)
&oi->address->u.prefix4,
lookup_msg(ospf_ism_state_msg, oi->state, NULL));
- oid_copy_addr(index, &(oi->address->u.prefix4), IN_ADDR_SIZE);
+ oid_copy_in_addr(index, &(oi->address->u.prefix4));
index[IN_ADDR_SIZE] = 0;
smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid,
@@ -2514,7 +2511,7 @@ static void ospfTrapVirtIfStateChange(struct ospf_interface *oi)
zlog_info("ospfTrapVirtIfStateChange trap sent");
- oid_copy_addr(index, &(oi->address->u.prefix4), IN_ADDR_SIZE);
+ oid_copy_in_addr(index, &(oi->address->u.prefix4));
index[IN_ADDR_SIZE] = 0;
smux_trap(ospf_variables, array_size(ospf_variables), ospf_trap_oid,
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index e6835ffc72..134d8b6d1d 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -577,6 +577,7 @@ DEFUN (ospf_network_area,
struct prefix_ipv4 p;
struct in_addr area_id;
int ret, format;
+ uint32_t count;
if (ospf->instance) {
vty_out(vty,
@@ -584,14 +585,15 @@ DEFUN (ospf_network_area,
return CMD_WARNING_CONFIG_FAILED;
}
- if (ospf->if_ospf_cli_count > 0) {
+ count = ospf_count_area_params(ospf);
+ if (count > 0) {
vty_out(vty,
"Please remove all ip ospf area x.x.x.x commands first.\n");
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"%s ospf vrf %s num of %u ip ospf area x config",
__func__, ospf->name ? ospf->name : "NIL",
- ospf->if_ospf_cli_count);
+ count);
return CMD_WARNING_CONFIG_FAILED;
}
@@ -1574,6 +1576,58 @@ DEFUN (ospf_area_nssa,
return ospf_area_nssa_cmd_handler(vty, argc, argv, 0, 0);
}
+DEFUN(ospf_area_nssa_suppress_fa, ospf_area_nssa_suppress_fa_cmd,
+ "area <A.B.C.D|(0-4294967295)> nssa suppress-fa",
+ "OSPF area parameters\n"
+ "OSPF area ID in IP address format\n"
+ "OSPF area ID as a decimal value\n"
+ "Configure OSPF area as nssa\n"
+ "Suppress forwarding address\n")
+{
+ int idx_ipv4_number = 1;
+ struct in_addr area_id;
+ int format;
+
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+ VTY_GET_OSPF_AREA_ID_NO_BB("NSSA", area_id, format,
+ argv[idx_ipv4_number]->arg);
+
+ ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
+ format);
+ ospf_area_nssa_suppress_fa_set(ospf, area_id);
+
+ ospf_schedule_abr_task(ospf);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_ospf_area_nssa_suppress_fa, no_ospf_area_nssa_suppress_fa_cmd,
+ "no area <A.B.C.D|(0-4294967295)> nssa suppress-fa",
+ NO_STR
+ "OSPF area parameters\n"
+ "OSPF area ID in IP address format\n"
+ "OSPF area ID as a decimal value\n"
+ "Configure OSPF area as nssa\n"
+ "Suppress forwarding address\n")
+{
+ int idx_ipv4_number = 2;
+ struct in_addr area_id;
+ int format;
+
+ VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf);
+
+ VTY_GET_OSPF_AREA_ID_NO_BB("nssa", area_id, format,
+ argv[idx_ipv4_number]->arg);
+
+ ospf_area_display_format_set(ospf, ospf_area_get(ospf, area_id),
+ format);
+ ospf_area_nssa_suppress_fa_unset(ospf, area_id);
+
+ ospf_schedule_abr_task(ospf);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (ospf_area_nssa_no_summary,
ospf_area_nssa_no_summary_cmd,
"area <A.B.C.D|(0-4294967295)> nssa no-summary",
@@ -7291,7 +7345,7 @@ DEFUN (show_ip_ospf_database_type_adv_router,
bool all_vrf = false;
int ret = CMD_SUCCESS;
int inst = 0;
- int idx = 0, idx_vrf = 0;
+ int idx_vrf = 0;
uint8_t use_vrf = 0;
bool uj = use_json(argc, argv);
json_object *json = NULL;
@@ -7336,7 +7390,7 @@ DEFUN (show_ip_ospf_database_type_adv_router,
}
ret = show_ip_ospf_database_type_adv_router_common(
- vty, ospf, idx ? 1 : 0, argc, argv, use_vrf, json, uj);
+ vty, ospf, 0, argc, argv, use_vrf, json, uj);
}
if (uj) {
@@ -8821,10 +8875,8 @@ DEFUN (ip_ospf_area,
if (count > 0) {
ospf = ospf_lookup_by_vrf_id(ifp->vrf_id);
- if (ospf) {
+ if (ospf)
ospf_interface_area_unset(ospf, ifp);
- ospf->if_ospf_cli_count -= count;
- }
}
return CMD_NOT_MY_INSTANCE;
@@ -8882,10 +8934,8 @@ DEFUN (ip_ospf_area,
params->if_area_id_fmt = format;
}
- if (ospf) {
+ if (ospf)
ospf_interface_area_set(ospf, ifp);
- ospf->if_ospf_cli_count++;
- }
return CMD_SUCCESS;
}
@@ -8951,7 +9001,6 @@ DEFUN (no_ip_ospf_area,
if (ospf) {
ospf_interface_area_unset(ospf, ifp);
- ospf->if_ospf_cli_count--;
ospf_area_check_free(ospf, area_id);
}
@@ -11823,6 +11872,10 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf)
vty_out(vty,
" area %s nssa no-summary\n",
buf);
+ if (area->suppress_fa)
+ vty_out(vty,
+ " area %s nssa suppress-fa\n",
+ buf);
}
if (area->default_cost != 1)
@@ -12684,6 +12737,8 @@ void ospf_vty_init(void)
install_element(OSPF_NODE, &ospf_area_nssa_translate_cmd);
install_element(OSPF_NODE, &ospf_area_nssa_no_summary_cmd);
install_element(OSPF_NODE, &no_ospf_area_nssa_no_summary_cmd);
+ install_element(OSPF_NODE, &ospf_area_nssa_suppress_fa_cmd);
+ install_element(OSPF_NODE, &no_ospf_area_nssa_suppress_fa_cmd);
install_element(OSPF_NODE, &no_ospf_area_nssa_cmd);
install_element(OSPF_NODE, &ospf_area_default_cost_cmd);
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 9856e60130..b1b8324074 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -476,41 +476,11 @@ struct ospf *ospf_lookup_by_inst_name(unsigned short instance, const char *name)
static void ospf_init(struct ospf *ospf)
{
- struct vrf *vrf;
- struct interface *ifp;
-
ospf_opaque_type11_lsa_init(ospf);
if (ospf->vrf_id != VRF_UNKNOWN)
ospf->oi_running = 1;
- /* Activate 'ip ospf area x' configured interfaces for given
- * vrf. Activate area on vrf x aware interfaces.
- * vrf_enable callback calls router_id_update which
- * internally will call ospf_if_update to trigger
- * network_run_state
- */
- vrf = vrf_lookup_by_id(ospf->vrf_id);
-
- FOR_ALL_INTERFACES (vrf, ifp) {
- struct ospf_if_params *params;
- struct route_node *rn;
- uint32_t count = 0;
-
- params = IF_DEF_PARAMS(ifp);
- if (OSPF_IF_PARAM_CONFIGURED(params, if_area))
- count++;
-
- for (rn = route_top(IF_OIFS_PARAMS(ifp)); rn; rn = route_next(rn))
- if ((params = rn->info) && OSPF_IF_PARAM_CONFIGURED(params, if_area))
- count++;
-
- if (count > 0) {
- ospf_interface_area_set(ospf, ifp);
- ospf->if_ospf_cli_count += count;
- }
- }
-
ospf_router_id_update(ospf);
}
@@ -554,6 +524,23 @@ struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id)
return (vrf->info) ? (struct ospf *)vrf->info : NULL;
}
+uint32_t ospf_count_area_params(struct ospf *ospf)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+ uint32_t count = 0;
+
+ if (ospf->vrf_id != VRF_UNKNOWN) {
+ vrf = vrf_lookup_by_id(ospf->vrf_id);
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ count += ospf_if_count_area_params(ifp);
+ }
+ }
+
+ return count;
+}
+
/* It should only be used when processing incoming info update from zebra.
* Other situations, it is not sufficient to lookup the ospf instance by
* vrf_name only without using the instance number.
@@ -1672,6 +1659,7 @@ int ospf_area_nssa_set(struct ospf *ospf, struct in_addr area_id)
/* set NSSA area defaults */
area->no_summary = 0;
+ area->suppress_fa = 0;
area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE;
area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED;
area->NSSATranslatorStabilityInterval =
@@ -1693,6 +1681,7 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc)
ospf->anyNSSA--;
/* set NSSA area defaults */
area->no_summary = 0;
+ area->suppress_fa = 0;
area->NSSATranslatorRole = OSPF_NSSA_ROLE_CANDIDATE;
area->NSSATranslatorState = OSPF_NSSA_TRANSLATE_DISABLED;
area->NSSATranslatorStabilityInterval =
@@ -1708,6 +1697,32 @@ int ospf_area_nssa_unset(struct ospf *ospf, struct in_addr area_id, int argc)
return 1;
}
+int ospf_area_nssa_suppress_fa_set(struct ospf *ospf, struct in_addr area_id)
+{
+ struct ospf_area *area;
+
+ area = ospf_area_lookup_by_area_id(ospf, area_id);
+ if (area == NULL)
+ return 0;
+
+ area->suppress_fa = 1;
+
+ return 1;
+}
+
+int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf, struct in_addr area_id)
+{
+ struct ospf_area *area;
+
+ area = ospf_area_lookup_by_area_id(ospf, area_id);
+ if (area == NULL)
+ return 0;
+
+ area->suppress_fa = 0;
+
+ return 1;
+}
+
int ospf_area_nssa_translator_role_set(struct ospf *ospf,
struct in_addr area_id, int role)
{
diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h
index 5d64ee9ba2..45e011fece 100644
--- a/ospfd/ospfd.h
+++ b/ospfd/ospfd.h
@@ -310,11 +310,6 @@ struct ospf {
/* Statistics for LSA used for new instantiation. */
uint32_t rx_lsa_count;
- /* Counter of "ip ospf area x.x.x.x" used
- * for mutual exclusion of network command under
- * router ospf or ip ospf area x under interface. */
- uint32_t if_ospf_cli_count;
-
struct route_table *distance_table;
/* Used during ospf instance going down send LSDB
@@ -476,7 +471,7 @@ struct ospf_area {
int shortcut_capability; /* Other ABRs agree on S-bit */
uint32_t default_cost; /* StubDefaultCost. */
int auth_type; /* Authentication type. */
-
+ int suppress_fa; /* Suppress forwarding address in NSSA ABR */
uint8_t NSSATranslatorRole; /* NSSA configured role */
#define OSPF_NSSA_ROLE_NEVER 0
@@ -650,6 +645,7 @@ extern struct ospf *ospf_new_alloc(unsigned short instance, const char *name);
extern struct ospf *ospf_lookup_by_inst_name(unsigned short instance,
const char *name);
extern struct ospf *ospf_lookup_by_vrf_id(vrf_id_t vrf_id);
+extern uint32_t ospf_count_area_params(struct ospf *ospf);
extern void ospf_finish(struct ospf *);
extern void ospf_process_refresh_data(struct ospf *ospf, bool reset);
extern void ospf_router_id_update(struct ospf *ospf);
@@ -668,6 +664,10 @@ extern int ospf_area_no_summary_set(struct ospf *, struct in_addr);
extern int ospf_area_no_summary_unset(struct ospf *, struct in_addr);
extern int ospf_area_nssa_set(struct ospf *, struct in_addr);
extern int ospf_area_nssa_unset(struct ospf *, struct in_addr, int);
+extern int ospf_area_nssa_suppress_fa_set(struct ospf *ospf,
+ struct in_addr area_id);
+extern int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf,
+ struct in_addr area_id);
extern int ospf_area_nssa_translator_role_set(struct ospf *, struct in_addr,
int);
extern int ospf_area_export_list_set(struct ospf *, struct ospf_area *,
diff --git a/pceplib/pcep_timers.c b/pceplib/pcep_timers.c
index e9d9d4b21d..d0a2349d05 100644
--- a/pceplib/pcep_timers.c
+++ b/pceplib/pcep_timers.c
@@ -457,10 +457,17 @@ void pceplib_external_timer_expire_handler(void *data)
}
pcep_timer *timer = (pcep_timer *)data;
+
pthread_mutex_lock(&timers_context_->timer_list_lock);
ordered_list_node *timer_node =
ordered_list_find2(timers_context_->timer_list, timer,
timer_list_node_timer_ptr_compare);
+
+ /* Remove timer from list */
+ if (timer_node)
+ ordered_list_remove_node2(timers_context_->timer_list,
+ timer_node);
+
pthread_mutex_unlock(&timers_context_->timer_list_lock);
/* Cannot continue if the timer does not exist */
@@ -474,9 +481,5 @@ void pceplib_external_timer_expire_handler(void *data)
timers_context_->expire_handler(timer->data, timer->timer_id);
- pthread_mutex_lock(&timers_context_->timer_list_lock);
- ordered_list_remove_node2(timers_context_->timer_list, timer_node);
- pthread_mutex_unlock(&timers_context_->timer_list_lock);
-
pceplib_free(PCEPLIB_INFRA, timer);
}
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index ae5b7940e9..4bbe7d35f0 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -1094,6 +1094,8 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
json_object_int_add(json_row, "helloPeriod",
pim_ifp->pim_hello_period);
+ json_object_int_add(json_row, "holdTime",
+ PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
json_object_string_add(json_row, "helloTimer",
hello_timer);
json_object_string_add(json_row, "helloStatStart",
@@ -1243,6 +1245,8 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
vty_out(vty, "------\n");
vty_out(vty, "Period : %d\n",
pim_ifp->pim_hello_period);
+ vty_out(vty, "HoldTime : %d\n",
+ PIM_IF_DEFAULT_HOLDTIME(pim_ifp));
vty_out(vty, "Timer : %s\n", hello_timer);
vty_out(vty, "StatStart : %s\n", stat_uptime);
vty_out(vty, "Receive : %d\n",
@@ -8987,7 +8991,7 @@ DEFUN (interface_ip_pim_hello,
DEFUN (interface_no_ip_pim_hello,
interface_no_ip_pim_hello_cmd,
- "no ip pim hello [(1-180) (1-180)]",
+ "no ip pim hello [(1-180) [(1-180)]]",
NO_STR
IP_STR
PIM_STR
diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c
index e852a87683..475e393cf0 100644
--- a/pimd/pim_nb_config.c
+++ b/pimd/pim_nb_config.c
@@ -1830,6 +1830,7 @@ int lib_interface_pim_hello_interval_modify(struct nb_cb_modify_args *args)
pim_ifp = ifp->info;
pim_ifp->pim_hello_period =
yang_dnode_get_uint8(args->dnode, NULL);
+ pim_ifp->pim_default_holdtime = -1;
break;
}
diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c
index 37bce7484c..b922d66912 100644
--- a/ripd/rip_snmp.c
+++ b/ripd/rip_snmp.c
@@ -271,7 +271,7 @@ static struct interface *rip2IfLookup(struct variable *v, oid name[],
if (ifp == NULL)
return NULL;
- oid_copy_addr(name + v->namelen, addr, sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen, addr);
*length = v->namelen + sizeof(struct in_addr);
@@ -320,8 +320,8 @@ static struct rip_peer *rip2PeerLookup(struct variable *v, oid name[],
|| (peer->domain
> (int)name[v->namelen
+ sizeof(struct in_addr)])) {
- oid_copy_addr(name + v->namelen, &peer->addr,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen,
+ &peer->addr);
name[v->namelen + sizeof(struct in_addr)] =
peer->domain;
*length =
@@ -334,8 +334,7 @@ static struct rip_peer *rip2PeerLookup(struct variable *v, oid name[],
if (!peer)
return NULL;
- oid_copy_addr(name + v->namelen, &peer->addr,
- sizeof(struct in_addr));
+ oid_copy_in_addr(name + v->namelen, &peer->addr);
name[v->namelen + sizeof(struct in_addr)] = peer->domain;
*length = sizeof(struct in_addr) + v->namelen + 1;
diff --git a/tests/lib/test_table.c b/tests/lib/test_table.c
index 290657bd56..9b6539e3bc 100644
--- a/tests/lib/test_table.c
+++ b/tests/lib/test_table.c
@@ -20,7 +20,7 @@
*/
#include <zebra.h>
-
+#include "printfrr.h"
#include "prefix.h"
#include "table.h"
@@ -113,7 +113,7 @@ static void print_subtree(struct route_node *rn, const char *legend,
printf(" ");
}
- printf("%s: %pFX", legend, &rn->p);
+ printfrr("%s: %pFX", legend, &rn->p);
if (!rn->info) {
printf(" (internal)");
}
diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py
index 04e9961f10..7ad5d8c9ab 100755
--- a/tests/topotests/conftest.py
+++ b/tests/topotests/conftest.py
@@ -2,13 +2,14 @@
Topotest conftest.py file.
"""
+import os
+import pdb
+import pytest
+
from lib.topogen import get_topogen, diagnose_env
from lib.topotest import json_cmp_result
+from lib.topotest import g_extra_config as topotest_extra_config
from lib.topolog import logger
-import pytest
-
-topology_only = False
-
def pytest_addoption(parser):
"""
@@ -16,20 +17,72 @@ def pytest_addoption(parser):
only run the setup_module() to setup the topology without running any tests.
"""
parser.addoption(
+ "--gdb-breakpoints",
+ metavar="SYMBOL[,SYMBOL...]",
+ help="Comma-separated list of functions to set gdb breakpoints on",
+ )
+
+ parser.addoption(
+ "--gdb-daemons",
+ metavar="DAEMON[,DAEMON...]",
+ help="Comma-separated list of daemons to spawn gdb on, or 'all'",
+ )
+
+ parser.addoption(
+ "--gdb-routers",
+ metavar="ROUTER[,ROUTER...]",
+ help="Comma-separated list of routers to spawn gdb on, or 'all'",
+ )
+
+ parser.addoption(
+ "--mininet-on-error",
+ action="store_true",
+ help="Mininet cli on test failure",
+ )
+
+ parser.addoption(
+ "--pause-after",
+ action="store_true",
+ help="Pause after each test",
+ )
+
+ parser.addoption(
+ "--shell",
+ metavar="ROUTER[,ROUTER...]",
+ help="Comma-separated list of routers to spawn shell on, or 'all'",
+ )
+
+ parser.addoption(
+ "--shell-on-error",
+ action="store_true",
+ help="Spawn shell on all routers on test failure",
+ )
+
+ parser.addoption(
"--topology-only",
action="store_true",
help="Only set up this topology, don't run tests",
)
+ parser.addoption(
+ "--vtysh",
+ metavar="ROUTER[,ROUTER...]",
+ help="Comma-separated list of routers to spawn vtysh on, or 'all'",
+ )
+
+ parser.addoption(
+ "--vtysh-on-error",
+ action="store_true",
+ help="Spawn vtysh on all routers on test failure",
+ )
+
def pytest_runtest_call():
"""
This function must be run after setup_module(), it does standarized post
setup routines. It is only being used for the 'topology-only' option.
"""
- global topology_only
-
- if topology_only:
+ if topotest_extra_config["topology_only"]:
tgen = get_topogen()
if tgen is not None:
# Allow user to play with the setup.
@@ -42,6 +95,8 @@ def pytest_assertrepr_compare(op, left, right):
"""
Show proper assertion error message for json_cmp results.
"""
+ del op
+
json_result = left
if not isinstance(json_result, json_cmp_result):
json_result = right
@@ -52,43 +107,105 @@ def pytest_assertrepr_compare(op, left, right):
def pytest_configure(config):
- "Assert that the environment is correctly configured."
-
- global topology_only
+ """
+ Assert that the environment is correctly configured, and get extra config.
+ """
if not diagnose_env():
- pytest.exit("enviroment has errors, please read the logs")
+ pytest.exit("environment has errors, please read the logs")
+
+ gdb_routers = config.getoption("--gdb-routers")
+ gdb_routers = gdb_routers.split(",") if gdb_routers else []
+ topotest_extra_config["gdb_routers"] = gdb_routers
+
+ gdb_daemons = config.getoption("--gdb-daemons")
+ gdb_daemons = gdb_daemons.split(",") if gdb_daemons else []
+ topotest_extra_config["gdb_daemons"] = gdb_daemons
+
+ gdb_breakpoints = config.getoption("--gdb-breakpoints")
+ gdb_breakpoints = gdb_breakpoints.split(",") if gdb_breakpoints else []
+ topotest_extra_config["gdb_breakpoints"] = gdb_breakpoints
+
+ mincli_on_error = config.getoption("--mininet-on-error")
+ topotest_extra_config["mininet_on_error"] = mincli_on_error
- if config.getoption("--topology-only"):
- topology_only = True
+ shell = config.getoption("--shell")
+ topotest_extra_config["shell"] = shell.split(",") if shell else []
+
+ pause_after = config.getoption("--pause-after")
+
+ shell_on_error = config.getoption("--shell-on-error")
+ topotest_extra_config["shell_on_error"] = shell_on_error
+
+ vtysh = config.getoption("--vtysh")
+ topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else []
+
+ vtysh_on_error = config.getoption("--vtysh-on-error")
+ topotest_extra_config["vtysh_on_error"] = vtysh_on_error
+
+ topotest_extra_config["pause_after"] = (
+ pause_after or shell or vtysh
+ )
+
+ topotest_extra_config["topology_only"] = config.getoption("--topology-only")
def pytest_runtest_makereport(item, call):
"Log all assert messages to default logger with error level"
- # Nothing happened
- if call.excinfo is None:
- return
- parent = item.parent
- modname = parent.module.__name__
+ # Nothing happened
+ if call.when == "call":
+ pause = topotest_extra_config["pause_after"]
+ else:
+ pause = False
- # Treat skips as non errors
- if call.excinfo.typename != "AssertionError":
- logger.info(
- 'assert skipped at "{}/{}": {}'.format(
- modname, item.name, call.excinfo.value
+ if call.excinfo is None:
+ error = False
+ else:
+ parent = item.parent
+ modname = parent.module.__name__
+
+ # Treat skips as non errors, don't pause after
+ if call.excinfo.typename != "AssertionError":
+ pause = False
+ error = False
+ logger.info(
+ 'assert skipped at "{}/{}": {}'.format(
+ modname, item.name, call.excinfo.value
+ )
+ )
+ else:
+ error = True
+ # Handle assert failures
+ parent._previousfailed = item # pylint: disable=W0212
+ logger.error(
+ 'assert failed at "{}/{}": {}'.format(modname, item.name, call.excinfo.value)
)
- )
- return
-
- # Handle assert failures
- parent._previousfailed = item
- logger.error(
- 'assert failed at "{}/{}": {}'.format(modname, item.name, call.excinfo.value)
- )
- # (topogen) Set topology error to avoid advancing in the test.
- tgen = get_topogen()
- if tgen is not None:
- # This will cause topogen to report error on `routers_have_failure`.
- tgen.set_error("{}/{}".format(modname, item.name))
+ # (topogen) Set topology error to avoid advancing in the test.
+ tgen = get_topogen()
+ if tgen is not None:
+ # This will cause topogen to report error on `routers_have_failure`.
+ tgen.set_error("{}/{}".format(modname, item.name))
+
+
+ if error and topotest_extra_config["shell_on_error"]:
+ for router in tgen.routers():
+ pause = True
+ tgen.net[router].runInWindow(os.getenv("SHELL", "bash"))
+
+ if error and topotest_extra_config["vtysh_on_error"]:
+ for router in tgen.routers():
+ pause = True
+ tgen.net[router].runInWindow("vtysh")
+
+ if error and topotest_extra_config["mininet_on_error"]:
+ tgen.mininet_cli()
+
+ if pause:
+ try:
+ user = raw_input('Testing paused, "pdb" to debug, "Enter" to continue: ')
+ except NameError:
+ user = input('Testing paused, "pdb" to debug, "Enter" to continue: ')
+ if user.strip() == "pdb":
+ pdb.set_trace()
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index 70b2cfd648..104b215078 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -50,7 +50,9 @@ from mininet.node import Node, OVSSwitch, Host
from mininet.log import setLogLevel, info
from mininet.cli import CLI
from mininet.link import Intf
+from mininet.term import makeTerm
+g_extra_config = {}
def gdb_core(obj, daemon, corefiles):
gdbcmds = """
@@ -1341,6 +1343,37 @@ class Router(Node):
logger.info("No daemon {} known".format(daemon))
# print "Daemons after:", self.daemons
+ # Run a command in a new window (gnome-terminal, screen, tmux, xterm)
+ def runInWindow(self, cmd, title=None):
+ topo_terminal = os.getenv("FRR_TOPO_TERMINAL")
+ if topo_terminal or (
+ "TMUX" not in os.environ and "STY" not in os.environ
+ ):
+ term = topo_terminal if topo_terminal else "xterm"
+ makeTerm(
+ self,
+ title=title if title else cmd,
+ term=term,
+ cmd=cmd)
+ else:
+ nscmd = "sudo nsenter -m -n -t {} {}".format(self.pid, cmd)
+ if "TMUX" in os.environ:
+ self.cmd("tmux select-layout main-horizontal")
+ wcmd = "tmux split-window -h"
+ cmd = "{} {}".format(wcmd, nscmd)
+ elif "STY" in os.environ:
+ if os.path.exists(
+ "/run/screen/S-{}/{}".format(
+ os.environ['USER'], os.environ['STY']
+ )
+ ):
+ wcmd = "screen"
+ else:
+ wcmd = "sudo -u {} screen".format(os.environ["SUDO_USER"])
+ cmd = "{} {}".format(wcmd, nscmd)
+ self.cmd(cmd)
+
+
def startRouter(self, tgen=None):
# Disable integrated-vtysh-config
self.cmd(
@@ -1393,6 +1426,14 @@ class Router(Node):
return "LDP/MPLS Tests need mpls kernel modules"
self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels")
+ shell_routers = g_extra_config["shell"]
+ if "all" in shell_routers or self.name in shell_routers:
+ self.runInWindow(os.getenv("SHELL", "bash"))
+
+ vtysh_routers = g_extra_config["vtysh"]
+ if "all" in vtysh_routers or self.name in vtysh_routers:
+ self.runInWindow("vtysh")
+
if self.daemons["eigrpd"] == 1:
eigrpd_path = os.path.join(self.daemondir, "eigrpd")
if not os.path.isfile(eigrpd_path):
@@ -1419,6 +1460,10 @@ class Router(Node):
def startRouterDaemons(self, daemons=None):
"Starts all FRR daemons for this router."
+ gdb_breakpoints = g_extra_config["gdb_breakpoints"]
+ gdb_daemons = g_extra_config["gdb_daemons"]
+ gdb_routers = g_extra_config["gdb_routers"]
+
bundle_data = ""
if os.path.exists("/etc/frr/support_bundle_commands.conf"):
@@ -1448,7 +1493,7 @@ class Router(Node):
# If `daemons` was specified then some upper API called us with
# specific daemons, otherwise just use our own configuration.
daemons_list = []
- if daemons != None:
+ if daemons is not None:
daemons_list = daemons
else:
# Append all daemons configured.
@@ -1456,47 +1501,67 @@ class Router(Node):
if self.daemons[daemon] == 1:
daemons_list.append(daemon)
- # Start Zebra first
- if "zebra" in daemons_list:
- zebra_path = os.path.join(self.daemondir, "zebra")
- zebra_option = self.daemons_options["zebra"]
- self.cmd(
- "ASAN_OPTIONS=log_path=zebra.asan {0} {1} --log file:zebra.log --log-level debug -s 90000000 -d > zebra.out 2> zebra.err".format(
- zebra_path, zebra_option
+ def start_daemon(daemon, extra_opts=None):
+ daemon_opts = self.daemons_options.get(daemon, "")
+ rediropt = " > {0}.out 2> {0}.err".format(daemon)
+ if daemon == "snmpd":
+ binary = "/usr/sbin/snmpd"
+ cmdenv = ""
+ cmdopt = "{} -C -c /etc/frr/snmpd.conf -p ".format(
+ daemon_opts
+ ) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype)
+ else:
+ binary = os.path.join(self.daemondir, daemon)
+ cmdenv = "ASAN_OPTIONS=log_path={0}.asan".format(daemon)
+ cmdopt = "{} --log file:{}.log --log-level debug".format(
+ daemon_opts, daemon
)
- )
- logger.debug("{}: {} zebra started".format(self, self.routertype))
+ if extra_opts:
+ cmdopt += " " + extra_opts
+
+ if (
+ (gdb_routers or gdb_daemons)
+ and (not gdb_routers
+ or self.name in gdb_routers
+ or "all" in gdb_routers)
+ and (not gdb_daemons
+ or daemon in gdb_daemons
+ or "all" in gdb_daemons)
+ ):
+ if daemon == "snmpd":
+ cmdopt += " -f "
+
+ cmdopt += rediropt
+ gdbcmd = "sudo -E gdb " + binary
+ if gdb_breakpoints:
+ gdbcmd += " -ex 'set breakpoint pending on'"
+ for bp in gdb_breakpoints:
+ gdbcmd += " -ex 'b {}'".format(bp)
+ gdbcmd += " -ex 'run {}'".format(cmdopt)
+
+ self.runInWindow(gdbcmd, daemon)
+ else:
+ if daemon != "snmpd":
+ cmdopt += " -d "
+ cmdopt += rediropt
+ self.cmd(" ".join([cmdenv, binary, cmdopt]))
+ logger.info("{}: {} {} started".format(self, self.routertype, daemon))
- # Remove `zebra` so we don't attempt to start it again.
+
+ # Start Zebra first
+ if "zebra" in daemons_list:
+ start_daemon("zebra", "-s 90000000")
while "zebra" in daemons_list:
daemons_list.remove("zebra")
# Start staticd next if required
if "staticd" in daemons_list:
- staticd_path = os.path.join(self.daemondir, "staticd")
- staticd_option = self.daemons_options["staticd"]
- self.cmd(
- "ASAN_OPTIONS=log_path=staticd.asan {0} {1} --log file:staticd.log --log-level debug -d > staticd.out 2> staticd.err".format(
- staticd_path, staticd_option
- )
- )
- logger.debug("{}: {} staticd started".format(self, self.routertype))
-
- # Remove `staticd` so we don't attempt to start it again.
+ start_daemon("staticd")
while "staticd" in daemons_list:
daemons_list.remove("staticd")
if "snmpd" in daemons_list:
- snmpd_path = "/usr/sbin/snmpd"
- snmpd_option = self.daemons_options["snmpd"]
- self.cmd(
- "{0} {1} -C -c /etc/frr/snmpd.conf -p /var/run/{2}/snmpd.pid -x /etc/frr/agentx > snmpd.out 2> snmpd.err".format(
- snmpd_path, snmpd_option, self.routertype
- )
- )
- logger.info("{}: {} snmpd started".format(self, self.routertype))
-
- # Remove `snmpd` so we don't attempt to start it again.
+ start_daemon("snmpd")
while "snmpd" in daemons_list:
daemons_list.remove("snmpd")
@@ -1508,17 +1573,9 @@ class Router(Node):
# Now start all the other daemons
for daemon in daemons_list:
- # Skip disabled daemons and zebra
if self.daemons[daemon] == 0:
continue
-
- daemon_path = os.path.join(self.daemondir, daemon)
- self.cmd(
- "ASAN_OPTIONS=log_path={2}.asan {0} {1} --log file:{2}.log --log-level debug -d > {2}.out 2> {2}.err".format(
- daemon_path, self.daemons_options.get(daemon, ""), daemon
- )
- )
- logger.debug("{}: {} {} started".format(self, self.routertype, daemon))
+ start_daemon(daemon)
# Check if daemons are running.
rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
diff --git a/tests/topotests/ospf_suppress_fa/__init__.py b/tests/topotests/ospf_suppress_fa/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/__init__.py
diff --git a/tests/topotests/ospf_suppress_fa/r1/ospfd.conf b/tests/topotests/ospf_suppress_fa/r1/ospfd.conf
new file mode 100644
index 0000000000..c02be35b14
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r1/ospfd.conf
@@ -0,0 +1,9 @@
+!
+interface r1-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 10.0.12.0/24 area 0
+!
diff --git a/tests/topotests/ospf_suppress_fa/r1/zebra.conf b/tests/topotests/ospf_suppress_fa/r1/zebra.conf
new file mode 100644
index 0000000000..c1e31fb474
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r1-eth0
+ ip address 10.0.12.1/24
+!
diff --git a/tests/topotests/ospf_suppress_fa/r2/ospfd.conf b/tests/topotests/ospf_suppress_fa/r2/ospfd.conf
new file mode 100644
index 0000000000..ebc7d252fd
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r2/ospfd.conf
@@ -0,0 +1,16 @@
+!
+interface r2-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface r2-eth1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 10.0.12.0/24 area 0
+ network 10.0.23.0/24 area 1
+ area 1 nssa
+!
diff --git a/tests/topotests/ospf_suppress_fa/r2/zebra.conf b/tests/topotests/ospf_suppress_fa/r2/zebra.conf
new file mode 100644
index 0000000000..9f1a26349e
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r2-eth0
+ ip address 10.0.12.2/24
+!
+interface r2-eth1
+ ip address 10.0.23.2/24
+!
diff --git a/tests/topotests/ospf_suppress_fa/r3/ospfd.conf b/tests/topotests/ospf_suppress_fa/r3/ospfd.conf
new file mode 100644
index 0000000000..08be11a7b7
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r3/ospfd.conf
@@ -0,0 +1,11 @@
+!
+interface r3-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ redistribute static
+ network 10.0.23.0/24 area 1
+ area 1 nssa
+!
diff --git a/tests/topotests/ospf_suppress_fa/r3/zebra.conf b/tests/topotests/ospf_suppress_fa/r3/zebra.conf
new file mode 100644
index 0000000000..f76cbf74d2
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r3/zebra.conf
@@ -0,0 +1,8 @@
+!
+ip route 3.3.1.1/32 Null0
+ip route 3.3.2.2/32 Null0
+ip route 3.3.3.3/32 Null0
+!
+interface r3-eth0
+ ip address 10.0.23.3/24
+!
diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot
new file mode 100644
index 0000000000..1036658f1a
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot
@@ -0,0 +1,66 @@
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #fcb314 dark yellow
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph ospf_topo1 {
+ label="ospf suppress-fa";
+
+ # Routers
+ r1 [
+ label="r1\nrtr-id 10.0.12.1",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ label="r2 (ABR)\nrtr-id 10.0.23.2",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ label="r3 (ASBR)\nrtr-id 10.0.23.3",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ label="s1\n10.0.12.0/24",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ label="s2\n10.0.23.0/24",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ subgraph cluster0 {
+ label="area 0"
+ r1 -- s1 [label="eth1\n.1"];
+ r2 -- s1 [label="eth1\n.2"];
+ }
+
+ subgraph cluster1 {
+ label="area 1\nNSSA"
+ r2 -- s2 [label="eth2\n.2"];
+ r3 -- s2 [label="eth1\n.3"];
+ }
+
+ { rank=same; r1; r2; r3; }
+}
diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg
new file mode 100644
index 0000000000..2907d799f5
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg
Binary files differ
diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py
new file mode 100644
index 0000000000..74d609c57e
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py
@@ -0,0 +1,181 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_suppres_fa.py
+# Carles Kishimoto
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_ospf_suppres_fa.py: Test OSPF suppress-fa feature
+- Topology: r1 --- R2 (ABR) --- R3 (redistribute static)
+
+test_ospf_set_suppress_fa()
+ 1) R1: Get a dict[LSA_ID] = fwd_addr for all type 5 LSA
+ 2) R2: Configure: area 1 nssa suppress-fa
+ 3) R1: Get a dict[LSA_ID] and compare fwd_address with 0.0.0.0
+
+test_ospf_unset_suppress_fa()
+ 4) R2: Configure: no area 1 nssa suppress-fa
+ 5) R1: Get a dict[LSA_ID] = fwd_addr and compare it with the dict obtained in 1)
+"""
+
+import os
+import sys
+import re
+import pytest
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+
+class NetworkTopo(Topo):
+ "OSPF topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+
+ tgen = get_topogen(self)
+
+ # Create routers
+ for router in range(1, 4):
+ tgen.add_router("r{}".format(router))
+
+ # R1-R2 backbone area
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # R2-R3 NSSA area
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(NetworkTopo, mod.__name__)
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registred routers, load the zebra and ospf configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def test_converge_protocols():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ topotest.sleep(10, "Waiting for OSPF convergence")
+
+def ospf_configure_suppress_fa(router_name, area):
+ "Configure OSPF suppress-fa in router_name"
+
+ tgen = get_topogen()
+ router = tgen.gears[router_name]
+ router.vtysh_cmd("conf t\nrouter ospf\narea {} nssa suppress-fa\nexit\n".format(area))
+
+def ospf_unconfigure_suppress_fa(router_name, area):
+ "Remove OSPF suppress-fa in router_name"
+
+ tgen = get_topogen()
+ router = tgen.gears[router_name]
+ router.vtysh_cmd("conf t\nrouter ospf\nno area {} nssa suppress-fa\nexit\n".format(area))
+
+def ospf_get_lsa_type5(router_name):
+ "Return a dict with link state id as key and forwarding addresses as value"
+
+ result = dict()
+ tgen = get_topogen()
+ router = tgen.gears[router_name]
+ cmd = "show ip ospf database external\n"
+ output = topotest.normalize_text(router.vtysh_cmd(cmd))
+ for line in output.splitlines():
+ re0 = re.match(r"\s+Link State ID: (\S+) \(External Network Number\)", line)
+ if re0:
+ lsa = re0.group(1)
+ re1 = re.match(r"\s+Forward Address: (\S+)", line)
+ if re1:
+ result[lsa] = re1.group(1)
+ return result
+
+@pytest.fixture(scope='module', name='original')
+def test_ospf_set_suppress_fa():
+ "Test OSPF area [x] nssa suppress-fa"
+
+ # Get current forwarding address for each LSA type-5 in r1
+ initial = ospf_get_lsa_type5("r1")
+
+ # Configure suppres-fa in r2 area 1
+ ospf_configure_suppress_fa("r2", "1")
+ topotest.sleep(10, "Waiting for OSPF convergence")
+
+ # Check forwarding address on r1 for all statics is 0.0.0.0
+ assertmsg = "Forwarding address is not 0.0.0.0 after enabling OSPF suppress-fa"
+ suppress = ospf_get_lsa_type5("r1")
+ for prefix in suppress:
+ assert suppress[prefix] == "0.0.0.0", assertmsg
+
+ # Return the original forwarding addresses so we can compare them
+ # in the test_ospf_unset_supress_fa
+ return initial
+
+def test_ospf_unset_supress_fa(original):
+ "Test OSPF no area [x] nssa suppress-fa"
+
+ # Remove suppress-fa in r2 area 1
+ ospf_unconfigure_suppress_fa("r2", "1")
+ topotest.sleep(10, "Waiting for OSPF convergence")
+
+ # Check forwarding address is the original value on r1 for all statics
+ assertmsg = "Forwarding address is not correct after removing OSPF suppress-fa"
+ restore = ospf_get_lsa_type5("r1")
+ for prefix in restore:
+ assert restore[prefix] == original[prefix], assertmsg
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf
index 0bc6547994..f902d3dd21 100644
--- a/tools/etc/frr/support_bundle_commands.conf
+++ b/tools/etc/frr/support_bundle_commands.conf
@@ -84,6 +84,25 @@ CMD_LIST_END
# CMD_LIST_END
# PIM Support Bundle Command List
-# PROC_NAME:pim
-# CMD_LIST_START
-# CMD_LIST_END
+PROC_NAME:pim
+CMD_LIST_START
+show ip multicast
+show ip pim interface
+show ip pim interface traffic
+show ip pim nexthop
+show ip pim neighbor
+show ip pim bsr
+show ip pim bsrp-info
+show ip pim bsm-database
+show ip pim rp-info
+show ip igmp groups
+show ip igmp interface
+show ip igmp join
+show ip igmp sources
+show ip pim upstream
+show ip mroute
+show ip pim join
+show ip pim state
+show ip pim statistics
+show ip pim rpf
+CMD_LIST_END
diff --git a/tools/frr.service.in b/tools/frr.service.in
index 836ce06be7..f67bc41b09 100644
--- a/tools/frr.service.in
+++ b/tools/frr.service.in
@@ -4,7 +4,7 @@ Documentation=https://frrouting.readthedocs.io/en/latest/setup.html
Wants=network.target
After=network-pre.target systemd-sysctl.service
Before=network.target
-OnFailure=heartbeat-failed@%n.service
+OnFailure=heartbeat-failed@%n
[Service]
Nice=-5
diff --git a/tools/frr@.service.in b/tools/frr@.service.in
index 1e5d252325..7b94a11f5b 100644
--- a/tools/frr@.service.in
+++ b/tools/frr@.service.in
@@ -4,7 +4,7 @@ Documentation=https://frrouting.readthedocs.io/en/latest/setup.html
Wants=network.target
After=network-pre.target systemd-sysctl.service
Before=network.target
-OnFailure=heartbeat-failed@%n.service
+OnFailure=heartbeat-failed@%n
[Service]
Nice=-5
diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in
index 3dbc6a1b43..2d925dbac3 100644
--- a/tools/frrcommon.sh.in
+++ b/tools/frrcommon.sh.in
@@ -149,6 +149,10 @@ daemon_prep() {
daemon_start() {
local dmninst daemon inst args instopt wrap bin
+
+ all=false
+ [ "$1" = "--all" ] && { all=true; shift; }
+
daemon_inst "$1"
ulimit -n $MAX_FDS > /dev/null 2> /dev/null
@@ -165,7 +169,11 @@ daemon_start() {
if eval "$all_wrap $wrap $bin $nsopt -d $frr_global_options $instopt $args"; then
log_success_msg "Started $dmninst"
- vtysh_b "$daemon"
+ if $all; then
+ debug "Skipping startup of vtysh until all have started"
+ else
+ vtysh_b "$daemon"
+ fi
else
log_failure_msg "Failed to start $dmninst!"
fi
@@ -237,8 +245,9 @@ print_status() {
all_start() {
daemon_list daemons
for dmninst in $daemons; do
- daemon_start "$dmninst"
+ daemon_start --all "$dmninst"
done
+ vtysh_b
}
all_stop() {
diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang
index f959ff8be5..2070649ec2 100644
--- a/yang/frr-pim.yang
+++ b/yang/frr-pim.yang
@@ -294,6 +294,9 @@ module frr-pim {
type uint8 {
range "1..180";
}
+ must ". > ./../hello-interval" {
+ error-message "HoldTime must be greater than Hello";
+ }
description
"Hello holdtime";
}
diff --git a/yang/subdir.am b/yang/subdir.am
index 47fc508901..da4432b622 100644
--- a/yang/subdir.am
+++ b/yang/subdir.am
@@ -86,3 +86,9 @@ endif
if PATHD
dist_yangmodels_DATA += yang/frr-pathd.yang
endif
+
+CLEANFILES += \
+ yang/*.c \
+ yang/ietf/*.c \
+ yang/confd/*.c \
+ #
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index 63ba6cd8d9..b482914418 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -3350,6 +3350,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = {
[ZEBRA_NHG_ADD] = zread_nhg_add,
[ZEBRA_NHG_DEL] = zread_nhg_del,
[ZEBRA_ROUTE_NOTIFY_REQUEST] = zread_route_notify_request,
+ [ZEBRA_EVPN_REMOTE_NH_ADD] = zebra_evpn_proc_remote_nh,
+ [ZEBRA_EVPN_REMOTE_NH_DEL] = zebra_evpn_proc_remote_nh,
};
/*
diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c
index 1c258a04f7..cabba707a0 100644
--- a/zebra/zebra_evpn_mh.c
+++ b/zebra/zebra_evpn_mh.c
@@ -3867,6 +3867,47 @@ static void zebra_evpn_mh_startup_delay_timer_start(const char *rc)
}
}
+/*****************************************************************************
+ * Nexthop management: nexthops associated with Type-2 routes that have
+ * an ES as destination are consolidated by BGP into a per-VRF nh->rmac
+ * mapping which is the installed as a remote neigh/fdb entry with a
+ * dummy (type-1) prefix referencing it.
+ * This handling is needed because Type-2 routes with ES as dest use NHG
+ * that are setup using EAD routes (i.e. such NHGs do not include the
+ * RMAC info).
+ ****************************************************************************/
+void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS)
+{
+ struct stream *s;
+ vrf_id_t vrf_id;
+ struct ipaddr nh;
+ struct ethaddr rmac;
+ struct prefix_evpn dummy_prefix;
+
+ s = msg;
+ vrf_id = stream_getl(s);
+ stream_get(&nh, s, sizeof(nh));
+
+ memset(&dummy_prefix, 0, sizeof(dummy_prefix));
+ dummy_prefix.family = AF_EVPN;
+ dummy_prefix.prefixlen = (sizeof(struct evpn_addr) * 8);
+ dummy_prefix.prefix.route_type = 1; /* XXX - fixup to type-1 def */
+
+ if (hdr->command == ZEBRA_EVPN_REMOTE_NH_ADD) {
+ stream_get(&rmac, s, sizeof(rmac));
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("evpn remote nh %d %pIA rmac %pEA add",
+ vrf_id, &nh, &rmac);
+ zebra_vxlan_evpn_vrf_route_add(vrf_id, &rmac, &nh,
+ (struct prefix *)&dummy_prefix);
+ } else {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("evpn remote nh %d %pIA del", vrf_id, &nh);
+ zebra_vxlan_evpn_vrf_route_del(vrf_id, &nh,
+ (struct prefix *)&dummy_prefix);
+ }
+}
+
/*****************************************************************************/
void zebra_evpn_mh_config_write(struct vty *vty)
{
diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h
index 2361a70bff..8861e80cee 100644
--- a/zebra/zebra_evpn_mh.h
+++ b/zebra/zebra_evpn_mh.h
@@ -382,5 +382,6 @@ extern void zebra_evpn_acc_bd_svi_set(struct zebra_if *vlan_zif,
extern void zebra_evpn_acc_bd_svi_mac_add(struct interface *vlan_if);
extern void zebra_evpn_es_bypass_update(struct zebra_evpn_es *es,
struct interface *ifp, bool bypass);
+extern void zebra_evpn_proc_remote_nh(ZAPI_HANDLER_ARGS);
#endif /* _ZEBRA_EVPN_MH_H */
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index bc2eac7a0b..4cd3b60a0f 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -149,6 +149,11 @@ static int host_rb_entry_compare(const struct host_rb_entry *hle1,
} else if (hle1->p.family == AF_INET6) {
return memcmp(&hle1->p.u.prefix6, &hle2->p.u.prefix6,
IPV6_MAX_BYTELEN);
+ } else if (hle1->p.family == AF_EVPN) {
+ /* a single dummy prefix of route_type BGP_EVPN_AD_ROUTE is
+ * used for all nexthops associated with a non-zero ESI
+ */
+ return 0;
} else {
zlog_debug("%s: Unexpected family type: %d", __func__,
hle1->p.family);