summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am3
-rw-r--r--babeld/babeld.c2
-rw-r--r--bgpd/bgp_attr_evpn.c1
-rw-r--r--bgpd/bgp_attr_evpn.h14
-rw-r--r--bgpd/bgp_debug.c26
-rw-r--r--bgpd/bgp_debug.h13
-rw-r--r--bgpd/bgp_evpn.c649
-rw-r--r--bgpd/bgp_evpn.h38
-rw-r--r--bgpd/bgp_evpn_mh.c4
-rw-r--r--bgpd/bgp_evpn_private.h19
-rw-r--r--bgpd/bgp_evpn_vty.c362
-rw-r--r--bgpd/bgp_evpn_vty.h4
-rw-r--r--bgpd/bgp_label.c2
-rw-r--r--bgpd/bgp_mac.c4
-rw-r--r--bgpd/bgp_memory.c1
-rw-r--r--bgpd/bgp_memory.h2
-rw-r--r--bgpd/bgp_nb_config.c64
-rw-r--r--bgpd/bgp_nexthop.c14
-rw-r--r--bgpd/bgp_nexthop.h27
-rw-r--r--bgpd/bgp_nht.c58
-rw-r--r--bgpd/bgp_nht.h1
-rw-r--r--bgpd/bgp_route.c100
-rw-r--r--bgpd/bgp_routemap.c213
-rw-r--r--bgpd/bgp_routemap_nb.c14
-rw-r--r--bgpd/bgp_routemap_nb.h8
-rw-r--r--bgpd/bgp_routemap_nb_config.c104
-rw-r--r--bgpd/bgp_snmp.c4
-rw-r--r--bgpd/bgp_updgrp_adv.c10
-rw-r--r--bgpd/bgp_updgrp_packet.c3
-rw-r--r--bgpd/bgp_zebra.c60
-rw-r--r--bgpd/bgpd.h35
-rwxr-xr-xbuildtest.sh2
-rw-r--r--configure.ac27
-rw-r--r--doc/developer/building-frr-for-centos6.rst1
-rw-r--r--doc/developer/building-frr-for-centos7.rst1
-rw-r--r--doc/developer/building-frr-for-centos8.rst1
-rw-r--r--doc/developer/building-frr-for-debian8.rst1
-rw-r--r--doc/developer/building-frr-for-debian9.rst1
-rw-r--r--doc/developer/building-frr-for-netbsd6.rst1
-rw-r--r--doc/developer/building-frr-for-netbsd7.rst1
-rw-r--r--doc/developer/include-compile.rst1
-rw-r--r--doc/developer/topotests.rst1
-rw-r--r--doc/user/basic.rst62
-rw-r--r--doc/user/bgp.rst109
-rw-r--r--doc/user/installation.rst24
-rw-r--r--doc/user/ospf6d.rst11
-rw-r--r--doc/user/ospfd.rst7
-rw-r--r--eigrpd/eigrp_cli.c2
-rw-r--r--isisd/isis_nb_config.c131
-rw-r--r--isisd/isis_vty_fabricd.c2
-rw-r--r--isisd/isisd.c50
-rw-r--r--isisd/isisd.h4
-rw-r--r--lib/command.c30
-rw-r--r--lib/compiler.h4
-rw-r--r--lib/log_filter.c16
-rw-r--r--lib/log_vty.c25
-rw-r--r--lib/log_vty.h5
-rw-r--r--lib/routemap.h4
-rw-r--r--lib/routemap_cli.c10
-rw-r--r--lib/subdir.am3
-rw-r--r--lib/thread.c184
-rw-r--r--lib/thread.h11
-rw-r--r--lib/vrf.c16
-rw-r--r--lib/vty.c54
-rw-r--r--lib/zlog.c150
-rw-r--r--lib/zlog.h29
-rw-r--r--lib/zlog_targets.c31
-rw-r--r--nhrpd/nhrp_main.c1
-rw-r--r--nhrpd/nhrp_vty.c2
-rw-r--r--ospf6d/ospf6_asbr.c34
-rw-r--r--ospf6d/ospf6_interface.c51
-rw-r--r--ospf6d/ospf6_interface.h8
-rw-r--r--ospf6d/ospf6_message.c1266
-rw-r--r--ospf6d/ospf6_message.h25
-rw-r--r--ospf6d/ospf6_neighbor.c67
-rw-r--r--ospf6d/ospf6_neighbor.h7
-rw-r--r--ospf6d/ospf6_network.c11
-rw-r--r--ospf6d/ospf6_network.h15
-rw-r--r--ospf6d/ospf6_nssa.c24
-rw-r--r--ospf6d/ospf6_top.c11
-rw-r--r--ospf6d/ospf6_top.h4
-rw-r--r--ospfd/ospf_lsa.c40
-rw-r--r--ospfd/ospf_neighbor.c3
-rw-r--r--ospfd/ospf_vty.c2
-rw-r--r--pathd/path_errors.c6
-rw-r--r--pathd/path_errors.h1
-rw-r--r--pathd/path_nb_config.c5
-rw-r--r--pathd/path_pcep.c71
-rw-r--r--pathd/path_pcep.h10
-rw-r--r--pathd/path_pcep_config.c99
-rw-r--r--pathd/path_pcep_config.h1
-rw-r--r--pathd/path_pcep_controller.c24
-rw-r--r--pathd/path_pcep_controller.h6
-rw-r--r--pathd/path_pcep_debug.c16
-rw-r--r--pathd/path_pcep_lib.c129
-rw-r--r--pathd/path_pcep_lib.h3
-rw-r--r--pathd/path_pcep_pcc.c157
-rw-r--r--pathd/path_pcep_pcc.h3
-rw-r--r--pathd/pathd.c23
-rw-r--r--pathd/pathd.h16
-rw-r--r--pbrd/pbr_main.c1
-rw-r--r--pbrd/pbr_vty.c4
-rw-r--r--pceplib/pcep_msg_objects.h5
-rw-r--r--pceplib/pcep_msg_objects_encoding.c7
-rw-r--r--pceplib/pcep_msg_tlvs.h16
-rw-r--r--pceplib/pcep_msg_tlvs_encoding.c10
-rw-r--r--pceplib/test/pcep_msg_tools_test.c4
-rw-r--r--python/xrelfo.py6
-rw-r--r--ripd/ripd.c2
-rw-r--r--ripngd/ripngd.c2
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json192
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json8
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json192
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json27
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json27
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json6
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json27
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json27
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json6
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf30
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf14
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json56
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json56
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json29
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json55
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json55
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json29
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json192
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json68
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json192
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json27
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json27
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json6
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json28
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json28
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json6
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf14
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf14
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json56
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json29
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json29
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json56
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json29
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json29
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py0
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf18
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf4
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf1
-rw-r--r--tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf4
-rwxr-xr-xtests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py385
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/__init__.py0
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf13
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py138
-rw-r--r--tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py4
-rw-r--r--tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py4
-rw-r--r--tests/topotests/bgp_route_map/test_route_map_topo1.py103
-rw-r--r--tests/topotests/ospf6_topo1/test_ospf6_topo1.py30
-rwxr-xr-xtests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py52
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json199
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py2636
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py439
-rw-r--r--tools/etc/frr/support_bundle_commands.conf22
-rwxr-xr-xtools/frr-reload.py47
-rw-r--r--vrrpd/vrrp_vty.c1
-rw-r--r--vtysh/vtysh.c30
-rw-r--r--vtysh/vtysh.h2
-rw-r--r--yang/frr-bgp-route-map.yang32
-rw-r--r--zebra/rib.h2
-rw-r--r--zebra/zebra_dplane.c135
-rw-r--r--zebra/zebra_dplane.h4
-rw-r--r--zebra/zebra_evpn.c26
-rw-r--r--zebra/zebra_evpn.h3
-rw-r--r--zebra/zebra_fpm_netlink.c33
-rw-r--r--zebra/zebra_mpls.c2
-rw-r--r--zebra/zebra_mpls.h1
-rw-r--r--zebra/zebra_mpls_openbsd.c3
-rw-r--r--zebra/zebra_pw.c124
-rw-r--r--zebra/zebra_rib.c3
-rw-r--r--zebra/zebra_vxlan.c33
-rw-r--r--zebra/zebra_vxlan_private.h1
185 files changed, 10108 insertions, 1186 deletions
diff --git a/Makefile.am b/Makefile.am
index a38029dcfa..8c9d7df77c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -109,8 +109,6 @@ CLEANFILES =
DISTCLEANFILES =
SUFFIXES =
-examplesdir = $(exampledir)
-
bin_PROGRAMS =
sbin_PROGRAMS =
sbin_SCRIPTS =
@@ -122,7 +120,6 @@ lib_LTLIBRARIES =
module_LTLIBRARIES =
pkginclude_HEADERS =
nodist_pkginclude_HEADERS =
-dist_examples_DATA =
dist_yangmodels_DATA =
man_MANS =
vtysh_scan =
diff --git a/babeld/babeld.c b/babeld/babeld.c
index 4d4dd2e194..b9623b64b5 100644
--- a/babeld/babeld.c
+++ b/babeld/babeld.c
@@ -819,6 +819,8 @@ babeld_quagga_init(void)
install_element(BABEL_NODE, &babel_ipv6_distribute_list_cmd);
install_element(BABEL_NODE, &babel_no_ipv6_distribute_list_cmd);
+ vrf_cmd_init(NULL, &babeld_privs);
+
babel_if_init();
/* Access list install. */
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c
index 1df646c346..0e341a8c6b 100644
--- a/bgpd/bgp_attr_evpn.c
+++ b/bgpd/bgp_attr_evpn.c
@@ -315,3 +315,4 @@ extern bool is_zero_gw_ip(const union gw_addr *gw_ip, const afi_t afi)
return false;
}
+
diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h
index 6fdf73fd1e..102509fdd7 100644
--- a/bgpd/bgp_attr_evpn.h
+++ b/bgpd/bgp_attr_evpn.h
@@ -30,7 +30,21 @@ union gw_addr {
struct in6_addr ipv6;
};
+enum overlay_index_type {
+ OVERLAY_INDEX_TYPE_NONE,
+ OVERLAY_INDEX_GATEWAY_IP,
+ OVERLAY_INDEX_ESI,
+ OVERLAY_INDEX_MAC,
+};
+
+/*
+ * Structure to store ovrelay index for EVPN type-5 route
+ * This structure stores ESI and Gateway IP overlay index.
+ * MAC overlay index is stored in the RMAC attribute.
+ */
struct bgp_route_evpn {
+ enum overlay_index_type type;
+ esi_t eth_s_id;
union gw_addr gw_ip;
};
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 8f286e66df..856afb05f8 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -2680,10 +2680,14 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi,
union prefixconstptr pu,
mpls_label_t *label, uint32_t num_labels,
int addpath_valid, uint32_t addpath_id,
+ struct bgp_route_evpn *overlay_index,
char *str, int size)
{
char rd_buf[RD_ADDRSTRLEN];
char tag_buf[30];
+ char overlay_index_buf[INET6_ADDRSTRLEN + 14];
+ const struct prefix_evpn *evp;
+
/* ' with addpath ID ' 17
* max strlen of uint32 + 10
* +/- (just in case) + 1
@@ -2701,6 +2705,23 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi,
snprintf(pathid_buf, sizeof(pathid_buf), " with addpath ID %u",
addpath_id);
+ overlay_index_buf[0] = '\0';
+ if (overlay_index && overlay_index->type == OVERLAY_INDEX_GATEWAY_IP) {
+ char obuf[INET6_ADDRSTRLEN];
+
+ obuf[0] = '\0';
+ evp = pu.evp;
+ if (is_evpn_prefix_ipaddr_v4(evp))
+ inet_ntop(AF_INET, &overlay_index->gw_ip, obuf,
+ sizeof(obuf));
+ else if (is_evpn_prefix_ipaddr_v6(evp))
+ inet_ntop(AF_INET6, &overlay_index->gw_ip, obuf,
+ sizeof(obuf));
+
+ snprintf(overlay_index_buf, sizeof(overlay_index_buf),
+ " gateway IP %s", obuf);
+ }
+
tag_buf[0] = '\0';
if (bgp_labeled_safi(safi) && num_labels) {
@@ -2720,9 +2741,10 @@ const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi,
}
if (prd)
- snprintfrr(str, size, "RD %s %pFX%s%s %s %s",
+ snprintfrr(str, size, "RD %s %pFX%s%s%s %s %s",
prefix_rd2str(prd, rd_buf, sizeof(rd_buf)), pu.p,
- tag_buf, pathid_buf, afi2str(afi), safi2str(safi));
+ overlay_index_buf, tag_buf, pathid_buf, afi2str(afi),
+ safi2str(safi));
else if (safi == SAFI_FLOWSPEC) {
char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
const struct prefix_fs *fs = pu.fs;
diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h
index fa8da1c345..d847fb84e7 100644
--- a/bgpd/bgp_debug.h
+++ b/bgpd/bgp_debug.h
@@ -37,7 +37,8 @@
#define DUMP_DETAIL 32
/* RD + Prefix + Path-Id */
-#define BGP_PRD_PATH_STRLEN (PREFIX_STRLEN + RD_ADDRSTRLEN + 20)
+#define BGP_PRD_PATH_STRLEN \
+ (PREFIX_STRLEN + RD_ADDRSTRLEN + INET6_ADDRSTRLEN + 34)
extern int dump_open;
extern int dump_update;
@@ -179,11 +180,11 @@ extern bool bgp_debug_update(struct peer *peer, const struct prefix *p,
extern bool bgp_debug_bestpath(struct bgp_dest *dest);
extern bool bgp_debug_zebra(const struct prefix *p);
-extern const char *
-bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi, const struct prefix_rd *prd,
- union prefixconstptr pu, mpls_label_t *label,
- uint32_t num_labels, int addpath_valid,
- uint32_t addpath_id, char *str, int size);
+extern const char *bgp_debug_rdpfxpath2str(
+ afi_t afi, safi_t safi, const struct prefix_rd *prd,
+ union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels,
+ int addpath_valid, uint32_t addpath_id,
+ struct bgp_route_evpn *overlay_index, char *str, int size);
const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data,
size_t datalen);
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index d8e57419ee..5ef593b9c0 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -53,6 +53,7 @@
#include "bgpd/bgp_addpath.h"
#include "bgpd/bgp_mac.h"
#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_nht.h"
/*
* Definitions and external declarations.
@@ -65,6 +66,28 @@ 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_remote_ip_hash_init(struct bgpevpn *evpn);
+static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *evpn);
+static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn,
+ struct bgp_path_info *pi);
+static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn,
+ struct bgp_path_info *pi);
+static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn,
+ void (*func)(struct hash_bucket *,
+ void *),
+ void *arg);
+static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn);
+static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp,
+ struct bgpevpn *vpn);
+static unsigned int vni_svi_hash_key_make(const void *p);
+static bool vni_svi_hash_cmp(const void *p1, const void *p2);
+static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn,
+ struct ipaddr *addr,
+ bool resolve);
+static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket,
+ void *args);
+static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket,
+ void *args);
static struct in_addr zero_vtep_ip;
/*
@@ -1261,7 +1284,8 @@ static int update_evpn_type5_route_entry(struct bgp *bgp_evpn,
/* update evpn type-5 route entry */
static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
- struct attr *src_attr)
+ struct attr *src_attr, afi_t src_afi,
+ safi_t src_safi)
{
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
@@ -1315,6 +1339,26 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ if (src_afi == AFI_IP6 &&
+ CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) {
+ if (src_attr &&
+ !IN6_IS_ADDR_UNSPECIFIED(&src_attr->mp_nexthop_global)) {
+ attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP;
+ memcpy(&attr.evpn_overlay.gw_ip.ipv6,
+ &src_attr->mp_nexthop_global,
+ sizeof(struct in6_addr));
+ }
+ } else if (src_afi == AFI_IP &&
+ CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) {
+ if (src_attr && src_attr->nexthop.s_addr != 0) {
+ attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP;
+ memcpy(&attr.evpn_overlay.gw_ip.ipv4,
+ &src_attr->nexthop, sizeof(struct in_addr));
+ }
+ }
+
/* Setup RT and encap extended community */
build_evpn_type5_route_extcomm(bgp_vrf, &attr);
@@ -2198,6 +2242,7 @@ static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
dest = bgp_route_next(dest)) {
for (pi = bgp_dest_get_bgp_path_info(dest);
(pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) {
+ bgp_evpn_remote_ip_hash_del(vpn, pi);
bgp_path_info_delete(dest, pi);
bgp_path_info_reap(dest, pi);
}
@@ -2381,6 +2426,7 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
bool new_pi = false;
bool use_l3nhg = false;
bool is_l3nhg_active = false;
+ char buf1[INET6_ADDRSTRLEN];
memset(pp, 0, sizeof(struct prefix));
ip_prefix_from_evpn_prefix(evp, pp);
@@ -2411,10 +2457,36 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
* make sure to set the flag for next hop attribute.
*/
attr = *parent_pi->attr;
- if (afi == AFI_IP6)
- evpn_convert_nexthop_to_ipv6(&attr);
- else
- attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ if (attr.evpn_overlay.type != OVERLAY_INDEX_GATEWAY_IP) {
+ if (afi == AFI_IP6)
+ evpn_convert_nexthop_to_ipv6(&attr);
+ else
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ } else {
+
+ /*
+ * If gateway IP overlay index is specified in the NLRI of
+ * EVPN RT-5, this gateway IP should be used as the nexthop
+ * for the prefix in the VRF
+ */
+ if (bgp_debug_zebra(NULL)) {
+ zlog_debug(
+ "Install gateway IP %s as nexthop for prefix %pFX in vrf %s",
+ inet_ntop(pp->family, &attr.evpn_overlay.gw_ip,
+ buf1, sizeof(buf1)), pp,
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+
+ if (afi == AFI_IP6) {
+ memcpy(&attr.mp_nexthop_global,
+ &attr.evpn_overlay.gw_ip.ipv6,
+ sizeof(struct in6_addr));
+ attr.mp_nexthop_len = IPV6_MAX_BYTELEN;
+ } else {
+ attr.nexthop = attr.evpn_overlay.gw_ip.ipv4;
+ 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);
@@ -2460,8 +2532,27 @@ static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
pi->attr = attr_new;
pi->uptime = bgp_clock();
}
- /* as it is an importation, change nexthop */
- bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF);
+
+ /* Gateway IP nexthop should be resolved */
+ if (attr.evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
+ if (bgp_find_or_add_nexthop(bgp_vrf, bgp_vrf, afi, safi, pi,
+ NULL, 0))
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID);
+ else {
+ if (BGP_DEBUG(nht, NHT)) {
+ inet_ntop(pp->family,
+ &attr.evpn_overlay.gw_ip,
+ buf1, sizeof(buf1));
+ zlog_debug("%s: gateway IP NH unresolved",
+ buf1);
+ }
+ bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID);
+ }
+ } else {
+
+ /* 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);
@@ -2565,6 +2656,9 @@ static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
pi->uptime = bgp_clock();
}
+ /* Add this route to remote IP hashtable */
+ bgp_evpn_remote_ip_hash_add(vpn, pi);
+
/* Perform route selection and update zebra, if required. */
ret = evpn_route_select_install(bgp, vpn, dest);
@@ -2628,8 +2722,10 @@ static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
&& (struct bgp_path_info *)pi->extra->parent == parent_pi)
break;
- if (!pi)
+ if (!pi) {
+ bgp_dest_unlock_node(dest);
return 0;
+ }
if (bgp_debug_zebra(NULL))
zlog_debug("... delete dest %p (l %d) pi %p (l %d, f 0x%x)",
@@ -2690,8 +2786,12 @@ static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
&& (struct bgp_path_info *)pi->extra->parent == parent_pi)
break;
- if (!pi)
+ if (!pi) {
+ bgp_dest_unlock_node(dest);
return 0;
+ }
+
+ bgp_evpn_remote_ip_hash_del(vpn, pi);
/* Mark entry for deletion */
bgp_path_info_delete(dest, pi);
@@ -3951,7 +4051,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
mpls_label_t label; /* holds the VNI as in the packet */
int ret;
afi_t gw_afi;
- bool is_valid_update = false;
+ bool is_valid_update = true;
/* Type-5 route should be 34 or 58 bytes:
* RD (8), ESI (10), Eth Tag (4), IP len (1), IP (4 or 16),
@@ -3980,9 +4080,9 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
/* Additional information outside of prefix - ESI and GW IP */
memset(&evpn, 0, sizeof(evpn));
- /* Fetch ESI */
+ /* Fetch ESI overlay index */
if (attr)
- memcpy(&attr->esi, pfx, sizeof(esi_t));
+ memcpy(&evpn.eth_s_id, pfx, sizeof(esi_t));
pfx += ESI_BYTES;
/* Fetch Ethernet Tag. */
@@ -4031,25 +4131,53 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
* field
*/
+ /*
+ * An update containing a non-zero gateway IP and a non-zero ESI
+ * at the same time is should be treated as withdraw
+ */
+ if (bgp_evpn_is_esi_valid(&evpn.eth_s_id)
+ && !is_zero_gw_ip(&evpn.gw_ip, gw_afi)) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%s - Rx EVPN Type-5 ESI and gateway-IP both non-zero.",
+ peer->host);
+ is_valid_update = false;
+ } else if (bgp_evpn_is_esi_valid(&evpn.eth_s_id))
+ evpn.type = OVERLAY_INDEX_ESI;
+ else if (!is_zero_gw_ip(&evpn.gw_ip, gw_afi))
+ evpn.type = OVERLAY_INDEX_GATEWAY_IP;
if (attr) {
- is_valid_update = true;
- if (is_zero_mac(&attr->rmac) &&
- is_zero_gw_ip(&evpn.gw_ip, gw_afi))
+ if (is_zero_mac(&attr->rmac)
+ && !bgp_evpn_is_esi_valid(&evpn.eth_s_id)
+ && is_zero_gw_ip(&evpn.gw_ip, gw_afi) && label == 0) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%s - Rx EVPN Type-5 ESI, gateway-IP, RMAC and label all zero",
+ peer->host);
is_valid_update = false;
+ }
if (is_mcast_mac(&attr->rmac) || is_bcast_mac(&attr->rmac))
is_valid_update = false;
}
/* Process the route. */
- if (is_valid_update)
+ if (attr && is_valid_update)
ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
&prd, &label, 1, 0, &evpn);
- else
+ else {
+ if (!is_valid_update) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(attr, attr_str, BUFSIZ);
+ zlog_warn(
+ "Invalid update from peer %s vrf %u prefix %pFX attr %s - treat as withdraw",
+ peer->hostname, peer->bgp->vrf_id, &p,
+ attr_str);
+ }
ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
&prd, &label, 1, &evpn);
+ }
return ret;
}
@@ -4078,7 +4206,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p,
/* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */
stream_putc(s, 8 + 10 + 4 + 1 + len + 3);
stream_put(s, prd->val, 8);
- if (attr)
+ if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_ESI)
stream_put(s, &attr->esi, sizeof(esi_t));
else
stream_put(s, 0, sizeof(esi_t));
@@ -4088,7 +4216,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p,
stream_put_ipv4(s, p_evpn_p->prefix_addr.ip.ipaddr_v4.s_addr);
else
stream_put(s, &p_evpn_p->prefix_addr.ip.ipaddr_v6, 16);
- if (attr) {
+ if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
const struct bgp_route_evpn *evpn_overlay =
bgp_attr_get_evpn_overlay(attr);
@@ -4301,7 +4429,7 @@ void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, const struct prefix *p,
struct prefix_evpn evp;
build_type5_prefix_from_ip_prefix(&evp, p);
- ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr);
+ ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr, afi, safi);
if (ret)
flog_err(EC_BGP_EVPN_ROUTE_CREATE,
"%u: Failed to create type-5 route for prefix %pFX",
@@ -5134,7 +5262,8 @@ struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni)
struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id,
- struct in_addr mcast_grp)
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex)
{
struct bgpevpn *vpn;
@@ -5148,6 +5277,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
vpn->originator_ip = originator_ip;
vpn->tenant_vrf_id = tenant_vrf_id;
vpn->mcast_grp = mcast_grp;
+ vpn->svi_ifindex = svi_ifindex;
/* Initialize route-target import and export lists */
vpn->import_rtl = list_new();
@@ -5168,6 +5298,9 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
return NULL;
}
+ bgp_evpn_remote_ip_hash_init(vpn);
+ bgp_evpn_link_to_vni_svi_hash(bgp, vpn);
+
/* add to l2vni list on corresponding vrf */
bgpevpn_link_to_l3vni(vpn);
@@ -5185,6 +5318,7 @@ struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
*/
void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
{
+ bgp_evpn_remote_ip_hash_destroy(vpn);
bgp_evpn_vni_es_cleanup(vpn);
bgpevpn_unlink_from_l3vni(vpn);
bgp_table_unlock(vpn->route_table);
@@ -5192,6 +5326,7 @@ void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
list_delete(&vpn->import_rtl);
list_delete(&vpn->export_rtl);
bf_release_index(bm->rd_idspace, vpn->rd_id);
+ hash_release(bgp->vni_svi_hash, vpn);
hash_release(bgp->vnihash, vpn);
QOBJ_UNREG(vpn);
XFREE(MTYPE_BGP_EVPN, vpn);
@@ -5308,8 +5443,10 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
} else {
/* Re-instate the current remote best path if any */
dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p);
- if (dest)
+ if (dest) {
evpn_zebra_reinstall_best_route(bgp, vpn, dest);
+ bgp_dest_unlock_node(dest);
+ }
}
return 0;
@@ -5603,6 +5740,9 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni)
*/
delete_routes_for_vni(bgp, vpn);
+ bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn);
+
+ vpn->svi_ifindex = 0;
/*
* tunnel is no longer active, del tunnel ip address from tip_hash
*/
@@ -5623,8 +5763,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni)
int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id,
- struct in_addr mcast_grp)
-
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex)
{
struct bgpevpn *vpn;
struct prefix_evpn p;
@@ -5636,18 +5776,65 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
if (is_vni_live(vpn)
&& IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)
&& IPV4_ADDR_SAME(&vpn->mcast_grp, &mcast_grp)
- && vpn->tenant_vrf_id == tenant_vrf_id)
+ && vpn->tenant_vrf_id == tenant_vrf_id
+ && vpn->svi_ifindex == svi_ifindex)
/* Probably some other param has changed that we don't
* care about. */
return 0;
bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp);
+ if (vpn->svi_ifindex != svi_ifindex) {
+
+ /*
+ * Unresolve all the gateway IP nexthops for this VNI
+ * for old SVI
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_unlink_nexthop,
+ vpn);
+ bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn);
+ vpn->svi_ifindex = svi_ifindex;
+ bgp_evpn_link_to_vni_svi_hash(bgp, vpn);
+
+ /*
+ * Resolve all the gateway IP nexthops for this VNI
+ * for new SVI
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_link_nexthop,
+ vpn);
+ }
+
/* Update tenant_vrf_id if it has changed. */
if (vpn->tenant_vrf_id != tenant_vrf_id) {
+
+ /*
+ * Unresolve all the gateway IP nexthops for this VNI
+ * in old tenant vrf
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_unlink_nexthop,
+ vpn);
bgpevpn_unlink_from_l3vni(vpn);
vpn->tenant_vrf_id = tenant_vrf_id;
bgpevpn_link_to_l3vni(vpn);
+
+ /*
+ * Resolve all the gateway IP nexthops for this VNI
+ * in new tenant vrf
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_link_nexthop,
+ vpn);
}
/* If tunnel endpoint IP has changed, update (and delete prior
@@ -5666,7 +5853,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
/* Create or update as appropriate. */
if (!vpn) {
vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id,
- mcast_grp);
+ mcast_grp, svi_ifindex);
if (!vpn) {
flog_err(
EC_BGP_VNI,
@@ -5768,6 +5955,8 @@ void bgp_evpn_cleanup(struct bgp *bgp)
hash_free(bgp->vrf_import_rt_hash);
bgp->vrf_import_rt_hash = NULL;
+ hash_free(bgp->vni_svi_hash);
+ bgp->vni_svi_hash = NULL;
hash_free(bgp->vnihash);
bgp->vnihash = NULL;
@@ -5786,6 +5975,9 @@ void bgp_evpn_init(struct bgp *bgp)
{
bgp->vnihash =
hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash");
+ bgp->vni_svi_hash =
+ hash_create(vni_svi_hash_key_make, vni_svi_hash_cmp,
+ "BGP VNI hash based on SVI ifindex");
bgp->import_rt_hash =
hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
"BGP Import RT Hash");
@@ -5878,3 +6070,408 @@ bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx)
return false;
}
+
+static void *bgp_evpn_remote_ip_hash_alloc(void *p)
+{
+ const struct evpn_remote_ip *key = (const struct evpn_remote_ip *)p;
+ struct evpn_remote_ip *ip;
+
+ ip = XMALLOC(MTYPE_EVPN_REMOTE_IP, sizeof(struct evpn_remote_ip));
+ *ip = *key;
+ ip->macip_path_list = list_new();
+
+ return ip;
+}
+
+static unsigned int bgp_evpn_remote_ip_hash_key_make(const void *p)
+{
+ const struct evpn_remote_ip *ip = p;
+ const struct ipaddr *addr = &ip->addr;
+
+ if (IS_IPADDR_V4(addr))
+ return jhash_1word(addr->ipaddr_v4.s_addr, 0);
+
+ return jhash2(addr->ipaddr_v6.s6_addr32,
+ array_size(addr->ipaddr_v6.s6_addr32), 0);
+}
+
+static bool bgp_evpn_remote_ip_hash_cmp(const void *p1, const void *p2)
+{
+ const struct evpn_remote_ip *ip1 = p1;
+ const struct evpn_remote_ip *ip2 = p2;
+
+ return (memcmp(&ip1->addr, &ip2->addr, sizeof(struct ipaddr)) == 0);
+}
+
+static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *vpn)
+{
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ vpn->remote_ip_hash = hash_create(bgp_evpn_remote_ip_hash_key_make,
+ bgp_evpn_remote_ip_hash_cmp,
+ "BGP EVPN remote IP hash");
+}
+
+static void bgp_evpn_remote_ip_hash_free(struct hash_bucket *bucket, void *args)
+{
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+ struct bgpevpn *vpn = (struct bgpevpn *)args;
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false);
+
+ list_delete(&ip->macip_path_list);
+
+ hash_release(vpn->remote_ip_hash, ip);
+ XFREE(MTYPE_EVPN_REMOTE_IP, ip);
+}
+
+static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *vpn)
+{
+ if (!evpn_resolve_overlay_index() || vpn->remote_ip_hash == NULL)
+ return;
+
+ hash_iterate(vpn->remote_ip_hash,
+ (void (*)(struct hash_bucket *, void *))bgp_evpn_remote_ip_hash_free,
+ vpn);
+
+ hash_free(vpn->remote_ip_hash);
+ vpn->remote_ip_hash = NULL;
+}
+
+/* Add a remote MAC/IP route to hash table */
+static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn,
+ struct bgp_path_info *pi)
+{
+ struct evpn_remote_ip tmp;
+ struct evpn_remote_ip *ip;
+ struct prefix_evpn *evp;
+
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ if (pi->type != ZEBRA_ROUTE_BGP || pi->sub_type != BGP_ROUTE_IMPORTED
+ || !CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+ return;
+
+ evp = (struct prefix_evpn *)&pi->net->p;
+
+ if (evp->family != AF_EVPN
+ || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE
+ || is_evpn_prefix_ipaddr_none(evp))
+ return;
+
+ tmp.addr = evp->prefix.macip_addr.ip;
+ ip = hash_lookup(vpn->remote_ip_hash, &tmp);
+ if (ip) {
+ if (listnode_lookup(ip->macip_path_list, pi) != NULL)
+ return;
+ (void)listnode_add(ip->macip_path_list, pi);
+ return;
+ }
+
+ ip = hash_get(vpn->remote_ip_hash, &tmp, bgp_evpn_remote_ip_hash_alloc);
+ if (!ip)
+ return;
+
+ (void)listnode_add(ip->macip_path_list, pi);
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true);
+}
+
+/* Delete a remote MAC/IP route from hash table */
+static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn,
+ struct bgp_path_info *pi)
+{
+ struct evpn_remote_ip tmp;
+ struct evpn_remote_ip *ip;
+ struct prefix_evpn *evp;
+
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ evp = (struct prefix_evpn *)&pi->net->p;
+
+ if (evp->family != AF_EVPN
+ || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE
+ || is_evpn_prefix_ipaddr_none(evp))
+ return;
+
+ tmp.addr = evp->prefix.macip_addr.ip;
+ ip = hash_lookup(vpn->remote_ip_hash, &tmp);
+ if (ip == NULL)
+ return;
+
+ listnode_delete(ip->macip_path_list, pi);
+
+ if (ip->macip_path_list->count == 0) {
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false);
+ hash_release(vpn->remote_ip_hash, ip);
+ XFREE(MTYPE_EVPN_REMOTE_IP, ip);
+ }
+}
+
+static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn,
+ void (*func)(struct hash_bucket *,
+ void *),
+ void *arg)
+{
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ hash_iterate(vpn->remote_ip_hash, func, arg);
+}
+
+static void show_remote_ip_entry(struct hash_bucket *bucket, void *args)
+{
+ char buf[INET6_ADDRSTRLEN];
+ char buf2[EVPN_ROUTE_STRLEN];
+ struct prefix_evpn *evp;
+
+ struct listnode *node = NULL;
+ struct bgp_path_info *pi = NULL;
+ struct vty *vty = (struct vty *)args;
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+
+ vty_out(vty, " Remote IP: %s\n",
+ ipaddr2str(&ip->addr, buf, sizeof(buf)));
+ vty_out(vty, " Linked MAC/IP routes:\n");
+ for (ALL_LIST_ELEMENTS_RO(ip->macip_path_list, node, pi)) {
+ evp = (struct prefix_evpn *)&pi->net->p;
+ prefix2str(evp, buf2, sizeof(buf2));
+ vty_out(vty, " %s\n", buf2);
+ }
+}
+
+void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, void *args)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct vty *vty = (struct vty *)args;
+
+ vty_out(vty, "VNI: %u\n", vpn->vni);
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))show_remote_ip_entry,
+ vty);
+ vty_out(vty, "\n");
+}
+
+static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket,
+ void *args)
+{
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+ struct bgpevpn *vpn = (struct bgpevpn *)args;
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true);
+}
+
+static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket,
+ void *args)
+{
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+ struct bgpevpn *vpn = (struct bgpevpn *)args;
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false);
+}
+
+static unsigned int vni_svi_hash_key_make(const void *p)
+{
+ const struct bgpevpn *vpn = p;
+
+ return jhash_1word(vpn->svi_ifindex, 0);
+}
+
+static bool vni_svi_hash_cmp(const void *p1, const void *p2)
+{
+ const struct bgpevpn *vpn1 = p1;
+ const struct bgpevpn *vpn2 = p2;
+
+ return (vpn1->svi_ifindex == vpn2->svi_ifindex);
+}
+
+static struct bgpevpn *bgp_evpn_vni_svi_hash_lookup(struct bgp *bgp,
+ ifindex_t svi)
+{
+ struct bgpevpn *vpn;
+ struct bgpevpn tmp;
+
+ memset(&tmp, 0, sizeof(struct bgpevpn));
+ tmp.svi_ifindex = svi;
+ vpn = hash_lookup(bgp->vni_svi_hash, &tmp);
+ return vpn;
+}
+
+static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ if (vpn->svi_ifindex == 0)
+ return;
+
+ hash_get(bgp->vni_svi_hash, vpn, hash_alloc_intern);
+}
+
+static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp,
+ struct bgpevpn *vpn)
+{
+ if (vpn->svi_ifindex == 0)
+ return;
+
+ hash_release(bgp->vni_svi_hash, vpn);
+}
+
+void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args)
+{
+ struct bgpevpn *evpn = (struct bgpevpn *)bucket->data;
+ struct vty *vty = (struct vty *)args;
+
+ vty_out(vty, "SVI: %u VNI: %u\n", evpn->svi_ifindex, evpn->vni);
+}
+
+/*
+ * This function is called for a bgp_nexthop_cache entry when the nexthop is
+ * gateway IP overlay index.
+ * This function returns true if there is a remote MAC/IP route for the gateway
+ * IP in the EVI of the nexthop SVI.
+ */
+bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc)
+{
+ struct bgp *bgp_evpn = NULL;
+ struct bgpevpn *vpn = NULL;
+ struct evpn_remote_ip tmp;
+ struct prefix *p;
+
+ if (!evpn_resolve_overlay_index())
+ return false;
+
+ if (!bnc->nexthop || bnc->nexthop->ifindex == 0)
+ return false;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return false;
+
+ /*
+ * Gateway IP is resolved by nht over SVI interface.
+ * Use this SVI to find corresponding EVI(L2 context)
+ */
+ vpn = bgp_evpn_vni_svi_hash_lookup(bgp_evpn, bnc->nexthop->ifindex);
+ if (!vpn)
+ return false;
+
+ if (vpn->bgp_vrf != bnc->bgp)
+ return false;
+
+ /*
+ * Check if the gateway IP is present in the EVI remote_ip_hash table
+ * which stores all the remote IP addresses received via MAC/IP routes
+ * in this EVI
+ */
+ memset(&tmp, 0, sizeof(struct evpn_remote_ip));
+
+ p = &bnc->prefix;
+ if (p->family == AF_INET) {
+ tmp.addr.ipa_type = IPADDR_V4;
+ memcpy(&(tmp.addr.ipaddr_v4), &(p->u.prefix4),
+ sizeof(struct in_addr));
+ } else if (p->family == AF_INET6) {
+ tmp.addr.ipa_type = IPADDR_V6;
+ memcpy(&(tmp.addr.ipaddr_v6), &(p->u.prefix6),
+ sizeof(struct in6_addr));
+ } else
+ return false;
+
+ if (hash_lookup(vpn->remote_ip_hash, &tmp) == NULL)
+ return false;
+
+ return true;
+}
+
+/* Resolve/Unresolve nexthops when a MAC/IP route is added/deleted */
+static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn,
+ struct ipaddr *addr,
+ bool resolve)
+{
+ afi_t afi;
+ struct prefix p;
+ struct bgp_nexthop_cache *bnc;
+ struct bgp_nexthop_cache_head *tree = NULL;
+
+ if (!vpn->bgp_vrf || vpn->svi_ifindex == 0)
+ return;
+
+ memset(&p, 0, sizeof(struct prefix));
+
+ if (addr->ipa_type == IPADDR_V4) {
+ afi = AFI_IP;
+ p.family = AF_INET;
+ memcpy(&(p.u.prefix4), &(addr->ipaddr_v4),
+ sizeof(struct in_addr));
+ p.prefixlen = IPV4_MAX_BITLEN;
+ } else if (addr->ipa_type == IPADDR_V6) {
+ afi = AFI_IP6;
+ p.family = AF_INET6;
+ memcpy(&(p.u.prefix6), &(addr->ipaddr_v6),
+ sizeof(struct in6_addr));
+ p.prefixlen = IPV6_MAX_BITLEN;
+ } else
+ return;
+
+ tree = &vpn->bgp_vrf->nexthop_cache_table[afi];
+ bnc = bnc_find(tree, &p, 0);
+
+ if (!bnc || !bnc->is_evpn_gwip_nexthop)
+ return;
+
+ if (!bnc->nexthop || bnc->nexthop->ifindex != vpn->svi_ifindex)
+ return;
+
+ if (BGP_DEBUG(nht, NHT)) {
+ char buf[PREFIX2STR_BUFFER];
+
+ prefix2str(&bnc->prefix, buf, sizeof(buf));
+ zlog_debug("%s(%u): vni %u mac/ip %s for NH %s",
+ vpn->bgp_vrf->name_pretty, vpn->tenant_vrf_id,
+ vpn->vni, (resolve ? "add" : "delete"), buf);
+ }
+
+ /*
+ * MAC/IP route or SVI or tenant vrf being added to EVI.
+ * Set nexthop as valid only if it is already L3 reachable
+ */
+ if (resolve && bnc->flags & BGP_NEXTHOP_EVPN_INCOMPLETE) {
+ bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->flags |= BGP_NEXTHOP_VALID;
+ bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED;
+ evaluate_paths(bnc);
+ }
+
+ /* MAC/IP route or SVI or tenant vrf being deleted from EVI */
+ if (!resolve && bnc->flags & BGP_NEXTHOP_VALID) {
+ bnc->flags &= ~BGP_NEXTHOP_VALID;
+ bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED;
+ evaluate_paths(bnc);
+ }
+}
+
+void bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ bgp_evpn_remote_ip_hash_init(vpn);
+
+ for (dest = bgp_table_top(vpn->route_table); dest;
+ dest = bgp_route_next(dest))
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ bgp_evpn_remote_ip_hash_add(vpn, pi);
+}
+
+void bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+
+ bgp_evpn_remote_ip_hash_destroy(vpn);
+}
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index 83a6dd84c8..eec746e3be 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -63,14 +63,18 @@ static inline int advertise_type5_routes(struct bgp *bgp_vrf,
if (!bgp_vrf->l3vni)
return 0;
- if (afi == AFI_IP &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST))
+ if ((afi == AFI_IP)
+ && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))
+ || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))))
return 1;
- if (afi == AFI_IP6 &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))
+ if ((afi == AFI_IP6)
+ && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))
+ || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))))
return 1;
return 0;
@@ -152,6 +156,14 @@ static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi)
return true;
}
+static inline bool evpn_resolve_overlay_index(void)
+{
+ struct bgp *bgp = NULL;
+
+ bgp = bgp_get_evpn();
+ return bgp ? bgp->resolve_overlay_index : false;
+}
+
extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf,
const struct prefix *p,
struct attr *src_attr, afi_t afi,
@@ -198,7 +210,8 @@ extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni);
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id,
- struct in_addr mcast_grp);
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex);
extern void bgp_evpn_flood_control_change(struct bgp *bgp);
extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp);
extern void bgp_evpn_cleanup(struct bgp *bgp);
@@ -206,4 +219,15 @@ 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);
+extern void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket,
+ void *args);
+extern void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args);
+extern bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc);
+extern void
+bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket,
+ void *arg);
+extern void
+bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket,
+ void *arg);
+
#endif /* _QUAGGA_BGP_EVPN_H */
diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c
index b191840f63..4b90937f62 100644
--- a/bgpd/bgp_evpn_mh.c
+++ b/bgpd/bgp_evpn_mh.c
@@ -269,8 +269,10 @@ static int bgp_evpn_es_route_uninstall(struct bgp *bgp, struct bgp_evpn_es *es,
parent_pi)
break;
- if (!pi)
+ if (!pi) {
+ bgp_dest_unlock_node(dest);
return 0;
+ }
/* Mark entry for deletion */
bgp_path_info_delete(dest, pi);
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index debed9f68b..c46f34bf74 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -62,6 +62,7 @@ RB_PROTOTYPE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node,
struct bgpevpn {
vni_t vni;
vrf_id_t tenant_vrf_id;
+ ifindex_t svi_ifindex;
uint32_t flags;
#define VNI_FLAG_CFGD 0x1 /* VNI is user configured */
#define VNI_FLAG_LIVE 0x2 /* VNI is "live" */
@@ -102,6 +103,15 @@ struct bgpevpn {
struct list *import_rtl;
struct list *export_rtl;
+ /*
+ * EVPN route that uses gateway IP overlay index as its nexthop
+ * needs to do a recursive lookup.
+ * A remote MAC/IP entry should be present for the gateway IP.
+ * Maintain a hash of the addresses received via remote MAC/IP routes
+ * for efficient gateway IP recursive lookup in this EVI
+ */
+ struct hash *remote_ip_hash;
+
/* Route table for EVPN routes for
* this VNI. */
struct bgp_table *route_table;
@@ -178,6 +188,12 @@ struct bgp_evpn_info {
bool is_anycast_mac;
};
+/* This structure defines an entry in remote_ip_hash */
+struct evpn_remote_ip {
+ struct ipaddr addr;
+ struct list *macip_path_list;
+};
+
static inline int is_vrf_rd_configured(struct bgp *bgp_vrf)
{
return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD));
@@ -612,7 +628,8 @@ extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni);
extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip,
vrf_id_t tenant_vrf_id,
- struct in_addr mcast_grp);
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex);
extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni);
extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn);
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 2a7c2ec853..190323859f 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -59,6 +59,17 @@ struct vni_walk_ctx {
int detail;
};
+int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, int *oly_idx,
+ enum overlay_index_type *oly)
+{
+ *oly = OVERLAY_INDEX_TYPE_NONE;
+ if (argv_find(argv, argc, "gateway-ip", oly_idx)) {
+ if (oly)
+ *oly = OVERLAY_INDEX_GATEWAY_IP;
+ }
+ return 1;
+}
+
static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt,
json_object *json)
{
@@ -520,6 +531,9 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
else
json_object_string_add(json, "advertiseSviMacIp",
"Disabled");
+ json_object_string_add(
+ json, "sviInterface",
+ ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
} else {
vty_out(vty, "VNI: %d", vpn->vni);
if (is_vni_live(vpn))
@@ -553,6 +567,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
else
vty_out(vty, " Advertise-svi-macip : %s\n",
"Disabled");
+ vty_out(vty, " SVI interface : %s\n",
+ ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
}
if (!json)
@@ -2279,7 +2295,7 @@ static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni)
/* tenant vrf will be updated when we get local_vni_add from
* zebra
*/
- vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp);
+ vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp, 0);
if (!vpn) {
flog_err(
EC_BGP_VNI,
@@ -2418,6 +2434,10 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp,
if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
if (!json)
vty_out(vty, "%% Network not in table\n");
+
+ if (dest)
+ bgp_dest_unlock_node(dest);
+
return;
}
@@ -2452,6 +2472,8 @@ static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp,
vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
path_cnt);
}
+
+ bgp_dest_unlock_node(dest);
}
/*
@@ -2488,6 +2510,10 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
if (!json)
vty_out(vty, "%% Network not in table\n");
+
+ if (dest)
+ bgp_dest_unlock_node(dest);
+
return;
}
@@ -2522,6 +2548,8 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
path_cnt);
}
+
+ bgp_dest_unlock_node(dest);
}
/* Disaplay EVPN routes for a ESI - VTY handler */
@@ -2592,6 +2620,10 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp,
if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
if (!json)
vty_out(vty, "%% Network not in table\n");
+
+ if (dest)
+ bgp_dest_unlock_node(dest);
+
return;
}
@@ -2627,6 +2659,8 @@ static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp,
vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
path_cnt);
}
+
+ bgp_dest_unlock_node(dest);
}
/*
@@ -2660,14 +2694,18 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp,
return;
table = bgp_dest_get_bgp_table_info(rd_dest);
- if (table == NULL)
+ if (table == NULL) {
+ bgp_dest_unlock_node(rd_dest);
return;
+ }
if (json) {
json_rd = json_object_new_object();
json_object_string_add(json_rd, "rd", rd_str);
}
+ bgp_dest_unlock_node(rd_dest);
+
/* Display all prefixes with this RD. */
for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
const struct prefix_evpn *evp =
@@ -2878,6 +2916,8 @@ static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp,
json_rd = NULL;
}
}
+
+ bgp_dest_unlock_node(dest);
}
if (json) {
@@ -3286,6 +3326,28 @@ static void evpn_unset_advertise_all_vni(struct bgp *bgp)
bgp_evpn_cleanup_on_disable(bgp);
}
+/* Set resolve overlay index flag */
+static void bgp_evpn_set_unset_resolve_overlay_index(struct bgp *bgp, bool set)
+{
+ if (set == bgp->resolve_overlay_index)
+ return;
+
+ if (set) {
+ bgp->resolve_overlay_index = true;
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_handle_resolve_overlay_index_set,
+ NULL);
+ } else {
+ hash_iterate(
+ bgp->vnihash,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_handle_resolve_overlay_index_unset,
+ NULL);
+ bgp->resolve_overlay_index = false;
+ }
+}
+
/*
* EVPN - use RFC8365 to auto-derive RT
*/
@@ -3784,10 +3846,11 @@ DEFUN_HIDDEN (no_bgp_evpn_advertise_vni_subnet,
DEFUN (bgp_evpn_advertise_type5,
bgp_evpn_advertise_type5_cmd,
- "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [route-map WORD]",
+ "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [gateway-ip] [route-map WORD]",
"Advertise prefix routes\n"
BGP_AFI_HELP_STR
BGP_SAFI_HELP_STR
+ "advertise gateway IP overlay index\n"
"route-map for filtering specific routes\n"
"Name of the route map\n")
{
@@ -3799,9 +3862,14 @@ DEFUN (bgp_evpn_advertise_type5,
safi_t safi = 0;
int ret = 0;
int rmap_changed = 0;
+ enum overlay_index_type oly = OVERLAY_INDEX_TYPE_NONE;
+ int idx_oly = 0;
+ bool adv_flag_changed = false;
argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+ argv_find_and_parse_oly_idx(argv, argc, &idx_oly, &oly);
+
ret = argv_find(argv, argc, "route-map", &idx_rmap);
if (ret) {
if (!bgp_vrf->adv_cmd_rmap[afi][safi].name)
@@ -3826,39 +3894,149 @@ DEFUN (bgp_evpn_advertise_type5,
return CMD_WARNING;
}
+ if ((oly != OVERLAY_INDEX_TYPE_NONE)
+ && (oly != OVERLAY_INDEX_GATEWAY_IP)) {
+ vty_out(vty, "%%Unknown overlay-index type specified");
+ return CMD_WARNING;
+ }
+
if (afi == AFI_IP) {
+ if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))
+ && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
+
+ /*
+ * this is the case for first time ever configuration
+ * adv ipv4 unicast is enabled for the first time.
+ * So no need to reset any flag
+ */
+ if (oly == OVERLAY_INDEX_TYPE_NONE)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ else if (oly == OVERLAY_INDEX_GATEWAY_IP)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ } else if ((oly == OVERLAY_INDEX_TYPE_NONE)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))) {
+
+ /*
+ * This is modify case from gateway-ip
+ * to no overlay index
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ } else if ((oly == OVERLAY_INDEX_GATEWAY_IP)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
+
+ /*
+ * This is modify case from no overlay index
+ * to gateway-ip
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ } else {
- /* if we are already advertising ipv4 prefix as type-5
- * nothing to do
- */
- if (!rmap_changed &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST))
- return CMD_WARNING;
- SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST);
+ /*
+ * Command is issued with the same option
+ * (no overlay index or gateway-ip) which was
+ * already configured. So nothing to do.
+ * However, route-map may have been modified.
+ * check if route-map has been modified.
+ * If not, return an error
+ */
+ if (!rmap_changed)
+ return CMD_WARNING;
+ }
} else {
+ if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))
+ && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) {
+
+ /*
+ * this is the case for first time ever configuration
+ * adv ipv6 unicast is enabled for the first time.
+ * So no need to reset any flag
+ */
+ if (oly == OVERLAY_INDEX_TYPE_NONE)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ else if (oly == OVERLAY_INDEX_GATEWAY_IP)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ } else if ((oly == OVERLAY_INDEX_TYPE_NONE)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))) {
+
+ /*
+ * This is modify case from gateway-ip
+ * to no overlay index
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ } else if ((oly == OVERLAY_INDEX_GATEWAY_IP)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) {
+
+ /*
+ * This is modify case from no overlay index
+ * to gateway-ip
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ } else {
- /* if we are already advertising ipv6 prefix as type-5
- * nothing to do
- */
- if (!rmap_changed &&
- CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))
- return CMD_WARNING;
- SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST);
+ /*
+ * Command is issued with the same option
+ * (no overlay index or gateway-ip) which was
+ * already configured. So nothing to do.
+ * However, route-map may have been modified.
+ * check if route-map has been modified.
+ * If not, return an error
+ */
+ if (!rmap_changed)
+ return CMD_WARNING;
+ }
}
- if (rmap_changed) {
+ if ((rmap_changed) || (adv_flag_changed)) {
+
+ /* If either of these are changed, then FRR needs to
+ * withdraw already advertised type5 routes.
+ */
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
- if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
- XFREE(MTYPE_ROUTE_MAP_NAME,
- bgp_vrf->adv_cmd_rmap[afi][safi].name);
- route_map_counter_decrement(
+ if (rmap_changed) {
+ if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_vrf->adv_cmd_rmap[afi][safi].name);
+ route_map_counter_decrement(
bgp_vrf->adv_cmd_rmap[afi][safi].map);
- bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
- bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
+ bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
+ bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
+ }
}
}
@@ -3912,22 +4090,30 @@ DEFUN (no_bgp_evpn_advertise_type5,
/* if we are not advertising ipv4 prefix as type-5
* nothing to do
*/
- if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) {
+ if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) ||
+ (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST);
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
}
} else {
/* if we are not advertising ipv6 prefix as type-5
* nothing to do
*/
- if (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) {
+ if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) ||
+ (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))){
bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST);
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
}
}
@@ -3977,6 +4163,23 @@ DEFPY (bgp_evpn_ead_evi_tx_disable,
return CMD_SUCCESS;
}
+DEFPY (bgp_evpn_enable_resolve_overlay_index,
+ bgp_evpn_enable_resolve_overlay_index_cmd,
+ "[no$no] enable-resolve-overlay-index",
+ NO_STR
+ "Enable Recursive Resolution of type-5 route overlay index\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (bgp != bgp_get_evpn()) {
+ vty_out(vty, "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ bgp_evpn_set_unset_resolve_overlay_index(bgp, no ? false : true);
+ return CMD_SUCCESS;
+}
+
DEFPY (bgp_evpn_advertise_pip_ip_mac,
bgp_evpn_advertise_pip_ip_mac_cmd,
"[no$no] advertise-pip [ip <A.B.C.D> [mac <X:X:X:X:X:X|X:X:X:X:X:X/M>]]",
@@ -4220,6 +4423,61 @@ DEFUN(show_bgp_l2vpn_evpn_vni,
return CMD_SUCCESS;
}
+DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_remote_ip_hash,
+ show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd,
+ "show bgp l2vpn evpn vni remote-ip-hash",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show VNI\n"
+ "Remote IP hash\n")
+{
+ struct bgp *bgp_evpn;
+ int idx = 0;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return CMD_WARNING;
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ hash_iterate(bgp_evpn->vnihash,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_show_remote_ip_hash,
+ vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_svi_hash,
+ show_bgp_l2vpn_evpn_vni_svi_hash_cmd,
+ "show bgp l2vpn evpn vni-svi-hash",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show vni-svi-hash\n")
+{
+ struct bgp *bgp_evpn;
+ int idx = 0;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return CMD_WARNING;
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ hash_iterate(bgp_evpn->vni_svi_hash,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_show_vni_svi_hash,
+ vty);
+
+ return CMD_SUCCESS;
+}
+
DEFPY(show_bgp_l2vpn_evpn_es_evi,
show_bgp_l2vpn_evpn_es_evi_cmd,
"show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]",
@@ -6061,6 +6319,9 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
if (bgp->evpn_info->advertise_svi_macip)
vty_out(vty, " advertise-svi-ip\n");
+ if (bgp->resolve_overlay_index)
+ vty_out(vty, " enable-resolve-overlay-index\n");
+
if (bgp_mh_info->host_routes_use_l3nhg !=
BGP_EVPN_MH_USE_ES_L3NHG_DEF) {
if (bgp_mh_info->host_routes_use_l3nhg)
@@ -6107,21 +6368,40 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
vty_out(vty, " flooding disable\n");
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) {
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) {
if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
vty_out(vty, " advertise ipv4 unicast route-map %s\n",
bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
else
- vty_out(vty, " advertise ipv4 unicast\n");
+ vty_out(vty,
+ " advertise ipv4 unicast\n");
+ } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) {
+ if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
+ vty_out(vty,
+ " advertise ipv4 unicast gateway-ip route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
+ else
+ vty_out(vty, " advertise ipv4 unicast gateway-ip\n");
}
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST)) {
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) {
if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
- vty_out(vty, " advertise ipv6 unicast route-map %s\n",
+ vty_out(vty,
+ " advertise ipv6 unicast route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
+ else
+ vty_out(vty,
+ " advertise ipv6 unicast\n");
+ } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) {
+ if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
+ vty_out(vty,
+ " advertise ipv6 unicast gateway-ip route-map %s\n",
bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
else
- vty_out(vty, " advertise ipv6 unicast\n");
+ vty_out(vty, " advertise ipv6 unicast gateway-ip\n");
}
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
@@ -6230,6 +6510,8 @@ void bgp_ethernetvpn_init(void)
install_element(BGP_EVPN_NODE, &bgp_evpn_use_es_l3nhg_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_rx_disable_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_tx_disable_cmd);
+ install_element(BGP_EVPN_NODE,
+ &bgp_evpn_enable_resolve_overlay_index_cmd);
/* test commands */
install_element(BGP_EVPN_NODE, &test_es_add_cmd);
@@ -6241,6 +6523,8 @@ void bgp_ethernetvpn_init(void)
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_vni_remote_ip_hash_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_svi_hash_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
diff --git a/bgpd/bgp_evpn_vty.h b/bgpd/bgp_evpn_vty.h
index 33f6e4f1b6..137365ddbf 100644
--- a/bgpd/bgp_evpn_vty.h
+++ b/bgpd/bgp_evpn_vty.h
@@ -28,6 +28,10 @@ extern void bgp_ethernetvpn_init(void);
#define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n"
#define EVPN_HELP_STR "Ethernet Virtual Private Network\n"
+extern int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc,
+ int *oly_idx,
+ enum overlay_index_type *oly);
+
/* Parse type from "type <ead|1|...>", return -1 on failure */
extern int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv,
int argc);
diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c
index bdab2ec36a..73ca9f07e0 100644
--- a/bgpd/bgp_label.c
+++ b/bgpd/bgp_label.c
@@ -89,8 +89,8 @@ int bgp_parse_fec_update(void)
bgp_set_valid_label(&dest->local_label);
}
SET_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED);
- bgp_dest_unlock_node(dest);
bgp_process(bgp, dest, afi, safi);
+ bgp_dest_unlock_node(dest);
return 1;
}
diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c
index 3d7bc08ac5..02b7e64869 100644
--- a/bgpd/bgp_mac.c
+++ b/bgpd/bgp_mac.c
@@ -200,8 +200,8 @@ static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer,
AFI_L2VPN, SAFI_EVPN, &prd,
p, label_pnt, num_labels,
pi->addpath_rx_id ? 1 : 0,
- pi->addpath_rx_id, pfx_buf,
- sizeof(pfx_buf));
+ pi->addpath_rx_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug(
"%s skip update of %s marked as removed",
peer->host, pfx_buf);
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index eb85936f0f..196e56a937 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -144,3 +144,4 @@ DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie");
DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service");
DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id");
DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function");
+DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry");
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
index c5ba371498..0021fa9c3d 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -142,4 +142,6 @@ DECLARE_MTYPE(BGP_SRV6_VPN);
DECLARE_MTYPE(BGP_SRV6_SID);
DECLARE_MTYPE(BGP_SRV6_FUNCTION);
+DECLARE_MTYPE(EVPN_REMOTE_IP);
+
#endif /* _QUAGGA_BGP_MEMORY_H */
diff --git a/bgpd/bgp_nb_config.c b/bgpd/bgp_nb_config.c
index 5189d7ba8f..ab22aee1ca 100644
--- a/bgpd/bgp_nb_config.c
+++ b/bgpd/bgp_nb_config.c
@@ -182,24 +182,52 @@ int bgp_router_destroy(struct nb_cb_destroy_args *args)
for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) {
if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
continue;
- if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
- BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
- BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
- BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
- BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP][SAFI_UNICAST],
- BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6][SAFI_UNICAST],
- BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
- (bgp == bgp_get_evpn() &&
- (CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST) ||
- CHECK_FLAG(tmp_bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
- BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST))) ||
- (tmp_bgp->vnihash && hashcount(tmp_bgp->vnihash))) {
+ if (CHECK_FLAG(tmp_bgp->af_flags[AFI_IP]
+ [SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP6]
+ [SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP6]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP6]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)
+ || (bgp == bgp_get_evpn()
+ && (CHECK_FLAG(
+ tmp_bgp->af_flags
+ [AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags
+ [AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags
+ [AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)
+ || CHECK_FLAG(
+ tmp_bgp->af_flags
+ [AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)))
+ || (tmp_bgp->vnihash
+ && hashcount(tmp_bgp->vnihash))) {
snprintf(
args->errmsg, args->errmsg_len,
"Cannot delete default BGP instance. Dependent VRF instances exist\n");
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
index c417dda398..e7bad42f97 100644
--- a/bgpd/bgp_nexthop.c
+++ b/bgpd/bgp_nexthop.c
@@ -835,6 +835,18 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
bnc->metric, bnc->path_count);
if (peer)
vty_out(vty, ", peer %s", peer->host);
+ if (bnc->is_evpn_gwip_nexthop)
+ vty_out(vty, " EVPN Gateway IP");
+ vty_out(vty, "\n");
+ bgp_show_nexthops_detail(vty, bgp, bnc);
+ } else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) {
+ vty_out(vty,
+ " %s overlay index unresolved [IGP metric %d], #paths %d",
+ inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix,
+ buf, sizeof(buf)),
+ bnc->metric, bnc->path_count);
+ if (bnc->is_evpn_gwip_nexthop)
+ vty_out(vty, " EVPN Gateway IP");
vty_out(vty, "\n");
bgp_show_nexthops_detail(vty, bgp, bnc);
} else {
@@ -844,6 +856,8 @@ static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
bnc->path_count);
if (peer)
vty_out(vty, ", peer %s", peer->host);
+ if (bnc->is_evpn_gwip_nexthop)
+ vty_out(vty, " EVPN Gateway IP");
vty_out(vty, "\n");
if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))
vty_out(vty, " Must be Connected\n");
diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h
index fe0a9646a0..16c2b6c65a 100644
--- a/bgpd/bgp_nexthop.h
+++ b/bgpd/bgp_nexthop.h
@@ -56,6 +56,10 @@ struct bgp_nexthop_cache {
time_t last_update;
uint16_t flags;
+/*
+ * If the nexthop is EVPN gateway IP NH, VALID flag is set only if the nexthop
+ * is RIB reachable as well as MAC/IP is present
+ */
#define BGP_NEXTHOP_VALID (1 << 0)
#define BGP_NEXTHOP_REGISTERED (1 << 1)
#define BGP_NEXTHOP_CONNECTED (1 << 2)
@@ -64,11 +68,29 @@ struct bgp_nexthop_cache {
#define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5)
#define BGP_NEXTHOP_LABELED_VALID (1 << 6)
+/*
+ * This flag is added for EVPN gateway IP nexthops.
+ * If the nexthop is RIB reachable, but a MAC/IP is not yet
+ * resolved, this flag is set.
+ * Following table explains the combination of L3 and L2 reachability w.r.t.
+ * VALID and INCOMPLETE flags
+ *
+ * | MACIP resolved | MACIP unresolved
+ *----------------|----------------|------------------
+ * L3 reachable | VALID = 1 | VALID = 0
+ * | INCOMPLETE = 0 | INCOMPLETE = 1
+ * ---------------|----------------|--------------------
+ * L3 unreachable | VALID = 0 | VALID = 0
+ * | INCOMPLETE = 0 | INCOMPLETE = 0
+ */
+#define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 7)
+
uint16_t change_flags;
#define BGP_NEXTHOP_CHANGED (1 << 0)
#define BGP_NEXTHOP_METRIC_CHANGED (1 << 1)
#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2)
+#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3)
/* Back pointer to the cache tree this entry belongs to. */
struct bgp_nexthop_cache_head *tree;
@@ -79,6 +101,11 @@ struct bgp_nexthop_cache {
LIST_HEAD(path_list, bgp_path_info) paths;
unsigned int path_count;
struct bgp *bgp;
+
+ /* This flag is set to TRUE for a bnc that is gateway IP overlay index
+ * nexthop.
+ */
+ bool is_evpn_gwip_nexthop;
};
extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a,
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 4b4a3716e6..742ef217d9 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -53,7 +53,6 @@ static void register_zebra_rnh(struct bgp_nexthop_cache *bnc,
int is_bgp_static_route);
static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc,
int is_bgp_static_route);
-static void evaluate_paths(struct bgp_nexthop_cache *bnc);
static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
static int bgp_nht_ifp_initial(struct thread *thread);
@@ -244,6 +243,9 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
}
}
+ if (pi && is_route_parent_evpn(pi))
+ bnc->is_evpn_gwip_nexthop = true;
+
if (is_bgp_static_route) {
SET_FLAG(bnc->flags, BGP_STATIC_ROUTE);
@@ -331,7 +333,7 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
return 1;
else if (safi == SAFI_UNICAST && pi
&& pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra
- && pi->extra->num_labels) {
+ && pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) {
return bgp_isvalid_labeled_nexthop(bnc);
} else
return (bgp_isvalid_nexthop(bnc));
@@ -387,6 +389,7 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc,
struct nexthop *nhlist_head = NULL;
struct nexthop *nhlist_tail = NULL;
int i;
+ bool evpn_resolved = false;
bnc->last_update = bgp_clock();
bnc->change_flags = 0;
@@ -417,7 +420,8 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc,
if (!bnc->nexthop_num)
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
- bnc->flags |= BGP_NEXTHOP_VALID;
+ if (!bnc->is_evpn_gwip_nexthop)
+ bnc->flags |= BGP_NEXTHOP_VALID;
bnc->metric = nhr->metric;
bnc->nexthop_num = nhr->nexthop_num;
@@ -488,7 +492,40 @@ static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc,
}
bnc_nexthop_free(bnc);
bnc->nexthop = nhlist_head;
+
+ /*
+ * Gateway IP nexthop is L3 reachable. Mark it as
+ * BGP_NEXTHOP_VALID only if it is recursively resolved with a
+ * remote EVPN RT-2.
+ * Else, mark it as BGP_NEXTHOP_EVPN_INCOMPLETE.
+ * When its mapping with EVPN RT-2 is established, unset
+ * BGP_NEXTHOP_EVPN_INCOMPLETE and set BGP_NEXTHOP_VALID.
+ */
+ if (bnc->is_evpn_gwip_nexthop) {
+ evpn_resolved = bgp_evpn_is_gateway_ip_resolved(bnc);
+
+ if (BGP_DEBUG(nht, NHT)) {
+ char buf2[PREFIX2STR_BUFFER];
+
+ prefix2str(&bnc->prefix, buf2, sizeof(buf2));
+ zlog_debug(
+ "EVPN gateway IP %s recursive MAC/IP lookup %s",
+ buf2,
+ (evpn_resolved ? "successful"
+ : "failed"));
+ }
+
+ if (evpn_resolved) {
+ bnc->flags |= BGP_NEXTHOP_VALID;
+ bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED;
+ } else {
+ bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->flags &= ~BGP_NEXTHOP_VALID;
+ }
+ }
} else {
+ bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
bnc->flags &= ~BGP_NEXTHOP_VALID;
bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID;
bnc->nexthop_num = nhr->nexthop_num;
@@ -694,6 +731,7 @@ void bgp_cleanup_nexthops(struct bgp *bgp)
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE);
}
}
}
@@ -888,7 +926,7 @@ static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc,
* RETURNS:
* void.
*/
-static void evaluate_paths(struct bgp_nexthop_cache *bnc)
+void evaluate_paths(struct bgp_nexthop_cache *bnc)
{
struct bgp_dest *dest;
struct bgp_path_info *path;
@@ -942,16 +980,18 @@ static void evaluate_paths(struct bgp_nexthop_cache *bnc)
* In case of unicast routes that were imported from vpn
* and that have labels, they are valid only if there are
* nexthops with labels
+ *
+ * If the nexthop is EVPN gateway-IP,
+ * do not check for a valid label.
*/
bool bnc_is_valid_nexthop = false;
bool path_valid = false;
- if (safi == SAFI_UNICAST &&
- path->sub_type == BGP_ROUTE_IMPORTED &&
- path->extra &&
- path->extra->num_labels) {
-
+ if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED
+ && path->extra && path->extra->num_labels
+ && (path->attr->evpn_overlay.type
+ != OVERLAY_INDEX_GATEWAY_IP)) {
bnc_is_valid_nexthop =
bgp_isvalid_labeled_nexthop(bnc) ? true : false;
} else {
diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h
index 9268b225ca..e006aa4469 100644
--- a/bgpd/bgp_nht.h
+++ b/bgpd/bgp_nht.h
@@ -90,6 +90,7 @@ extern void bgp_nht_register_nexthops(struct bgp *bgp);
*/
extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer);
extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer);
+extern void evaluate_paths(struct bgp_nexthop_cache *bnc);
/* APIs for setting up and allocating L3 nexthop group ids */
extern uint32_t bgp_l3nhg_id_alloc(void);
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 959a87d583..4637cef3eb 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -3451,23 +3451,6 @@ struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance,
return new;
}
-static void overlay_index_update(struct attr *attr,
- union gw_addr *gw_ip)
-{
- if (!attr)
- return;
- if (gw_ip == NULL) {
- struct bgp_route_evpn eo;
-
- memset(&eo, 0, sizeof(eo));
- bgp_attr_set_evpn_overlay(attr, &eo);
- } else {
- struct bgp_route_evpn eo = {.gw_ip = *gw_ip};
-
- bgp_attr_set_evpn_overlay(attr, &eo);
- }
-}
-
static bool overlay_index_equal(afi_t afi, struct bgp_path_info *path,
union gw_addr *gw_ip)
{
@@ -3650,6 +3633,11 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (has_valid_label)
assert(label != NULL);
+ /* Update overlay index of the attribute */
+ if (afi == AFI_L2VPN && evpn)
+ memcpy(&attr->evpn_overlay, evpn,
+ sizeof(struct bgp_route_evpn));
+
/* When peer's soft reconfiguration enabled. Record input packet in
Adj-RIBs-In. */
if (!soft_reconfig
@@ -3825,12 +3813,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
goto filtered;
}
- /* Update Overlay Index */
- if (afi == AFI_L2VPN) {
- overlay_index_update(&new_attr,
- evpn == NULL ? NULL : &evpn->gw_ip);
- }
-
/* The flag BGP_NODE_FIB_INSTALL_PENDING is for the following
* condition :
* Suppress fib is enabled
@@ -3865,10 +3847,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
&& (!has_valid_label
|| memcmp(&(bgp_path_info_extra_get(pi))->label, label,
num_labels * sizeof(mpls_label_t))
- == 0)
- && (overlay_index_equal(
- afi, pi,
- evpn == NULL ? NULL : &evpn->gw_ip))) {
+ == 0)) {
if (get_active_bdc_from_pi(pi, afi, safi)
&& peer->sort == BGP_PEER_EBGP
&& CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) {
@@ -3876,7 +3855,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label,
num_labels, addpath_id ? 1 : 0,
- addpath_id, pfx_buf,
+ addpath_id, evpn, pfx_buf,
sizeof(pfx_buf));
zlog_debug("%s rcvd %s", peer->host,
pfx_buf);
@@ -3902,7 +3881,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label,
num_labels, addpath_id ? 1 : 0,
- addpath_id, pfx_buf,
+ addpath_id, evpn, pfx_buf,
sizeof(pfx_buf));
zlog_debug(
"%s rcvd %s...duplicate ignored",
@@ -3929,8 +3908,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, evpn,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug(
"%s rcvd %s, flapped quicker than processing",
peer->host, pfx_buf);
@@ -3943,7 +3922,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
num_labels, addpath_id ? 1 : 0,
- addpath_id, pfx_buf,
+ addpath_id, evpn, pfx_buf,
sizeof(pfx_buf));
zlog_debug("%s rcvd %s", peer->host, pfx_buf);
}
@@ -4216,8 +4195,8 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
}
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, evpn,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug("%s rcvd %s", peer->host, pfx_buf);
}
@@ -4248,11 +4227,6 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
}
}
- /* Update Overlay Index */
- if (afi == AFI_L2VPN) {
- overlay_index_update(new->attr,
- evpn == NULL ? NULL : &evpn->gw_ip);
- }
/* Nexthop reachability check. */
if (((afi == AFI_IP || afi == AFI_IP6)
&& (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST))
@@ -4362,8 +4336,8 @@ filtered:
}
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, evpn,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug("%s rcvd UPDATE about %s -- DENIED due to: %s",
peer->host, pfx_buf, reason);
}
@@ -4447,8 +4421,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(
afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug(
"%s withdrawing route %s not in adj-in",
peer->host, pfx_buf);
@@ -4467,8 +4441,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
/* Logging. */
if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug("%s rcvd UPDATE about %s -- withdrawn", peer->host,
pfx_buf);
}
@@ -4488,8 +4462,8 @@ int bgp_withdraw(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
}
} else if (bgp_debug_update(peer, p, NULL, 1)) {
bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
- addpath_id ? 1 : 0, addpath_id, pfx_buf,
- sizeof(pfx_buf));
+ addpath_id ? 1 : 0, addpath_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
zlog_debug("%s Can't find the route %s", peer->host, pfx_buf);
}
@@ -6182,6 +6156,7 @@ void bgp_static_add(struct bgp *bgp)
struct bgp_table *table;
struct bgp_static *bgp_static;
+ SET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS);
FOREACH_AFI_SAFI (afi, safi)
for (dest = bgp_table_top(bgp->route[afi][safi]); dest;
dest = bgp_route_next(dest)) {
@@ -6208,6 +6183,7 @@ void bgp_static_add(struct bgp *bgp)
safi);
}
}
+ UNSET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS);
}
/* Called from bgp_delete(). Delete all static routes from the BGP
@@ -9865,6 +9841,11 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
json_nexthop_global = json_object_new_object();
}
+ if (safi == SAFI_EVPN) {
+ if (!json_paths)
+ vty_out(vty, " Route %pRN", bn);
+ }
+
if (path->extra) {
char tag_buf[30];
@@ -9876,12 +9857,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
}
if (safi == SAFI_EVPN) {
if (!json_paths) {
- vty_out(vty, " Route %pFX",
- (struct prefix_evpn *)
- bgp_dest_get_prefix(bn));
if (tag_buf[0] != '\0')
vty_out(vty, " VNI %s", tag_buf);
- vty_out(vty, "\n");
} else {
if (tag_buf[0])
json_object_string_add(json_path, "VNI",
@@ -9928,6 +9905,27 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
}
}
+ if (safi == SAFI_EVPN
+ && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
+ char gwip_buf[INET6_ADDRSTRLEN];
+
+ if (is_evpn_prefix_ipaddr_v4((struct prefix_evpn *)&bn->p))
+ inet_ntop(AF_INET, &attr->evpn_overlay.gw_ip.ipv4,
+ gwip_buf, sizeof(gwip_buf));
+ else
+ inet_ntop(AF_INET6, &attr->evpn_overlay.gw_ip.ipv6,
+ gwip_buf, sizeof(gwip_buf));
+
+ if (json_paths)
+ json_object_string_add(json_path, "gatewayIP",
+ gwip_buf);
+ else
+ vty_out(vty, " Gateway IP %s", gwip_buf);
+ }
+
+ if (safi == SAFI_EVPN)
+ vty_out(vty, "\n");
+
/* Line1 display AS-path, Aggregator */
if (attr->aspath) {
if (json_paths) {
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 806a771cfb..9a7b7b3cf2 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -1084,6 +1084,71 @@ static const struct route_map_rule_cmd route_match_evpn_rd_cmd = {
route_match_rd_free
};
+static enum route_map_cmd_result_t
+route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object)
+{
+ struct ipaddr *gw_ip = rule;
+ struct bgp_path_info *path;
+ struct prefix_evpn *evp;
+
+ if (prefix->family != AF_EVPN)
+ return RMAP_OKAY;
+
+ evp = (struct prefix_evpn *)prefix;
+ if (evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE)
+ return RMAP_OKAY;
+
+ if ((is_evpn_prefix_ipaddr_v4(evp) && IPADDRSZ(gw_ip) != 4)
+ || (is_evpn_prefix_ipaddr_v6(evp) && IPADDRSZ(gw_ip) != 16))
+ return RMAP_OKAY;
+
+ path = object;
+
+ /* Set gateway-ip value. */
+ path->attr->evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP;
+ memcpy(&path->attr->evpn_overlay.gw_ip, &gw_ip->ip.addr,
+ IPADDRSZ(gw_ip));
+
+ return RMAP_OKAY;
+}
+
+/*
+ * Route map `evpn gateway-ip' compile function.
+ * Given string is converted to struct ipaddr structure
+ */
+static void *route_set_evpn_gateway_ip_compile(const char *arg)
+{
+ struct ipaddr *gw_ip = NULL;
+ int ret;
+
+ gw_ip = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct ipaddr));
+
+ ret = str2ipaddr(arg, gw_ip);
+ if (ret < 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip);
+ return NULL;
+ }
+ return gw_ip;
+}
+
+/* Free route map's compiled `evpn gateway_ip' value. */
+static void route_set_evpn_gateway_ip_free(void *rule)
+{
+ struct ipaddr *gw_ip = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip);
+}
+
+/* Route map commands for set evpn gateway-ip ipv4. */
+struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv4_cmd = {
+ "evpn gateway-ip ipv4", route_set_evpn_gateway_ip,
+ route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free};
+
+/* Route map commands for set evpn gateway-ip ipv6. */
+struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv6_cmd = {
+ "evpn gateway-ip ipv6", route_set_evpn_gateway_ip,
+ route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free};
+
/* Route map commands for VRF route leak with source vrf matching */
static enum route_map_cmd_result_t
route_match_vrl_source_vrf(void *rule, const struct prefix *prefix,
@@ -4067,6 +4132,148 @@ DEFUN_YANG (no_match_evpn_rd,
return nb_cli_apply_changes(vty, NULL);
}
+DEFUN_YANG (set_evpn_gw_ip_ipv4,
+ set_evpn_gw_ip_ipv4_cmd,
+ "set evpn gateway-ip ipv4 A.B.C.D",
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv4 address\n"
+ "Gateway IP address in IPv4 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']";
+ char xpath_value[XPATH_MAXLEN];
+
+ ret = str2sockunion(argv[4]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (su.sin.sin_addr.s_addr == 0
+ || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) {
+ vty_out(vty,
+ "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4",
+ xpath);
+
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_evpn_gw_ip_ipv4,
+ no_set_evpn_gw_ip_ipv4_cmd,
+ "no set evpn gateway-ip ipv4 A.B.C.D",
+ NO_STR
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv4 address\n"
+ "Gateway IP address in IPv4 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']";
+
+ ret = str2sockunion(argv[5]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (su.sin.sin_addr.s_addr == 0
+ || IPV4_CLASS_DE(ntohl(su.sin.sin_addr.s_addr))) {
+ vty_out(vty,
+ "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_evpn_gw_ip_ipv6,
+ set_evpn_gw_ip_ipv6_cmd,
+ "set evpn gateway-ip ipv6 X:X::X:X",
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv6 address\n"
+ "Gateway IP address in IPv6 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']";
+ char xpath_value[XPATH_MAXLEN];
+
+ ret = str2sockunion(argv[4]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr)
+ || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) {
+ vty_out(vty,
+ "%% Gateway IP cannot be a linklocal or multicast address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6",
+ xpath);
+
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_evpn_gw_ip_ipv6,
+ no_set_evpn_gw_ip_ipv6_cmd,
+ "no set evpn gateway-ip ipv6 X:X::X:X",
+ NO_STR
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv4 address\n"
+ "Gateway IP address in IPv4 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']";
+
+ ret = str2sockunion(argv[5]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr)
+ || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) {
+ vty_out(vty,
+ "%% Gateway IP cannot be a linklocal or multicast address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
DEFPY_YANG(match_vrl_source_vrf,
match_vrl_source_vrf_cmd,
"match source-vrf NAME$vrf_name",
@@ -6124,6 +6331,8 @@ void bgp_route_map_init(void)
route_map_install_match(&route_match_evpn_default_route_cmd);
route_map_install_match(&route_match_vrl_source_vrf_cmd);
+ route_map_install_set(&route_set_evpn_gateway_ip_ipv4_cmd);
+ route_map_install_set(&route_set_evpn_gateway_ip_ipv6_cmd);
route_map_install_set(&route_set_table_id_cmd);
route_map_install_set(&route_set_srte_color_cmd);
route_map_install_set(&route_set_ip_nexthop_cmd);
@@ -6167,6 +6376,10 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &no_match_evpn_rd_cmd);
install_element(RMAP_NODE, &match_evpn_default_route_cmd);
install_element(RMAP_NODE, &no_match_evpn_default_route_cmd);
+ install_element(RMAP_NODE, &set_evpn_gw_ip_ipv4_cmd);
+ install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv4_cmd);
+ install_element(RMAP_NODE, &set_evpn_gw_ip_ipv6_cmd);
+ install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv6_cmd);
install_element(RMAP_NODE, &match_vrl_source_vrf_cmd);
install_element(RMAP_NODE, &no_match_vrl_source_vrf_cmd);
diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c
index b165c5d0ee..1254591b87 100644
--- a/bgpd/bgp_routemap_nb.c
+++ b/bgpd/bgp_routemap_nb.c
@@ -372,6 +372,20 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
}
},
{
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy,
+ }
+ },
+ {
.xpath = NULL,
},
}
diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h
index a15f521513..f0e492eb61 100644
--- a/bgpd/bgp_routemap_nb.h
+++ b/bgpd/bgp_routemap_nb.h
@@ -130,6 +130,14 @@ int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_mod
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(struct nb_cb_destroy_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(struct nb_cb_modify_args *args);
int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy(
+ struct nb_cb_destroy_args *args);
#ifdef __cplusplus
}
diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c
index ff08c16a82..e541d117be 100644
--- a/bgpd/bgp_routemap_nb_config.c
+++ b/bgpd/bgp_routemap_nb_config.c
@@ -2637,3 +2637,107 @@ lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_spec
{
return lib_route_map_entry_set_destroy(args);
}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4
+ */
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "evpn gateway-ip ipv4";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv4", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6
+ */
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "evpn gateway-ip ipv6";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv6", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
index 61a6467ab6..4baa730c8d 100644
--- a/bgpd/bgp_snmp.c
+++ b/bgpd/bgp_snmp.c
@@ -689,12 +689,12 @@ static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[],
dest = bgp_node_lookup(bgp->rib[AFI_IP][SAFI_UNICAST],
(struct prefix *)addr);
if (dest) {
- bgp_dest_unlock_node(dest);
-
for (path = bgp_dest_get_bgp_path_info(dest); path;
path = path->next)
if (sockunion_same(&path->peer->su, &su))
return path;
+
+ bgp_dest_unlock_node(dest);
}
} else {
offset = name + v->namelen;
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index bb0c95e32f..18829aa747 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -829,6 +829,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
struct bgp_path_info_extra tmp_pie;
tmp_attr = *pi->attr;
+ tmp_attr.aspath = attr.aspath;
prep_for_rmap_apply(&tmp_pi, &tmp_pie, dest, pi,
pi->peer, &tmp_attr);
@@ -838,11 +839,12 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
bgp_dest_get_prefix(dest), &tmp_pi);
if (ret == RMAP_DENYMATCH) {
+ /* The aspath belongs to 'attr' */
+ tmp_attr.aspath = NULL;
bgp_attr_flush(&tmp_attr);
continue;
} else {
new_attr = bgp_attr_intern(&tmp_attr);
- new_attr->aspath = attr.aspath;
subgroup_announce_reset_nhop(
(peer_cap_enhe(peer, afi, safi)
@@ -883,7 +885,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
/* If default route is present in the local RIB, advertise the
* route
*/
- if (dest != NULL) {
+ if (dest) {
for (pi = bgp_dest_get_bgp_path_info(dest); pi;
pi = pi->next) {
if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
@@ -895,6 +897,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
dest, subgrp, &attr,
pi);
}
+ bgp_dest_unlock_node(dest);
}
} else {
if (!CHECK_FLAG(subgrp->sflags,
@@ -907,7 +910,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
* clear adj_out for the 0.0.0.0/0 prefix in the BGP
* table.
*/
- if (dest != NULL) {
+ if (dest) {
/* Remove the adjacency for the previously
* advertised default route
*/
@@ -923,6 +926,7 @@ void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
/* Free allocated information. */
adj_free(adj);
}
+ bgp_dest_unlock_node(dest);
}
/* Advertise the default route */
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
index 6418decd16..038ef4f798 100644
--- a/bgpd/bgp_updgrp_packet.c
+++ b/bgpd/bgp_updgrp_packet.c
@@ -862,6 +862,7 @@ struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p,
label_pnt, num_labels,
addpath_encode, addpath_tx_id,
+ &adv->baa->attr->evpn_overlay,
pfx_buf, sizeof(pfx_buf));
zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s",
subgrp->update_group->id, subgrp->id,
@@ -1031,7 +1032,7 @@ struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp)
bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, NULL, 0,
addpath_encode, addpath_tx_id,
- pfx_buf, sizeof(pfx_buf));
+ NULL, pfx_buf, sizeof(pfx_buf));
zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s -- unreachable",
subgrp->update_group->id, subgrp->id,
pfx_buf);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index c2c114d2c9..5d3176537b 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -1058,9 +1058,19 @@ static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp,
* connected routes leaked into a VRF.
*/
if (is_evpn) {
- api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
- SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
- api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
+
+ /*
+ * If the nexthop is EVPN overlay index gateway IP,
+ * treat the nexthop as NEXTHOP_TYPE_IPV4
+ * Else, mark the nexthop as onlink.
+ */
+ if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP)
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+ else {
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
+ api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
+ }
} else if (nh_othervrf &&
api_nh->gate.ipv4.s_addr == INADDR_ANY) {
api_nh->type = NEXTHOP_TYPE_IFINDEX;
@@ -1085,9 +1095,19 @@ static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp,
api_nh->vrf_id = nh_bgp->vrf_id;
if (is_evpn) {
- api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
- SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
- api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
+
+ /*
+ * If the nexthop is EVPN overlay index gateway IP,
+ * treat the nexthop as NEXTHOP_TYPE_IPV4
+ * Else, mark the nexthop as onlink.
+ */
+ if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP)
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ else {
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
+ api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
+ }
} else if (nh_othervrf) {
if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) {
api_nh->type = NEXTHOP_TYPE_IFINDEX;
@@ -1392,8 +1412,13 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
api_nh->label_num = 1;
api_nh->labels[0] = label;
}
- memcpy(&api_nh->rmac, &(mpinfo->attr->rmac),
- sizeof(struct ethaddr));
+
+ if (is_evpn
+ && mpinfo->attr->evpn_overlay.type
+ != OVERLAY_INDEX_GATEWAY_IP)
+ memcpy(&api_nh->rmac, &(mpinfo->attr->rmac),
+ sizeof(struct ethaddr));
+
api_nh->weight = nh_weight;
if (mpinfo->extra
@@ -2444,8 +2469,6 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient,
if (!dest)
return -1;
- bgp_dest_unlock_node(dest);
-
switch (note) {
case ZAPI_ROUTE_INSTALLED:
new_select = NULL;
@@ -2474,6 +2497,8 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient,
flog_err(EC_BGP_INVALID_ROUTE,
"selected route %pRN not found",
dest);
+
+ bgp_dest_unlock_node(dest);
return -1;
}
}
@@ -2504,6 +2529,8 @@ static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient,
__func__, dest);
break;
}
+
+ bgp_dest_unlock_node(dest);
return 0;
}
@@ -2805,6 +2832,7 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS)
struct in_addr vtep_ip = {INADDR_ANY};
vrf_id_t tenant_vrf_id = VRF_DEFAULT;
struct in_addr mcast_grp = {INADDR_ANY};
+ ifindex_t svi_ifindex = 0;
s = zclient->ibuf;
vni = stream_getl(s);
@@ -2812,6 +2840,7 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS)
vtep_ip.s_addr = stream_get_ipv4(s);
stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t));
mcast_grp.s_addr = stream_get_ipv4(s);
+ stream_get(&svi_ifindex, s, sizeof(ifindex_t));
}
bgp = bgp_lookup_by_vrf_id(vrf_id);
@@ -2819,16 +2848,17 @@ static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS)
return 0;
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("Rx VNI %s VRF %s VNI %u tenant-vrf %s",
- (cmd == ZEBRA_VNI_ADD) ? "add" : "del",
- vrf_id_to_name(vrf_id), vni,
- vrf_id_to_name(tenant_vrf_id));
+ zlog_debug(
+ "Rx VNI %s VRF %s VNI %u tenant-vrf %s SVI ifindex %u",
+ (cmd == ZEBRA_VNI_ADD) ? "add" : "del",
+ vrf_id_to_name(vrf_id), vni,
+ vrf_id_to_name(tenant_vrf_id), svi_ifindex);
if (cmd == ZEBRA_VNI_ADD)
return bgp_evpn_local_vni_add(
bgp, vni,
vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id,
- tenant_vrf_id, mcast_grp);
+ tenant_vrf_id, mcast_grp, svi_ifindex);
else
return bgp_evpn_local_vni_del(bgp, vni);
}
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index b18cd4ca77..776f4b0a21 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -510,16 +510,18 @@ struct bgp {
uint16_t af_flags[AFI_MAX][SAFI_MAX];
#define BGP_CONFIG_DAMPENING (1 << 0)
/* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */
-#define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1)
-#define BGP_L2VPN_EVPN_ADVERTISE_IPV6_UNICAST (1 << 2)
-#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 3)
-#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 4)
+#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST (1 << 1)
+#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP (1 << 2)
+#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST (1 << 3)
+#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP (1 << 4)
+#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 5)
+#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 6)
/* import/export between address families */
-#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 5)
-#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 6)
+#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 7)
+#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 8)
/* vrf-route leaking flags */
-#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 7)
-#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 8)
+#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 9)
+#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 10)
/* BGP per AF peer count */
uint32_t af_peer_count[AFI_MAX][SAFI_MAX];
@@ -638,6 +640,14 @@ struct bgp {
/* EVI hash table */
struct hash *vnihash;
+ /*
+ * VNI hash table based on SVI ifindex as its key.
+ * We use SVI ifindex as key to lookup a VNI table for gateway IP
+ * overlay index recursive lookup.
+ * For this purpose, a hashtable is added which optimizes this lookup.
+ */
+ struct hash *vni_svi_hash;
+
/* EVPN enable - advertise gateway macip routes */
int advertise_gw_macip;
@@ -683,6 +693,15 @@ struct bgp {
/* Hash table of EVPN nexthops maintained per-tenant-VRF */
struct hash *evpn_nh_table;
+ /*
+ * Flag resolve_overlay_index is used for recursive resolution
+ * procedures for EVPN type-5 route's gateway IP overlay index.
+ * When this flag is set, we build remote-ip-hash for
+ * all L2VNIs and resolve overlay index nexthops using this hash.
+ * Overlay index nexthops remain unresolved if this flag is not set.
+ */
+ bool resolve_overlay_index;
+
/* vrf flags */
uint32_t vrf_flags;
#define BGP_VRF_AUTO (1 << 0)
diff --git a/buildtest.sh b/buildtest.sh
index b4b02d10bf..eeae82fc44 100755
--- a/buildtest.sh
+++ b/buildtest.sh
@@ -4,7 +4,7 @@
# builds some git commit of FRR in some different configurations
# usage: buildtest.sh [commit [configurations...]]
-basecfg="--prefix=/usr --enable-user=frr --enable-group=frr --enable-vty-group=frr --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/frr --enable-exampledir=/etc/frr/samples --localstatedir=/var/run/frr --libdir=/usr/lib64/frr --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-pimd --enable-werror"
+basecfg="--prefix=/usr --enable-user=frr --enable-group=frr --enable-vty-group=frr --enable-configfile-mask=0660 --enable-logfile-mask=0640 --enable-vtysh --sysconfdir=/etc/frr --localstatedir=/var/run/frr --libdir=/usr/lib64/frr --enable-rtadv --disable-static --enable-isisd --enable-multipath=0 --enable-pimd --enable-werror"
configs_base="gcc|$basecfg"
diff --git a/configure.ac b/configure.ac
index 96bacc9ed4..0ea209bbfa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -114,15 +114,6 @@ AC_PATH_PROG([PERL], [perl])
PKG_PROG_PKG_CONFIG
dnl default is to match previous behavior
-exampledir=${sysconfdir}
-AC_ARG_ENABLE([exampledir],
- AS_HELP_STRING([--enable-exampledir],
- [specify alternate directory for examples]),
- exampledir="$enableval",)
-dnl XXX add --exampledir to autoconf standard directory list somehow
-AC_SUBST([exampledir])
-
-dnl default is to match previous behavior
pkgsrcrcdir=""
AC_ARG_ENABLE([pkgsrcrcdir],
AS_HELP_STRING([--enable-pkgsrcrcdir],
@@ -663,8 +654,6 @@ AC_ARG_ENABLE([irdp],
AS_HELP_STRING([--disable-irdp], [disable IRDP server support in zebra (enabled by default if supported)]))
AC_ARG_ENABLE([capabilities],
AS_HELP_STRING([--disable-capabilities], [disable using POSIX capabilities]))
-AC_ARG_ENABLE([rusage],
- AS_HELP_STRING([--disable-rusage], [disable using getrusage]))
AC_ARG_ENABLE([gcc_ultra_verbose],
AS_HELP_STRING([--enable-gcc-ultra-verbose], [enable ultra verbose GCC warnings]))
AC_ARG_ENABLE([backtrace],
@@ -794,7 +783,6 @@ fi
if test "$enable_datacenter" = "yes" ; then
AC_DEFINE([HAVE_DATACENTER], [1], [Compile extensions for a DataCenter])
- AC_MSG_WARN([The --enable-datacenter compile time option is deprecated. Please modify the init script to pass -F datacenter to the daemons instead.])
DFLT_NAME="datacenter"
else
DFLT_NAME="traditional"
@@ -2241,6 +2229,10 @@ AC_CHECK_DECL([CLOCK_MONOTONIC],
AC_DEFINE([HAVE_CLOCK_MONOTONIC], [1], [Have monotonic clock])
], [AC_MSG_RESULT([no])], [FRR_INCLUDES])
+AC_CHECK_DECL([CLOCK_THREAD_CPUTIME_ID], [
+ AC_DEFINE([HAVE_CLOCK_THREAD_CPUTIME_ID], [1], [Have cpu-time clock])
+], [AC_MSG_RESULT([no])], [FRR_INCLUDES])
+
AC_SEARCH_LIBS([clock_nanosleep], [rt], [
AC_DEFINE([HAVE_CLOCK_NANOSLEEP], [1], [Have clock_nanosleep()])
])
@@ -2693,7 +2685,6 @@ make : ${MAKE-make}
linker flags : ${LDFLAGS} ${SAN_FLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM}
state file directory : ${frr_statedir}
config file directory : `eval echo \`echo ${sysconfdir}\``
-example directory : `eval echo \`echo ${exampledir}\``
module directory : ${CFG_MODULE}
script directory : ${CFG_SCRIPT}
user to run as : ${enable_user}
@@ -2707,6 +2698,16 @@ vici socket path : ${vici_socket}
The above user and group must have read/write access to the state file
directory and to the config files in the config file directory."
+if test -n "$enable_datacenter"; then
+ AC_MSG_WARN([The --enable-datacenter compile time option is deprecated. Please modify the init script to pass -F datacenter to the daemons instead.])
+fi
+if test -n "$enable_time_check"; then
+ AC_MSG_WARN([The --enable-time-check compile time option is deprecated. Please use the service cputime-stats configuration option instead.])
+fi
+if test -n "$enable_cpu_time"; then
+ AC_MSG_WARN([The --enable-cpu-time compile time option is deprecated. Please use the service cputime-warning NNN configuration option instead.])
+fi
+
if test "$enable_doc" != "no" -a "$frr_py_mod_sphinx" = "false"; then
AC_MSG_WARN([sphinx is missing but required to build documentation])
fi
diff --git a/doc/developer/building-frr-for-centos6.rst b/doc/developer/building-frr-for-centos6.rst
index 5d3be492de..7a7af42119 100644
--- a/doc/developer/building-frr-for-centos6.rst
+++ b/doc/developer/building-frr-for-centos6.rst
@@ -172,7 +172,6 @@ an example.)
--enable-user=frr \
--enable-group=frr \
--enable-vty-group=frrvty \
- --disable-exampledir \
--disable-ldpd \
--enable-fpm \
--with-pkg-git-version \
diff --git a/doc/developer/building-frr-for-centos7.rst b/doc/developer/building-frr-for-centos7.rst
index 8d0aea943c..93b9993a38 100644
--- a/doc/developer/building-frr-for-centos7.rst
+++ b/doc/developer/building-frr-for-centos7.rst
@@ -67,7 +67,6 @@ an example.)
--enable-group=frr \
--enable-vty-group=frrvty \
--enable-systemd=yes \
- --disable-exampledir \
--disable-ldpd \
--enable-fpm \
--with-pkg-git-version \
diff --git a/doc/developer/building-frr-for-centos8.rst b/doc/developer/building-frr-for-centos8.rst
index 77fe489358..65c93286b7 100644
--- a/doc/developer/building-frr-for-centos8.rst
+++ b/doc/developer/building-frr-for-centos8.rst
@@ -60,7 +60,6 @@ an example.)
--enable-group=frr \
--enable-vty-group=frrvty \
--enable-systemd=yes \
- --disable-exampledir \
--disable-ldpd \
--enable-fpm \
--with-pkg-git-version \
diff --git a/doc/developer/building-frr-for-debian8.rst b/doc/developer/building-frr-for-debian8.rst
index 51dd07c42a..475e0303fc 100644
--- a/doc/developer/building-frr-for-debian8.rst
+++ b/doc/developer/building-frr-for-debian8.rst
@@ -57,7 +57,6 @@ an example.)
cd frr
./bootstrap.sh
./configure \
- --enable-exampledir=/usr/share/doc/frr/examples/ \
--localstatedir=/var/run/frr \
--sbindir=/usr/lib/frr \
--sysconfdir=/etc/frr \
diff --git a/doc/developer/building-frr-for-debian9.rst b/doc/developer/building-frr-for-debian9.rst
index 919b010314..1981127b3d 100644
--- a/doc/developer/building-frr-for-debian9.rst
+++ b/doc/developer/building-frr-for-debian9.rst
@@ -44,7 +44,6 @@ an example.)
cd frr
./bootstrap.sh
./configure \
- --enable-exampledir=/usr/share/doc/frr/examples/ \
--localstatedir=/var/opt/frr \
--sbindir=/usr/lib/frr \
--sysconfdir=/etc/frr \
diff --git a/doc/developer/building-frr-for-netbsd6.rst b/doc/developer/building-frr-for-netbsd6.rst
index e50d11130a..a78f8b3c2f 100644
--- a/doc/developer/building-frr-for-netbsd6.rst
+++ b/doc/developer/building-frr-for-netbsd6.rst
@@ -64,7 +64,6 @@ an example)
export CPPFLAGS="-I/usr/pkg/include"
./configure \
--sysconfdir=/usr/pkg/etc/frr \
- --enable-exampledir=/usr/pkg/share/examples/frr \
--enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \
--localstatedir=/var/run/frr \
--enable-multipath=64 \
diff --git a/doc/developer/building-frr-for-netbsd7.rst b/doc/developer/building-frr-for-netbsd7.rst
index 32d1145edc..a52ece19a1 100644
--- a/doc/developer/building-frr-for-netbsd7.rst
+++ b/doc/developer/building-frr-for-netbsd7.rst
@@ -55,7 +55,6 @@ an example)
export CPPFLAGS="-I/usr/pkg/include"
./configure \
--sysconfdir=/usr/pkg/etc/frr \
- --enable-exampledir=/usr/pkg/share/examples/frr \
--enable-pkgsrcrcdir=/usr/pkg/share/examples/rc.d \
--localstatedir=/var/run/frr \
--enable-multipath=64 \
diff --git a/doc/developer/include-compile.rst b/doc/developer/include-compile.rst
index 0ff0ae3ffe..d9fa260221 100644
--- a/doc/developer/include-compile.rst
+++ b/doc/developer/include-compile.rst
@@ -15,7 +15,6 @@ obtained by running ``./configure -h``. The options shown below are examples.
./configure \
--prefix=/usr \
--includedir=\${prefix}/include \
- --enable-exampledir=\${prefix}/share/doc/frr/examples \
--bindir=\${prefix}/bin \
--sbindir=\${prefix}/lib/frr \
--libdir=\${prefix}/lib/frr \
diff --git a/doc/developer/topotests.rst b/doc/developer/topotests.rst
index ba03aa9045..18317cd33c 100644
--- a/doc/developer/topotests.rst
+++ b/doc/developer/topotests.rst
@@ -221,7 +221,6 @@ for ``master`` branch:
--prefix=/usr/lib/frr --sysconfdir=/etc/frr \
--localstatedir=/var/run/frr \
--sbindir=/usr/lib/frr --bindir=/usr/lib/frr \
- --enable-exampledir=/usr/lib/frr/examples \
--with-moduledir=/usr/lib/frr/modules \
--enable-multipath=0 --enable-rtadv \
--enable-tcp-zebra --enable-fpm --enable-pimd \
diff --git a/doc/user/basic.rst b/doc/user/basic.rst
index 0db2361296..92357bec83 100644
--- a/doc/user/basic.rst
+++ b/doc/user/basic.rst
@@ -58,7 +58,13 @@ Basic Config Commands
.. clicmd:: hostname HOSTNAME
- Set hostname of the router.
+ Set hostname of the router. It is only for current ``vtysh``, it will not be
+ saved to any configuration file even with ``write file``.
+
+.. clicmd:: domainname DOMAINNAME
+
+ Set domainname of the router. It is only for current ``vtysh``, it will not
+ be saved to any configuration file even with ``write file``.
.. clicmd:: domainname DOMAINNAME
@@ -74,6 +80,39 @@ Basic Config Commands
Set enable password. The ``no`` form of the command deletes the enable
password.
+.. clicmd:: service cputime-stats
+
+ Collect CPU usage statistics for individual FRR event handlers and CLI
+ commands. This is enabled by default and can be disabled if the extra
+ overhead causes a noticeable slowdown on your system.
+
+ Disabling these statistics will also make the
+ :clicmd:`service cputime-warning (1-4294967295)` limit non-functional.
+
+.. clicmd:: service cputime-warning (1-4294967295)
+
+ Warn if the CPU usage of an event handler or CLI command exceeds the
+ specified limit (in milliseconds.) Such warnings are generally indicative
+ of some routine in FRR mistakenly blocking/hogging the processing loop and
+ should be reported as a FRR bug.
+
+ The default limit is 5 seconds (i.e. 5000), but this can be changed by the
+ deprecated ``--enable-time-check=...`` compile-time option.
+
+ This command has no effect if :clicmd:`service cputime-stats` is disabled.
+
+.. clicmd:: service walltime-warning (1-4294967295)
+
+ Warn if the total wallclock time spent handling an event or executing a CLI
+ command exceeds the specified limit (in milliseconds.) This includes time
+ spent waiting for I/O or other tasks executing and may produce excessive
+ warnings if the system is overloaded. (This may still be useful to
+ provide an immediate sign that FRR is not operating correctly due to
+ externally caused starvation.)
+
+ The default limit is 5 seconds as above, including the same deprecated
+ ``--enable-time-check=...`` compile-time option.
+
.. clicmd:: log trap LEVEL
These commands are deprecated and are present only for historical
@@ -174,11 +213,17 @@ Basic Config Commands
is used to start the daemon then this command is turned on by default
and cannot be turned off and the [no] form of the command is dissallowed.
-.. clicmd:: log-filter WORD [DAEMON]
+.. clicmd:: log filtered-file [FILENAME [LEVEL]]
+
+ Configure a destination file for filtered logs with the
+ :clicmd:`log filter-text WORD` command.
+
+.. clicmd:: log filter-text WORD
This command forces logs to be filtered on a specific string. A log message
will only be printed if it matches on one of the filters in the log-filter
- table. Can be daemon independent.
+ table. The filter only applies to file logging targets configured with
+ :clicmd:`log filtered-file [FILENAME [LEVEL]]`.
.. note::
@@ -187,10 +232,15 @@ Basic Config Commands
Log filters prevent this but you should still expect a small performance
hit due to filtering each of all those logs.
-.. clicmd:: log-filter clear [DAEMON]
+ .. note::
+
+ This setting is not saved to ``frr.conf`` and not shown in
+ :clicmd:`show running-config`. It is intended for ephemeral debugging
+ purposes only.
+
+.. clicmd:: clear log filter-text
- This command clears all current filters in the log-filter table. Can be
- daemon independent.
+ This command clears all current filters in the log-filter table.
.. clicmd:: log immediate-mode
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 4e78900e8d..7f23f7a633 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -2696,6 +2696,115 @@ remote VTEP.
Note that you should not enable both the advertise-svi-ip and the advertise-default-gw
at the same time.
+.. _bgp-evpn-overlay-index-gateway-ip:
+
+EVPN Overlay Index Gateway IP
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Draft https://tools.ietf.org/html/draft-ietf-bess-evpn-prefix-advertisement-11
+explains the use of overlay indexes for recursive route resolution for EVPN
+type-5 route.
+
+We support gateway IP overlay index.
+A gateway IP, advertised with EVPN prefix route, is used to find an EVPN MAC/IP
+route with its IP field same as the gateway IP. This MAC/IP entry provides the
+nexthop VTEP and the tunnel information required for the VxLAN encapsulation.
+
+Functionality:
+
+::
+
+ . +--------+ BGP +--------+ BGP +--------+ +--------+
+ SN1 | | IPv4 | | EVPN | | | |
+ ======+ Host1 +------+ PE1 +------+ PE2 +------+ Host2 +
+ | | | | | | | |
+ +--------+ +--------+ +--------+ +--------+
+
+Consider above topology where prefix SN1 is connected behind host1. Host1
+advertises SN1 to PE1 over BGP IPv4 session. PE1 advertises SN1 to PE2 using
+EVPN type-5 route with host1 IP as the gateway IP. PE1 also advertises
+Host1 MAC/IP as type-2 route which is used to resolve host1 gateway IP.
+
+PE2 receives this type-5 route and imports it into the vrf based on route
+targets. BGP prefix imported into the vrf uses gateway IP as its BGP nexthop.
+This route is installed into zebra if following conditions are satisfied:
+1. Gateway IP nexthop is L3 reachable.
+2. PE2 has received EVPN type-2 route with IP field set to gateway IP.
+
+Topology requirements:
+1. This feature is supported for asymmetric routing model only. While
+ sending packets to SN1, ingress PE (PE2) performs routing and
+ egress PE (PE1) performs only bridging.
+2. This feature supports only tratitional(non vlan-aware) bridge model. Bridge
+ interface associated with L2VNI is an L3 interface. i.e., this interface is
+ configured with an address in the L2VNI subnet. Note that the gateway IP
+ should also have an address in the same subnet.
+3. As this feature works in asymmetric routing model, all L2VNIs and corresponding
+ VxLAN and bridge interfaces should be present at all the PEs.
+4. L3VNI configuration is required to generate and import EVPN type-5 routes.
+ L3VNI VxLAN and bridge interfaces also should be present.
+
+A PE can use one of the following two mechanisms to advertise an EVPN type-5
+route with gateway IP.
+
+1. CLI to add gateway IP while generating EVPN type-5 route from a BGP IPv4/IPv6
+prefix:
+
+.. index:: advertise <ipv4|ipv6> unicast [gateway-ip]
+.. clicmd:: [no] advertise <ipv4|ipv6> unicast [gateway-ip]
+
+When this CLI is configured for a BGP vrf under L2VPN EVPN address family, EVPN
+type-5 routes are generated for BGP prefixes in the vrf. Nexthop of the BGP
+prefix becomes the gateway IP of the corresponding type-5 route.
+
+If the above command is configured without the "gateway-ip" keyword, type-5
+routes are generated without overlay index.
+
+2. Add gateway IP to EVPN type-5 route using a route-map:
+
+.. index:: set evpn gateway-ip <ipv4|ipv6> <addr>
+.. clicmd:: [no] set evpn gateway-ip <ipv4|ipv6> <addr>
+
+When route-map with above set clause is applied as outbound policy in BGP, it
+will set the gateway-ip in EVPN type-5 NLRI.
+
+Example configuration:
+
+.. code-block:: frr
+
+ router bgp 100
+ neighbor 192.168.0.1 remote-as 101
+ !
+ address-family ipv4 l2vpn evpn
+ neighbor 192.168.0.1 route-map RMAP out
+ exit-address-family
+ !
+ route-map RMAP permit 10
+ set evpn gateway-ip 10.0.0.1
+ set evpn gateway-ip 10::1
+
+A PE that receives a type-5 route with gateway IP overlay index should have
+"enable-resolve-overlay-index" configuration enabled to recursively resolve the
+overlay index nexthop and install the prefix into zebra.
+
+.. index:: enable-resolve-overlay-index
+.. clicmd:: [no] enable-resolve-overlay-index
+
+Example configuration:
+
+.. code-block:: frr
+
+ router bgp 65001
+ bgp router-id 192.168.100.1
+ no bgp ebgp-requires-policy
+ neighbor 10.0.1.2 remote-as 65002
+ !
+ address-family l2vpn evpn
+ neighbor 10.0.1.2 activate
+ advertise-all-vni
+ enable-resolve-overlay-index
+ exit-address-family
+ !
+
EVPN Multihoming
^^^^^^^^^^^^^^^^
diff --git a/doc/user/installation.rst b/doc/user/installation.rst
index f7e45a6231..63254555d9 100644
--- a/doc/user/installation.rst
+++ b/doc/user/installation.rst
@@ -199,6 +199,10 @@ options from the list below.
.. option:: --enable-datacenter
+ This option is deprecated as it is superseded by the `-F` (profile) command
+ line option which allows adjusting the setting at startup rather than
+ compile time.
+
Enable system defaults to work as if in a Data Center. See defaults.h
for what is changed by this configure option.
@@ -343,20 +347,17 @@ options from the list below.
.. option:: --enable-time-check XXX
- When this is enabled with a XXX value in microseconds, any thread that
- runs for over this value will cause a warning to be issued to the log.
- If you do not specify any value or don't include this option then
- the default time is 5 seconds. If --disable-time-check is specified
- then no warning is issued for any thread run length.
+ This option is deprecated as it was replaced by the
+ :clicmd:`service cputime-stats` CLI command, which may be adjusted at
+ runtime rather than being a compile-time setting. See there for further
+ detail.
.. option:: --disable-cpu-time
- Disable cpu process accounting, this command also disables the `show thread cpu`
- command. If this option is disabled, --enable-time-check is ignored. This
- disabling of cpu time effectively means that the getrusage call is skipped.
- Since this is a process switch into the kernel, systems with high FRR
- load might see improvement in behavior. Be aware that `show thread cpu`
- is considered a good data gathering tool from the perspective of developers.
+ This option is deprecated as it was replaced by the
+ :clicmd:`service cputime-warning NNN` CLI command, which may be adjusted at
+ runtime rather than being a compile-time setting. See there for further
+ detail.
.. option:: --enable-pcreposix
@@ -561,7 +562,6 @@ the options you chose:
./configure \
--prefix=/usr \
- --enable-exampledir=/usr/share/doc/frr/examples/ \
--localstatedir=/var/run/frr \
--sbindir=/usr/lib/frr \
--sysconfdir=/etc/frr \
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index d26062e2b9..1e8c66ab2e 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -70,6 +70,13 @@ OSPF6 router
Use this command to control the maximum number of parallel routes that
OSPFv3 can support. The default is 64.
+.. clicmd:: write-multiplier (1-100)
+
+ Use this command to tune the amount of work done in the packet read and
+ write threads before relinquishing control. The parameter is the number
+ of packets to process before returning. The default value of this parameter
+ is 20.
+
.. _ospf6-area:
@@ -83,7 +90,7 @@ NSSA Support in OSPFv3
=======================
The configuration of NSSA areas in OSPFv3 is supported using the CLI command
-area A.B.C.D nssa in ospf6 router configuration mode.
+``area A.B.C.D nssa`` in ospf6 router configuration mode.
The following functionalities are implemented as per RFC 3101:
1. Advertising Type-7 LSA into NSSA area when external route is redistributed
@@ -91,7 +98,7 @@ The following functionalities are implemented as per RFC 3101:
2. Processing Type-7 LSA received from neighbor and installing route in the
route table
3. Support for NSSA ABR functionality which is generating Type-5 LSA when
- backbone area is configured. Currently translation od TYpe-7 LSA to Type-5 LSA
+ backbone area is configured. Currently translation of Type-7 LSA to Type-5 LSA
is enabled by default.
4. Support for NSSA Translator functionality when there are multiple NSSA ABR
in an area
diff --git a/doc/user/ospfd.rst b/doc/user/ospfd.rst
index 80ab279596..692ce8c1b2 100644
--- a/doc/user/ospfd.rst
+++ b/doc/user/ospfd.rst
@@ -299,6 +299,13 @@ To start OSPF process you have to specify the OSPF router.
a specific destination. The upper limit may differ if you change the value
of MULTIPATH_NUM during compilation. The default is MULTIPATH_NUM (64).
+.. clicmd:: write-multiplier (1-100)
+
+ Use this command to tune the amount of work done in the packet read and
+ write threads before relinquishing control. The parameter is the number
+ of packets to process before returning. The defult value of this parameter
+ is 20.
+
.. _ospf-area:
Areas
diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c
index 3a978cae33..47de929fc3 100644
--- a/eigrpd/eigrp_cli.c
+++ b/eigrpd/eigrp_cli.c
@@ -919,6 +919,8 @@ eigrp_cli_init(void)
install_element(EIGRP_NODE, &eigrp_neighbor_cmd);
install_element(EIGRP_NODE, &eigrp_redistribute_source_metric_cmd);
+ vrf_cmd_init(NULL, &eigrpd_privs);
+
install_node(&eigrp_interface_node);
if_cmd_init();
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index 2622c5b51b..4a01c728f0 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -111,6 +111,28 @@ int isis_instance_is_type_modify(struct nb_cb_modify_args *args)
return NB_OK;
}
+struct sysid_iter {
+ struct area_addr *addr;
+ bool same;
+};
+
+static int sysid_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct sysid_iter *iter = arg;
+ struct area_addr addr;
+ const char *net;
+
+ net = yang_dnode_get_string(dnode, NULL);
+ addr.addr_len = dotformat2buff(addr.area_addr, net);
+
+ if (memcmp(GETSYSID(iter->addr), GETSYSID((&addr)), ISIS_SYS_ID_LEN)) {
+ iter->same = false;
+ return YANG_ITER_STOP;
+ }
+
+ return YANG_ITER_CONTINUE;
+}
+
/*
* XPath: /frr-isisd:isis/instance/area-address
*/
@@ -119,14 +141,12 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args)
struct isis_area *area;
struct area_addr addr, *addrr = NULL, *addrp = NULL;
struct listnode *node;
+ struct sysid_iter iter;
uint8_t buff[255];
const char *net_title = yang_dnode_get_string(args->dnode, NULL);
switch (args->event) {
case NB_EV_VALIDATE:
- area = nb_running_get_entry(args->dnode, NULL, false);
- if (area == NULL)
- return NB_ERR_VALIDATION;
addr.addr_len = dotformat2buff(buff, net_title);
memcpy(addr.area_addr, buff, addr.addr_len);
if (addr.area_addr[addr.addr_len - 1] != 0) {
@@ -135,15 +155,18 @@ int isis_instance_area_address_create(struct nb_cb_create_args *args)
"nsel byte (last byte) in area address must be 0");
return NB_ERR_VALIDATION;
}
- if (area->isis->sysid_set) {
- /* Check that the SystemID portions match */
- if (memcmp(area->isis->sysid, GETSYSID((&addr)),
- ISIS_SYS_ID_LEN)) {
- snprintf(
- args->errmsg, args->errmsg_len,
- "System ID must not change when defining additional area addresses");
- return NB_ERR_VALIDATION;
- }
+
+ iter.addr = &addr;
+ iter.same = true;
+
+ yang_dnode_iterate(sysid_iter_cb, &iter, args->dnode,
+ "../area-address");
+
+ if (!iter.same) {
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "System ID must not change when defining additional area addresses");
+ return NB_ERR_VALIDATION;
}
break;
case NB_EV_PREPARE:
@@ -2360,14 +2383,14 @@ int isis_instance_segment_routing_prefix_sid_map_prefix_sid_n_flag_clear_modify(
int isis_instance_mpls_ldp_sync_create(struct nb_cb_create_args *args)
{
struct isis_area *area;
+ const char *vrfname;
switch (args->event) {
case NB_EV_VALIDATE:
- area = nb_running_get_entry(args->dnode, NULL, false);
- if (area == NULL || area->isis == NULL)
- return NB_ERR_VALIDATION;
+ vrfname = yang_dnode_get_string(
+ lyd_parent(lyd_parent(args->dnode)), "./vrf");
- if (area->isis->vrf_id != VRF_DEFAULT) {
+ if (strcmp(vrfname, VRF_DEFAULT_NAME)) {
snprintf(args->errmsg, args->errmsg_len,
"LDP-Sync only runs on Default VRF");
return NB_ERR_VALIDATION;
@@ -2404,14 +2427,15 @@ int isis_instance_mpls_ldp_sync_holddown_modify(struct nb_cb_modify_args *args)
{
struct isis_area *area;
uint16_t holddown;
+ const char *vrfname;
switch (args->event) {
case NB_EV_VALIDATE:
- area = nb_running_get_entry(args->dnode, NULL, false);
- if (area == NULL || area->isis == NULL)
- return NB_ERR_VALIDATION;
+ vrfname = yang_dnode_get_string(
+ lyd_parent(lyd_parent(lyd_parent(args->dnode))),
+ "./vrf");
- if (area->isis->vrf_id != VRF_DEFAULT) {
+ if (strcmp(vrfname, VRF_DEFAULT_NAME)) {
snprintf(args->errmsg, args->errmsg_len,
"LDP-Sync only runs on Default VRF");
return NB_ERR_VALIDATION;
@@ -3180,26 +3204,14 @@ int lib_interface_isis_mpls_ldp_sync_modify(struct nb_cb_modify_args *args)
struct isis_circuit *circuit;
struct ldp_sync_info *ldp_sync_info;
bool ldp_sync_enable;
- struct interface *ifp;
+ const char *vrfname;
switch (args->event) {
case NB_EV_VALIDATE:
- ifp = nb_running_get_entry(
- lyd_parent(lyd_parent(lyd_parent(args->dnode))), NULL,
- false);
- if (ifp == NULL)
- return NB_ERR_VALIDATION;
- if (if_is_loopback(ifp)) {
- snprintf(args->errmsg, args->errmsg_len,
- "LDP-Sync does not run on loopback interface");
- return NB_ERR_VALIDATION;
- }
-
- circuit = nb_running_get_entry(args->dnode, NULL, false);
- if (circuit == NULL || circuit->area == NULL)
- break;
-
- if (circuit->isis->vrf_id != VRF_DEFAULT) {
+ vrfname = yang_dnode_get_string(
+ lyd_parent(lyd_parent(lyd_parent(args->dnode))),
+ "./vrf");
+ if (strcmp(vrfname, VRF_DEFAULT_NAME)) {
snprintf(args->errmsg, args->errmsg_len,
"LDP-Sync only runs on Default VRF");
return NB_ERR_VALIDATION;
@@ -3236,27 +3248,14 @@ int lib_interface_isis_mpls_holddown_modify(struct nb_cb_modify_args *args)
struct isis_circuit *circuit;
struct ldp_sync_info *ldp_sync_info;
uint16_t holddown;
- struct interface *ifp;
+ const char *vrfname;
switch (args->event) {
case NB_EV_VALIDATE:
-
- ifp = nb_running_get_entry(
- lyd_parent(lyd_parent(lyd_parent(args->dnode))), NULL,
- false);
- if (ifp == NULL)
- return NB_ERR_VALIDATION;
- if (if_is_loopback(ifp)) {
- snprintf(args->errmsg, args->errmsg_len,
- "LDP-Sync does not run on loopback interface");
- return NB_ERR_VALIDATION;
- }
-
- circuit = nb_running_get_entry(args->dnode, NULL, false);
- if (circuit == NULL || circuit->area == NULL)
- break;
-
- if (circuit->isis->vrf_id != VRF_DEFAULT) {
+ vrfname = yang_dnode_get_string(
+ lyd_parent(lyd_parent(lyd_parent(args->dnode))),
+ "./vrf");
+ if (strcmp(vrfname, VRF_DEFAULT_NAME)) {
snprintf(args->errmsg, args->errmsg_len,
"LDP-Sync only runs on Default VRF");
return NB_ERR_VALIDATION;
@@ -3282,26 +3281,14 @@ int lib_interface_isis_mpls_holddown_destroy(struct nb_cb_destroy_args *args)
{
struct isis_circuit *circuit;
struct ldp_sync_info *ldp_sync_info;
- struct interface *ifp;
+ const char *vrfname;
switch (args->event) {
case NB_EV_VALIDATE:
- ifp = nb_running_get_entry(
- lyd_parent(lyd_parent(lyd_parent(args->dnode))), NULL,
- false);
- if (ifp == NULL)
- return NB_ERR_VALIDATION;
- if (if_is_loopback(ifp)) {
- snprintf(args->errmsg, args->errmsg_len,
- "LDP-Sync does not run on loopback interface");
- return NB_ERR_VALIDATION;
- }
-
- circuit = nb_running_get_entry(args->dnode, NULL, false);
- if (circuit == NULL || circuit->area == NULL)
- break;
-
- if (circuit->isis->vrf_id != VRF_DEFAULT) {
+ vrfname = yang_dnode_get_string(
+ lyd_parent(lyd_parent(lyd_parent(args->dnode))),
+ "./vrf");
+ if (strcmp(vrfname, VRF_DEFAULT_NAME)) {
snprintf(args->errmsg, args->errmsg_len,
"LDP-Sync only runs on Default VRF");
return NB_ERR_VALIDATION;
diff --git a/isisd/isis_vty_fabricd.c b/isisd/isis_vty_fabricd.c
index a19fcc240f..2c1b6e932f 100644
--- a/isisd/isis_vty_fabricd.c
+++ b/isisd/isis_vty_fabricd.c
@@ -187,7 +187,7 @@ DEFUN (show_lsp_flooding,
vty_out(vty, "Area %s:\n",
area->area_tag ? area->area_tag : "null");
if (lspid) {
- lsp = lsp_for_arg(head, lspid, isis);
+ lsp = lsp_for_sysid(head, lspid, isis);
if (lsp)
lsp_print_flooding(vty, lsp, isis);
continue;
diff --git a/isisd/isisd.c b/isisd/isisd.c
index 7f56903fc9..6c1308af0a 100644
--- a/isisd/isisd.c
+++ b/isisd/isisd.c
@@ -716,6 +716,8 @@ void isis_vrf_init(void)
{
vrf_init(isis_vrf_new, isis_vrf_enable, isis_vrf_disable,
isis_vrf_delete, isis_vrf_enable);
+
+ vrf_cmd_init(NULL, &isisd_privs);
}
void isis_terminate()
@@ -2168,21 +2170,21 @@ DEFUN(show_isis_summary, show_isis_summary_cmd,
return CMD_SUCCESS;
}
-struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
- struct isis *isis)
+struct isis_lsp *lsp_for_sysid(struct lspdb_head *head, const char *sysid_str,
+ struct isis *isis)
{
char sysid[255] = {0};
- uint8_t number[3];
+ uint8_t number[3] = {0};
const char *pos;
uint8_t lspid[ISIS_SYS_ID_LEN + 2] = {0};
struct isis_dynhn *dynhn;
struct isis_lsp *lsp = NULL;
- if (!argv)
+ if (!sysid_str)
return NULL;
/*
- * extract fragment and pseudo id from the string argv
+ * extract fragment and pseudo id from the string sysid_str
* in the forms:
* (a) <systemid/hostname>.<pseudo-id>-<framenent> or
* (b) <systemid/hostname>.<pseudo-id> or
@@ -2190,10 +2192,10 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
* Where systemid is in the form:
* xxxx.xxxx.xxxx
*/
- if (argv)
- strlcpy(sysid, argv, sizeof(sysid));
- if (argv && strlen(argv) > 3) {
- pos = argv + strlen(argv) - 3;
+ strlcpy(sysid, sysid_str, sizeof(sysid));
+
+ if (strlen(sysid_str) > 3) {
+ pos = sysid_str + strlen(sysid_str) - 3;
if (strncmp(pos, "-", 1) == 0) {
memcpy(number, ++pos, 2);
lspid[ISIS_SYS_ID_LEN + 1] =
@@ -2206,14 +2208,13 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
memcpy(number, ++pos, 2);
lspid[ISIS_SYS_ID_LEN] =
(uint8_t)strtol((char *)number, NULL, 16);
- sysid[pos - argv - 1] = '\0';
+ sysid[pos - sysid_str - 1] = '\0';
}
}
/*
- * Try to find the lsp-id if the argv
- * string is in
- * the form
+ * Try to find the lsp-id if the sysid_str
+ * is in the form
* hostname.<pseudo-id>-<fragment>
*/
if (sysid2buff(lspid, sysid)) {
@@ -2231,15 +2232,15 @@ struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
void show_isis_database_lspdb(struct vty *vty, struct isis_area *area,
int level, struct lspdb_head *lspdb,
- const char *argv, int ui_level)
+ const char *sysid_str, int ui_level)
{
struct isis_lsp *lsp;
int lsp_count;
if (lspdb_count(lspdb) > 0) {
- lsp = lsp_for_arg(lspdb, argv, area->isis);
+ lsp = lsp_for_sysid(lspdb, sysid_str, area->isis);
- if (lsp != NULL || argv == NULL) {
+ if (lsp != NULL || sysid_str == NULL) {
vty_out(vty, "IS-IS Level-%d link-state database:\n",
level + 1);
@@ -2255,7 +2256,7 @@ void show_isis_database_lspdb(struct vty *vty, struct isis_area *area,
else
lsp_print(lsp, vty, area->dynhostname,
area->isis);
- } else if (argv == NULL) {
+ } else if (sysid_str == NULL) {
lsp_count =
lsp_print_all(vty, lspdb, ui_level,
area->dynhostname, area->isis);
@@ -2265,7 +2266,7 @@ void show_isis_database_lspdb(struct vty *vty, struct isis_area *area,
}
}
-static void show_isis_database_common(struct vty *vty, const char *argv,
+static void show_isis_database_common(struct vty *vty, const char *sysid_str,
int ui_level, struct isis *isis)
{
struct listnode *node;
@@ -2281,7 +2282,7 @@ static void show_isis_database_common(struct vty *vty, const char *argv,
for (level = 0; level < ISIS_LEVELS; level++)
show_isis_database_lspdb(vty, area, level,
- &area->lspdb[level], argv,
+ &area->lspdb[level], sysid_str,
ui_level);
}
}
@@ -2301,8 +2302,8 @@ static void show_isis_database_common(struct vty *vty, const char *argv,
* [ show isis database detail <sysid>.<pseudo-id>-<fragment-number> ]
* [ show isis database detail <hostname>.<pseudo-id>-<fragment-number> ]
*/
-static int show_isis_database(struct vty *vty, const char *argv, int ui_level,
- const char *vrf_name, bool all_vrf)
+static int show_isis_database(struct vty *vty, const char *sysid_str,
+ int ui_level, const char *vrf_name, bool all_vrf)
{
struct listnode *node;
struct isis *isis;
@@ -2310,14 +2311,15 @@ static int show_isis_database(struct vty *vty, const char *argv, int ui_level,
if (vrf_name) {
if (all_vrf) {
for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis))
- show_isis_database_common(vty, argv, ui_level,
- isis);
+ show_isis_database_common(vty, sysid_str,
+ ui_level, isis);
return CMD_SUCCESS;
}
isis = isis_lookup_by_vrfname(vrf_name);
if (isis)
- show_isis_database_common(vty, argv, ui_level, isis);
+ show_isis_database_common(vty, sysid_str, ui_level,
+ isis);
}
return CMD_SUCCESS;
diff --git a/isisd/isisd.h b/isisd/isisd.h
index b2c9af55b7..60dcf066dd 100644
--- a/isisd/isisd.h
+++ b/isisd/isisd.h
@@ -274,8 +274,8 @@ void isis_area_destroy(struct isis_area *area);
void isis_filter_update(struct access_list *access);
void isis_prefix_list_update(struct prefix_list *plist);
void print_debug(struct vty *, int, int);
-struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
- struct isis *isis);
+struct isis_lsp *lsp_for_sysid(struct lspdb_head *head, const char *sysid_str,
+ struct isis *isis);
void isis_area_invalidate_routes(struct isis_area *area, int levels);
void isis_area_verify_routes(struct isis_area *area);
diff --git a/lib/command.c b/lib/command.c
index 7be54907ed..e00d84a051 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -434,6 +434,36 @@ static int config_write_host(struct vty *vty)
}
log_config_write(vty);
+ /* print disable always, but enable only if default is flipped
+ * => prep for future removal of compile-time knob
+ */
+ if (!cputime_enabled)
+ vty_out(vty, "no service cputime-stats\n");
+#ifdef EXCLUDE_CPU_TIME
+ else
+ vty_out(vty, "service cputime-stats\n");
+#endif
+
+ if (!cputime_threshold)
+ vty_out(vty, "no service cputime-warning\n");
+#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
+ else /* again, always print non-default */
+#else
+ else if (cputime_threshold != 5000000)
+#endif
+ vty_out(vty, "service cputime-warning %lu\n",
+ cputime_threshold);
+
+ if (!walltime_threshold)
+ vty_out(vty, "no service walltime-warning\n");
+#if defined(CONSUMED_TIME_CHECK) && CONSUMED_TIME_CHECK != 5000000
+ else /* again, always print non-default */
+#else
+ else if (walltime_threshold != 5000000)
+#endif
+ vty_out(vty, "service walltime-warning %lu\n",
+ walltime_threshold);
+
if (host.advanced)
vty_out(vty, "service advanced-vty\n");
diff --git a/lib/compiler.h b/lib/compiler.h
index e805eb8be4..970ed297fc 100644
--- a/lib/compiler.h
+++ b/lib/compiler.h
@@ -347,7 +347,11 @@ extern "C" {
#define PRIx64 "Lx"
#else /* !_FRR_ATTRIBUTE_PRINTFRR */
+#ifdef __NetBSD__
+#define PRINTFRR(a, b) __attribute__((format(gnu_syslog, a, b)))
+#else
#define PRINTFRR(a, b) __attribute__((format(printf, a, b)))
+#endif
/* frr-format plugin is C-only for now, so no point in doing these shenanigans
* for C++... (also they can break some C++ stuff...)
diff --git a/lib/log_filter.c b/lib/log_filter.c
index 721e57a628..f01497dead 100644
--- a/lib/log_filter.c
+++ b/lib/log_filter.c
@@ -111,13 +111,14 @@ int zlog_filter_dump(char *buf, size_t max_size)
return len;
}
-static int search_buf(const char *buf)
+static int search_buf(const char *buf, size_t len)
{
char *found = NULL;
frr_with_mutex(&logfilterlock) {
for (int i = 0; i < zlog_filter_count; i++) {
- found = strstr(buf, zlog_filters[i]);
+ found = memmem(buf, len, zlog_filters[i],
+ strlen(zlog_filters[i]));
if (found != NULL)
return 0;
}
@@ -131,12 +132,15 @@ static void zlog_filterfile_fd(struct zlog_target *zt, struct zlog_msg *msgs[],
{
struct zlog_msg *msgfilt[nmsgs];
size_t i, o = 0;
+ const char *text;
+ size_t text_len;
for (i = 0; i < nmsgs; i++) {
- if (zlog_msg_prio(msgs[i]) >= LOG_DEBUG
- && search_buf(zlog_msg_text(msgs[i], NULL)) < 0)
- continue;
-
+ if (zlog_msg_prio(msgs[i]) >= LOG_DEBUG) {
+ text = zlog_msg_text(msgs[i], &text_len);
+ if (search_buf(text, text_len) < 0)
+ continue;
+ }
msgfilt[o++] = msgs[i];
}
diff --git a/lib/log_vty.c b/lib/log_vty.c
index 9dbf216d31..cbb8de8976 100644
--- a/lib/log_vty.c
+++ b/lib/log_vty.c
@@ -34,6 +34,7 @@
#define ZLOG_MAXLVL(a, b) MAX(a, b)
DEFINE_HOOK(zlog_rotate, (), ());
+DEFINE_HOOK(zlog_cli_show, (struct vty * vty), (vty));
static const int log_default_lvl = LOG_DEBUG;
@@ -57,7 +58,7 @@ static struct zlog_cfg_filterfile zt_filterfile = {
},
};
-static const char *zlog_progname;
+const char *zlog_progname;
static const char *zlog_protoname;
static const struct facility_map {
@@ -94,7 +95,14 @@ static const char * const zlog_priority[] = {
"notifications", "informational", "debugging", NULL,
};
-static const char *facility_name(int facility)
+const char *zlog_priority_str(int priority)
+{
+ if (priority > LOG_DEBUG)
+ return "???";
+ return zlog_priority[priority];
+}
+
+const char *facility_name(int facility)
{
const struct facility_map *fm;
@@ -104,7 +112,7 @@ static const char *facility_name(int facility)
return "";
}
-static int facility_match(const char *str)
+int facility_match(const char *str)
{
const struct facility_map *fm;
@@ -194,6 +202,8 @@ DEFUN_NOSH (show_logging,
vty_out(vty, "Record priority: %s\n",
(zt_file.record_priority ? "enabled" : "disabled"));
vty_out(vty, "Timestamp precision: %d\n", zt_file.ts_subsec);
+
+ hook_call(zlog_cli_show, vty);
return CMD_SUCCESS;
}
@@ -588,8 +598,9 @@ DEFUN (no_config_log_filterfile,
DEFPY (log_filter,
log_filter_cmd,
- "[no] log-filter WORD$filter",
+ "[no] log filter-text WORD$filter",
NO_STR
+ "Logging control\n"
FILTER_LOG_STR
"String to filter by\n")
{
@@ -616,8 +627,9 @@ DEFPY (log_filter,
/* Clear all log filters */
DEFPY (log_filter_clear,
log_filter_clear_cmd,
- "clear log-filter",
+ "clear log filter-text",
CLEAR_STR
+ "Logging control\n"
FILTER_LOG_STR)
{
zlog_filter_clear();
@@ -627,8 +639,9 @@ DEFPY (log_filter_clear,
/* Show log filter */
DEFPY (show_log_filter,
show_log_filter_cmd,
- "show log-filter",
+ "show logging filter-text",
SHOW_STR
+ "Show current logging configuration\n"
FILTER_LOG_STR)
{
char log_filters[ZLOG_FILTERS_MAX * (ZLOG_FILTER_LENGTH_MAX + 3)] = "";
diff --git a/lib/log_vty.h b/lib/log_vty.h
index f0fb7d3dba..db46b3cb5b 100644
--- a/lib/log_vty.h
+++ b/lib/log_vty.h
@@ -34,9 +34,14 @@ extern void log_config_write(struct vty *vty);
extern int log_level_match(const char *s);
extern void log_show_syslog(struct vty *vty);
+extern int facility_match(const char *str);
+extern const char *facility_name(int facility);
+
DECLARE_HOOK(zlog_rotate, (), ());
extern void zlog_rotate(void);
+DECLARE_HOOK(zlog_cli_show, (struct vty * vty), (vty));
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/routemap.h b/lib/routemap.h
index 5b6b64eaeb..4d76ae1536 100644
--- a/lib/routemap.h
+++ b/lib/routemap.h
@@ -366,6 +366,10 @@ DECLARE_QOBJ_TYPE(route_map);
(strmatch(A, "frr-bgp-route-map:ipv4-vpn-address"))
#define IS_SET_BGP_IPV4_NH(A) \
(strmatch(A, "frr-bgp-route-map:set-ipv4-nexthop"))
+#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(A) \
+ (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv4"))
+#define IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(A) \
+ (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv6"))
/* Prototypes. */
extern void route_map_init(void);
diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c
index e11b9eea74..bf982cfa2b 100644
--- a/lib/routemap_cli.c
+++ b/lib/routemap_cli.c
@@ -1248,6 +1248,16 @@ void route_map_action_show(struct vty *vty, struct lyd_node *dnode,
yang_dnode_get_string(
dnode,
"./rmap-set-action/frr-bgp-route-map:ipv4-nexthop"));
+ } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV4(action)) {
+ vty_out(vty, " set evpn gateway-ip ipv4 %s\n",
+ yang_dnode_get_string(
+ dnode,
+ "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4"));
+ } else if (IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(action)) {
+ vty_out(vty, " set evpn gateway-ip ipv6 %s\n",
+ yang_dnode_get_string(
+ dnode,
+ "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6"));
}
}
diff --git a/lib/subdir.am b/lib/subdir.am
index 4015c67ea8..90301d800a 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -168,6 +168,7 @@ clippy_scan += \
lib/northbound_cli.c \
lib/plist.c \
lib/routemap_cli.c \
+ lib/thread.c \
lib/vty.c \
# end
@@ -463,7 +464,7 @@ endif
SUFFIXES += .xref
%.xref: % $(CLIPPY)
- $(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py $(XRELFO_FLAGS) -o $@ $<
+ $(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py $(WERROR) $(XRELFO_FLAGS) -o $@ $<
# dependencies added in python/makefile.py
frr.xref:
diff --git a/lib/thread.c b/lib/thread.c
index 3af89fad5a..835aa38115 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -90,7 +90,22 @@ static struct list *masters;
static void thread_free(struct thread_master *master, struct thread *thread);
+#ifndef EXCLUDE_CPU_TIME
+#define EXCLUDE_CPU_TIME 0
+#endif
+#ifndef CONSUMED_TIME_CHECK
+#define CONSUMED_TIME_CHECK 0
+#endif
+
+bool cputime_enabled = !EXCLUDE_CPU_TIME;
+unsigned long cputime_threshold = CONSUMED_TIME_CHECK;
+unsigned long walltime_threshold = CONSUMED_TIME_CHECK;
+
/* CLI start ---------------------------------------------------------------- */
+#ifndef VTYSH_EXTRACT_PL
+#include "lib/thread_clippy.c"
+#endif
+
static unsigned int cpu_record_hash_key(const struct cpu_thread_history *a)
{
int size = sizeof(a->func);
@@ -120,7 +135,6 @@ static void cpu_record_hash_free(void *a)
XFREE(MTYPE_THREAD_STATS, hist);
}
-#ifndef EXCLUDE_CPU_TIME
static void vty_out_cpu_thread_history(struct vty *vty,
struct cpu_thread_history *a)
{
@@ -187,6 +201,14 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
struct thread_master *m;
struct listnode *ln;
+ if (!cputime_enabled)
+ vty_out(vty,
+ "\n"
+ "Collecting CPU time statistics is currently disabled. Following statistics\n"
+ "will be zero or may display data from when collection was enabled. Use the\n"
+ " \"service cputime-stats\" command to start collecting data.\n"
+ "\nCounters and wallclock times are always maintained and should be accurate.\n");
+
memset(&tmp, 0, sizeof(tmp));
tmp.funcname = "TOTAL";
tmp.types = filter;
@@ -236,7 +258,6 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)
if (tmp.total_calls > 0)
vty_out_cpu_thread_history(vty, &tmp);
}
-#endif
static void cpu_record_hash_clear(struct hash_bucket *bucket, void *args[])
{
@@ -306,7 +327,6 @@ static uint8_t parse_filter(const char *filterstr)
return filter;
}
-#ifndef EXCLUDE_CPU_TIME
DEFUN_NOSH (show_thread_cpu,
show_thread_cpu_cmd,
"show thread cpu [FILTER]",
@@ -331,7 +351,61 @@ DEFUN_NOSH (show_thread_cpu,
cpu_record_print(vty, filter);
return CMD_SUCCESS;
}
-#endif
+
+DEFPY (service_cputime_stats,
+ service_cputime_stats_cmd,
+ "[no] service cputime-stats",
+ NO_STR
+ "Set up miscellaneous service\n"
+ "Collect CPU usage statistics\n")
+{
+ cputime_enabled = !no;
+ return CMD_SUCCESS;
+}
+
+DEFPY (service_cputime_warning,
+ service_cputime_warning_cmd,
+ "[no] service cputime-warning (1-4294967295)",
+ NO_STR
+ "Set up miscellaneous service\n"
+ "Warn for tasks exceeding CPU usage threshold\n"
+ "Warning threshold in milliseconds\n")
+{
+ if (no)
+ cputime_threshold = 0;
+ else
+ cputime_threshold = cputime_warning * 1000;
+ return CMD_SUCCESS;
+}
+
+ALIAS (service_cputime_warning,
+ no_service_cputime_warning_cmd,
+ "no service cputime-warning",
+ NO_STR
+ "Set up miscellaneous service\n"
+ "Warn for tasks exceeding CPU usage threshold\n")
+
+DEFPY (service_walltime_warning,
+ service_walltime_warning_cmd,
+ "[no] service walltime-warning (1-4294967295)",
+ NO_STR
+ "Set up miscellaneous service\n"
+ "Warn for tasks exceeding total wallclock threshold\n"
+ "Warning threshold in milliseconds\n")
+{
+ if (no)
+ walltime_threshold = 0;
+ else
+ walltime_threshold = walltime_warning * 1000;
+ return CMD_SUCCESS;
+}
+
+ALIAS (service_walltime_warning,
+ no_service_walltime_warning_cmd,
+ "no service walltime-warning",
+ NO_STR
+ "Set up miscellaneous service\n"
+ "Warn for tasks exceeding total wallclock threshold\n")
static void show_thread_poll_helper(struct vty *vty, struct thread_master *m)
{
@@ -421,11 +495,15 @@ DEFUN (clear_thread_cpu,
void thread_cmd_init(void)
{
-#ifndef EXCLUDE_CPU_TIME
install_element(VIEW_NODE, &show_thread_cpu_cmd);
-#endif
install_element(VIEW_NODE, &show_thread_poll_cmd);
install_element(ENABLE_NODE, &clear_thread_cpu_cmd);
+
+ install_element(CONFIG_NODE, &service_cputime_stats_cmd);
+ install_element(CONFIG_NODE, &service_cputime_warning_cmd);
+ install_element(CONFIG_NODE, &no_service_cputime_warning_cmd);
+ install_element(CONFIG_NODE, &service_walltime_warning_cmd);
+ install_element(CONFIG_NODE, &no_service_walltime_warning_cmd);
}
/* CLI end ------------------------------------------------------------------ */
@@ -1743,9 +1821,14 @@ static unsigned long timeval_elapsed(struct timeval a, struct timeval b)
unsigned long thread_consumed_time(RUSAGE_T *now, RUSAGE_T *start,
unsigned long *cputime)
{
+#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID
+ *cputime = (now->cpu.tv_sec - start->cpu.tv_sec) * TIMER_SECOND_MICRO
+ + (now->cpu.tv_nsec - start->cpu.tv_nsec) / 1000;
+#else
/* This is 'user + sys' time. */
*cputime = timeval_elapsed(now->cpu.ru_utime, start->cpu.ru_utime)
+ timeval_elapsed(now->cpu.ru_stime, start->cpu.ru_stime);
+#endif
return timeval_elapsed(now->real, start->real);
}
@@ -1778,13 +1861,23 @@ void thread_set_yield_time(struct thread *thread, unsigned long yield_time)
void thread_getrusage(RUSAGE_T *r)
{
+ monotime(&r->real);
+ if (!cputime_enabled) {
+ memset(&r->cpu, 0, sizeof(r->cpu));
+ return;
+ }
+
+#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID
+ /* not currently implemented in Linux's vDSO, but maybe at some point
+ * in the future?
+ */
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &r->cpu);
+#else /* !HAVE_CLOCK_THREAD_CPUTIME_ID */
#if defined RUSAGE_THREAD
#define FRR_RUSAGE RUSAGE_THREAD
#else
#define FRR_RUSAGE RUSAGE_SELF
#endif
- monotime(&r->real);
-#ifndef EXCLUDE_CPU_TIME
getrusage(FRR_RUSAGE, &(r->cpu));
#endif
}
@@ -1802,13 +1895,14 @@ void thread_getrusage(RUSAGE_T *r)
*/
void thread_call(struct thread *thread)
{
-#ifndef EXCLUDE_CPU_TIME
- _Atomic unsigned long realtime, cputime;
- unsigned long exp;
- unsigned long helper;
-#endif
RUSAGE_T before, after;
+ /* if the thread being called is the CLI, it may change cputime_enabled
+ * ("service cputime-stats" command), which can result in nonsensical
+ * and very confusing warnings
+ */
+ bool cputime_enabled_here = cputime_enabled;
+
if (thread->master->ready_run_loop)
before = thread->master->last_getrusage;
else
@@ -1828,43 +1922,45 @@ void thread_call(struct thread *thread)
GETRUSAGE(&after);
thread->master->last_getrusage = after;
-#ifndef EXCLUDE_CPU_TIME
- realtime = thread_consumed_time(&after, &before, &helper);
- cputime = helper;
+ unsigned long walltime, cputime;
+ unsigned long exp;
+
+ walltime = thread_consumed_time(&after, &before, &cputime);
- /* update realtime */
- atomic_fetch_add_explicit(&thread->hist->real.total, realtime,
+ /* update walltime */
+ atomic_fetch_add_explicit(&thread->hist->real.total, walltime,
memory_order_seq_cst);
exp = atomic_load_explicit(&thread->hist->real.max,
memory_order_seq_cst);
- while (exp < realtime
+ while (exp < walltime
&& !atomic_compare_exchange_weak_explicit(
- &thread->hist->real.max, &exp, realtime,
- memory_order_seq_cst, memory_order_seq_cst))
+ &thread->hist->real.max, &exp, walltime,
+ memory_order_seq_cst, memory_order_seq_cst))
;
- /* update cputime */
- atomic_fetch_add_explicit(&thread->hist->cpu.total, cputime,
- memory_order_seq_cst);
- exp = atomic_load_explicit(&thread->hist->cpu.max,
- memory_order_seq_cst);
- while (exp < cputime
- && !atomic_compare_exchange_weak_explicit(
- &thread->hist->cpu.max, &exp, cputime,
- memory_order_seq_cst, memory_order_seq_cst))
- ;
+ if (cputime_enabled_here && cputime_enabled) {
+ /* update cputime */
+ atomic_fetch_add_explicit(&thread->hist->cpu.total, cputime,
+ memory_order_seq_cst);
+ exp = atomic_load_explicit(&thread->hist->cpu.max,
+ memory_order_seq_cst);
+ while (exp < cputime
+ && !atomic_compare_exchange_weak_explicit(
+ &thread->hist->cpu.max, &exp, cputime,
+ memory_order_seq_cst, memory_order_seq_cst))
+ ;
+ }
atomic_fetch_add_explicit(&thread->hist->total_calls, 1,
memory_order_seq_cst);
atomic_fetch_or_explicit(&thread->hist->types, 1 << thread->add_type,
memory_order_seq_cst);
-#ifdef CONSUMED_TIME_CHECK
- if (cputime > CONSUMED_TIME_CHECK) {
+ if (cputime_enabled_here && cputime_enabled && cputime_threshold
+ && cputime > cputime_threshold) {
/*
- * We have a CPU Hog on our hands. The time FRR
- * has spent doing actual work ( not sleeping )
- * is greater than 5 seconds.
+ * We have a CPU Hog on our hands. The time FRR has spent
+ * doing actual work (not sleeping) is greater than 5 seconds.
* Whinge about it now, so we're aware this is yet another task
* to fix.
*/
@@ -1874,13 +1970,13 @@ void thread_call(struct thread *thread)
EC_LIB_SLOW_THREAD_CPU,
"CPU HOG: task %s (%lx) ran for %lums (cpu time %lums)",
thread->xref->funcname, (unsigned long)thread->func,
- realtime / 1000, cputime / 1000);
- } else if (realtime > CONSUMED_TIME_CHECK) {
+ walltime / 1000, cputime / 1000);
+
+ } else if (walltime_threshold && walltime > walltime_threshold) {
/*
- * The runtime for a task is greater than 5 seconds, but
- * the cpu time is under 5 seconds. Let's whine
- * about this because this could imply some sort of
- * scheduling issue.
+ * The runtime for a task is greater than 5 seconds, but the
+ * cpu time is under 5 seconds. Let's whine about this because
+ * this could imply some sort of scheduling issue.
*/
atomic_fetch_add_explicit(&thread->hist->total_wall_warn,
1, memory_order_seq_cst);
@@ -1888,10 +1984,8 @@ void thread_call(struct thread *thread)
EC_LIB_SLOW_THREAD_WALL,
"STARVATION: task %s (%lx) ran for %lums (cpu time %lums)",
thread->xref->funcname, (unsigned long)thread->func,
- realtime / 1000, cputime / 1000);
+ walltime / 1000, cputime / 1000);
}
-#endif /* CONSUMED_TIME_CHECK */
-#endif /* Exclude CPU Time */
}
/* Execute thread */
diff --git a/lib/thread.h b/lib/thread.h
index fee728dbf9..abd94ff4f0 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -33,8 +33,19 @@
extern "C" {
#endif
+extern bool cputime_enabled;
+extern unsigned long cputime_threshold;
+/* capturing wallclock time is always enabled since it is fast (reading
+ * hardware TSC w/o syscalls)
+ */
+extern unsigned long walltime_threshold;
+
struct rusage_t {
+#ifdef HAVE_CLOCK_THREAD_CPUTIME_ID
+ struct timespec cpu;
+#else
struct rusage cpu;
+#endif
struct timeval real;
};
#define RUSAGE_T struct rusage_t
diff --git a/lib/vrf.c b/lib/vrf.c
index a04f2ddeb7..03d9a62c0f 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -827,10 +827,24 @@ DEFUN_YANG (no_vrf,
return CMD_WARNING_CONFIG_FAILED;
}
+ if (vrf_get_backend() == VRF_BACKEND_VRF_LITE) {
+ /*
+ * Remove the VRF interface config. Currently, we allow to
+ * remove only inactive VRFs, so we use VRF_DEFAULT_NAME here,
+ * because when the VRF is removed from kernel, the interface
+ * is moved to the default VRF. If we ever allow removing
+ * active VRFs, this code have to be updated accordingly.
+ */
+ snprintf(xpath_list, sizeof(xpath_list),
+ "/frr-interface:lib/interface[name='%s'][vrf='%s']",
+ vrfname, VRF_DEFAULT_NAME);
+ nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL);
+ }
+
snprintf(xpath_list, sizeof(xpath_list), FRR_VRF_KEY_XPATH, vrfname);
nb_cli_enqueue_change(vty, xpath_list, NB_OP_DESTROY, NULL);
- return nb_cli_apply_changes(vty, xpath_list);
+ return nb_cli_apply_changes(vty, NULL);
}
diff --git a/lib/vty.c b/lib/vty.c
index 50d116c564..f64ab83847 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -502,37 +502,37 @@ static int vty_command(struct vty *vty, char *buf)
zlog_notice("%s%s", prompt_str, buf);
}
-#ifdef CONSUMED_TIME_CHECK
- {
- RUSAGE_T before;
- RUSAGE_T after;
- unsigned long realtime, cputime;
+ RUSAGE_T before;
+ RUSAGE_T after;
+ unsigned long walltime, cputime;
- GETRUSAGE(&before);
-#endif /* CONSUMED_TIME_CHECK */
+ /* cmd_execute() may change cputime_enabled if we're executing the
+ * "service cputime-stats" command, which can result in nonsensical
+ * and very confusing warnings
+ */
+ bool cputime_enabled_here = cputime_enabled;
- ret = cmd_execute(vty, buf, NULL, 0);
+ GETRUSAGE(&before);
- /* Get the name of the protocol if any */
- protocolname = frr_protoname;
+ ret = cmd_execute(vty, buf, NULL, 0);
-#ifdef CONSUMED_TIME_CHECK
- GETRUSAGE(&after);
- realtime = thread_consumed_time(&after, &before, &cputime);
- if (cputime > CONSUMED_TIME_CHECK) {
- /* Warn about CPU hog that must be fixed. */
- flog_warn(
- EC_LIB_SLOW_THREAD_CPU,
- "CPU HOG: command took %lums (cpu time %lums): %s",
- realtime / 1000, cputime / 1000, buf);
- } else if (realtime > CONSUMED_TIME_CHECK) {
- flog_warn(
- EC_LIB_SLOW_THREAD_WALL,
- "STARVATION: command took %lums (cpu time %lums): %s",
- realtime / 1000, cputime / 1000, buf);
- }
- }
-#endif /* CONSUMED_TIME_CHECK */
+ GETRUSAGE(&after);
+
+ walltime = thread_consumed_time(&after, &before, &cputime);
+
+ if (cputime_enabled_here && cputime_enabled && cputime_threshold
+ && cputime > cputime_threshold)
+ /* Warn about CPU hog that must be fixed. */
+ flog_warn(EC_LIB_SLOW_THREAD_CPU,
+ "CPU HOG: command took %lums (cpu time %lums): %s",
+ walltime / 1000, cputime / 1000, buf);
+ else if (walltime_threshold && walltime > walltime_threshold)
+ flog_warn(EC_LIB_SLOW_THREAD_WALL,
+ "STARVATION: command took %lums (cpu time %lums): %s",
+ walltime / 1000, cputime / 1000, buf);
+
+ /* Get the name of the protocol if any */
+ protocolname = frr_protoname;
if (ret != CMD_SUCCESS)
switch (ret) {
diff --git a/lib/zlog.c b/lib/zlog.c
index 89ab9265d1..6fd52cae62 100644
--- a/lib/zlog.c
+++ b/lib/zlog.c
@@ -67,6 +67,7 @@ DEFINE_HOOK(zlog_aux_init, (const char *prefix, int prio_min),
char zlog_prefix[128];
size_t zlog_prefixsz;
int zlog_tmpdirfd = -1;
+int zlog_instance = -1;
static atomic_bool zlog_ec = true, zlog_xid = true;
@@ -107,6 +108,7 @@ struct zlog_msg {
size_t stackbufsz;
char *text;
size_t textlen;
+ size_t hdrlen;
/* This is always ISO8601 with sub-second precision 9 here, it's
* converted for callers as needed. ts_dot points to the "."
@@ -116,8 +118,23 @@ struct zlog_msg {
* Valid if ZLOG_TS_ISO8601 is set.
* (0 if timestamp has not been formatted yet)
*/
- uint32_t ts_flags;
char ts_str[32], *ts_dot, ts_zonetail[8];
+ uint32_t ts_flags;
+
+ /* "mmm dd hh:mm:ss" for 3164 legacy syslog - too dissimilar from
+ * the above, so just kept separately here.
+ */
+ uint32_t ts_3164_flags;
+ char ts_3164_str[16];
+
+ /* at the time of writing, 16 args was the actual maximum of arguments
+ * to a single zlog call. Particularly printing flag bitmasks seems
+ * to drive this. That said, the overhead of dynamically sizing this
+ * probably outweighs the value. If anything, a printfrr extension
+ * for printing flag bitmasks might be a good idea.
+ */
+ struct fmt_outpos argpos[24];
+ size_t n_argpos;
};
/* thread-local log message buffering
@@ -204,8 +221,15 @@ static inline void zlog_tls_set(struct zlog_tls *val)
#endif
#ifdef CAN_DO_TLS
-static long zlog_gettid(void)
+static intmax_t zlog_gettid(void)
{
+#ifndef __OpenBSD__
+ /* accessing a TLS variable is much faster than a syscall */
+ static thread_local intmax_t cached_tid = -1;
+ if (cached_tid != -1)
+ return cached_tid;
+#endif
+
long rv = -1;
#ifdef HAVE_PTHREAD_GETTHREADID_NP
rv = pthread_getthreadid_np();
@@ -225,6 +249,10 @@ static long zlog_gettid(void)
rv = mach_thread_self();
mach_port_deallocate(mach_task_self(), rv);
#endif
+
+#ifndef __OpenBSD__
+ cached_tid = rv;
+#endif
return rv;
}
@@ -244,7 +272,7 @@ void zlog_tls_buffer_init(void)
for (i = 0; i < array_size(zlog_tls->msgp); i++)
zlog_tls->msgp[i] = &zlog_tls->msgs[i];
- snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
+ snprintfrr(mmpath, sizeof(mmpath), "logbuf.%jd", zlog_gettid());
mmfd = openat(zlog_tmpdirfd, mmpath,
O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
@@ -311,7 +339,7 @@ void zlog_tls_buffer_fini(void)
zlog_tls_free(zlog_tls);
zlog_tls_set(NULL);
- snprintfrr(mmpath, sizeof(mmpath), "logbuf.%ld", zlog_gettid());
+ snprintfrr(mmpath, sizeof(mmpath), "logbuf.%jd", zlog_gettid());
if (do_unlink && unlinkat(zlog_tmpdirfd, mmpath, 0))
zlog_err("unlink logbuf: %s (%d)", strerror(errno), errno);
}
@@ -326,6 +354,24 @@ void zlog_tls_buffer_fini(void)
}
#endif
+void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid)
+{
+#ifndef __OpenBSD__
+ static thread_local intmax_t cached_pid = -1;
+ if (cached_pid != -1)
+ *pid = cached_pid;
+ else
+ cached_pid = *pid = (intmax_t)getpid();
+#else
+ *pid = (intmax_t)getpid();
+#endif
+#ifdef CAN_DO_TLS
+ *tid = zlog_gettid();
+#else
+ *tid = *pid;
+#endif
+}
+
static inline void zlog_tls_free(void *arg)
{
struct zlog_tls *zlog_tls = arg;
@@ -592,15 +638,19 @@ const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen)
if (need)
need += bputch(&fb, ' ');
- hdrlen = need;
+ msg->hdrlen = hdrlen = need;
assert(hdrlen < msg->stackbufsz);
+ fb.outpos = msg->argpos;
+ fb.outpos_n = array_size(msg->argpos);
+ fb.outpos_i = 0;
+
va_copy(args, msg->args);
need += vbprintfrr(&fb, msg->fmt, args);
va_end(args);
msg->textlen = need;
- need += bputch(&fb, '\0');
+ need += bputch(&fb, '\n');
if (need <= msg->stackbufsz)
msg->text = msg->stackbuf;
@@ -612,25 +662,42 @@ const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen)
fb.buf = msg->text;
fb.len = need;
fb.pos = msg->text + hdrlen;
+ fb.outpos_i = 0;
va_copy(args, msg->args);
vbprintfrr(&fb, msg->fmt, args);
va_end(args);
- bputch(&fb, '\0');
+ bputch(&fb, '\n');
}
+
+ msg->n_argpos = fb.outpos_i;
}
if (textlen)
*textlen = msg->textlen;
return msg->text;
}
+void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen, size_t *n_argpos,
+ const struct fmt_outpos **argpos)
+{
+ if (!msg->text)
+ zlog_msg_text(msg, NULL);
+
+ if (hdrlen)
+ *hdrlen = msg->hdrlen;
+ if (n_argpos)
+ *n_argpos = msg->n_argpos;
+ if (argpos)
+ *argpos = msg->argpos;
+}
+
#define ZLOG_TS_FORMAT (ZLOG_TS_ISO8601 | ZLOG_TS_LEGACY)
#define ZLOG_TS_FLAGS ~ZLOG_TS_PREC
-size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
- uint32_t flags)
+size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out, uint32_t flags)
{
+ size_t outsz = out ? (out->buf + out->len - out->pos) : 0;
size_t len1;
if (!(flags & ZLOG_TS_FORMAT))
@@ -672,36 +739,78 @@ size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
len1 = strlen(msg->ts_str);
if (flags & ZLOG_TS_LEGACY) {
- if (len1 + 1 > outsz)
- return 0;
+ if (!out)
+ return len1;
+
+ if (len1 > outsz) {
+ memset(out->pos, 0, outsz);
+ out->pos += outsz;
+ return len1;
+ }
/* just swap out the formatting, faster than redoing it */
for (char *p = msg->ts_str; p < msg->ts_str + len1; p++) {
switch (*p) {
case '-':
- *out++ = '/';
+ *out->pos++ = '/';
break;
case 'T':
- *out++ = ' ';
+ *out->pos++ = ' ';
break;
default:
- *out++ = *p;
+ *out->pos++ = *p;
}
}
- *out = '\0';
return len1;
} else {
size_t len2 = strlen(msg->ts_zonetail);
- if (len1 + len2 + 1 > outsz)
- return 0;
- memcpy(out, msg->ts_str, len1);
- memcpy(out + len1, msg->ts_zonetail, len2);
- out[len1 + len2] = '\0';
+ if (!out)
+ return len1 + len2;
+
+ if (len1 + len2 > outsz) {
+ memset(out->pos, 0, outsz);
+ out->pos += outsz;
+ return len1 + len2;
+ }
+
+ memcpy(out->pos, msg->ts_str, len1);
+ out->pos += len1;
+ memcpy(out->pos, msg->ts_zonetail, len2);
+ out->pos += len2;
return len1 + len2;
}
}
+size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out, uint32_t flags)
+{
+ flags &= ZLOG_TS_UTC;
+
+ if (!msg->ts_3164_str[0] || flags != msg->ts_3164_flags) {
+ /* these are "hardcoded" in RFC3164, so they're here too... */
+ static const char *const months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ };
+ struct tm tm;
+
+ /* RFC3164 explicitly asks for local time, but common usage
+ * also includes UTC.
+ */
+ if (flags & ZLOG_TS_UTC)
+ gmtime_r(&msg->ts.tv_sec, &tm);
+ else
+ localtime_r(&msg->ts.tv_sec, &tm);
+
+ snprintfrr(msg->ts_3164_str, sizeof(msg->ts_3164_str),
+ "%3s %2d %02d:%02d:%02d", months[tm.tm_mon],
+ tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ msg->ts_3164_flags = flags;
+ }
+ return bputs(out, msg->ts_3164_str);
+}
+
void zlog_set_prefix_ec(bool enable)
{
atomic_store_explicit(&zlog_ec, enable, memory_order_relaxed);
@@ -777,6 +886,7 @@ void zlog_init(const char *progname, const char *protoname,
{
zlog_uid = uid;
zlog_gid = gid;
+ zlog_instance = instance;
if (instance) {
snprintfrr(zlog_tmpdir, sizeof(zlog_tmpdir),
diff --git a/lib/zlog.h b/lib/zlog.h
index c421c16f38..d9c8952ac5 100644
--- a/lib/zlog.h
+++ b/lib/zlog.h
@@ -31,14 +31,19 @@
#include "frrcu.h"
#include "memory.h"
#include "hook.h"
+#include "printfrr.h"
#ifdef __cplusplus
extern "C" {
#endif
+DECLARE_MGROUP(LOG);
+
extern char zlog_prefix[];
extern size_t zlog_prefixsz;
extern int zlog_tmpdirfd;
+extern int zlog_instance;
+extern const char *zlog_progname;
struct xref_logmsg {
struct xref xref;
@@ -143,9 +148,18 @@ struct zlog_msg;
extern int zlog_msg_prio(struct zlog_msg *msg);
extern const struct xref_logmsg *zlog_msg_xref(struct zlog_msg *msg);
-/* pass NULL as textlen if you don't need it. */
+/* text is NOT \0 terminated; instead there is a \n after textlen since the
+ * logging targets would jump extra hoops otherwise for a single byte. (the
+ * \n is not included in textlen)
+ *
+ * calling this with NULL textlen is likely wrong.
+ * use "%.*s", (int)textlen, text when passing to printf-like functions
+ */
extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen);
+extern void zlog_msg_args(struct zlog_msg *msg, size_t *hdrlen,
+ size_t *n_argpos, const struct fmt_outpos **argpos);
+
/* timestamp formatting control flags */
/* sub-second digit count */
@@ -161,9 +175,18 @@ extern const char *zlog_msg_text(struct zlog_msg *msg, size_t *textlen);
/* default is local time zone */
#define ZLOG_TS_UTC (1 << 10)
-extern size_t zlog_msg_ts(struct zlog_msg *msg, char *out, size_t outsz,
+extern size_t zlog_msg_ts(struct zlog_msg *msg, struct fbuf *out,
uint32_t flags);
+/* "mmm dd hh:mm:ss" for RFC3164 syslog. Only ZLOG_TS_UTC for flags. */
+extern size_t zlog_msg_ts_3164(struct zlog_msg *msg, struct fbuf *out,
+ uint32_t flags);
+
+/* currently just returns the current PID/TID since we never write another
+ * thread's messages
+ */
+extern void zlog_msg_pid(struct zlog_msg *msg, intmax_t *pid, intmax_t *tid);
+
/* This list & struct implements the actual logging targets. It is accessed
* lock-free from all threads, and thus MUST only be changed atomically, i.e.
* RCU.
@@ -254,6 +277,8 @@ extern void zlog_tls_buffer_fini(void);
/* Enable or disable 'immediate' output - default is to buffer messages. */
extern void zlog_set_immediate(bool set_p);
+extern const char *zlog_priority_str(int priority);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/zlog_targets.c b/lib/zlog_targets.c
index 7799fbfda7..48785ad298 100644
--- a/lib/zlog_targets.c
+++ b/lib/zlog_targets.c
@@ -31,7 +31,6 @@
* absolute end.
*/
-DECLARE_MGROUP(LOG);
DEFINE_MGROUP_ACTIVEATEXIT(LOG, "logging subsystem");
DEFINE_MTYPE_STATIC(LOG, LOG_FD, "log file target");
@@ -79,13 +78,17 @@ void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs)
int prio = zlog_msg_prio(msg);
if (prio <= zt->prio_min) {
+ struct fbuf fbuf = {
+ .buf = ts_buf,
+ .pos = ts_pos,
+ .len = sizeof(ts_buf),
+ };
+
iov[iovpos].iov_base = ts_pos;
- if (iovpos > 0)
- *ts_pos++ = '\n';
- ts_pos += zlog_msg_ts(msg, ts_pos,
- sizeof(ts_buf) - 1
- - (ts_pos - ts_buf),
- ZLOG_TS_LEGACY | zte->ts_subsec);
+ zlog_msg_ts(msg, &fbuf,
+ ZLOG_TS_LEGACY | zte->ts_subsec);
+ ts_pos = fbuf.pos;
+
*ts_pos++ = ' ';
iov[iovpos].iov_len =
ts_pos - (char *)iov[iovpos].iov_base;
@@ -107,7 +110,7 @@ void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs)
iov[iovpos].iov_base =
(char *)zlog_msg_text(msg, &textlen);
- iov[iovpos].iov_len = textlen;
+ iov[iovpos].iov_len = textlen + 1;
iovpos++;
}
@@ -120,11 +123,6 @@ void zlog_fd(struct zlog_target *zt, struct zlog_msg *msgs[], size_t nmsgs)
if (iovpos > 0 && (ts_buf + sizeof(ts_buf) - ts_pos < TS_LEN
|| i + 1 == nmsgs
|| array_size(iov) - iovpos < 5)) {
- iov[iovpos].iov_base = (char *)"\n";
- iov[iovpos].iov_len = 1;
-
- iovpos++;
-
writev(fd, iov, iovpos);
iovpos = 0;
@@ -439,13 +437,16 @@ static void zlog_syslog(struct zlog_target *zt, struct zlog_msg *msgs[],
{
size_t i;
struct zlt_syslog *zte = container_of(zt, struct zlt_syslog, zt);
+ const char *text;
+ size_t text_len;
for (i = 0; i < nmsgs; i++) {
if (zlog_msg_prio(msgs[i]) > zt->prio_min)
continue;
- syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%s",
- zlog_msg_text(msgs[i], NULL));
+ text = zlog_msg_text(msgs[i], &text_len);
+ syslog(zlog_msg_prio(msgs[i]) | zte->syslog_facility, "%.*s",
+ (int)text_len, text);
}
}
diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c
index c2111a7706..54b7850207 100644
--- a/nhrpd/nhrp_main.c
+++ b/nhrpd/nhrp_main.c
@@ -118,6 +118,7 @@ static struct quagga_signal_t sighandlers[] = {
static const struct frr_yang_module_info *const nhrpd_yang_modules[] = {
&frr_filter_info,
&frr_interface_info,
+ &frr_vrf_info,
};
FRR_DAEMON_INFO(nhrpd, NHRP, .vty_port = NHRP_VTY_PORT,
diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c
index 420ea12ec1..963fa4d995 100644
--- a/nhrpd/nhrp_vty.c
+++ b/nhrpd/nhrp_vty.c
@@ -1260,6 +1260,8 @@ void nhrp_config_init(void)
install_element(CONFIG_NODE, &nhrp_multicast_nflog_group_cmd);
install_element(CONFIG_NODE, &no_nhrp_multicast_nflog_group_cmd);
+ vrf_cmd_init(NULL, &nhrpd_privs);
+
/* interface specific commands */
install_node(&nhrp_interface_node);
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index c17af758b0..ba355a347e 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -58,7 +58,7 @@ DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_EXTERNAL_INFO, "OSPF6 ext. info");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_DIST_ARGS, "OSPF6 Distribute arguments");
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_REDISTRIBUTE, "OSPF6 Redistribute arguments");
-static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id);
+static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type);
static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
struct ospf6_redist *red, int type);
@@ -1111,8 +1111,7 @@ void ospf6_asbr_routemap_update(const char *mapname)
type));
ospf6_asbr_redistribute_unset(ospf6, red, type);
ospf6_asbr_routemap_set(red, mapname);
- ospf6_asbr_redistribute_set(
- type, ospf6->vrf_id);
+ ospf6_asbr_redistribute_set(ospf6, type);
}
}
}
@@ -1231,15 +1230,9 @@ void ospf6_asbr_status_update(struct ospf6 *ospf6, int status)
OSPF6_ROUTER_LSA_SCHEDULE(oa);
}
-static void ospf6_asbr_redistribute_set(int type, vrf_id_t vrf_id)
+static void ospf6_asbr_redistribute_set(struct ospf6 *ospf6, int type)
{
- struct ospf6 *ospf6 = NULL;
- ospf6_zebra_redistribute(type, vrf_id);
-
- ospf6 = ospf6_lookup_by_vrf_id(vrf_id);
-
- if (!ospf6)
- return;
+ ospf6_zebra_redistribute(type, ospf6->vrf_id);
ospf6_asbr_status_update(ospf6, ++ospf6->redist_count);
}
@@ -1263,7 +1256,6 @@ static void ospf6_asbr_redistribute_unset(struct ospf6 *ospf6,
}
ospf6_asbr_routemap_unset(red);
- zlog_debug("%s: redist_count %d", __func__, ospf6->redist_count);
ospf6_asbr_status_update(ospf6, --ospf6->redist_count);
}
@@ -1585,12 +1577,13 @@ DEFUN (ospf6_redistribute,
if (type < 0)
return CMD_WARNING_CONFIG_FAILED;
- red = ospf6_redist_add(ospf6, type, 0);
+ red = ospf6_redist_lookup(ospf6, type, 0);
if (!red)
- return CMD_SUCCESS;
+ ospf6_redist_add(ospf6, type, 0);
+ else
+ ospf6_asbr_redistribute_unset(ospf6, red, type);
- ospf6_asbr_redistribute_unset(ospf6, red, type);
- ospf6_asbr_redistribute_set(type, ospf6->vrf_id);
+ ospf6_asbr_redistribute_set(ospf6, type);
return CMD_SUCCESS;
}
@@ -1615,13 +1608,14 @@ DEFUN (ospf6_redistribute_routemap,
if (type < 0)
return CMD_WARNING_CONFIG_FAILED;
- red = ospf6_redist_add(ospf6, type, 0);
+ red = ospf6_redist_lookup(ospf6, type, 0);
if (!red)
- return CMD_SUCCESS;
+ red = ospf6_redist_add(ospf6, type, 0);
+ else
+ ospf6_asbr_redistribute_unset(ospf6, red, type);
- ospf6_asbr_redistribute_unset(ospf6, red, type);
ospf6_asbr_routemap_set(red, argv[idx_word]->arg);
- ospf6_asbr_redistribute_set(type, ospf6->vrf_id);
+ ospf6_asbr_redistribute_set(ospf6, type);
return CMD_SUCCESS;
}
diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c
index 6763e7457f..553967e2e3 100644
--- a/ospf6d/ospf6_interface.c
+++ b/ospf6d/ospf6_interface.c
@@ -185,6 +185,8 @@ struct ospf6_interface *ospf6_interface_create(struct interface *ifp)
oi = XCALLOC(MTYPE_OSPF6_IF, sizeof(struct ospf6_interface));
+ oi->obuf = ospf6_fifo_new();
+
oi->area = (struct ospf6_area *)NULL;
oi->neighbor_list = list_new();
oi->neighbor_list->cmp = ospf6_neighbor_cmp;
@@ -243,6 +245,8 @@ void ospf6_interface_delete(struct ospf6_interface *oi)
QOBJ_UNREG(oi);
+ ospf6_fifo_free(oi->obuf);
+
for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on))
ospf6_neighbor_delete(on);
@@ -885,6 +889,15 @@ int interface_down(struct thread *thread)
ospf6_sso(oi->interface->ifindex, &allspfrouters6,
IPV6_LEAVE_GROUP, ospf6->fd);
+ /* deal with write fifo */
+ ospf6_fifo_flush(oi->obuf);
+ if (oi->on_write_q) {
+ listnode_delete(ospf6->oi_write_q, oi);
+ if (list_isempty(ospf6->oi_write_q))
+ thread_cancel(&ospf6->t_write);
+ oi->on_write_q = 0;
+ }
+
ospf6_interface_state_change(OSPF6_INTERFACE_DOWN, oi);
return 0;
@@ -1619,6 +1632,9 @@ void ospf6_interface_start(struct ospf6_interface *oi)
if (oi->area_id_format == OSPF6_AREA_FMT_UNSET)
return;
+ if (oi->area)
+ return;
+
ospf6 = ospf6_lookup_by_vrf_id(oi->interface->vrf_id);
if (!ospf6)
return;
@@ -1969,6 +1985,38 @@ DEFUN (no_auto_cost_reference_bandwidth,
}
+DEFUN (ospf6_write_multiplier,
+ ospf6_write_multiplier_cmd,
+ "write-multiplier (1-100)",
+ "Write multiplier\n"
+ "Maximum number of interface serviced per write\n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, o);
+ uint32_t write_oi_count;
+
+ write_oi_count = strtol(argv[1]->arg, NULL, 10);
+ if (write_oi_count < 1 || write_oi_count > 100) {
+ vty_out(vty, "write-multiplier value is invalid\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ o->write_oi_count = write_oi_count;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ospf6_write_multiplier,
+ no_ospf6_write_multiplier_cmd,
+ "no write-multiplier (1-100)",
+ NO_STR
+ "Write multiplier\n"
+ "Maximum number of interface serviced per write\n")
+{
+ VTY_DECLVAR_CONTEXT(ospf6, o);
+
+ o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT;
+ return CMD_SUCCESS;
+}
+
DEFUN (ipv6_ospf6_hellointerval,
ipv6_ospf6_hellointerval_cmd,
"ipv6 ospf6 hello-interval (1-65535)",
@@ -2641,6 +2689,9 @@ void ospf6_interface_init(void)
/* reference bandwidth commands */
install_element(OSPF6_NODE, &auto_cost_reference_bandwidth_cmd);
install_element(OSPF6_NODE, &no_auto_cost_reference_bandwidth_cmd);
+ /* write-multiplier commands */
+ install_element(OSPF6_NODE, &ospf6_write_multiplier_cmd);
+ install_element(OSPF6_NODE, &no_ospf6_write_multiplier_cmd);
}
/* Clear the specified interface structure */
diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h
index 796d75e897..530efc3bd2 100644
--- a/ospf6d/ospf6_interface.h
+++ b/ospf6d/ospf6_interface.h
@@ -56,6 +56,9 @@ struct ospf6_interface {
/* I/F transmission delay */
uint32_t transdelay;
+ /* Packet send buffer. */
+ struct ospf6_fifo *obuf; /* Output queue */
+
/* Network Type */
uint8_t type;
bool type_cfg;
@@ -118,6 +121,9 @@ struct ospf6_interface {
struct ospf6_route_table *route_connected;
+ /* last hello sent */
+ struct timeval last_hello;
+
/* prefix-list name to filter connected prefix */
char *plist_name;
@@ -130,6 +136,8 @@ struct ospf6_interface {
char *profile;
} bfd_config;
+ int on_write_q;
+
/* Statistics Fields */
uint32_t hello_in;
uint32_t hello_out;
diff --git a/ospf6d/ospf6_message.c b/ospf6d/ospf6_message.c
index 1cf4585c95..5f23aab80a 100644
--- a/ospf6d/ospf6_message.c
+++ b/ospf6d/ospf6_message.c
@@ -50,6 +50,8 @@
#include <netinet/ip6.h>
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_MESSAGE, "OSPF6 message");
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PACKET, "OSPF6 packet");
+DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_FIFO, "OSPF6 FIFO queue");
unsigned char conf_debug_ospf6_message[6] = {0x03, 0, 0, 0, 0, 0};
static const struct message ospf6_message_type_str[] = {
@@ -252,6 +254,137 @@ void ospf6_lsack_print(struct ospf6_header *oh, int action)
}
}
+static struct ospf6_packet *ospf6_packet_new(size_t size)
+{
+ struct ospf6_packet *new;
+
+ new = XCALLOC(MTYPE_OSPF6_PACKET, sizeof(struct ospf6_packet));
+ new->s = stream_new(size);
+
+ return new;
+}
+
+static void ospf6_packet_free(struct ospf6_packet *op)
+{
+ if (op->s)
+ stream_free(op->s);
+
+ XFREE(MTYPE_OSPF6_PACKET, op);
+}
+
+struct ospf6_fifo *ospf6_fifo_new(void)
+{
+ struct ospf6_fifo *new;
+
+ new = XCALLOC(MTYPE_OSPF6_FIFO, sizeof(struct ospf6_fifo));
+ return new;
+}
+
+/* Add new packet to fifo. */
+static void ospf6_fifo_push(struct ospf6_fifo *fifo, struct ospf6_packet *op)
+{
+ if (fifo->tail)
+ fifo->tail->next = op;
+ else
+ fifo->head = op;
+
+ fifo->tail = op;
+
+ fifo->count++;
+}
+
+/* Add new packet to head of fifo. */
+static void ospf6_fifo_push_head(struct ospf6_fifo *fifo,
+ struct ospf6_packet *op)
+{
+ op->next = fifo->head;
+
+ if (fifo->tail == NULL)
+ fifo->tail = op;
+
+ fifo->head = op;
+
+ fifo->count++;
+}
+
+/* Delete first packet from fifo. */
+static struct ospf6_packet *ospf6_fifo_pop(struct ospf6_fifo *fifo)
+{
+ struct ospf6_packet *op;
+
+ op = fifo->head;
+
+ if (op) {
+ fifo->head = op->next;
+
+ if (fifo->head == NULL)
+ fifo->tail = NULL;
+
+ fifo->count--;
+ }
+
+ return op;
+}
+
+/* Return first fifo entry. */
+static struct ospf6_packet *ospf6_fifo_head(struct ospf6_fifo *fifo)
+{
+ return fifo->head;
+}
+
+/* Flush ospf packet fifo. */
+void ospf6_fifo_flush(struct ospf6_fifo *fifo)
+{
+ struct ospf6_packet *op;
+ struct ospf6_packet *next;
+
+ for (op = fifo->head; op; op = next) {
+ next = op->next;
+ ospf6_packet_free(op);
+ }
+ fifo->head = fifo->tail = NULL;
+ fifo->count = 0;
+}
+
+/* Free ospf packet fifo. */
+void ospf6_fifo_free(struct ospf6_fifo *fifo)
+{
+ ospf6_fifo_flush(fifo);
+
+ XFREE(MTYPE_OSPF6_FIFO, fifo);
+}
+
+static void ospf6_packet_add(struct ospf6_interface *oi,
+ struct ospf6_packet *op)
+{
+ /* Add packet to end of queue. */
+ ospf6_fifo_push(oi->obuf, op);
+
+ /* Debug of packet fifo*/
+ /* ospf_fifo_debug (oi->obuf); */
+}
+
+static void ospf6_packet_add_top(struct ospf6_interface *oi,
+ struct ospf6_packet *op)
+{
+ /* Add packet to head of queue. */
+ ospf6_fifo_push_head(oi->obuf, op);
+
+ /* Debug of packet fifo*/
+ /* ospf_fifo_debug (oi->obuf); */
+}
+
+static void ospf6_packet_delete(struct ospf6_interface *oi)
+{
+ struct ospf6_packet *op;
+
+ op = ospf6_fifo_pop(oi->obuf);
+
+ if (op)
+ ospf6_packet_free(op);
+}
+
+
static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
struct ospf6_interface *oi,
struct ospf6_header *oh)
@@ -263,7 +396,10 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
int neighborchange = 0;
int neighbor_ifindex_change = 0;
int backupseen = 0;
+ int64_t latency = 0;
+ struct timeval timestamp;
+ monotime(&timestamp);
hello = (struct ospf6_hello *)((caddr_t)oh
+ sizeof(struct ospf6_header));
@@ -305,6 +441,17 @@ static void ospf6_hello_recv(struct in6_addr *src, struct in6_addr *dst,
on->priority = hello->priority;
}
+ /* check latency against hello period */
+ if (on->hello_in)
+ latency = monotime_since(&on->last_hello, NULL)
+ - (oi->hello_interval * 1000000);
+ /* log if latency exceeds the hello period */
+ if (latency > (oi->hello_interval * 1000000))
+ zlog_warn("%s RX %pI4 high latency %" PRId64 "us.", __func__,
+ &on->router_id, latency);
+ on->last_hello = timestamp;
+ on->hello_in++;
+
/* Always override neighbor's source address */
memcpy(&on->linklocal_addr, src, sizeof(struct in6_addr));
@@ -1512,23 +1659,19 @@ void ospf6_message_terminate(void)
iobuflen = 0;
}
-int ospf6_receive(struct thread *thread)
+enum ospf6_read_return_enum {
+ OSPF6_READ_ERROR,
+ OSPF6_READ_CONTINUE,
+};
+
+static int ospf6_read_helper(int sockfd, struct ospf6 *ospf6)
{
- int sockfd;
- unsigned int len;
+ int len;
struct in6_addr src, dst;
ifindex_t ifindex;
struct iovec iovector[2];
struct ospf6_interface *oi;
struct ospf6_header *oh;
- struct ospf6 *ospf6;
-
- /* add next read thread */
- ospf6 = THREAD_ARG(thread);
- sockfd = THREAD_FD(thread);
-
- thread_add_read(master, ospf6_receive, ospf6, ospf6->fd,
- &ospf6->t_ospf6_receive);
/* initialize */
memset(&src, 0, sizeof(src));
@@ -1542,9 +1685,12 @@ int ospf6_receive(struct thread *thread)
/* receive message */
len = ospf6_recvmsg(&src, &dst, &ifindex, iovector, sockfd);
- if (len > iobuflen) {
+ if (len < 0)
+ return OSPF6_READ_ERROR;
+
+ if ((uint)len > iobuflen) {
flog_err(EC_LIB_DEVELOPMENT, "Excess message read");
- return 0;
+ return OSPF6_READ_ERROR;
}
oi = ospf6_interface_lookup_by_ifindex(ifindex, ospf6->vrf_id);
@@ -1553,19 +1699,19 @@ int ospf6_receive(struct thread *thread)
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
RECV_HDR))
zlog_debug("Message received on disabled interface");
- return 0;
+ return OSPF6_READ_CONTINUE;
}
if (CHECK_FLAG(oi->flag, OSPF6_INTERFACE_PASSIVE)) {
if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_UNKNOWN,
RECV_HDR))
zlog_debug("%s: Ignore message on passive interface %s",
__func__, oi->interface->name);
- return 0;
+ return OSPF6_READ_CONTINUE;
}
oh = (struct ospf6_header *)recvbuf;
if (ospf6_rxpacket_examin(oi, oh, len) != MSG_OK)
- return 0;
+ return OSPF6_READ_CONTINUE;
/* Being here means, that no sizing/alignment issues were detected in
the input packet. This renders the additional checks performed below
@@ -1626,91 +1772,243 @@ int ospf6_receive(struct thread *thread)
assert(0);
}
+ return OSPF6_READ_CONTINUE;
+}
+
+int ospf6_receive(struct thread *thread)
+{
+ int sockfd;
+ struct ospf6 *ospf6;
+ int count = 0;
+
+ /* add next read thread */
+ ospf6 = THREAD_ARG(thread);
+ sockfd = THREAD_FD(thread);
+
+ thread_add_read(master, ospf6_receive, ospf6, ospf6->fd,
+ &ospf6->t_ospf6_receive);
+
+ while (count < ospf6->write_oi_count) {
+ count++;
+ switch (ospf6_read_helper(sockfd, ospf6)) {
+ case OSPF6_READ_ERROR:
+ return 0;
+ case OSPF6_READ_CONTINUE:
+ break;
+ }
+ }
+
return 0;
}
-static void ospf6_send(struct in6_addr *src, struct in6_addr *dst,
- struct ospf6_interface *oi, struct ospf6_header *oh)
+static void ospf6_make_header(uint8_t type, struct ospf6_interface *oi,
+ struct stream *s)
{
- unsigned int len;
- char srcname[64];
- struct iovec iovector[2];
+ struct ospf6_header *oh;
- /* initialize */
- iovector[0].iov_base = (caddr_t)oh;
- iovector[0].iov_len = ntohs(oh->length);
- iovector[1].iov_base = NULL;
- iovector[1].iov_len = 0;
+ oh = (struct ospf6_header *)STREAM_DATA(s);
+
+ oh->version = (uint8_t)OSPFV3_VERSION;
+ oh->type = type;
- /* fill OSPF header */
- oh->version = OSPFV3_VERSION;
- /* message type must be set before */
- /* message length must be set before */
oh->router_id = oi->area->ospf6->router_id;
oh->area_id = oi->area->area_id;
- /* checksum is calculated by kernel */
oh->instance_id = oi->instance_id;
oh->reserved = 0;
+ stream_forward_endp(s, OSPF6_HEADER_SIZE);
+}
- /* Log */
- if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND_HDR)) {
- if (src)
- inet_ntop(AF_INET6, src, srcname, sizeof(srcname));
- else
- memset(srcname, 0, sizeof(srcname));
- zlog_debug("%s send on %s",
- lookup_msg(ospf6_message_type_str, oh->type, NULL),
- oi->interface->name);
- zlog_debug(" src: %s", srcname);
- zlog_debug(" dst: %pI6", dst);
+static void ospf6_fill_header(struct ospf6_interface *oi, struct stream *s,
+ uint16_t length)
+{
+ struct ospf6_header *oh;
+
+ oh = (struct ospf6_header *)STREAM_DATA(s);
+
+ oh->length = htons(length);
+}
+
+static void ospf6_fill_lsupdate_header(struct stream *s, uint32_t lsa_num)
+{
+ struct ospf6_header *oh;
+ struct ospf6_lsupdate *lsu;
+
+ oh = (struct ospf6_header *)STREAM_DATA(s);
+
+ lsu = (struct ospf6_lsupdate *)((caddr_t)oh
+ + sizeof(struct ospf6_header));
+ lsu->lsa_number = htonl(lsa_num);
+}
+
+static uint32_t ospf6_packet_max(struct ospf6_interface *oi)
+{
+ assert(oi->ifmtu > sizeof(struct ip6_hdr));
+ return oi->ifmtu - (sizeof(struct ip6_hdr));
+}
+
+static uint16_t ospf6_make_hello(struct ospf6_interface *oi, struct stream *s)
+{
+ struct listnode *node, *nnode;
+ struct ospf6_neighbor *on;
+ uint16_t length = OSPF6_HELLO_MIN_SIZE;
+
+ stream_putl(s, oi->interface->ifindex);
+ stream_putc(s, oi->priority);
+ stream_putc(s, oi->area->options[0]);
+ stream_putc(s, oi->area->options[1]);
+ stream_putc(s, oi->area->options[2]);
+ stream_putw(s, oi->hello_interval);
+ stream_putw(s, oi->dead_interval);
+ stream_put_ipv4(s, oi->drouter);
+ stream_put_ipv4(s, oi->bdrouter);
+ for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) {
+ if (on->state < OSPF6_NEIGHBOR_INIT)
+ continue;
+
+ if ((length + sizeof(uint32_t) + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(oi)) {
+ if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO,
+ SEND))
+ zlog_debug(
+ "sending Hello message: exceeds I/F MTU");
+ break;
+ }
+
+ stream_put_ipv4(s, on->router_id);
+ length += sizeof(uint32_t);
+ }
+
+ return length;
+}
+
+static int ospf6_write(struct thread *thread)
+{
+ struct ospf6 *ospf6 = THREAD_ARG(thread);
+ struct ospf6_interface *oi;
+ struct ospf6_interface *last_serviced_oi = NULL;
+ struct ospf6_header *oh;
+ struct ospf6_packet *op;
+ struct listnode *node;
+ char srcname[64], dstname[64];
+ struct iovec iovector[2];
+ int pkt_count = 0;
+ int len;
+ int64_t latency = 0;
+ struct timeval timestamp;
+
+ if (ospf6->fd < 0) {
+ zlog_warn("ospf6_write failed to send, fd %d", ospf6->fd);
+ return -1;
+ }
+
+ node = listhead(ospf6->oi_write_q);
+ assert(node);
+ oi = listgetdata(node);
+
+ while ((pkt_count < ospf6->write_oi_count) && oi
+ && (last_serviced_oi != oi)) {
+
+ op = ospf6_fifo_head(oi->obuf);
+ assert(op);
+ assert(op->length >= OSPF6_HEADER_SIZE);
+
+ iovector[0].iov_base = (caddr_t)stream_pnt(op->s);
+ iovector[0].iov_len = op->length;
+ iovector[1].iov_base = NULL;
+ iovector[1].iov_len = 0;
+
+ oh = (struct ospf6_header *)STREAM_DATA(op->s);
+
+ len = ospf6_sendmsg(oi->linklocal_addr, &op->dst,
+ oi->interface->ifindex, iovector,
+ ospf6->fd);
+ if (len != op->length)
+ flog_err(EC_LIB_DEVELOPMENT,
+ "Could not send entire message");
+
+ if (IS_OSPF6_DEBUG_MESSAGE(oh->type, SEND)) {
+ inet_ntop(AF_INET6, &op->dst, dstname, sizeof(dstname));
+ inet_ntop(AF_INET6, oi->linklocal_addr, srcname,
+ sizeof(srcname));
+ zlog_debug("%s send on %s",
+ lookup_msg(ospf6_message_type_str, oh->type,
+ NULL),
+ oi->interface->name);
+ zlog_debug(" src: %s", srcname);
+ zlog_debug(" dst: %s", dstname);
+ }
switch (oh->type) {
case OSPF6_MESSAGE_TYPE_HELLO:
- ospf6_hello_print(oh, OSPF6_ACTION_RECV);
+ monotime(&timestamp);
+ if (oi->hello_out)
+ latency = monotime_since(&oi->last_hello, NULL)
+ - (oi->hello_interval * 1000000);
+
+ /* log if latency exceeds the hello period */
+ if (latency > (oi->hello_interval * 1000000))
+ zlog_warn("%s hello TX high latency %" PRId64
+ "us.",
+ __func__, latency);
+ oi->last_hello = timestamp;
+ oi->hello_out++;
+ ospf6_hello_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_DBDESC:
- ospf6_dbdesc_print(oh, OSPF6_ACTION_RECV);
+ oi->db_desc_out++;
+ ospf6_dbdesc_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_LSREQ:
- ospf6_lsreq_print(oh, OSPF6_ACTION_RECV);
+ oi->ls_req_out++;
+ ospf6_lsreq_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_LSUPDATE:
- ospf6_lsupdate_print(oh, OSPF6_ACTION_RECV);
+ oi->ls_upd_out++;
+ ospf6_lsupdate_print(oh, OSPF6_ACTION_SEND);
break;
case OSPF6_MESSAGE_TYPE_LSACK:
- ospf6_lsack_print(oh, OSPF6_ACTION_RECV);
+ oi->ls_ack_out++;
+ ospf6_lsack_print(oh, OSPF6_ACTION_SEND);
break;
default:
zlog_debug("Unknown message");
assert(0);
break;
}
- }
+ /* Now delete packet from queue. */
+ ospf6_packet_delete(oi);
+
+ /* Move this interface to the tail of write_q to
+ serve everyone in a round robin fashion */
+ list_delete_node(ospf6->oi_write_q, node);
+ if (ospf6_fifo_head(oi->obuf) == NULL) {
+ oi->on_write_q = 0;
+ last_serviced_oi = NULL;
+ oi = NULL;
+ } else {
+ listnode_add(ospf6->oi_write_q, oi);
+ }
- /* send message */
- if (oi->area->ospf6->fd != -1) {
- len = ospf6_sendmsg(src, dst, oi->interface->ifindex, iovector,
- oi->area->ospf6->fd);
- if (len != ntohs(oh->length))
- flog_err(EC_LIB_DEVELOPMENT,
- "Could not send entire message");
+ /* Setup to service from the head of the queue again */
+ if (!list_isempty(ospf6->oi_write_q)) {
+ node = listhead(ospf6->oi_write_q);
+ oi = listgetdata(node);
+ }
}
-}
-static uint32_t ospf6_packet_max(struct ospf6_interface *oi)
-{
- assert(oi->ifmtu > sizeof(struct ip6_hdr));
- return oi->ifmtu - (sizeof(struct ip6_hdr));
+ /* If packets still remain in queue, call write thread. */
+ if (!list_isempty(ospf6->oi_write_q))
+ thread_add_write(master, ospf6_write, ospf6, ospf6->fd,
+ &ospf6->t_write);
+
+ return 0;
}
int ospf6_hello_send(struct thread *thread)
{
struct ospf6_interface *oi;
- struct ospf6_header *oh;
- struct ospf6_hello *hello;
- uint8_t *p;
- struct listnode *node, *nnode;
- struct ospf6_neighbor *on;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
oi->thread_send_hello = (struct thread *)NULL;
@@ -1722,88 +2020,44 @@ int ospf6_hello_send(struct thread *thread)
return 0;
}
- if (iobuflen == 0) {
- zlog_debug("Unable to send Hello on interface %s iobuflen is 0",
- oi->interface->name);
+ op = ospf6_packet_new(oi->ifmtu);
+
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_HELLO, oi, op->s);
+
+ /* Prepare OSPF Hello body */
+ length += ospf6_make_hello(oi, op->s);
+ if (length == OSPF6_HEADER_SIZE) {
+ /* Hello overshooting MTU */
+ ospf6_packet_free(op);
return 0;
}
- /* set next thread */
- thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval,
- &oi->thread_send_hello);
+ /* Fill OSPF header. */
+ ospf6_fill_header(oi, op->s, length);
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- hello = (struct ospf6_hello *)((caddr_t)oh
- + sizeof(struct ospf6_header));
+ /* Set packet length. */
+ op->length = length;
- hello->interface_id = htonl(oi->interface->ifindex);
- hello->priority = oi->priority;
- hello->options[0] = oi->area->options[0];
- hello->options[1] = oi->area->options[1];
- hello->options[2] = oi->area->options[2];
- hello->hello_interval = htons(oi->hello_interval);
- hello->dead_interval = htons(oi->dead_interval);
- hello->drouter = oi->drouter;
- hello->bdrouter = oi->bdrouter;
+ op->dst = allspfrouters6;
- p = (uint8_t *)((caddr_t)hello + sizeof(struct ospf6_hello));
+ /* Add packet to the top of the interface output queue, so that they
+ * can't get delayed by things like long queues of LS Update packets
+ */
+ ospf6_packet_add_top(oi, op);
- for (ALL_LIST_ELEMENTS(oi->neighbor_list, node, nnode, on)) {
- if (on->state < OSPF6_NEIGHBOR_INIT)
- continue;
-
- if (p - sendbuf + sizeof(uint32_t) > ospf6_packet_max(oi)) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_HELLO,
- SEND_HDR))
- zlog_debug(
- "sending Hello message: exceeds I/F MTU");
- break;
- }
-
- memcpy(p, &on->router_id, sizeof(uint32_t));
- p += sizeof(uint32_t);
- }
-
- oh->type = OSPF6_MESSAGE_TYPE_HELLO;
- oh->length = htons(p - sendbuf);
+ /* set next thread */
+ thread_add_timer(master, ospf6_hello_send, oi, oi->hello_interval,
+ &oi->thread_send_hello);
- oi->hello_out++;
+ OSPF6_MESSAGE_WRITE_ON(oi);
- ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
return 0;
}
-int ospf6_dbdesc_send(struct thread *thread)
+static uint16_t ospf6_make_dbdesc(struct ospf6_neighbor *on, struct stream *s)
{
- struct ospf6_neighbor *on;
- struct ospf6_header *oh;
- struct ospf6_dbdesc *dbdesc;
- uint8_t *p;
+ uint16_t length = OSPF6_DB_DESC_MIN_SIZE;
struct ospf6_lsa *lsa, *lsanext;
- struct in6_addr *dst;
-
- on = (struct ospf6_neighbor *)THREAD_ARG(thread);
- on->thread_send_dbdesc = (struct thread *)NULL;
-
- if (on->state < OSPF6_NEIGHBOR_EXSTART) {
- if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND_HDR))
- zlog_debug(
- "Quit to send DbDesc to neighbor %s state %s",
- on->name, ospf6_neighbor_state_str[on->state]);
- return 0;
- }
-
- /* set next thread if master */
- if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT))
- thread_add_timer(master, ospf6_dbdesc_send, on,
- on->ospf6_if->rxmt_interval,
- &on->thread_send_dbdesc);
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- dbdesc = (struct ospf6_dbdesc *)((caddr_t)oh
- + sizeof(struct ospf6_header));
/* if this is initial one, initialize sequence number for DbDesc */
if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)
@@ -1811,45 +2065,79 @@ int ospf6_dbdesc_send(struct thread *thread)
on->dbdesc_seqnum = monotime(NULL);
}
- dbdesc->options[0] = on->ospf6_if->area->options[0];
- dbdesc->options[1] = on->ospf6_if->area->options[1];
- dbdesc->options[2] = on->ospf6_if->area->options[2];
- dbdesc->ifmtu = htons(on->ospf6_if->ifmtu);
- dbdesc->bits = on->dbdesc_bits;
- dbdesc->seqnum = htonl(on->dbdesc_seqnum);
+ /* reserved */
+ stream_putc(s, 0); /* reserved 1 */
+ stream_putc(s, on->ospf6_if->area->options[0]);
+ stream_putc(s, on->ospf6_if->area->options[1]);
+ stream_putc(s, on->ospf6_if->area->options[2]);
+ stream_putw(s, on->ospf6_if->ifmtu);
+ stream_putc(s, 0); /* reserved 2 */
+ stream_putc(s, on->dbdesc_bits);
+ stream_putl(s, on->dbdesc_seqnum);
/* if this is not initial one, set LSA headers in dbdesc */
- p = (uint8_t *)((caddr_t)dbdesc + sizeof(struct ospf6_dbdesc));
if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_IBIT)) {
for (ALL_LSDB(on->dbdesc_list, lsa, lsanext)) {
ospf6_lsa_age_update_to_send(lsa,
on->ospf6_if->transdelay);
/* MTU check */
- if (p - sendbuf + sizeof(struct ospf6_lsa_header)
+ if ((length + sizeof(struct ospf6_lsa_header)
+ + OSPF6_HEADER_SIZE)
> ospf6_packet_max(on->ospf6_if)) {
ospf6_lsa_unlock(lsa);
if (lsanext)
ospf6_lsa_unlock(lsanext);
break;
}
- memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
- p += sizeof(struct ospf6_lsa_header);
+ stream_put(s, lsa->header,
+ sizeof(struct ospf6_lsa_header));
+ length += sizeof(struct ospf6_lsa_header);
}
}
+ return length;
+}
+
+int ospf6_dbdesc_send(struct thread *thread)
+{
+ struct ospf6_neighbor *on;
+ uint16_t length = OSPF6_HEADER_SIZE;
+ struct ospf6_packet *op;
- oh->type = OSPF6_MESSAGE_TYPE_DBDESC;
- oh->length = htons(p - sendbuf);
+ on = (struct ospf6_neighbor *)THREAD_ARG(thread);
+ on->thread_send_dbdesc = (struct thread *)NULL;
+ if (on->state < OSPF6_NEIGHBOR_EXSTART) {
+ if (IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_DBDESC, SEND))
+ zlog_debug(
+ "Quit to send DbDesc to neighbor %s state %s",
+ on->name, ospf6_neighbor_state_str[on->state]);
+ return 0;
+ }
+
+ /* set next thread if master */
+ if (CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT))
+ thread_add_timer(master, ospf6_dbdesc_send, on,
+ on->ospf6_if->rxmt_interval,
+ &on->thread_send_dbdesc);
+
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_DBDESC, on->ospf6_if, op->s);
+
+ length += ospf6_make_dbdesc(on, op->s);
+ ospf6_fill_header(on->ospf6_if, op->s, length);
+
+ /* Set packet length. */
+ op->length = length;
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
- dst = &allspfrouters6;
+ op->dst = allspfrouters6;
else
- dst = &on->linklocal_addr;
+ op->dst = on->linklocal_addr;
- on->ospf6_if->db_desc_out++;
+ ospf6_packet_add(on->ospf6_if, op);
- ospf6_send(on->ospf6_if->linklocal_addr, dst, on->ospf6_if, oh);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
return 0;
}
@@ -1902,13 +2190,84 @@ int ospf6_dbdesc_send_newone(struct thread *thread)
return 0;
}
+static uint16_t ospf6_make_lsreq(struct ospf6_neighbor *on, struct stream *s)
+{
+ uint16_t length = 0;
+ struct ospf6_lsa *lsa, *lsanext, *last_req = NULL;
+
+ for (ALL_LSDB(on->request_list, lsa, lsanext)) {
+ if ((length + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(on->ospf6_if)) {
+ ospf6_lsa_unlock(lsa);
+ if (lsanext)
+ ospf6_lsa_unlock(lsanext);
+ break;
+ }
+ stream_putw(s, 0); /* reserved */
+ stream_putw(s, ntohs(lsa->header->type));
+ stream_putl(s, ntohl(lsa->header->id));
+ stream_putl(s, ntohl(lsa->header->adv_router));
+ length += sizeof(struct ospf6_lsreq_entry);
+ last_req = lsa;
+ }
+
+ if (last_req != NULL) {
+ if (on->last_ls_req != NULL)
+ on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req);
+
+ ospf6_lsa_lock(last_req);
+ on->last_ls_req = last_req;
+ }
+
+ return length;
+}
+
+static uint16_t ospf6_make_lsack_neighbor(struct ospf6_neighbor *on,
+ struct ospf6_packet **op)
+{
+ uint16_t length = 0;
+ struct ospf6_lsa *lsa, *lsanext;
+ int lsa_cnt = 0;
+
+ for (ALL_LSDB(on->lsack_list, lsa, lsanext)) {
+ if ((length + sizeof(struct ospf6_lsa_header)
+ + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(on->ospf6_if)) {
+ /* if we run out of packet size/space here,
+ better to try again soon. */
+ if (lsa_cnt) {
+ ospf6_fill_header(on->ospf6_if, (*op)->s,
+ length + OSPF6_HEADER_SIZE);
+
+ (*op)->length = length + OSPF6_HEADER_SIZE;
+ (*op)->dst = on->linklocal_addr;
+ ospf6_packet_add(on->ospf6_if, *op);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
+ /* new packet */
+ *op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK,
+ on->ospf6_if, (*op)->s);
+ length = 0;
+ lsa_cnt = 0;
+ }
+ }
+ ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
+ stream_put((*op)->s, lsa->header,
+ sizeof(struct ospf6_lsa_header));
+ length += sizeof(struct ospf6_lsa_header);
+
+ assert(lsa->lock == 2);
+ ospf6_lsdb_remove(lsa, on->lsack_list);
+ lsa_cnt++;
+ }
+ return length;
+}
+
int ospf6_lsreq_send(struct thread *thread)
{
struct ospf6_neighbor *on;
- struct ospf6_header *oh;
- struct ospf6_lsreq_entry *e;
- uint8_t *p;
- struct ospf6_lsa *lsa, *lsanext, *last_req;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
on->thread_send_lsreq = (struct thread *)NULL;
@@ -1929,49 +2288,31 @@ int ospf6_lsreq_send(struct thread *thread)
return 0;
}
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- last_req = NULL;
-
- /* set Request entries in lsreq */
- p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header));
- for (ALL_LSDB(on->request_list, lsa, lsanext)) {
- /* MTU check */
- if (p - sendbuf + sizeof(struct ospf6_lsreq_entry)
- > ospf6_packet_max(on->ospf6_if)) {
- ospf6_lsa_unlock(lsa);
- if (lsanext)
- ospf6_lsa_unlock(lsanext);
- break;
- }
-
- e = (struct ospf6_lsreq_entry *)p;
- e->type = lsa->header->type;
- e->id = lsa->header->id;
- e->adv_router = lsa->header->adv_router;
- p += sizeof(struct ospf6_lsreq_entry);
- last_req = lsa;
- }
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSREQ, on->ospf6_if, op->s);
- if (last_req != NULL) {
- if (on->last_ls_req != NULL)
- on->last_ls_req = ospf6_lsa_unlock(on->last_ls_req);
+ length += ospf6_make_lsreq(on, op->s);
- ospf6_lsa_lock(last_req);
- on->last_ls_req = last_req;
+ if (length == OSPF6_HEADER_SIZE) {
+ /* Hello overshooting MTU */
+ ospf6_packet_free(op);
+ return 0;
}
- oh->type = OSPF6_MESSAGE_TYPE_LSREQ;
- oh->length = htons(p - sendbuf);
+ /* Fill OSPF header. */
+ ospf6_fill_header(on->ospf6_if, op->s, length);
- on->ospf6_if->ls_req_out++;
+ /* Set packet length */
+ op->length = length;
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
- ospf6_send(on->ospf6_if->linklocal_addr, &allspfrouters6,
- on->ospf6_if, oh);
+ op->dst = allspfrouters6;
else
- ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr,
- on->ospf6_if, oh);
+ op->dst = on->linklocal_addr;
+
+ ospf6_packet_add(on->ospf6_if, op);
+
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
/* set next thread */
if (on->request_list->count != 0) {
@@ -1986,43 +2327,117 @@ int ospf6_lsreq_send(struct thread *thread)
static void ospf6_send_lsupdate(struct ospf6_neighbor *on,
struct ospf6_interface *oi,
- struct ospf6_header *oh)
+ struct ospf6_packet *op)
{
if (on) {
- on->ospf6_if->ls_upd_out++;
if ((on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
|| (on->ospf6_if->state == OSPF6_INTERFACE_DR)
- || (on->ospf6_if->state == OSPF6_INTERFACE_BDR)) {
- ospf6_send(on->ospf6_if->linklocal_addr,
- &allspfrouters6, on->ospf6_if, oh);
- } else {
- ospf6_send(on->ospf6_if->linklocal_addr,
- &on->linklocal_addr, on->ospf6_if, oh);
- }
+ || (on->ospf6_if->state == OSPF6_INTERFACE_BDR))
+ op->dst = allspfrouters6;
+ else
+ op->dst = on->linklocal_addr;
+ oi = on->ospf6_if;
} else if (oi) {
-
- oi->ls_upd_out++;
-
if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
|| (oi->state == OSPF6_INTERFACE_DR)
- || (oi->state == OSPF6_INTERFACE_BDR)) {
- ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
- } else {
- ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh);
+ || (oi->state == OSPF6_INTERFACE_BDR))
+ op->dst = allspfrouters6;
+ else
+ op->dst = alldrouters6;
+ }
+ if (oi) {
+ ospf6_packet_add(oi, op);
+ OSPF6_MESSAGE_WRITE_ON(oi);
+ }
+}
+
+static uint16_t ospf6_make_lsupdate_list(struct ospf6_neighbor *on,
+ struct ospf6_packet **op, int *lsa_cnt)
+{
+ uint16_t length = OSPF6_LS_UPD_MIN_SIZE;
+ struct ospf6_lsa *lsa, *lsanext;
+
+ /* skip over fixed header */
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+
+ for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) {
+ if ((length + (unsigned int)OSPF6_LSA_SIZE(lsa->header)
+ + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(on->ospf6_if)) {
+ ospf6_fill_header(on->ospf6_if, (*op)->s,
+ length + OSPF6_HEADER_SIZE);
+ (*op)->length = length + OSPF6_HEADER_SIZE;
+ ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt);
+ ospf6_send_lsupdate(on, NULL, *op);
+
+ /* refresh packet */
+ *op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ length = OSPF6_LS_UPD_MIN_SIZE;
+ *lsa_cnt = 0;
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE,
+ on->ospf6_if, (*op)->s);
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+ }
+ ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
+ stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header));
+ (*lsa_cnt)++;
+ length += OSPF6_LSA_SIZE(lsa->header);
+ assert(lsa->lock == 2);
+ ospf6_lsdb_remove(lsa, on->lsupdate_list);
+ }
+ return length;
+}
+
+static uint16_t ospf6_make_ls_retrans_list(struct ospf6_neighbor *on,
+ struct ospf6_packet **op,
+ int *lsa_cnt)
+{
+ uint16_t length = OSPF6_LS_UPD_MIN_SIZE;
+ struct ospf6_lsa *lsa, *lsanext;
+
+ /* skip over fixed header */
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+
+ for (ALL_LSDB(on->retrans_list, lsa, lsanext)) {
+ if ((length + (unsigned int)OSPF6_LSA_SIZE(lsa->header)
+ + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(on->ospf6_if)) {
+ ospf6_fill_header(on->ospf6_if, (*op)->s,
+ length + OSPF6_HEADER_SIZE);
+ (*op)->length = length + OSPF6_HEADER_SIZE;
+ ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt);
+ if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
+ (*op)->dst = allspfrouters6;
+ else
+ (*op)->dst = on->linklocal_addr;
+
+ ospf6_packet_add(on->ospf6_if, *op);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
+
+ /* refresh packet */
+ *op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ length = OSPF6_LS_UPD_MIN_SIZE;
+ *lsa_cnt = 0;
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE,
+ on->ospf6_if, (*op)->s);
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
}
+ ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
+ stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header));
+ (*lsa_cnt)++;
+ length += OSPF6_LSA_SIZE(lsa->header);
}
+ return length;
}
int ospf6_lsupdate_send_neighbor(struct thread *thread)
{
struct ospf6_neighbor *on;
- struct ospf6_header *oh;
- struct ospf6_lsupdate *lsupdate;
- uint8_t *p;
- int lsa_cnt;
- struct ospf6_lsa *lsa, *lsanext;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
+ int lsa_cnt = 0;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
on->thread_send_lsupdate = (struct thread *)NULL;
@@ -2038,119 +2453,41 @@ int ospf6_lsupdate_send_neighbor(struct thread *thread)
return 0;
}
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
- + sizeof(struct ospf6_header));
-
- p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
- lsa_cnt = 0;
-
- /* lsupdate_list lists those LSA which doesn't need to be
- retransmitted. remove those from the list */
- for (ALL_LSDB(on->lsupdate_list, lsa, lsanext)) {
- /* MTU check */
- if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header))
- > ospf6_packet_max(on->ospf6_if)) {
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
- ospf6_send_lsupdate(on, NULL, oh);
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate
- *)((caddr_t)oh
- + sizeof(struct
- ospf6_header));
-
- p = (uint8_t *)((caddr_t)lsupdate
- + sizeof(struct
- ospf6_lsupdate));
- lsa_cnt = 0;
- }
- }
-
- ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
- memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
- p += OSPF6_LSA_SIZE(lsa->header);
- lsa_cnt++;
-
- assert(lsa->lock == 2);
- ospf6_lsdb_remove(lsa, on->lsupdate_list);
- }
-
+ /* first do lsupdate_list */
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s);
+ length += ospf6_make_lsupdate_list(on, &op, &lsa_cnt);
if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
- ospf6_send_lsupdate(on, NULL, oh);
- }
-
- /* The addresses used for retransmissions are different from those sent
- the
- first time and so we need to separate them here.
- */
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
- + sizeof(struct ospf6_header));
- p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
- lsa_cnt = 0;
-
- for (ALL_LSDB(on->retrans_list, lsa, lsanext)) {
- /* MTU check */
- if ((p - sendbuf + (unsigned int)OSPF6_LSA_SIZE(lsa->header))
- > ospf6_packet_max(on->ospf6_if)) {
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
- if (on->ospf6_if->state
- == OSPF6_INTERFACE_POINTTOPOINT) {
- ospf6_send(on->ospf6_if->linklocal_addr,
- &allspfrouters6,
- on->ospf6_if, oh);
- } else {
- ospf6_send(on->ospf6_if->linklocal_addr,
- &on->linklocal_addr,
- on->ospf6_if, oh);
- }
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate
- *)((caddr_t)oh
- + sizeof(struct
- ospf6_header));
- p = (uint8_t *)((caddr_t)lsupdate
- + sizeof(struct
- ospf6_lsupdate));
- lsa_cnt = 0;
- }
- }
-
- ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
- memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
- p += OSPF6_LSA_SIZE(lsa->header);
- lsa_cnt++;
- }
-
+ /* Fill OSPF header. */
+ ospf6_fill_header(on->ospf6_if, op->s, length);
+ ospf6_fill_lsupdate_header(op->s, lsa_cnt);
+ op->length = length;
+ ospf6_send_lsupdate(on, NULL, op);
+
+ /* prepare new packet */
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ length = OSPF6_HEADER_SIZE;
+ lsa_cnt = 0;
+ } else {
+ stream_reset(op->s);
+ length = OSPF6_HEADER_SIZE;
+ }
+
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s);
+ /* now do retransmit list */
+ length += ospf6_make_ls_retrans_list(on, &op, &lsa_cnt);
if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
+ ospf6_fill_header(on->ospf6_if, op->s, length);
+ ospf6_fill_lsupdate_header(op->s, lsa_cnt);
+ op->length = length;
if (on->ospf6_if->state == OSPF6_INTERFACE_POINTTOPOINT)
- ospf6_send(on->ospf6_if->linklocal_addr,
- &allspfrouters6, on->ospf6_if, oh);
+ op->dst = allspfrouters6;
else
- ospf6_send(on->ospf6_if->linklocal_addr,
- &on->linklocal_addr, on->ospf6_if, oh);
- }
+ op->dst = on->linklocal_addr;
+ ospf6_packet_add(on->ospf6_if, op);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
+ } else
+ ospf6_packet_free(op);
if (on->lsupdate_list->count != 0) {
on->thread_send_lsupdate = NULL;
@@ -2168,44 +2505,78 @@ int ospf6_lsupdate_send_neighbor(struct thread *thread)
int ospf6_lsupdate_send_neighbor_now(struct ospf6_neighbor *on,
struct ospf6_lsa *lsa)
{
- struct ospf6_header *oh;
- struct ospf6_lsupdate *lsupdate;
- uint8_t *p;
- int lsa_cnt = 0;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
- + sizeof(struct ospf6_header));
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, on->ospf6_if, op->s);
- p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
+ /* skip over fixed header */
+ stream_forward_endp(op->s, OSPF6_LS_UPD_MIN_SIZE);
ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
- memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
- p += OSPF6_LSA_SIZE(lsa->header);
- lsa_cnt++;
-
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
+ stream_put(op->s, lsa->header, OSPF6_LSA_SIZE(lsa->header));
+ length = OSPF6_HEADER_SIZE + OSPF6_LS_UPD_MIN_SIZE
+ + OSPF6_LSA_SIZE(lsa->header);
+ ospf6_fill_header(on->ospf6_if, op->s, length);
+ ospf6_fill_lsupdate_header(op->s, 1);
+ op->length = length;
if (IS_OSPF6_DEBUG_FLOODING
|| IS_OSPF6_DEBUG_MESSAGE(OSPF6_MESSAGE_TYPE_LSUPDATE, SEND_HDR))
zlog_debug("%s: Send lsupdate with lsa %s (age %u)", __func__,
lsa->name, ntohs(lsa->header->age));
- ospf6_send_lsupdate(on, NULL, oh);
+ ospf6_send_lsupdate(on, NULL, op);
return 0;
}
+static uint16_t ospf6_make_lsupdate_interface(struct ospf6_interface *oi,
+ struct ospf6_packet **op,
+ int *lsa_cnt)
+{
+ uint16_t length = OSPF6_LS_UPD_MIN_SIZE;
+ struct ospf6_lsa *lsa, *lsanext;
+
+ /* skip over fixed header */
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+
+ for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) {
+ if (length + (unsigned int)OSPF6_LSA_SIZE(lsa->header)
+ + OSPF6_HEADER_SIZE
+ > ospf6_packet_max(oi)) {
+ ospf6_fill_header(oi, (*op)->s,
+ length + OSPF6_HEADER_SIZE);
+ (*op)->length = length + OSPF6_HEADER_SIZE;
+ ospf6_fill_lsupdate_header((*op)->s, *lsa_cnt);
+ ospf6_send_lsupdate(NULL, oi, *op);
+
+ /* refresh packet */
+ *op = ospf6_packet_new(oi->ifmtu);
+ length = OSPF6_LS_UPD_MIN_SIZE;
+ *lsa_cnt = 0;
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi,
+ (*op)->s);
+ stream_forward_endp((*op)->s, OSPF6_LS_UPD_MIN_SIZE);
+ }
+
+ ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
+ stream_put((*op)->s, lsa->header, OSPF6_LSA_SIZE(lsa->header));
+ (*lsa_cnt)++;
+ length += OSPF6_LSA_SIZE(lsa->header);
+
+ assert(lsa->lock == 2);
+ ospf6_lsdb_remove(lsa, oi->lsupdate_list);
+ }
+ return length;
+}
+
int ospf6_lsupdate_send_interface(struct thread *thread)
{
struct ospf6_interface *oi;
- struct ospf6_header *oh;
- struct ospf6_lsupdate *lsupdate;
- uint8_t *p;
- int lsa_cnt;
- struct ospf6_lsa *lsa, *lsanext;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
+ int lsa_cnt = 0;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
oi->thread_send_lsupdate = (struct thread *)NULL;
@@ -2224,59 +2595,17 @@ int ospf6_lsupdate_send_interface(struct thread *thread)
if (oi->lsupdate_list->count == 0)
return 0;
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate *)((caddr_t)oh
- + sizeof(struct ospf6_header));
-
- p = (uint8_t *)((caddr_t)lsupdate + sizeof(struct ospf6_lsupdate));
- lsa_cnt = 0;
-
- for (ALL_LSDB(oi->lsupdate_list, lsa, lsanext)) {
- /* MTU check */
- if ((p - sendbuf + ((unsigned int)OSPF6_LSA_SIZE(lsa->header)))
- > ospf6_packet_max(oi)) {
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
- ospf6_send_lsupdate(NULL, oi, oh);
- if (IS_OSPF6_DEBUG_MESSAGE(
- OSPF6_MESSAGE_TYPE_LSUPDATE, SEND))
- zlog_debug("%s: LSUpdate length %d",
- __func__, ntohs(oh->length));
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- lsupdate = (struct ospf6_lsupdate
- *)((caddr_t)oh
- + sizeof(struct
- ospf6_header));
-
- p = (uint8_t *)((caddr_t)lsupdate
- + sizeof(struct
- ospf6_lsupdate));
- lsa_cnt = 0;
- }
- }
-
- ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
- memcpy(p, lsa->header, OSPF6_LSA_SIZE(lsa->header));
- p += OSPF6_LSA_SIZE(lsa->header);
- lsa_cnt++;
-
- assert(lsa->lock == 2);
- ospf6_lsdb_remove(lsa, oi->lsupdate_list);
- }
-
+ op = ospf6_packet_new(oi->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSUPDATE, oi, op->s);
+ length += ospf6_make_lsupdate_interface(oi, &op, &lsa_cnt);
if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSUPDATE;
- oh->length = htons(p - sendbuf);
- lsupdate->lsa_number = htonl(lsa_cnt);
-
- ospf6_send_lsupdate(NULL, oi, oh);
- }
+ /* Fill OSPF header. */
+ ospf6_fill_header(oi, op->s, length);
+ ospf6_fill_lsupdate_header(op->s, lsa_cnt);
+ op->length = length;
+ ospf6_send_lsupdate(NULL, oi, op);
+ } else
+ ospf6_packet_free(op);
if (oi->lsupdate_list->count > 0) {
oi->thread_send_lsupdate = NULL;
@@ -2290,10 +2619,8 @@ int ospf6_lsupdate_send_interface(struct thread *thread)
int ospf6_lsack_send_neighbor(struct thread *thread)
{
struct ospf6_neighbor *on;
- struct ospf6_header *oh;
- uint8_t *p;
- struct ospf6_lsa *lsa, *lsanext;
- int lsa_cnt = 0;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
on = (struct ospf6_neighbor *)THREAD_ARG(thread);
on->thread_send_lsack = (struct thread *)NULL;
@@ -2310,53 +2637,24 @@ int ospf6_lsack_send_neighbor(struct thread *thread)
if (on->lsack_list->count == 0)
return 0;
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
-
- p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header));
-
- for (ALL_LSDB(on->lsack_list, lsa, lsanext)) {
- /* MTU check */
- if (p - sendbuf + sizeof(struct ospf6_lsa_header)
- > ospf6_packet_max(on->ospf6_if)) {
- /* if we run out of packet size/space here,
- better to try again soon. */
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSACK;
- oh->length = htons(p - sendbuf);
-
- on->ospf6_if->ls_ack_out++;
+ op = ospf6_packet_new(on->ospf6_if->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, on->ospf6_if, op->s);
- ospf6_send(on->ospf6_if->linklocal_addr,
- &on->linklocal_addr, on->ospf6_if,
- oh);
-
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
- p = (uint8_t *)((caddr_t)oh
- + sizeof(struct ospf6_header));
- lsa_cnt = 0;
- }
- }
+ length += ospf6_make_lsack_neighbor(on, &op);
- ospf6_lsa_age_update_to_send(lsa, on->ospf6_if->transdelay);
- memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
- p += sizeof(struct ospf6_lsa_header);
-
- assert(lsa->lock == 2);
- ospf6_lsdb_remove(lsa, on->lsack_list);
- lsa_cnt++;
+ if (length == OSPF6_HEADER_SIZE) {
+ ospf6_packet_free(op);
+ return 0;
}
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSACK;
- oh->length = htons(p - sendbuf);
-
- on->ospf6_if->ls_ack_out++;
+ /* Fill OSPF header. */
+ ospf6_fill_header(on->ospf6_if, op->s, length);
- ospf6_send(on->ospf6_if->linklocal_addr, &on->linklocal_addr,
- on->ospf6_if, oh);
- }
+ /* Set packet length, dst and queue to FIFO. */
+ op->length = length;
+ op->dst = on->linklocal_addr;
+ ospf6_packet_add(on->ospf6_if, op);
+ OSPF6_MESSAGE_WRITE_ON(on->ospf6_if);
if (on->lsack_list->count > 0)
thread_add_event(master, ospf6_lsack_send_neighbor, on, 0,
@@ -2365,13 +2663,42 @@ int ospf6_lsack_send_neighbor(struct thread *thread)
return 0;
}
+static uint16_t ospf6_make_lsack_interface(struct ospf6_interface *oi,
+ struct ospf6_packet *op)
+{
+ uint16_t length = 0;
+ struct ospf6_lsa *lsa, *lsanext;
+
+ for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) {
+ if ((length + sizeof(struct ospf6_lsa_header)
+ + OSPF6_HEADER_SIZE)
+ > ospf6_packet_max(oi)) {
+ /* if we run out of packet size/space here,
+ better to try again soon. */
+ THREAD_OFF(oi->thread_send_lsack);
+ thread_add_event(master, ospf6_lsack_send_interface, oi,
+ 0, &oi->thread_send_lsack);
+
+ ospf6_lsa_unlock(lsa);
+ if (lsanext)
+ ospf6_lsa_unlock(lsanext);
+ break;
+ }
+ ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
+ stream_put(op->s, lsa->header, sizeof(struct ospf6_lsa_header));
+ length += sizeof(struct ospf6_lsa_header);
+
+ assert(lsa->lock == 2);
+ ospf6_lsdb_remove(lsa, oi->lsack_list);
+ }
+ return length;
+}
+
int ospf6_lsack_send_interface(struct thread *thread)
{
struct ospf6_interface *oi;
- struct ospf6_header *oh;
- uint8_t *p;
- struct ospf6_lsa *lsa, *lsanext;
- int lsa_cnt = 0;
+ struct ospf6_packet *op;
+ uint16_t length = OSPF6_HEADER_SIZE;
oi = (struct ospf6_interface *)THREAD_ARG(thread);
oi->thread_send_lsack = (struct thread *)NULL;
@@ -2389,47 +2716,29 @@ int ospf6_lsack_send_interface(struct thread *thread)
if (oi->lsack_list->count == 0)
return 0;
- memset(sendbuf, 0, iobuflen);
- oh = (struct ospf6_header *)sendbuf;
-
- p = (uint8_t *)((caddr_t)oh + sizeof(struct ospf6_header));
+ op = ospf6_packet_new(oi->ifmtu);
+ ospf6_make_header(OSPF6_MESSAGE_TYPE_LSACK, oi, op->s);
- for (ALL_LSDB(oi->lsack_list, lsa, lsanext)) {
- /* MTU check */
- if (p - sendbuf + sizeof(struct ospf6_lsa_header)
- > ospf6_packet_max(oi)) {
- /* if we run out of packet size/space here,
- better to try again soon. */
- THREAD_OFF(oi->thread_send_lsack);
- thread_add_event(master, ospf6_lsack_send_interface, oi,
- 0, &oi->thread_send_lsack);
+ length += ospf6_make_lsack_interface(oi, op);
- ospf6_lsa_unlock(lsa);
- if (lsanext)
- ospf6_lsa_unlock(lsanext);
- break;
- }
-
- ospf6_lsa_age_update_to_send(lsa, oi->transdelay);
- memcpy(p, lsa->header, sizeof(struct ospf6_lsa_header));
- p += sizeof(struct ospf6_lsa_header);
-
- assert(lsa->lock == 2);
- ospf6_lsdb_remove(lsa, oi->lsack_list);
- lsa_cnt++;
+ if (length == OSPF6_HEADER_SIZE) {
+ ospf6_packet_free(op);
+ return 0;
}
+ /* Fill OSPF header. */
+ ospf6_fill_header(oi, op->s, length);
- if (lsa_cnt) {
- oh->type = OSPF6_MESSAGE_TYPE_LSACK;
- oh->length = htons(p - sendbuf);
+ /* Set packet length, dst and queue to FIFO. */
+ op->length = length;
+ if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
+ || (oi->state == OSPF6_INTERFACE_DR)
+ || (oi->state == OSPF6_INTERFACE_BDR))
+ op->dst = allspfrouters6;
+ else
+ op->dst = alldrouters6;
- if ((oi->state == OSPF6_INTERFACE_POINTTOPOINT)
- || (oi->state == OSPF6_INTERFACE_DR)
- || (oi->state == OSPF6_INTERFACE_BDR))
- ospf6_send(oi->linklocal_addr, &allspfrouters6, oi, oh);
- else
- ospf6_send(oi->linklocal_addr, &alldrouters6, oi, oh);
- }
+ ospf6_packet_add(oi, op);
+ OSPF6_MESSAGE_WRITE_ON(oi);
if (oi->lsack_list->count > 0)
thread_add_event(master, ospf6_lsack_send_interface, oi, 0,
@@ -2438,7 +2747,6 @@ int ospf6_lsack_send_interface(struct thread *thread)
return 0;
}
-
/* Commands */
DEFUN(debug_ospf6_message, debug_ospf6_message_cmd,
"debug ospf6 message <unknown|hello|dbdesc|lsreq|lsupdate|lsack|all> [<send|recv|send-hdr|recv-hdr>]",
diff --git a/ospf6d/ospf6_message.h b/ospf6d/ospf6_message.h
index 7c108bd452..0cd10ef825 100644
--- a/ospf6d/ospf6_message.h
+++ b/ospf6d/ospf6_message.h
@@ -63,6 +63,27 @@ extern unsigned char conf_debug_ospf6_message[];
#define OSPF6_MESSAGE_TYPE_LSACK 0x5 /* Flooding acknowledgment */
#define OSPF6_MESSAGE_TYPE_ALL 0x6 /* For debug option */
+struct ospf6_packet {
+ struct ospf6_packet *next;
+
+ /* Pointer to data stream. */
+ struct stream *s;
+
+ /* IP destination address. */
+ struct in6_addr dst;
+
+ /* OSPF6 packet length. */
+ uint16_t length;
+};
+
+/* OSPF packet queue structure. */
+struct ospf6_fifo {
+ unsigned long count;
+
+ struct ospf6_packet *head;
+ struct ospf6_packet *tail;
+};
+
/* OSPFv3 packet header */
#define OSPF6_HEADER_SIZE 16U
struct ospf6_header {
@@ -136,6 +157,10 @@ extern void ospf6_lsreq_print(struct ospf6_header *, int action);
extern void ospf6_lsupdate_print(struct ospf6_header *, int action);
extern void ospf6_lsack_print(struct ospf6_header *, int action);
+extern struct ospf6_fifo *ospf6_fifo_new(void);
+extern void ospf6_fifo_flush(struct ospf6_fifo *fifo);
+extern void ospf6_fifo_free(struct ospf6_fifo *fifo);
+
extern int ospf6_iobuf_size(unsigned int size);
extern void ospf6_message_terminate(void);
extern int ospf6_receive(struct thread *thread);
diff --git a/ospf6d/ospf6_neighbor.c b/ospf6d/ospf6_neighbor.c
index 9323da8be3..8cf05183e1 100644
--- a/ospf6d/ospf6_neighbor.c
+++ b/ospf6d/ospf6_neighbor.c
@@ -145,6 +145,8 @@ void ospf6_neighbor_delete(struct ospf6_neighbor *on)
THREAD_OFF(on->inactivity_timer);
+ THREAD_OFF(on->last_dbdesc_release_timer);
+
THREAD_OFF(on->thread_send_dbdesc);
THREAD_OFF(on->thread_send_lsreq);
THREAD_OFF(on->thread_send_lsupdate);
@@ -350,6 +352,16 @@ int negotiation_done(struct thread *thread)
return 0;
}
+static int ospf6_neighbor_last_dbdesc_release(struct thread *thread)
+{
+ struct ospf6_neighbor *on = THREAD_ARG(thread);
+
+ assert(on);
+ memset(&on->dbdesc_last, 0, sizeof(struct ospf6_dbdesc));
+
+ return 0;
+}
+
int exchange_done(struct thread *thread)
{
struct ospf6_neighbor *on;
@@ -366,10 +378,13 @@ int exchange_done(struct thread *thread)
THREAD_OFF(on->thread_send_dbdesc);
ospf6_lsdb_remove_all(on->dbdesc_list);
- /* XXX
- thread_add_timer (master, ospf6_neighbor_last_dbdesc_release, on,
- on->ospf6_if->dead_interval);
- */
+ /* RFC 2328 (10.8): Release the last dbdesc after dead_interval */
+ if (!CHECK_FLAG(on->dbdesc_bits, OSPF6_DBDESC_MSBIT)) {
+ THREAD_OFF(on->last_dbdesc_release_timer);
+ thread_add_timer(master, ospf6_neighbor_last_dbdesc_release, on,
+ on->ospf6_if->dead_interval,
+ &on->last_dbdesc_release_timer);
+ }
if (on->request_list->count == 0)
ospf6_neighbor_state_change(OSPF6_NEIGHBOR_FULL, on,
@@ -969,10 +984,9 @@ static void ospf6_neighbor_show_detail(struct vty *vty,
}
}
-static void ospf6_neighbor_show_detail_common(struct vty *vty, int argc,
- struct cmd_token **argv,
- struct ospf6 *ospf6, int idx_type,
- int detail_idx, int json_idx)
+static void ospf6_neighbor_show_detail_common(struct vty *vty,
+ struct ospf6 *ospf6, bool uj,
+ bool detail, bool drchoice)
{
struct ospf6_neighbor *on;
struct ospf6_interface *oi;
@@ -980,18 +994,15 @@ static void ospf6_neighbor_show_detail_common(struct vty *vty, int argc,
struct listnode *i, *j, *k;
json_object *json = NULL;
json_object *json_array = NULL;
- bool uj = use_json(argc, argv);
void (*showfunc)(struct vty *, struct ospf6_neighbor *,
json_object *json, bool use_json);
- showfunc = ospf6_neighbor_show;
-
- if ((uj && argc == detail_idx) || (!uj && argc == json_idx)) {
- if (!strncmp(argv[idx_type]->arg, "de", 2))
- showfunc = ospf6_neighbor_show_detail;
- else if (!strncmp(argv[idx_type]->arg, "dr", 2))
- showfunc = ospf6_neighbor_show_drchoice;
- }
+ if (detail)
+ showfunc = ospf6_neighbor_show_detail;
+ else if (drchoice)
+ showfunc = ospf6_neighbor_show_drchoice;
+ else
+ showfunc = ospf6_neighbor_show;
if (uj) {
json = json_object_new_object();
@@ -1036,28 +1047,28 @@ DEFUN(show_ipv6_ospf6_neighbor, show_ipv6_ospf6_neighbor_cmd,
"Display details\n"
"Display DR choices\n" JSON_STR)
{
- int idx_type = 4;
- int detail_idx = 5;
- int json_idx = 6;
struct ospf6 *ospf6;
struct listnode *node;
const char *vrf_name = NULL;
bool all_vrf = false;
int idx_vrf = 0;
+ int idx_type = 4;
+ bool uj = use_json(argc, argv);
+ bool detail = false;
+ bool drchoice = false;
OSPF6_CMD_CHECK_RUNNING();
OSPF6_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf);
- if (idx_vrf > 0) {
- idx_type += 2;
- detail_idx += 2;
- json_idx += 2;
- }
+
+ if (argv_find(argv, argc, "detail", &idx_type))
+ detail = true;
+ else if (argv_find(argv, argc, "drchoice", &idx_type))
+ drchoice = true;
for (ALL_LIST_ELEMENTS_RO(om6->ospf6, node, ospf6)) {
if (all_vrf || strcmp(ospf6->name, vrf_name) == 0) {
- ospf6_neighbor_show_detail_common(vty, argc, argv,
- ospf6, idx_type,
- detail_idx, json_idx);
+ ospf6_neighbor_show_detail_common(vty, ospf6, uj,
+ detail, drchoice);
if (!all_vrf)
break;
}
diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h
index 47f8c834e2..729b1d2e85 100644
--- a/ospf6d/ospf6_neighbor.h
+++ b/ospf6d/ospf6_neighbor.h
@@ -47,6 +47,10 @@ struct ospf6_neighbor {
uint32_t state_change;
struct timeval last_changed;
+ /* last received hello */
+ struct timeval last_hello;
+ uint32_t hello_in;
+
/* Neighbor Router ID */
in_addr_t router_id;
@@ -89,6 +93,9 @@ struct ospf6_neighbor {
/* Inactivity timer */
struct thread *inactivity_timer;
+ /* Timer to release the last dbdesc packet */
+ struct thread *last_dbdesc_release_timer;
+
/* Thread for sending message */
struct thread *thread_send_dbdesc;
struct thread *thread_send_lsreq;
diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c
index 76f98fecdd..5961cfe66a 100644
--- a/ospf6d/ospf6_network.c
+++ b/ospf6d/ospf6_network.c
@@ -257,10 +257,13 @@ int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst,
rmsghdr.msg_control = (caddr_t)cmsgbuf;
rmsghdr.msg_controllen = sizeof(cmsgbuf);
- retval = recvmsg(ospf6_sock, &rmsghdr, 0);
- if (retval < 0)
- zlog_warn("recvmsg failed: %s", safe_strerror(errno));
- else if (retval == iov_totallen(message))
+ retval = recvmsg(ospf6_sock, &rmsghdr, MSG_DONTWAIT);
+ if (retval < 0) {
+ if (errno != EAGAIN && errno != EWOULDBLOCK)
+ zlog_warn("stream_recvmsg failed: %s",
+ safe_strerror(errno));
+ return retval;
+ } else if (retval == iov_totallen(message))
zlog_warn("recvmsg read full buffer size: %d", retval);
/* source address */
diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h
index 08d8be4445..3886a0d263 100644
--- a/ospf6d/ospf6_network.h
+++ b/ospf6d/ospf6_network.h
@@ -36,4 +36,19 @@ extern int ospf6_recvmsg(struct in6_addr *src, struct in6_addr *dst,
ifindex_t *ifindex, struct iovec *message,
int ospf6_sock);
+#define OSPF6_MESSAGE_WRITE_ON(oi) \
+ do { \
+ bool list_was_empty = \
+ list_isempty(oi->area->ospf6->oi_write_q); \
+ if ((oi)->on_write_q == 0) { \
+ listnode_add(oi->area->ospf6->oi_write_q, (oi)); \
+ (oi)->on_write_q = 1; \
+ } \
+ if (list_was_empty \
+ && !list_isempty(oi->area->ospf6->oi_write_q)) \
+ thread_add_write(master, ospf6_write, oi->area->ospf6, \
+ oi->area->ospf6->fd, \
+ &oi->area->ospf6->t_write); \
+ } while (0)
+
#endif /* OSPF6_NETWORK_H */
diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c
index dfae51cec1..9f8cdf8fb7 100644
--- a/ospf6d/ospf6_nssa.c
+++ b/ospf6d/ospf6_nssa.c
@@ -139,6 +139,7 @@ void ospf6_abr_nssa_check_status(struct ospf6 *ospf6)
struct listnode *lnode, *nnode;
for (ALL_LIST_ELEMENTS(ospf6->area_list, lnode, nnode, area)) {
+ uint8_t old_state = area->NSSATranslatorState;
if (IS_OSPF6_DEBUG_NSSA)
zlog_debug("%s: checking area %s flag %x", __func__,
@@ -177,16 +178,21 @@ void ospf6_abr_nssa_check_status(struct ospf6 *ospf6)
}
}
}
+
+ /* RFC3101, 3.1:
+ * All NSSA border routers must set the E-bit in the Type-1
+ * router-LSAs of their directly attached non-stub areas, even
+ * when they are not translating.
+ */
+ if (old_state != area->NSSATranslatorState) {
+ if (old_state == OSPF6_NSSA_TRANSLATE_DISABLED)
+ ospf6_asbr_status_update(ospf6,
+ ++ospf6->redist_count);
+ else
+ ospf6_asbr_status_update(ospf6,
+ --ospf6->redist_count);
+ }
}
- /* RFC3101, 3.1:
- * All NSSA border routers must set the E-bit in the Type-1
- * router-LSAs of their directly attached non-stub areas, even
- * when they are not translating.
- */
- if (CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR) && (ospf6->anyNSSA))
- ospf6_asbr_status_update(ospf6, ++ospf6->redist_count);
- else
- ospf6_asbr_status_update(ospf6, --ospf6->redist_count);
}
/* Mark the summary LSA's as unapproved, when ABR status changes.*/
diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c
index cd1b5b99f8..6cea5032bd 100644
--- a/ospf6d/ospf6_top.c
+++ b/ospf6d/ospf6_top.c
@@ -232,6 +232,8 @@ void ospf6_vrf_init(void)
{
vrf_init(ospf6_vrf_new, ospf6_vrf_enable, ospf6_vrf_disable,
ospf6_vrf_delete, ospf6_vrf_enable);
+
+ vrf_cmd_init(NULL, &ospf6d_privs);
}
static void ospf6_top_lsdb_hook_add(struct ospf6_lsa *lsa)
@@ -406,6 +408,7 @@ static struct ospf6 *ospf6_create(const char *name)
o->external_id_table = route_table_init();
+ o->write_oi_count = OSPF6_WRITE_INTERFACE_COUNT_DEFAULT;
o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH;
o->distance_table = route_table_init();
@@ -413,6 +416,8 @@ static struct ospf6 *ospf6_create(const char *name)
o->max_multipath = MULTIPATH_NUM;
+ o->oi_write_q = list_new();
+
QOBJ_REG(o, ospf6);
/* Make ospf protocol socket. */
@@ -482,6 +487,7 @@ void ospf6_delete(struct ospf6 *o)
ospf6_distance_reset(o);
route_table_finish(o->distance_table);
+ list_delete(&o->oi_write_q);
if (o->vrf_id != VRF_UNKNOWN) {
vrf = vrf_lookup_by_id(o->vrf_id);
@@ -1665,6 +1671,11 @@ static int config_write_ospf6(struct vty *vty)
vty_out(vty, " auto-cost reference-bandwidth %d\n",
ospf6->ref_bandwidth);
+ if (ospf6->write_oi_count
+ != OSPF6_WRITE_INTERFACE_COUNT_DEFAULT)
+ vty_out(vty, " write-multiplier %d\n",
+ ospf6->write_oi_count);
+
/* LSA timers print. */
if (ospf6->lsa_minarrival != OSPF_MIN_LS_ARRIVAL)
vty_out(vty, " timers lsa min-arrival %d\n",
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
index e4dfebe1de..5b739ac76b 100644
--- a/ospf6d/ospf6_top.h
+++ b/ospf6d/ospf6_top.h
@@ -128,7 +128,10 @@ struct ospf6 {
struct thread *maxage_remover;
struct thread *t_distribute_update; /* Distirbute update timer. */
struct thread *t_ospf6_receive; /* OSPF6 receive timer */
+#define OSPF6_WRITE_INTERFACE_COUNT_DEFAULT 20
+ struct thread *t_write;
+ int write_oi_count; /* Num of packets sent per thread invocation */
uint32_t ref_bandwidth;
/* Distance parameters */
@@ -150,6 +153,7 @@ struct ospf6 {
/* Count of NSSA areas */
uint8_t anyNSSA;
struct thread *t_abr_task; /* ABR task timer. */
+ struct list *oi_write_q;
uint32_t redist_count;
QOBJ_FIELDS;
diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c
index 49829d86f1..c850df55bb 100644
--- a/ospfd/ospf_lsa.c
+++ b/ospfd/ospf_lsa.c
@@ -2084,6 +2084,8 @@ void ospf_external_lsa_rid_change(struct ospf *ospf)
{
struct external_info *ei;
struct ospf_external_aggr_rt *aggr;
+ struct ospf_lsa *lsa = NULL;
+ int force;
int type;
for (type = 0; type < ZEBRA_ROUTE_MAX; type++) {
@@ -2112,24 +2114,48 @@ void ospf_external_lsa_rid_change(struct ospf *ospf)
continue;
if (is_prefix_default(
- (struct prefix_ipv4 *)&ei->p))
+ (struct prefix_ipv4 *)&ei->p))
continue;
- if (!ospf_redistribute_check(ospf, ei, NULL))
- continue;
+ lsa = ospf_external_info_find_lsa(ospf, &ei->p);
aggr = ospf_external_aggr_match(ospf, &ei->p);
if (aggr) {
+
+ if (!ospf_redistribute_check(ospf, ei,
+ NULL))
+ continue;
+
if (IS_DEBUG_OSPF(lsa, EXTNL_LSA_AGGR))
zlog_debug(
"Originate Summary LSA after reset/router-ID change");
+
/* Here the LSA is originated as new */
ospf_originate_summary_lsa(ospf, aggr,
ei);
- } else if (!ospf_external_lsa_originate(ospf,
- ei))
- flog_warn(EC_OSPF_LSA_INSTALL_FAILURE,
- "LSA: AS-external-LSA was not originated.");
+ } else if (lsa) {
+ /* LSA needs to be refreshed even if
+ * there is no change in the route
+ * params if the LSA is in maxage.
+ */
+ if (IS_LSA_MAXAGE(lsa))
+ force = LSA_REFRESH_FORCE;
+ else
+ force = LSA_REFRESH_IF_CHANGED;
+
+ ospf_external_lsa_refresh(ospf, lsa,
+ ei, force, 0);
+ } else {
+ if (!ospf_redistribute_check(ospf, ei,
+ NULL))
+ continue;
+
+ if (!ospf_external_lsa_originate(ospf,
+ NULL))
+ flog_warn(
+ EC_OSPF_LSA_INSTALL_FAILURE,
+ "LSA: AS-external-LSA was not originated.");
+ }
}
}
}
diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c
index a1b35b2fcd..8725497f2d 100644
--- a/ospfd/ospf_neighbor.c
+++ b/ospfd/ospf_neighbor.c
@@ -407,6 +407,9 @@ void ospf_renegotiate_optional_capabilities(struct ospf *top)
}
}
+ /* Refresh/Re-originate external LSAs (Type-7 and Type-5).*/
+ ospf_external_lsa_rid_change(top);
+
return;
}
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index cb64187d72..54ce248d89 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -12959,6 +12959,8 @@ void ospf_vty_init(void)
install_element(OSPF_NODE, &ospf_max_multipath_cmd);
install_element(OSPF_NODE, &no_ospf_max_multipath_cmd);
+ vrf_cmd_init(NULL, &ospfd_privs);
+
/* Init interface related vty commands. */
ospf_vty_if_init();
diff --git a/pathd/path_errors.c b/pathd/path_errors.c
index f8560a848c..112a3d5ee9 100644
--- a/pathd/path_errors.c
+++ b/pathd/path_errors.c
@@ -61,6 +61,12 @@ static struct log_ref ferr_path_warn[] = {
.suggestion = "Check the connectivity between the PCC and the PCE"
},
{
+ .code = EC_PATH_PCEP_PROTOCOL_ERROR,
+ .title = "PCEP protocol error",
+ .description = "The PCE did not respect the PCEP protocol",
+ .suggestion = "Open an Issue with all relevant log files"
+ },
+ {
.code = EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
.title = "PCC connection error",
.description = "The PCEP module did not try to connect because it is missing a source address",
diff --git a/pathd/path_errors.h b/pathd/path_errors.h
index 72e127f26b..5d05b6650b 100644
--- a/pathd/path_errors.h
+++ b/pathd/path_errors.h
@@ -28,6 +28,7 @@ enum path_log_refs {
EC_PATH_PCEP_PCC_FINI,
EC_PATH_PCEP_PCC_CONF_UPDATE,
EC_PATH_PCEP_LIB_CONNECT,
+ EC_PATH_PCEP_PROTOCOL_ERROR,
EC_PATH_PCEP_MISSING_SOURCE_ADDRESS,
EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
diff --git a/pathd/path_nb_config.c b/pathd/path_nb_config.c
index 5b0f5b44e5..e1fbb37f55 100644
--- a/pathd/path_nb_config.c
+++ b/pathd/path_nb_config.c
@@ -266,7 +266,7 @@ int pathd_srte_policy_create(struct nb_cb_create_args *args)
color = yang_dnode_get_uint32(args->dnode, "./color");
yang_dnode_get_ip(&endpoint, args->dnode, "./endpoint");
- policy = srte_policy_add(color, &endpoint);
+ policy = srte_policy_add(color, &endpoint, SRTE_ORIGIN_LOCAL, NULL);
nb_running_set_entry(args->dnode, policy);
SET_FLAG(policy->flags, F_POLICY_NEW);
@@ -388,7 +388,8 @@ int pathd_srte_policy_candidate_path_create(struct nb_cb_create_args *args)
policy = nb_running_get_entry(args->dnode, NULL, true);
preference = yang_dnode_get_uint32(args->dnode, "./preference");
- candidate = srte_candidate_add(policy, preference);
+ candidate =
+ srte_candidate_add(policy, preference, SRTE_ORIGIN_LOCAL, NULL);
nb_running_set_entry(args->dnode, candidate);
SET_FLAG(candidate->flags, F_CANDIDATE_NEW);
diff --git a/pathd/path_pcep.c b/pathd/path_pcep.c
index ad24c2eb02..ce631eb7b0 100644
--- a/pathd/path_pcep.c
+++ b/pathd/path_pcep.c
@@ -37,6 +37,7 @@
#include "pathd/path_pcep_controller.h"
#include "pathd/path_pcep_lib.h"
#include "pathd/path_pcep_config.h"
+#include "pathd/path_pcep_debug.h"
DEFINE_MTYPE(PATHD, PCEP, "PCEP module");
@@ -51,6 +52,7 @@ static int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id,
void *payload);
static int pcep_main_event_start_sync(int pcc_id);
static int pcep_main_event_start_sync_cb(struct path *path, void *arg);
+static int pcep_main_event_initiate_candidate(struct path *path);
static int pcep_main_event_update_candidate(struct path *path);
static int pcep_main_event_remove_candidate_segments(const char *originator,
bool force);
@@ -64,6 +66,9 @@ static int pathd_candidate_removed_handler(struct srte_candidate *candidate);
static struct path_metric *pcep_copy_metrics(struct path_metric *metric);
static struct path_hop *pcep_copy_hops(struct path_hop *hop);
+/* Other static functions */
+static void notify_status(struct path *path, bool not_changed);
+
/* Module Functions */
static int pcep_module_finish(void);
static int pcep_module_late_init(struct thread_master *tm);
@@ -165,6 +170,21 @@ void pcep_free_path(struct path *path)
XFREE(MTYPE_PCEP, path);
}
+/* ------------ Other Static Functions ------------ */
+
+void notify_status(struct path *path, bool not_changed)
+{
+ struct path *resp = NULL;
+
+ if ((resp = path_pcep_config_get_path(&path->nbkey))) {
+ resp->srp_id = path->srp_id;
+ flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
+ "(%s) Send report for candidate path %s", __func__,
+ path->name);
+ pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp,
+ not_changed);
+ }
+}
/* ------------ Main Thread Even Handler ------------ */
@@ -177,6 +197,11 @@ int pcep_main_event_handler(enum pcep_main_event_type type, int pcc_id,
case PCEP_MAIN_EVENT_START_SYNC:
ret = pcep_main_event_start_sync(pcc_id);
break;
+ case PCEP_MAIN_EVENT_INITIATE_CANDIDATE:
+ assert(payload != NULL);
+ ret = pcep_main_event_initiate_candidate(
+ (struct path *)payload);
+ break;
case PCEP_MAIN_EVENT_UPDATE_CANDIDATE:
assert(payload != NULL);
ret = pcep_main_event_update_candidate((struct path *)payload);
@@ -209,19 +234,49 @@ int pcep_main_event_start_sync_cb(struct path *path, void *arg)
return 1;
}
+int pcep_main_event_initiate_candidate(struct path *path)
+{
+ int ret = 0;
+
+ ret = path_pcep_config_initiate_path(path);
+ if (path->do_remove) {
+ struct pcep_error *error;
+ error = XCALLOC(MTYPE_PCEP, sizeof(*error));
+ error->path = path;
+ error->error_type = PCEP_ERRT_INVALID_OPERATION;
+ switch (ret) {
+ case ERROR_19_1:
+ error->error_value =
+ PCEP_ERRV_LSP_UPDATE_FOR_NON_DELEGATED_LSP;
+ break;
+ case ERROR_19_3:
+ error->error_value =
+ PCEP_ERRV_LSP_UPDATE_UNKNOWN_PLSP_ID;
+ break;
+ case ERROR_19_9:
+ error->error_value = PCEP_ERRV_LSP_NOT_PCE_INITIATED;
+ break;
+ default:
+ zlog_warn("(%s)PCE tried to REMOVE unknown error!",
+ __func__);
+ XFREE(MTYPE_PCEP, error);
+ pcep_free_path(path);
+ return ret;
+ break;
+ }
+ pcep_ctrl_send_error(pcep_g->fpt, path->pcc_id, error);
+ } else if (ret != PATH_NB_ERR && path->srp_id != 0)
+ notify_status(path, ret == PATH_NB_NO_CHANGE);
+ return ret;
+}
+
int pcep_main_event_update_candidate(struct path *path)
{
- struct path *resp = NULL;
int ret = 0;
ret = path_pcep_config_update_path(path);
- if (ret != PATH_NB_ERR && path->srp_id != 0) {
- if ((resp = path_pcep_config_get_path(&path->nbkey))) {
- resp->srp_id = path->srp_id;
- pcep_ctrl_send_report(pcep_g->fpt, path->pcc_id, resp,
- ret == PATH_NB_NO_CHANGE);
- }
- }
+ if (ret != PATH_NB_ERR && path->srp_id != 0)
+ notify_status(path, ret == PATH_NB_NO_CHANGE);
return ret;
}
diff --git a/pathd/path_pcep.h b/pathd/path_pcep.h
index d0af674ff9..597a4b6c83 100644
--- a/pathd/path_pcep.h
+++ b/pathd/path_pcep.h
@@ -317,6 +317,16 @@ struct pcep_glob {
extern struct pcep_glob *pcep_g;
+struct pcep_error {
+ struct path *path;
+ int error_type;
+ int error_value;
+ /* Rfc 8281 PcInitiated error on bad values */
+#define ERROR_19_1 1
+#define ERROR_19_3 2
+#define ERROR_19_9 3
+};
+
/* Path Helper Functions */
struct path *pcep_new_path(void);
struct path_hop *pcep_new_hop(void);
diff --git a/pathd/path_pcep_config.c b/pathd/path_pcep_config.c
index 609f091559..0349618304 100644
--- a/pathd/path_pcep_config.c
+++ b/pathd/path_pcep_config.c
@@ -32,6 +32,8 @@
#define MAX_FLOAT_LEN 22
#define INETADDR4_MAXLEN 16
#define INETADDR6_MAXLEN 40
+#define INITIATED_CANDIDATE_PREFERENCE 255
+#define INITIATED_POLICY_COLOR 1
static void copy_candidate_objfun_info(struct srte_candidate *candidate,
@@ -147,7 +149,7 @@ struct path *candidate_to_path(struct srte_candidate *candidate)
.plsp_id = 0,
.name = name,
.type = candidate->type,
- .srp_id = 0,
+ .srp_id = policy->srp_id,
.req_id = 0,
.binding_sid = policy->binding_sid,
.status = status,
@@ -276,6 +278,93 @@ path_pcep_config_list_path_hops(struct srte_segment_list *segment_list)
return hop;
}
+int path_pcep_config_initiate_path(struct path *path)
+{
+ struct srte_policy *policy;
+ struct srte_candidate *candidate;
+
+ if (path->do_remove) {
+ zlog_warn("PCE %s tried to REMOVE pce-initiate a path ",
+ path->originator);
+ candidate = lookup_candidate(&path->nbkey);
+ if (candidate) {
+ if (!path->is_delegated) {
+ zlog_warn(
+ "(%s)PCE tried to REMOVE but it's not Delegated!",
+ __func__);
+ return ERROR_19_1;
+ }
+ if (candidate->type != SRTE_CANDIDATE_TYPE_DYNAMIC) {
+ zlog_warn(
+ "(%s)PCE tried to REMOVE but it's not PCE origin!",
+ __func__);
+ return ERROR_19_9;
+ }
+ zlog_warn(
+ "(%s)PCE tried to REMOVE found canidate!, let's remove",
+ __func__);
+ candidate->policy->srp_id = path->srp_id;
+ SET_FLAG(candidate->policy->flags, F_POLICY_DELETED);
+ SET_FLAG(candidate->flags, F_CANDIDATE_DELETED);
+ } else {
+ zlog_warn("(%s)PCE tried to REMOVE not existing LSP!",
+ __func__);
+ return ERROR_19_3;
+ }
+ srte_apply_changes();
+ } else {
+ assert(!IS_IPADDR_NONE(&path->nbkey.endpoint));
+
+ if (path->nbkey.preference == 0)
+ path->nbkey.preference = INITIATED_CANDIDATE_PREFERENCE;
+
+ if (path->nbkey.color == 0)
+ path->nbkey.color = INITIATED_POLICY_COLOR;
+
+ candidate = lookup_candidate(&path->nbkey);
+ if (!candidate) {
+ policy = srte_policy_add(
+ path->nbkey.color, &path->nbkey.endpoint,
+ SRTE_ORIGIN_PCEP, path->originator);
+ strlcpy(policy->name, path->name, sizeof(policy->name));
+ policy->binding_sid = path->binding_sid;
+ SET_FLAG(policy->flags, F_POLICY_NEW);
+ candidate = srte_candidate_add(
+ policy, path->nbkey.preference,
+ SRTE_ORIGIN_PCEP, path->originator);
+ strlcpy(candidate->name, path->name,
+ sizeof(candidate->name));
+ SET_FLAG(candidate->flags, F_CANDIDATE_NEW);
+ } else {
+ policy = candidate->policy;
+ if ((path->originator != candidate->originator)
+ || (path->originator != policy->originator)) {
+ /* There is already an initiated path from
+ * another PCE, show a warning and regect the
+ * initiated path */
+ zlog_warn(
+ "PCE %s tried to initiate a path already initiated by PCE %s",
+ path->originator,
+ candidate->originator);
+ return 1;
+ }
+ if ((policy->protocol_origin != SRTE_ORIGIN_PCEP)
+ || (candidate->protocol_origin
+ != SRTE_ORIGIN_PCEP)) {
+ /* There is already an initiated path from
+ * another PCE, show a warning and regect the
+ * initiated path */
+ zlog_warn(
+ "PCE %s tried to initiate a path created localy",
+ path->originator);
+ return 1;
+ }
+ }
+ return path_pcep_config_update_path(path);
+ }
+ return 0;
+}
+
int path_pcep_config_update_path(struct path *path)
{
assert(path != NULL);
@@ -381,8 +470,12 @@ struct srte_candidate *lookup_candidate(struct lsp_nb_key *key)
char *candidate_name(struct srte_candidate *candidate)
{
- return asprintfrr(MTYPE_PCEP, "%s-%s", candidate->policy->name,
- candidate->name);
+ if (candidate->protocol_origin == SRTE_ORIGIN_PCEP
+ || candidate->protocol_origin == SRTE_ORIGIN_BGP)
+ return asprintfrr(MTYPE_PCEP, "%s", candidate->policy->name);
+ else
+ return asprintfrr(MTYPE_PCEP, "%s-%s", candidate->policy->name,
+ candidate->name);
}
enum pcep_lsp_operational_status
diff --git a/pathd/path_pcep_config.h b/pathd/path_pcep_config.h
index 223dd10c82..e56d497aa0 100644
--- a/pathd/path_pcep_config.h
+++ b/pathd/path_pcep_config.h
@@ -38,6 +38,7 @@ typedef int (*path_list_cb_t)(struct path *path, void *arg);
void path_pcep_refine_path(struct path *path);
struct path *path_pcep_config_get_path(struct lsp_nb_key *key);
void path_pcep_config_list_path(path_list_cb_t cb, void *arg);
+int path_pcep_config_initiate_path(struct path *path);
int path_pcep_config_update_path(struct path *path);
struct path *candidate_to_path(struct srte_candidate *candidate);
diff --git a/pathd/path_pcep_controller.c b/pathd/path_pcep_controller.c
index 528dcc3539..449c40c16c 100644
--- a/pathd/path_pcep_controller.c
+++ b/pathd/path_pcep_controller.c
@@ -57,6 +57,7 @@ enum pcep_ctrl_event_type {
EV_PCEPLIB_EVENT,
EV_RESET_PCC_SESSION,
EV_SEND_REPORT,
+ EV_SEND_ERROR,
EV_PATH_REFINED
};
@@ -328,6 +329,14 @@ int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
}
+int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id,
+ struct pcep_error *error)
+{
+ struct ctrl_state *ctrl_state = get_ctrl_state(fpt);
+ return send_to_thread(ctrl_state, pcc_id, EV_SEND_ERROR, 0, error);
+}
+
+
/* ------------ Internal Functions Called from Main Thread ------------ */
int pcep_ctrl_halt_cb(struct frr_pthread *fpt, void **res)
@@ -368,6 +377,13 @@ void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id,
path);
}
+void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path)
+{
+ send_to_main(ctrl_state, pcc_id, PCEP_MAIN_EVENT_INITIATE_CANDIDATE,
+ path);
+}
+
void pcep_thread_remove_candidate_path_segments(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state)
{
@@ -743,6 +759,7 @@ int pcep_thread_event_handler(struct thread *thread)
struct pcep_refine_path_event_data *refine_data = NULL;
struct path *path_copy = NULL;
+ struct pcep_error *error = NULL;
switch (type) {
case EV_UPDATE_PCC_OPTS:
@@ -818,6 +835,13 @@ int pcep_thread_event_handler(struct thread *thread)
refine_data = (struct pcep_refine_path_event_data *)payload;
pcep_thread_path_refined_event(ctrl_state, refine_data);
break;
+ case EV_SEND_ERROR:
+ assert(payload != NULL);
+ error = (struct pcep_error *)payload;
+ pcc_state = pcep_pcc_get_pcc_by_id(ctrl_state->pcc, pcc_id);
+ pcep_pcc_send_error(ctrl_state, pcc_state, error,
+ (bool)sub_type);
+ break;
default:
flog_warn(EC_PATH_PCEP_RECOVERABLE_INTERNAL_ERROR,
"Unexpected event received in controller thread: %u",
diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h
index 1b7c3a4c72..f55cc0db72 100644
--- a/pathd/path_pcep_controller.h
+++ b/pathd/path_pcep_controller.h
@@ -27,6 +27,7 @@ struct pcc_state;
enum pcep_main_event_type {
PCEP_MAIN_EVENT_UNDEFINED = 0,
PCEP_MAIN_EVENT_START_SYNC,
+ PCEP_MAIN_EVENT_INITIATE_CANDIDATE,
PCEP_MAIN_EVENT_UPDATE_CANDIDATE,
PCEP_MAIN_EVENT_REMOVE_CANDIDATE_LSP,
};
@@ -137,10 +138,15 @@ struct pcep_pcc_info *pcep_ctrl_get_pcc_info(struct frr_pthread *fpt,
int pcep_ctrl_send_report(struct frr_pthread *fpt, int pcc_id,
struct path *path, bool is_stable);
+int pcep_ctrl_send_error(struct frr_pthread *fpt, int pcc_id,
+ struct pcep_error *error);
+
/* Functions called from the controller thread */
void pcep_thread_start_sync(struct ctrl_state *ctrl_state, int pcc_id);
void pcep_thread_update_path(struct ctrl_state *ctrl_state, int pcc_id,
struct path *path);
+void pcep_thread_initiate_path(struct ctrl_state *ctrl_state, int pcc_id,
+ struct path *path);
void pcep_thread_cancel_timer(struct thread **thread);
void pcep_thread_schedule_reconnect(struct ctrl_state *ctrl_state, int pcc_id,
int retry_count, struct thread **thread);
diff --git a/pathd/path_pcep_debug.c b/pathd/path_pcep_debug.c
index e14f6bc4a5..b0802ae6c3 100644
--- a/pathd/path_pcep_debug.c
+++ b/pathd/path_pcep_debug.c
@@ -780,6 +780,10 @@ const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type)
switch (tlv_type) {
case PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR:
return "NO_PATH_VECTOR";
+ case PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST:
+ return "OBJECTIVE_FUNCTION_LIST";
+ case PCEP_OBJ_TLV_TYPE_VENDOR_INFO:
+ return "VENDOR_INFO";
case PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY:
return "STATEFUL_PCE_CAPABILITY";
case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
@@ -802,6 +806,18 @@ const char *pcep_tlv_type_name(enum pcep_object_tlv_types tlv_type)
return "PATH_SETUP_TYPE";
case PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE_CAPABILITY:
return "PATH_SETUP_TYPE_CAPABILITY";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_ID:
+ return "SRPOLICY_POL_ID";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_POL_NAME:
+ return "SRPOLICY_POL_NAME";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_ID:
+ return "SRPOLICY_CPATH_ID";
+ case PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE:
+ return "SRPOLICY_CPATH_PREFERENCE";
+ case PCEP_OBJ_TLV_TYPE_UNKNOWN:
+ return "UNKNOWN";
+ case PCEP_OBJ_TLV_TYPE_ARBITRARY:
+ return "ARBITRARY";
default:
return "UNKNOWN";
}
diff --git a/pathd/path_pcep_lib.c b/pathd/path_pcep_lib.c
index e9d699de47..9fc8c091f8 100644
--- a/pathd/path_pcep_lib.c
+++ b/pathd/path_pcep_lib.c
@@ -35,6 +35,7 @@ DEFINE_MTYPE_STATIC(PATHD, PCEPLIB_MESSAGES, "PCEPlib PCEP Messages");
#define DEFAULT_LSAP_SETUP_PRIO 4
#define DEFAULT_LSAP_HOLDING_PRIO 4
#define DEFAULT_LSAP_LOCAL_PRETECTION false
+#define MAX_PATH_NAME_SIZE 255
/* pceplib logging callback */
static int pceplib_logging_cb(int level, const char *fmt, va_list args);
@@ -76,8 +77,18 @@ static void pcep_lib_parse_srp(struct path *path, struct pcep_object_srp *srp);
static void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp);
static void pcep_lib_parse_lspa(struct path *path,
struct pcep_object_lspa *lspa);
+static void pcep_lib_parse_lsp_symbolic_name(
+ struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv);
static void pcep_lib_parse_metric(struct path *path,
struct pcep_object_metric *obj);
+static void
+pcep_lib_parse_endpoints_ipv4(struct path *path,
+ struct pcep_object_endpoints_ipv4 *obj);
+static void
+pcep_lib_parse_endpoints_ipv6(struct path *path,
+ struct pcep_object_endpoints_ipv6 *obj);
+static void pcep_lib_parse_vendor_info(struct path *path,
+ struct pcep_object_vendor_info *obj);
static void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero);
static struct path_hop *pcep_lib_parse_ero_sr(struct path_hop *next,
struct pcep_ro_subobj_sr *sr);
@@ -160,7 +171,7 @@ pcep_lib_connect(struct ipaddr *src_addr, int src_port, struct ipaddr *dst_addr,
}
config->support_stateful_pce_lsp_update = true;
- config->support_pce_lsp_instantiation = false;
+ config->support_pce_lsp_instantiation = pcep_options->pce_initiated;
config->support_include_db_version = false;
config->support_lsp_triggered_resync = false;
config->support_lsp_delta_sync = false;
@@ -381,9 +392,25 @@ struct pcep_message *pcep_lib_format_request(struct pcep_caps *caps,
}
}
-struct pcep_message *pcep_lib_format_error(int error_type, int error_value)
+struct pcep_message *pcep_lib_format_error(int error_type, int error_value,
+ struct path *path)
{
- return pcep_msg_create_error(error_type, error_value);
+ double_linked_list *objs, *srp_tlvs;
+ struct pcep_object_srp *srp;
+ struct pcep_object_tlv_header *tlv;
+
+ if ((path == NULL) || (path->srp_id == 0))
+ return pcep_msg_create_error(error_type, error_value);
+
+ objs = dll_initialize();
+ srp_tlvs = dll_initialize();
+ tlv = (struct pcep_object_tlv_header *)pcep_tlv_create_path_setup_type(
+ SR_TE_PST);
+ dll_append(srp_tlvs, tlv);
+ srp = pcep_obj_create_srp(path->do_remove, path->srp_id, srp_tlvs);
+ dll_append(objs, srp);
+ return pcep_msg_create_error_with_objects(error_type, error_value,
+ objs);
}
struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid)
@@ -417,6 +444,9 @@ struct path *pcep_lib_parse_path(struct pcep_message *msg)
struct pcep_object_metric *metric = NULL;
struct pcep_object_bandwidth *bandwidth = NULL;
struct pcep_object_objective_function *of = NULL;
+ struct pcep_object_endpoints_ipv4 *epv4 = NULL;
+ struct pcep_object_endpoints_ipv6 *epv6 = NULL;
+ struct pcep_object_vendor_info *vendor_info = NULL;
path = pcep_new_path();
@@ -470,6 +500,21 @@ struct path *pcep_lib_parse_path(struct pcep_message *msg)
path->has_pce_objfun = true;
path->pce_objfun = of->of_code;
break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV4):
+ epv4 = (struct pcep_object_endpoints_ipv4 *)obj;
+ pcep_lib_parse_endpoints_ipv4(path, epv4);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_ENDPOINTS,
+ PCEP_OBJ_TYPE_ENDPOINT_IPV6):
+ epv6 = (struct pcep_object_endpoints_ipv6 *)obj;
+ pcep_lib_parse_endpoints_ipv6(path, epv6);
+ break;
+ case CLASS_TYPE(PCEP_OBJ_CLASS_VENDOR_INFO,
+ PCEP_OBJ_TYPE_VENDOR_INFO):
+ vendor_info = (struct pcep_object_vendor_info *)obj;
+ pcep_lib_parse_vendor_info(path, vendor_info);
+ break;
default:
flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_OBJECT,
"Unexpected PCEP object %s (%u) / %s (%u)",
@@ -632,7 +677,8 @@ double_linked_list *pcep_lib_format_path(struct pcep_caps *caps,
tlv = (struct pcep_object_tlv_header *)
pcep_tlv_create_tlv_arbitrary(
binding_sid_lsp_tlv_data,
- sizeof(binding_sid_lsp_tlv_data), 65505);
+ sizeof(binding_sid_lsp_tlv_data),
+ PCEP_OBJ_TYPE_CISCO_BSID);
assert(tlv != NULL);
dll_append(lsp_tlvs, tlv);
}
@@ -855,6 +901,12 @@ void pcep_lib_parse_rp(struct path *path, struct pcep_object_rp *rp)
double_linked_list_node *node;
struct pcep_object_tlv_header *tlv;
+ if (tlvs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty RP's TLV plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
/* We ignore the other flags and priority for now */
path->req_id = rp->request_id;
path->has_pce_objfun = false;
@@ -884,6 +936,12 @@ void pcep_lib_parse_srp(struct path *path, struct pcep_object_srp *srp)
path->do_remove = srp->flag_lsp_remove;
path->srp_id = srp->srp_id_number;
+ if (tlvs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty SRP's TLV plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
for (node = tlvs->head; node != NULL; node = node->next_node) {
tlv = (struct pcep_object_tlv_header *)node->data;
switch (tlv->type) {
@@ -904,6 +962,8 @@ void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp)
double_linked_list *tlvs = lsp->header.tlv_list;
double_linked_list_node *node;
struct pcep_object_tlv_header *tlv;
+ struct pcep_object_tlv_symbolic_path_name *name;
+ struct pcep_object_tlv_arbitrary *arb_tlv;
path->plsp_id = lsp->plsp_id;
path->status = lsp->operational_status;
@@ -913,12 +973,27 @@ void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp)
path->is_synching = lsp->flag_s;
path->is_delegated = lsp->flag_d;
- if (tlvs == NULL)
+ if (tlvs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty LSP's TLV plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
return;
+ }
for (node = tlvs->head; node != NULL; node = node->next_node) {
tlv = (struct pcep_object_tlv_header *)node->data;
switch (tlv->type) {
+ case PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME:
+ name = (struct pcep_object_tlv_symbolic_path_name *)tlv;
+ pcep_lib_parse_lsp_symbolic_name(path, name);
+ break;
+ case PCEP_OBJ_TYPE_CISCO_BSID:
+ arb_tlv = (struct pcep_object_tlv_arbitrary *)tlv;
+ memcpy(&path->binding_sid, arb_tlv->data + 2,
+ sizeof(path->binding_sid));
+ path->binding_sid = ntohl(path->binding_sid);
+ path->binding_sid = (path->binding_sid >> 12);
+ break;
default:
flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
"Unexpected LSP TLV %s (%u)",
@@ -928,6 +1003,16 @@ void pcep_lib_parse_lsp(struct path *path, struct pcep_object_lsp *lsp)
}
}
+void pcep_lib_parse_lsp_symbolic_name(
+ struct path *path, struct pcep_object_tlv_symbolic_path_name *tlv)
+{
+ uint16_t size = tlv->symbolic_path_name_length;
+ assert(path->name == NULL);
+ size = size > MAX_PATH_NAME_SIZE ? MAX_PATH_NAME_SIZE : size;
+ path->name = XCALLOC(MTYPE_PCEP, size);
+ strlcpy((char *)path->name, tlv->symbolic_path_name, size + 1);
+}
+
void pcep_lib_parse_lspa(struct path *path, struct pcep_object_lspa *lspa)
{
path->has_affinity_filters = true;
@@ -952,6 +1037,34 @@ void pcep_lib_parse_metric(struct path *path, struct pcep_object_metric *obj)
path->first_metric = metric;
}
+void pcep_lib_parse_endpoints_ipv4(struct path *path,
+ struct pcep_object_endpoints_ipv4 *obj)
+{
+ SET_IPADDR_V4(&path->pcc_addr);
+ path->pcc_addr.ipaddr_v4 = obj->src_ipv4;
+ SET_IPADDR_V4(&path->nbkey.endpoint);
+ path->nbkey.endpoint.ipaddr_v4 = obj->dst_ipv4;
+}
+
+void pcep_lib_parse_endpoints_ipv6(struct path *path,
+ struct pcep_object_endpoints_ipv6 *obj)
+{
+ SET_IPADDR_V6(&path->pcc_addr);
+ path->pcc_addr.ipaddr_v6 = obj->src_ipv6;
+ SET_IPADDR_V6(&path->nbkey.endpoint);
+ path->nbkey.endpoint.ipaddr_v6 = obj->dst_ipv6;
+}
+
+void pcep_lib_parse_vendor_info(struct path *path,
+ struct pcep_object_vendor_info *obj)
+{
+ if (obj->enterprise_number == ENTERPRISE_NUMBER_CISCO
+ && obj->enterprise_specific_info == ENTERPRISE_COLOR_CISCO)
+ path->nbkey.color = obj->enterprise_specific_info1;
+ else
+ path->nbkey.color = 0;
+}
+
void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero)
{
struct path_hop *hop = NULL;
@@ -959,6 +1072,12 @@ void pcep_lib_parse_ero(struct path *path, struct pcep_object_ro *ero)
double_linked_list_node *node;
struct pcep_object_ro_subobj *obj;
+ if (objs == NULL) {
+ flog_warn(EC_PATH_PCEP_UNEXPECTED_PCEP_TLV,
+ "Unexpected Empty ERO's sub_obj plsp-id:(%d)",
+ path ? (int32_t)path->plsp_id : -1);
+ return;
+ }
for (node = objs->tail; node != NULL; node = node->prev_node) {
obj = (struct pcep_object_ro_subobj *)node->data;
switch (obj->ro_subobj_type) {
diff --git a/pathd/path_pcep_lib.h b/pathd/path_pcep_lib.h
index 3f34edcb3f..524f385d14 100644
--- a/pathd/path_pcep_lib.h
+++ b/pathd/path_pcep_lib.h
@@ -37,7 +37,8 @@ struct pcep_message *pcep_lib_format_request(struct pcep_caps *caps,
struct path *path);
struct pcep_message *pcep_lib_format_request_cancelled(uint32_t reqid);
-struct pcep_message *pcep_lib_format_error(int error_type, int error_value);
+struct pcep_message *pcep_lib_format_error(int error_type, int error_value,
+ struct path *path);
struct path *pcep_lib_parse_path(struct pcep_message *msg);
void pcep_lib_parse_capabilities(struct pcep_message *msg,
struct pcep_caps *caps);
diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c
index 779c400b86..81a338ac63 100644
--- a/pathd/path_pcep_pcc.c
+++ b/pathd/path_pcep_pcc.c
@@ -93,7 +93,8 @@ static void send_pcep_message(struct pcc_state *pcc_state,
struct pcep_message *msg);
static void send_pcep_error(struct pcc_state *pcc_state,
enum pcep_error_type error_type,
- enum pcep_error_value error_value);
+ enum pcep_error_value error_value,
+ struct path *trigger_path);
static void send_report(struct pcc_state *pcc_state, struct path *path);
static void send_comp_request(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
@@ -541,8 +542,8 @@ void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
return;
}
- PCEP_DEBUG("%s Send report for candidate path %s", pcc_state->tag,
- path->name);
+ PCEP_DEBUG("(%s)%s Send report for candidate path %s", __func__,
+ pcc_state->tag, path->name);
/* ODL and Cisco requires the first reported
* LSP to have a DOWN status, the later status changes
@@ -555,6 +556,8 @@ void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
/* If no update is expected and the real status wasn't down, we need to
* send a second report with the real status */
if (is_stable && (real_status != PCEP_LSP_OPERATIONAL_DOWN)) {
+ PCEP_DEBUG("(%s)%s Send report for candidate path (!DOWN) %s",
+ __func__, pcc_state->tag, path->name);
path->srp_id = 0;
path->status = real_status;
send_report(pcc_state, path);
@@ -564,6 +567,19 @@ void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
}
+void pcep_pcc_send_error(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_error *error,
+ bool sub_type)
+{
+
+ PCEP_DEBUG("(%s) Send error after PcInitiated ", __func__);
+
+
+ send_pcep_error(pcc_state, error->error_type, error->error_value,
+ error->path);
+ pcep_free_path(error->path);
+ XFREE(MTYPE_PCEP, error);
+}
/* ------------ Timeout handler ------------ */
void pcep_pcc_timeout_handler(struct ctrl_state *ctrl_state,
@@ -651,6 +667,9 @@ void pcep_pcc_pathd_event_handler(struct ctrl_state *ctrl_state,
PCEP_DEBUG("%s Candidate path %s removed", pcc_state->tag,
path->name);
path->was_removed = true;
+ /* Removed as response to a PcInitiated 'R'emove*/
+ /* RFC 8281 #5.4 LSP Deletion*/
+ path->do_remove = path->was_removed;
if (pcc_state->caps.is_stateful)
send_report(pcc_state, path);
return;
@@ -1203,14 +1222,113 @@ void handle_pcep_lsp_initiate(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state,
struct pcep_message *msg)
{
- PCEP_DEBUG("%s Received LSP initiate, not supported yet",
- pcc_state->tag);
+ char err[MAX_ERROR_MSG_SIZE] = "";
+ struct path *path;
+
+ path = pcep_lib_parse_path(msg);
+
+ if (!pcc_state->pce_opts->config_opts.pce_initiated) {
+ /* PCE Initiated is not enabled */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "Not allowed PCE initiated path received: %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state, PCEP_ERRT_LSP_INSTANTIATE_ERROR,
+ PCEP_ERRV_UNACCEPTABLE_INSTANTIATE_ERROR, path);
+ return;
+ }
- /* TODO when we support both PCC and PCE initiated sessions,
- * we should first check the session type before
- * rejecting this message. */
- send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
- PCEP_ERRV_LSP_NOT_PCE_INITIATED);
+ if (path->do_remove) {
+ // lookup in nbkey sequential as no endpoint
+ struct nbkey_map_data *key;
+ char endpoint[46];
+
+ frr_each (nbkey_map, &pcc_state->nbkey_map, key) {
+ ipaddr2str(&key->nbkey.endpoint, endpoint,
+ sizeof(endpoint));
+ flog_warn(
+ EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "FOR_EACH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ",
+ key->nbkey.color, endpoint, path->plsp_id);
+ if (path->plsp_id == key->plspid) {
+ flog_warn(
+ EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "FOR_EACH MATCH nbkey [color (%d) endpoint (%s)] path [plsp_id (%d)] ",
+ key->nbkey.color, endpoint,
+ path->plsp_id);
+ path->nbkey = key->nbkey;
+ break;
+ }
+ }
+ } else {
+ if (path->first_hop == NULL /*ero sets first_hop*/) {
+ /* If the PCC receives a PCInitiate message without an
+ * ERO and the R flag in the SRP object != zero, then it
+ * MUST send a PCErr message with Error-type=6
+ * (Mandatory Object missing) and Error-value=9 (ERO
+ * object missing). */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "ERO object missing or incomplete : %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state,
+ PCEP_ERRT_LSP_INSTANTIATE_ERROR,
+ PCEP_ERRV_INTERNAL_ERROR, path);
+ return;
+ }
+
+ if (path->plsp_id != 0) {
+ /* If the PCC receives a PCInitiate message with a
+ * non-zero PLSP-ID and the R flag in the SRP object set
+ * to zero, then it MUST send a PCErr message with
+ * Error-type=19 (Invalid Operation) and Error-value=8
+ * (Non-zero PLSP-ID in the LSP Initiate Request) */
+ flog_warn(
+ EC_PATH_PCEP_PROTOCOL_ERROR,
+ "PCE initiated path with non-zero PLSP ID: %s",
+ format_pcep_message(msg));
+ send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_INIT_NON_ZERO_PLSP_ID,
+ path);
+ return;
+ }
+
+ if (path->name == NULL) {
+ /* If the PCC receives a PCInitiate message without a
+ * SYMBOLIC-PATH-NAME TLV, then it MUST send a PCErr
+ * message with Error-type=10 (Reception of an invalid
+ * object) and Error-value=8 (SYMBOLIC-PATH-NAME TLV
+ * missing) */
+ flog_warn(
+ EC_PATH_PCEP_PROTOCOL_ERROR,
+ "PCE initiated path without symbolic name: %s",
+ format_pcep_message(msg));
+ send_pcep_error(
+ pcc_state, PCEP_ERRT_RECEPTION_OF_INV_OBJECT,
+ PCEP_ERRV_SYMBOLIC_PATH_NAME_TLV_MISSING, path);
+ return;
+ }
+ }
+
+ /* TODO: If there is a conflict with the symbolic path name of an
+ * existing LSP, the PCC MUST send a PCErr message with Error-type=23
+ * (Bad Parameter value) and Error-value=1 (SYMBOLIC-PATH-NAME in
+ * use) */
+
+ specialize_incoming_path(pcc_state, path);
+ /* TODO: Validate the PCC address received from the PCE is valid */
+ PCEP_DEBUG("%s Received LSP initiate", pcc_state->tag);
+ PCEP_DEBUG_PATH("%s", format_path(path));
+
+ if (validate_incoming_path(pcc_state, path, err, sizeof(err))) {
+ pcep_thread_initiate_path(ctrl_state, pcc_state->id, path);
+ } else {
+ /* FIXME: Monitor the amount of errors from the PCE and
+ * possibly disconnect and blacklist */
+ flog_warn(EC_PATH_PCEP_UNSUPPORTED_PCEP_FEATURE,
+ "Unsupported PCEP protocol feature: %s", err);
+ pcep_free_path(path);
+ send_pcep_error(pcc_state, PCEP_ERRT_INVALID_OPERATION,
+ PCEP_ERRV_LSP_NOT_PCE_INITIATED, path);
+ }
}
void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
@@ -1232,7 +1350,7 @@ void handle_pcep_comp_reply(struct ctrl_state *ctrl_state,
pcc_state->tag, path->req_id);
PCEP_DEBUG_PATH("%s", format_path(path));
send_pcep_error(pcc_state, PCEP_ERRT_UNKNOWN_REQ_REF,
- PCEP_ERRV_UNASSIGNED);
+ PCEP_ERRV_UNASSIGNED, NULL);
return;
}
@@ -1447,13 +1565,14 @@ void send_pcep_message(struct pcc_state *pcc_state, struct pcep_message *msg)
void send_pcep_error(struct pcc_state *pcc_state,
enum pcep_error_type error_type,
- enum pcep_error_value error_value)
+ enum pcep_error_value error_value,
+ struct path *trigger_path)
{
struct pcep_message *msg;
PCEP_DEBUG("%s Sending PCEP error type %s (%d) value %s (%d)",
pcc_state->tag, pcep_error_type_name(error_type), error_type,
pcep_error_value_name(error_type, error_value), error_value);
- msg = pcep_lib_format_error(error_type, error_value);
+ msg = pcep_lib_format_error(error_type, error_value, trigger_path);
send_pcep_message(pcc_state, msg);
}
@@ -1504,7 +1623,8 @@ void specialize_outgoing_path(struct pcc_state *pcc_state, struct path *path)
/* Updates the path for the PCC */
void specialize_incoming_path(struct pcc_state *pcc_state, struct path *path)
{
- set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr);
+ if (IS_IPADDR_NONE(&path->pcc_addr))
+ set_pcc_address(pcc_state, &path->nbkey, &path->pcc_addr);
path->sender = pcc_state->pce_opts->addr;
path->pcc_id = pcc_state->id;
path->update_origin = SRTE_ORIGIN_PCEP;
@@ -1538,7 +1658,7 @@ bool validate_incoming_path(struct pcc_state *pcc_state, struct path *path,
}
if (err_type != 0) {
- send_pcep_error(pcc_state, err_type, err_value);
+ send_pcep_error(pcc_state, err_type, err_value, NULL);
return false;
}
@@ -1564,7 +1684,6 @@ void send_comp_request(struct ctrl_state *ctrl_state,
if (!pcc_state->is_best) {
return;
}
- /* TODO: Add a timer to retry the computation request ? */
specialize_outgoing_path(pcc_state, req->path);
@@ -1579,10 +1698,7 @@ void send_comp_request(struct ctrl_state *ctrl_state,
send_pcep_message(pcc_state, msg);
req->was_sent = true;
- /* TODO: Enable this back when the pcep config changes are merged back
- */
- // timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds;
- timeout = 30;
+ timeout = pcc_state->pce_opts->config_opts.pcep_request_time_seconds;
pcep_thread_schedule_timeout(ctrl_state, pcc_state->id,
TO_COMPUTATION_REQUEST, timeout,
(void *)req, &req->t_retry);
@@ -1641,7 +1757,6 @@ void set_pcc_address(struct pcc_state *pcc_state, struct lsp_nb_key *nbkey,
}
}
-
/* ------------ Data Structure Helper Functions ------------ */
void lookup_plspid(struct pcc_state *pcc_state, struct path *path)
diff --git a/pathd/path_pcep_pcc.h b/pathd/path_pcep_pcc.h
index ceac6f3278..9e712baf16 100644
--- a/pathd/path_pcep_pcc.h
+++ b/pathd/path_pcep_pcc.h
@@ -125,6 +125,9 @@ void pcep_pcc_sync_done(struct ctrl_state *ctrl_state,
void pcep_pcc_send_report(struct ctrl_state *ctrl_state,
struct pcc_state *pcc_state, struct path *path,
bool is_stable);
+void pcep_pcc_send_error(struct ctrl_state *ctrl_state,
+ struct pcc_state *pcc_state, struct pcep_error *path,
+ bool is_stable);
int pcep_pcc_multi_pce_sync_path(struct ctrl_state *ctrl_state, int pcc_id,
struct pcc_state **pcc_state_list);
int pcep_pcc_multi_pce_remove_pcc(struct ctrl_state *ctrl_state,
diff --git a/pathd/pathd.c b/pathd/pathd.c
index 9dc3a41638..90b8727284 100644
--- a/pathd/pathd.c
+++ b/pathd/pathd.c
@@ -294,7 +294,9 @@ void srte_segment_set_local_modification(struct srte_segment_list *s_list,
* @param endpoint The IP address of the policy endpoint
* @return The created policy
*/
-struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint)
+struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint,
+ enum srte_protocol_origin origin,
+ const char *originator)
{
struct srte_policy *policy;
@@ -302,6 +304,11 @@ struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint)
policy->color = color;
policy->endpoint = *endpoint;
policy->binding_sid = MPLS_LABEL_NONE;
+ policy->protocol_origin = origin;
+ if (originator != NULL)
+ strlcpy(policy->originator, originator,
+ sizeof(policy->originator));
+
RB_INIT(srte_candidate_head, &policy->candidate_paths);
RB_INSERT(srte_policy_head, &srte_policies, policy);
@@ -646,7 +653,9 @@ void srte_policy_apply_changes(struct srte_policy *policy)
* @return The added candidate path
*/
struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
- uint32_t preference)
+ uint32_t preference,
+ enum srte_protocol_origin origin,
+ const char *originator)
{
struct srte_candidate *candidate;
struct srte_lsp *lsp;
@@ -658,7 +667,17 @@ struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
candidate->policy = policy;
candidate->type = SRTE_CANDIDATE_TYPE_UNDEFINED;
candidate->discriminator = frr_weak_random();
+ candidate->protocol_origin = origin;
+ if (originator != NULL) {
+ strlcpy(candidate->originator, originator,
+ sizeof(candidate->originator));
+ lsp->protocol_origin = origin;
+ }
+ if (candidate->protocol_origin == SRTE_ORIGIN_PCEP
+ || candidate->protocol_origin == SRTE_ORIGIN_BGP) {
+ candidate->type = SRTE_CANDIDATE_TYPE_DYNAMIC;
+ }
lsp->candidate = candidate;
candidate->lsp = lsp;
diff --git a/pathd/pathd.h b/pathd/pathd.h
index 7d38272e85..f790a0e3c9 100644
--- a/pathd/pathd.h
+++ b/pathd/pathd.h
@@ -339,6 +339,12 @@ struct srte_policy {
/* Binding SID */
mpls_label_t binding_sid;
+ /* The Protocol-Origin. */
+ enum srte_protocol_origin protocol_origin;
+
+ /* The Originator */
+ char originator[64];
+
/* Operational Status of the policy */
enum srte_policy_status status;
@@ -352,6 +358,8 @@ struct srte_policy {
#define F_POLICY_NEW 0x0002
#define F_POLICY_MODIFIED 0x0004
#define F_POLICY_DELETED 0x0008
+ /* SRP id for PcInitiated support */
+ int srp_id;
};
RB_HEAD(srte_policy_head, srte_policy);
RB_PROTOTYPE(srte_policy_head, srte_policy, entry, srte_policy_compare)
@@ -385,7 +393,9 @@ int srte_segment_entry_set_nai(struct srte_segment_entry *segment,
void srte_segment_set_local_modification(struct srte_segment_list *s_list,
struct srte_segment_entry *s_entry,
uint32_t ted_sid);
-struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint);
+struct srte_policy *srte_policy_add(uint32_t color, struct ipaddr *endpoint,
+ enum srte_protocol_origin origin,
+ const char *originator);
void srte_policy_del(struct srte_policy *policy);
struct srte_policy *srte_policy_find(uint32_t color, struct ipaddr *endpoint);
int srte_policy_update_ted_sid(void);
@@ -395,7 +405,9 @@ void srte_apply_changes(void);
void srte_clean_zebra(void);
void srte_policy_apply_changes(struct srte_policy *policy);
struct srte_candidate *srte_candidate_add(struct srte_policy *policy,
- uint32_t preference);
+ uint32_t preference,
+ enum srte_protocol_origin origin,
+ const char *originator);
void srte_candidate_del(struct srte_candidate *candidate);
void srte_candidate_set_bandwidth(struct srte_candidate *candidate,
float bandwidth, bool required);
diff --git a/pbrd/pbr_main.c b/pbrd/pbr_main.c
index 1badaf95bd..7861559034 100644
--- a/pbrd/pbr_main.c
+++ b/pbrd/pbr_main.c
@@ -119,6 +119,7 @@ struct quagga_signal_t pbr_signals[] = {
static const struct frr_yang_module_info *const pbrd_yang_modules[] = {
&frr_filter_info,
&frr_interface_info,
+ &frr_vrf_info,
};
FRR_DAEMON_INFO(pbrd, PBR, .vty_port = PBR_VTY_PORT,
diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c
index 216834fe0c..3d56fc3daa 100644
--- a/pbrd/pbr_vty.c
+++ b/pbrd/pbr_vty.c
@@ -1143,10 +1143,14 @@ static const struct cmd_variable_handler pbr_map_name[] = {
}
};
+extern struct zebra_privs_t pbr_privs;
+
void pbr_vty_init(void)
{
cmd_variable_handler_register(pbr_map_name);
+ vrf_cmd_init(NULL, &pbr_privs);
+
install_node(&interface_node);
if_cmd_init();
diff --git a/pceplib/pcep_msg_objects.h b/pceplib/pcep_msg_objects.h
index 959a6f8cf6..f26618e291 100644
--- a/pceplib/pcep_msg_objects.h
+++ b/pceplib/pcep_msg_objects.h
@@ -385,11 +385,16 @@ struct pcep_object_lsp {
bool flag_c;
};
+#define ENTERPRISE_NUMBER_CISCO 9
+#define ENTERPRISE_COLOR_CISCO 65540
/* RFC 7470 */
struct pcep_object_vendor_info {
struct pcep_object_header header;
uint32_t enterprise_number;
uint32_t enterprise_specific_info;
+ uint32_t enterprise_specific_info1; /* cisco sends color for PcInit */
+ uint32_t enterprise_specific_info2;
+ uint32_t enterprise_specific_info3;
};
/* RFC 8282 */
diff --git a/pceplib/pcep_msg_objects_encoding.c b/pceplib/pcep_msg_objects_encoding.c
index 9ab96f7bce..69420f8e7a 100644
--- a/pceplib/pcep_msg_objects_encoding.c
+++ b/pceplib/pcep_msg_objects_encoding.c
@@ -1339,8 +1339,15 @@ pcep_decode_obj_vendor_info(struct pcep_object_header *hdr,
struct pcep_object_vendor_info *obj =
(struct pcep_object_vendor_info *)common_object_create(
hdr, sizeof(struct pcep_object_vendor_info));
+
obj->enterprise_number = ntohl(*((uint32_t *)(obj_buf)));
obj->enterprise_specific_info = ntohl(*((uint32_t *)(obj_buf + 4)));
+ if (obj->enterprise_number == ENTERPRISE_NUMBER_CISCO
+ && obj->enterprise_specific_info == ENTERPRISE_COLOR_CISCO)
+ obj->enterprise_specific_info1 =
+ ntohl(*((uint32_t *)(obj_buf + 8)));
+ else
+ obj->enterprise_specific_info1 = 0;
return (struct pcep_object_header *)obj;
}
diff --git a/pceplib/pcep_msg_tlvs.h b/pceplib/pcep_msg_tlvs.h
index 5197201e40..6dd2b56552 100644
--- a/pceplib/pcep_msg_tlvs.h
+++ b/pceplib/pcep_msg_tlvs.h
@@ -53,16 +53,16 @@ extern "C" {
* https://www.iana.org/assignments/pcep/pcep.xhtml */
enum pcep_object_tlv_types {
PCEP_OBJ_TLV_TYPE_NO_PATH_VECTOR = 1,
- PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST = 4, /* RFC 5541 */
+ PCEP_OBJ_TLV_TYPE_OBJECTIVE_FUNCTION_LIST = 4, /* RFC 5541 */
PCEP_OBJ_TLV_TYPE_VENDOR_INFO = 7, /* RFC 7470 */
PCEP_OBJ_TLV_TYPE_STATEFUL_PCE_CAPABILITY = 16, /* RFC 8231 */
- PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME = 17, /* RFC 8232 */
- PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS = 18, /* RFC 8231 */
- PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS = 19, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_SYMBOLIC_PATH_NAME = 17, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_IPV4_LSP_IDENTIFIERS = 18, /* RFC 8231 */
+ PCEP_OBJ_TLV_TYPE_IPV6_LSP_IDENTIFIERS = 19, /* RFC 8231 */
PCEP_OBJ_TLV_TYPE_LSP_ERROR_CODE = 20, /* RFC 8232 */
PCEP_OBJ_TLV_TYPE_RSVP_ERROR_SPEC = 21, /* RFC 8232 */
PCEP_OBJ_TLV_TYPE_LSP_DB_VERSION = 23, /* RFC 8232 */
- PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID = 24, /* RFC 8232 */
+ PCEP_OBJ_TLV_TYPE_SPEAKER_ENTITY_ID = 24, /* RFC 8232 */
PCEP_OBJ_TLV_TYPE_SR_PCE_CAPABILITY =
26, /* draft-ietf-pce-segment-routing-16 */
PCEP_OBJ_TLV_TYPE_PATH_SETUP_TYPE = 28, /* RFC 8408 */
@@ -77,10 +77,12 @@ enum pcep_object_tlv_types {
PCEP_OBJ_TLV_TYPE_SRPOLICY_CPATH_PREFERENCE =
63, /*TDB5 draft-barth-pce-segment-routing-policy-cp-04 */
PCEP_OBJ_TLV_TYPE_UNKNOWN = 128,
- PCEP_OBJ_TLV_TYPE_ARBITRARY =
- 65533 /* Max IANA To write arbitrary data */
+ PCEP_OBJ_TYPE_CISCO_BSID = 65505,
+ /* Max IANA To write arbitrary data */
+ PCEP_OBJ_TLV_TYPE_ARBITRARY = 65533
};
+
struct pcep_object_tlv_header {
enum pcep_object_tlv_types type;
/* Pointer into encoded_message field from the pcep_message */
diff --git a/pceplib/pcep_msg_tlvs_encoding.c b/pceplib/pcep_msg_tlvs_encoding.c
index 37f3353f76..d59c97c9da 100644
--- a/pceplib/pcep_msg_tlvs_encoding.c
+++ b/pceplib/pcep_msg_tlvs_encoding.c
@@ -838,7 +838,15 @@ struct pcep_object_tlv_header *pcep_decode_tlv(const uint8_t *tlv_buf)
return NULL;
}
- tlv_decoder_funcptr tlv_decoder = tlv_decoders[tlv_hdr.type];
+ tlv_decoder_funcptr tlv_decoder = NULL;
+ if (tlv_hdr.type == PCEP_OBJ_TYPE_CISCO_BSID) {
+ pcep_log(LOG_INFO,
+ "%s: Cisco BSID TLV decoder found for TLV type [%d]",
+ __func__, tlv_hdr.type);
+ tlv_decoder = tlv_decoders[PCEP_OBJ_TLV_TYPE_ARBITRARY];
+ } else {
+ tlv_decoder = tlv_decoders[tlv_hdr.type];
+ }
if (tlv_decoder == NULL) {
pcep_log(LOG_INFO, "%s: No TLV decoder found for TLV type [%d]",
__func__, tlv_hdr.type);
diff --git a/pceplib/test/pcep_msg_tools_test.c b/pceplib/test/pcep_msg_tools_test.c
index 5a7644b21a..e25ddb2179 100644
--- a/pceplib/test/pcep_msg_tools_test.c
+++ b/pceplib/test/pcep_msg_tools_test.c
@@ -809,8 +809,8 @@ void test_pcep_msg_read_pcep_report_cisco_pcc()
CU_ASSERT_EQUAL(lsp->header.object_type, PCEP_OBJ_TYPE_LSP);
CU_ASSERT_EQUAL(lsp->header.encoded_object_length, 60);
CU_ASSERT_PTR_NOT_NULL(lsp->header.tlv_list);
- /* The TLV with ID 65505 is not recognized, and its not in the list */
- CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 2);
+ /* The TLV with ID 65505 is now recognized, and its in the list */
+ CU_ASSERT_EQUAL(lsp->header.tlv_list->num_entries, 3);
CU_ASSERT_EQUAL(lsp->plsp_id, 524303);
CU_ASSERT_EQUAL(lsp->operational_status, PCEP_LSP_OPERATIONAL_DOWN);
CU_ASSERT_TRUE(lsp->flag_a);
diff --git a/python/xrelfo.py b/python/xrelfo.py
index 0ecd008579..17262da8d9 100644
--- a/python/xrelfo.py
+++ b/python/xrelfo.py
@@ -357,6 +357,7 @@ def main():
argp.add_argument('--out-by-file', type=str, help='write by-file JSON output')
argp.add_argument('-Wlog-format', action='store_const', const=True)
argp.add_argument('-Wlog-args', action='store_const', const=True)
+ argp.add_argument('-Werror', action='store_const', const=True)
argp.add_argument('--profile', action='store_const', const=True)
argp.add_argument('binaries', metavar='BINARY', nargs='+', type=str, help='files to read (ELF files or libtool objects)')
args = argp.parse_args()
@@ -380,9 +381,12 @@ def _main(args):
traceback.print_exc()
for option in dir(args):
- if option.startswith('W'):
+ if option.startswith('W') and option != 'Werror':
checks = sorted(xrelfo.check(args))
sys.stderr.write(''.join([c[-1] for c in checks]))
+
+ if args.Werror and len(checks) > 0:
+ errors += 1
break
diff --git a/ripd/ripd.c b/ripd/ripd.c
index c6c82fb65a..7d940efd9c 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -3690,6 +3690,8 @@ void rip_vrf_init(void)
{
vrf_init(rip_vrf_new, rip_vrf_enable, rip_vrf_disable, rip_vrf_delete,
rip_vrf_enable);
+
+ vrf_cmd_init(NULL, &ripd_privs);
}
void rip_vrf_terminate(void)
diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c
index 3b8d2076f3..a0ea18f3e9 100644
--- a/ripngd/ripngd.c
+++ b/ripngd/ripngd.c
@@ -2691,6 +2691,8 @@ void ripng_vrf_init(void)
{
vrf_init(ripng_vrf_new, ripng_vrf_enable, ripng_vrf_disable,
ripng_vrf_delete, ripng_vrf_enable);
+
+ vrf_cmd_init(NULL, &ripngd_privs);
}
void ripng_vrf_terminate(void)
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json
new file mode 100644
index 0000000000..2eeebad4b0
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_base.json
@@ -0,0 +1,192 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json
new file mode 100644
index 0000000000..419bcc3dd9
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt2.json
@@ -0,0 +1,8 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]": null,
+ "[3]:[0]:[32]:[10.100.0.1]": null,
+ "[3]:[0]:[32]:[10.100.0.2]": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json
new file mode 100644
index 0000000000..2eeebad4b0
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vni_routes_no_rt5.json
@@ -0,0 +1,192 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json
new file mode 100644
index 0000000000..833f98657b
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_base.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"50.0.1.11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } } \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json
new file mode 100644
index 0000000000..833f98657b
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt2.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"50.0.1.11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } } \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json
new file mode 100644
index 0000000000..4a292bddbe
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv4_no_rt5.json
@@ -0,0 +1,6 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": null } }
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json
new file mode 100644
index 0000000000..3dc3fcf9cb
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_base.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"50:0:1::11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global"
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json
new file mode 100644
index 0000000000..3dc3fcf9cb
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt2.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"50:0:1::11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global"
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json
new file mode 100644
index 0000000000..6c11d894eb
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgp_vrf_ipv6_no_rt5.json
@@ -0,0 +1,6 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": null } } \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf
new file mode 100644
index 0000000000..63aa99a832
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/bgpd.conf
@@ -0,0 +1,30 @@
+router bgp 101
+ bgp router-id 10.100.0.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.2 remote-as 102
+ !
+ address-family l2vpn evpn
+ neighbor 10.0.1.2 activate
+ advertise-all-vni
+ exit-address-family
+!
+router bgp 101 vrf vrf-blue
+ bgp router-id 10.100.0.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 50.0.1.11 remote-as 111
+ neighbor 50:0:1::11 remote-as 111
+ !
+ address-family ipv4 unicast
+ no neighbor 50:0:1::11 activate
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ neighbor 50:0:1::11 activate
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ advertise ipv4 unicast gateway-ip
+ advertise ipv6 unicast gateway-ip
+ exit-address-family \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf
new file mode 100644
index 0000000000..99a2e89ef3
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra.conf
@@ -0,0 +1,14 @@
+!
+log file zebra.log
+!
+ip route 10.100.0.2/32 10.0.1.2
+!
+vrf vrf-blue
+ vni 1000 prefix-routes-only
+ exit-vrf
+!
+interface lo
+ ip address 10.100.0.1/32
+interface PE1-eth0
+ ip address 10.0.1.1/24
+!
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json
new file mode 100644
index 0000000000..2dcf35d91b
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_base.json
@@ -0,0 +1,56 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32":[
+ {
+ "prefix":"100.0.0.21\/32",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json
new file mode 100644
index 0000000000..2dcf35d91b
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt2.json
@@ -0,0 +1,56 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32":[
+ {
+ "prefix":"100.0.0.21\/32",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json
new file mode 100644
index 0000000000..9c3091dc50
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv4_no_rt5.json
@@ -0,0 +1,29 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32": null
+}
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json
new file mode 100644
index 0000000000..229c927656
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_base.json
@@ -0,0 +1,55 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128":[
+ {
+ "prefix":"100::21\/128",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json
new file mode 100644
index 0000000000..229c927656
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt2.json
@@ -0,0 +1,55 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128":[
+ {
+ "prefix":"100::21\/128",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json
new file mode 100644
index 0000000000..94f82e6d4c
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE1/zebra_vrf_ipv6_no_rt5.json
@@ -0,0 +1,29 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json
new file mode 100644
index 0000000000..7b8d38e492
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_base.json
@@ -0,0 +1,192 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json
new file mode 100644
index 0000000000..6273b3e728
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt2.json
@@ -0,0 +1,68 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]": null,
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json
new file mode 100644
index 0000000000..7b8d38e492
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vni_routes_no_rt5.json
@@ -0,0 +1,192 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json
new file mode 100644
index 0000000000..c03d70195f
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_base.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } } \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json
new file mode 100644
index 0000000000..7f1b8d2ef4
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt2.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":null,
+ "bestpath":null,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json
new file mode 100644
index 0000000000..52e4311635
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv4_no_rt5.json
@@ -0,0 +1,6 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": null } } \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json
new file mode 100644
index 0000000000..1d90c9c798
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_base.json
@@ -0,0 +1,28 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global",
+ "used":true
+ }
+ ]
+ }
+] } } \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json
new file mode 100644
index 0000000000..a0e63c6e25
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt2.json
@@ -0,0 +1,28 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":null,
+ "bestpath":null,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json
new file mode 100644
index 0000000000..789fe69b28
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgp_vrf_ipv6_no_rt5.json
@@ -0,0 +1,6 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": null } } \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf
new file mode 100644
index 0000000000..59fee15dfc
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/bgpd.conf
@@ -0,0 +1,14 @@
+router bgp 102
+ bgp router-id 10.100.0.2
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.1 remote-as 101
+ !
+ address-family l2vpn evpn
+ neighbor 10.0.1.1 activate
+ advertise-all-vni
+ enable-resolve-overlay-index
+ exit-address-family
+!
+router bgp 101 vrf vrf-blue
+ bgp router-id 10.100.0.2
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf
new file mode 100644
index 0000000000..b78cdcc512
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra.conf
@@ -0,0 +1,14 @@
+!
+log file zebra.log
+!
+ip route 10.100.0.1/32 10.0.1.1
+!
+vrf vrf-blue
+ vni 1000 prefix-routes-only
+ exit-vrf
+!
+interface lo
+ ip address 10.100.0.2/32
+interface PE2-eth0
+ ip address 10.0.1.2/24
+!
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json
new file mode 100644
index 0000000000..b3a3640be4
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_base.json
@@ -0,0 +1,56 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32":[
+ {
+ "prefix":"100.0.0.21\/32",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":40,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json
new file mode 100644
index 0000000000..996fe52f44
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt2.json
@@ -0,0 +1,29 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json
new file mode 100644
index 0000000000..996fe52f44
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv4_no_rt5.json
@@ -0,0 +1,29 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json
new file mode 100644
index 0000000000..d5be22a2ba
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_base.json
@@ -0,0 +1,56 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128":[
+ {
+ "prefix":"100::21\/128",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":40,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json
new file mode 100644
index 0000000000..94f82e6d4c
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt2.json
@@ -0,0 +1,29 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json
new file mode 100644
index 0000000000..94f82e6d4c
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/PE2/zebra_vrf_ipv6_no_rt5.json
@@ -0,0 +1,29 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py b/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/__init__.py
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf
new file mode 100644
index 0000000000..7608ec95c3
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 111
+ bgp router-id 10.100.0.11
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 50.0.1.1 remote-as 101
+ neighbor 50:0:1::1 remote-as 101
+ !
+ address-family ipv4 unicast
+ network 100.0.0.21/32
+ no neighbor 50:0:1::1 activate
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ network 100::21/128
+ neighbor 50:0:1::1 activate
+ exit-address-family
+
+
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf
new file mode 100644
index 0000000000..c8c832e9d8
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int host1-eth0
+ ip address 50.0.1.11/24
+ ipv6 address 50:0:1::11/48
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf
new file mode 100644
index 0000000000..cdf4cb4feb
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf
new file mode 100644
index 0000000000..9135545c58
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/host2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int host1-eth0
+ ip address 50.0.1.21/24
+ ipv6 address 50:0:1::21/48
diff --git a/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py
new file mode 100755
index 0000000000..fbce2809e0
--- /dev/null
+++ b/tests/topotests/bgp-evpn-overlay-index-gateway/test_bgp_evpn_overlay_index_gateway.py
@@ -0,0 +1,385 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. ("NetDEF")
+# in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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_bgp_evpn_overlay_index_gateway.py: Test EVPN gateway IP overlay index functionality
+Following functionality is covered:
+
+ +--------+ BGP +--------+ BGP +--------+ +--------+
+ SN1 | | IPv4/v6 | | EVPN | | | |
+ ======+ Host1 +---------+ PE1 +------+ PE2 +------+ Host2 +
+ | | | | | | | |
+ +--------+ +--------+ +--------+ +--------+
+
+ Host1 is connected to PE1 and host2 is connected to PE2
+ Host1 and PE1 have IPv4/v6 BGP sessions.
+ PE1 and PE2 gave EVPN session.
+ Host1 advertises IPv4/v6 prefixes to PE1.
+ PE1 advertises these prefixes to PE2 as EVPN type-5 routes.
+ Gateway IP for these EVPN type-5 routes is host1 IP.
+ Host1 MAC/IP is advertised by PE1 as EVPN type-2 route
+
+Following testcases are covered:
+TC_1:
+Check BGP and zebra states for above topology at PE1 and PE2.
+
+TC_2:
+Stop advertising prefixes from host1. It should withdraw type-5 routes. Check states at PE1 and PE2
+Advertise the prefixes again. Check states.
+
+TC_3:
+Shut down VxLAN interface at PE1. This should withdraw type-2 routes. Check states at PE1 and PE2.
+Enable VxLAN interface again. Check states.
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+import time
+import platform
+
+#Current Working Directory
+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
+from lib.topolog import logger
+from lib.common_config import (
+ step,
+ write_test_header,
+ write_test_footer,
+ generate_support_bundle,
+)
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+#Global variables
+PES = ['PE1', 'PE2']
+HOSTS = ['host1', 'host2']
+PE_SUFFIX = {'PE1': '1', 'PE2': '2'}
+HOST_SUFFIX = {'host1': '1', 'host2': '2'}
+TRIGGERS = ["base", "no_rt5", "no_rt2"]
+
+
+class TemplateTopo(Topo):
+ """Test topology builder"""
+
+ def build(self, *_args, **_opts):
+ """Build function"""
+ tgen = get_topogen(self)
+
+ # This function only purpose is to define allocation and relationship
+ # between routers and add links.
+
+ # Create routers
+ for pe in PES:
+ tgen.add_router(pe)
+ for host in HOSTS:
+ tgen.add_router(host)
+
+ krel = platform.release()
+ logger.info('Kernel version ' + krel)
+
+ #Add links
+ tgen.add_link(tgen.gears['PE1'], tgen.gears['PE2'], 'PE1-eth0', 'PE2-eth0')
+ tgen.add_link(tgen.gears['PE1'], tgen.gears['host1'], 'PE1-eth1', 'host1-eth0')
+ tgen.add_link(tgen.gears['PE2'], tgen.gears['host2'], 'PE2-eth1', 'host2-eth0')
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15. Kernel present {}".format(kernelv))
+ return
+
+ if topotest.version_cmp(kernelv, '4.15') == 0:
+ l3mdev_accept = 1
+ logger.info('setting net.ipv4.tcp_l3mdev_accept={}'.format(l3mdev_accept))
+ else:
+ l3mdev_accept = 0
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ tgen.start_topology()
+
+ # Configure MAC address for hosts as these MACs are advertised with EVPN type-2 routes
+ for (name, host) in tgen.gears.items():
+ if name not in HOSTS:
+ continue
+
+ host_mac = "1a:2b:3c:4d:5e:6{}".format(HOST_SUFFIX[name])
+ host.run("ip link set dev {}-eth0 down").format(name)
+ host.run("ip link set dev {0}-eth0 address {1}".format(name, host_mac))
+ host.run("ip link set dev {}-eth0 up").format(name)
+
+ # Configure PE VxLAN and Bridge interfaces
+ for (name, pe) in tgen.gears.items():
+ if name not in PES:
+ continue
+ vtep_ip = "10.100.0.{}".format(PE_SUFFIX[name])
+ bridge_ip = "50.0.1.{}/24".format(PE_SUFFIX[name])
+ bridge_ipv6 = "50:0:1::{}/48".format(PE_SUFFIX[name])
+
+ pe.run("ip link add vrf-blue type vrf table 10")
+ pe.run("ip link set dev vrf-blue up")
+ pe.run("ip link add vxlan100 type vxlan id 100 dstport 4789 local {}".format(vtep_ip))
+ pe.run("ip link add name br100 type bridge stp_state 0")
+ pe.run("ip link set dev vxlan100 master br100")
+ pe.run("ip link set dev {}-eth1 master br100".format(name))
+ pe.run("ip addr add {} dev br100".format(bridge_ip))
+ pe.run("ip link set up dev br100")
+ pe.run("ip link set up dev vxlan100")
+ pe.run("ip link set up dev {}-eth1".format(name))
+ pe.run("ip link set dev br100 master vrf-blue")
+ pe.run("ip -6 addr add {} dev br100".format(bridge_ipv6))
+
+ pe.run("ip link add vxlan1000 type vxlan id 1000 dstport 4789 local {}".format(vtep_ip))
+ pe.run("ip link add name br1000 type bridge stp_state 0")
+ pe.run("ip link set dev vxlan1000 master br100")
+ pe.run("ip link set up dev br1000")
+ pe.run("ip link set up dev vxlan1000")
+ pe.run("ip link set dev br1000 master vrf-blue")
+
+ pe.run("sysctl -w net.ipv4.ip_forward=1")
+ pe.run("sysctl -w net.ipv6.conf.all.forwarding=1")
+ pe.run("sysctl -w net.ipv4.udp_l3mdev_accept={}".format(l3mdev_accept))
+ pe.run("sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept))
+
+ # For all registred routers, load the zebra configuration file
+ for (name, router) in tgen.routers().items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(name))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(name))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ logger.info("Running setup_module() done")
+ topotest.sleep(200)
+
+
+def teardown_module(mod):
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def evpn_gateway_ip_show_op_check(trigger=" "):
+ """
+ This function checks CLI O/P for commands mentioned in show_commands for a given trigger
+ :param trigger: Should be a trigger present in TRIGGERS
+ :return: Returns a tuple (result: None for success, retmsg: Log message to be printed on failure)
+ """
+ tgen = get_topogen()
+
+ if trigger not in TRIGGERS:
+ return "Unexpected trigger", "Unexpected trigger {}".format(trigger)
+
+ show_commands = {'bgp_vni_routes': 'show bgp l2vpn evpn route vni 100 json',
+ 'bgp_vrf_ipv4' : 'show bgp vrf vrf-blue ipv4 json',
+ 'bgp_vrf_ipv6' : 'show bgp vrf vrf-blue ipv6 json',
+ 'zebra_vrf_ipv4': 'show ip route vrf vrf-blue json',
+ 'zebra_vrf_ipv6': 'show ipv6 route vrf vrf-blue json'}
+
+ for (name, pe) in tgen.gears.items():
+ if name not in PES:
+ continue
+
+ for (cmd_key, command) in show_commands.items():
+ expected_op_file = "{0}/{1}/{2}_{3}.json".format(CWD, name, cmd_key, trigger)
+ expected_op = json.loads(open(expected_op_file).read())
+
+ test_func = partial(topotest.router_json_cmp, pe, command, expected_op)
+ ret, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{0}" JSON output mismatch for {1}'.format(name, command)
+ if result is not None:
+ return result, assertmsg
+
+ return None, "Pass"
+
+
+def test_evpn_gateway_ip_basic_topo(request):
+ """
+ Tets EVPN overlay index gateway IP functionality. VErify show O/Ps on PE1 and PE2
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15")
+ write_test_footer(tc_name)
+ return
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Check O/Ps for EVPN gateway IP overlay Index functionality at PE1 and PE2")
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("base")
+
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_gateway_ip_flap_rt5(request):
+ """
+ Withdraw EVPN type-5 routes and check O/Ps at PE1 and PE2
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15")
+ write_test_footer(tc_name)
+ return
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ h1 = tgen.gears['host1']
+
+ step("Withdraw type-5 routes")
+
+ h1.run('vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv4" \
+ -c "no network 100.0.0.21/32"')
+ h1.run('vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv6" \
+ -c "no network 100::21/128"')
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("no_rt5")
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ step("Advertise type-5 routes again")
+
+ h1.run('vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv4" \
+ -c "network 100.0.0.21/32"')
+ h1.run('vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv6" \
+ -c "network 100::21/128"')
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("base")
+ if result is not None:
+ generate_support_bundle()
+
+ assert result is None, assertmsg
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_gateway_ip_flap_rt2(request):
+ """
+ Withdraw EVPN type-2 routes and check O/Ps at PE1 and PE2
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15")
+ write_test_footer(tc_name)
+ return
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+
+ step("Shut down VxLAN interface at PE1 which results in withdraw of type-2 routes")
+
+ pe1 = tgen.gears['PE1']
+
+ pe1.run('ip link set dev vxlan100 down')
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("no_rt2")
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ step("Bring up VxLAN interface at PE1 and advertise type-2 routes again")
+
+ pe1.run('ip link set dev vxlan100 up')
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("base")
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ write_test_footer(tc_name)
+
+
+def test_memory_leak():
+ """Run the memory leak test and report results"""
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_route_route_map_match2/__init__.py b/tests/topotests/bgp_default_route_route_map_match2/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/__init__.py
diff --git a/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf
new file mode 100644
index 0000000000..ee7a92f116
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf
@@ -0,0 +1,13 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 default-originate route-map default
+ exit-address-family
+!
+ip prefix-list r2 permit 10.0.0.0/22
+!
+route-map default permit 10
+ match ip address prefix-list r2
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf
new file mode 100644
index 0000000000..e2c399e536
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf
new file mode 100644
index 0000000000..00c96cc58b
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf
new file mode 100644
index 0000000000..f355ab1517
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 10.0.0.1/22
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py
new file mode 100644
index 0000000000..42a6b6edf6
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if default-originate works with conditional match.
+If 10.0.0.0/22 is recived from r2, then we announce 0.0.0.0/0
+to r2.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from mininet.topo import Topo
+from lib.common_config import step
+
+
+class TemplateTopo(Topo):
+ def build(self, *_args, **_opts):
+ tgen = get_topogen(self)
+
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_default_originate_route_map():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_default_route_is_valid(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
+ expected = {"paths": [{"valid": True}]}
+ return topotest.json_cmp(output, expected)
+
+ step("Converge network")
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see bgp convergence at r2"
+
+ step("Withdraw 10.0.0.0/22 from R2")
+ router.vtysh_cmd(
+ "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected"
+ )
+
+ step("Check if we don't have 0.0.0.0/0 at R2")
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5)
+ assert result is not None, "0.0.0.0/0 exists at r2"
+
+ step("Announce 10.0.0.0/22 from R2")
+ router.vtysh_cmd("conf t\nrouter bgp\naddress-family ipv4\nredistribute connected")
+
+ step("Check if we have 0.0.0.0/0 at R2")
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "0.0.0.0/0 does not exist at r2"
+
+ step("Withdraw 10.0.0.0/22 from R2 again")
+ router.vtysh_cmd(
+ "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected"
+ )
+
+ step("Check if we don't have 0.0.0.0/0 at R2 again")
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5)
+ assert result is not None, "0.0.0.0/0 exists at r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf
index 6ef8b1c0f4..32ac7c517b 100644
--- a/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf
+++ b/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf
@@ -12,6 +12,7 @@ bgp community-list standard default seq 5 permit 65000:1
route-map default permit 10
match community default
set metric 123
+ set as-path prepend 65000 65000 65000
!
route-map internal permit 10
set community 65000:1
diff --git a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py
index d9ea5db278..12d1d01bfb 100644
--- a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py
+++ b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py
@@ -94,7 +94,9 @@ def test_bgp_default_originate_route_map():
def _bgp_default_route_has_metric(router):
output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
- expected = {"paths": [{"metric": 123}]}
+ expected = {
+ "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}]
+ }
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge, router)
diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf
index cb07ea9fdf..6f6d394402 100644
--- a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf
+++ b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf
@@ -8,4 +8,5 @@ router bgp 65000
!
route-map default permit 10
set metric 123
+ set as-path prepend 65000 65000 65000
!
diff --git a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py
index 9a22c58b16..2622c33f5b 100644
--- a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py
+++ b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py
@@ -93,7 +93,9 @@ def test_bgp_default_originate_route_map():
def _bgp_default_route_has_metric(router):
output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
- expected = {"paths": [{"metric": 123}]}
+ expected = {
+ "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}]
+ }
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_converge, router)
diff --git a/tests/topotests/bgp_route_map/test_route_map_topo1.py b/tests/topotests/bgp_route_map/test_route_map_topo1.py
index 0158e24d31..74172501db 100644
--- a/tests/topotests/bgp_route_map/test_route_map_topo1.py
+++ b/tests/topotests/bgp_route_map/test_route_map_topo1.py
@@ -20,47 +20,6 @@
# OF THIS SOFTWARE.
#
-#################################
-# TOPOLOGY
-#################################
-"""
-
- +-------+
- +------- | R2 |
- | +-------+
- | |
- +-------+ |
- | R1 | |
- +-------+ |
- | |
- | +-------+ +-------+
- +---------- | R3 |----------| R4 |
- +-------+ +-------+
-
-"""
-
-#################################
-# TEST SUMMARY
-#################################
-"""
-Following tests are covered to test route-map functionality:
-TC_34:
- Verify if route-maps is applied in both inbound and
- outbound direction to same neighbor/interface.
-TC_36:
- Test permit/deny statements operation in route-maps with a
- permutation and combination of permit/deny in prefix-lists
-TC_35:
- Test multiple sequence numbers in a single route-map for different
- match/set clauses.
-TC_37:
- Test add/remove route-maps with multiple set
- clauses and without any match statement.(Set only)
-TC_38:
- Test add/remove route-maps with multiple match
- clauses and without any set statement.(Match only)
-"""
-
import sys
import json
import time
@@ -91,6 +50,7 @@ from lib.common_config import (
create_bgp_community_lists,
interface_status,
create_route_maps,
+ create_static_routes,
create_prefix_lists,
verify_route_maps,
check_address_types,
@@ -107,6 +67,46 @@ from lib.bgp import (
)
from lib.topojson import build_topo_from_json, build_config_from_json
+#################################
+# TOPOLOGY
+#################################
+"""
+
+ +-------+
+ +------- | R2 |
+ | +-------+
+ | |
+ +-------+ |
+ | R1 | |
+ +-------+ |
+ | |
+ | +-------+ +-------+
+ +---------- | R3 |----------| R4 |
+ +-------+ +-------+
+
+"""
+
+#################################
+# TEST SUMMARY
+#################################
+"""
+Following tests are covered to test route-map functionality:
+TC_34:
+ Verify if route-maps is applied in both inbound and
+ outbound direction to same neighbor/interface.
+TC_36:
+ Test permit/deny statements operation in route-maps with a
+ permutation and combination of permit/deny in prefix-lists
+TC_35:
+ Test multiple sequence numbers in a single route-map for different
+ match/set clauses.
+TC_37:
+ Test add/remove route-maps with multiple set
+ clauses and without any match statement.(Set only)
+TC_38:
+ Test add/remove route-maps with multiple match
+ clauses and without any set statement.(Match only)
+"""
# Global variables
bgp_convergence = False
@@ -475,8 +475,8 @@ def test_route_map_inbound_outbound_same_neighbor_p0(request):
result = verify_rib(
tgen, adt, dut, input_dict_2, protocol=protocol, expected=False
)
- assert result is not True, "Testcase {} : Failed \n"
- "routes are not present in rib \n Error: {}".format(tc_name, result)
+ assert result is not True, ("Testcase {} : Failed \n"
+ "routes are not present in rib \n Error: {}".format(tc_name, result))
logger.info("Expected behaviour: {}".format(result))
# Verifying RIB routes
@@ -495,8 +495,8 @@ def test_route_map_inbound_outbound_same_neighbor_p0(request):
result = verify_rib(
tgen, adt, dut, input_dict, protocol=protocol, expected=False
)
- assert result is not True, "Testcase {} : Failed \n "
- "routes are not present in rib \n Error: {}".format(tc_name, result)
+ assert result is not True, ("Testcase {} : Failed \n "
+ "routes are not present in rib \n Error: {}".format(tc_name, result))
logger.info("Expected behaviour: {}".format(result))
write_test_footer(tc_name)
@@ -687,14 +687,17 @@ def test_route_map_with_action_values_combination_of_prefix_action_p0(
}
# tgen.mininet_cli()
- result = verify_rib(
- tgen, adt, dut, input_dict_2, protocol=protocol, expected=False
- )
if "deny" in [prefix_action, rmap_action]:
- assert result is not True, "Testcase {} : Failed \n "
- "Routes are still present \n Error: {}".format(tc_name, result)
+ result = verify_rib(
+ tgen, adt, dut, input_dict_2, protocol=protocol, expected=False
+ )
+ assert result is not True, ("Testcase {} : Failed \n "
+ "Routes are still present \n Error: {}".format(tc_name, result))
logger.info("Expected behaviour: {}".format(result))
else:
+ result = verify_rib(
+ tgen, adt, dut, input_dict_2, protocol=protocol
+ )
assert result is True, "Testcase {} : Failed \n Error: {}".format(
tc_name, result
)
diff --git a/tests/topotests/ospf6_topo1/test_ospf6_topo1.py b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py
index f8c3476e18..bbd18a57ff 100644
--- a/tests/topotests/ospf6_topo1/test_ospf6_topo1.py
+++ b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py
@@ -360,6 +360,36 @@ def test_linux_ipv6_kernel_routingTable():
)
+def test_ospfv3_routingTable_write_multiplier():
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # For debugging, uncomment the next line
+ # tgen.mininet_cli()
+
+ # Modify R1 write muliplier and reset the interfaces
+ r1 = tgen.gears["r1"]
+
+ r1.vtysh_cmd("conf t\nrouter ospf6\n write-multiplier 100")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5")
+
+ # Verify OSPFv3 Routing Table
+ for router, rnode in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+ assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
+
+
def test_shutdown_check_stderr():
tgen = get_topogen()
diff --git a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py
index e1857abc4f..b158099d9a 100755
--- a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py
+++ b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py
@@ -5,7 +5,7 @@
# Part of NetDEF Topology Tests
#
# Copyright (c) 2021 by Niral Networks, Inc. ("Niral Networks")
-# Used Copyright (c) 2016 by Network Device Education Foundation,
+# Used Copyright (c) 2016 by Network Device Education Foundation,
# Inc. ("NetDEF") in this file.
#
# Permission to use, copy, modify, and/or distribute this software
@@ -179,13 +179,9 @@ def setup_module(mod):
"ip link set {0}-stubnet master {0}-cust1",
]
- cmds1 = [
- "ip link set {0}-sw5 master {0}-cust1",
- ]
+ cmds1 = ["ip link set {0}-sw5 master {0}-cust1"]
- cmds2 = [
- "ip link set {0}-sw6 master {0}-cust1",
- ]
+ cmds2 = ["ip link set {0}-sw6 master {0}-cust1"]
# For all registered routers, load the zebra configuration file
for rname, router in tgen.routers().items():
@@ -219,6 +215,7 @@ def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
+
def test_wait_protocol_convergence():
"Wait for OSPFv3 to converge"
tgen = get_topogen()
@@ -261,7 +258,7 @@ def compare_show_ipv6_vrf(rname, expected):
# Use the vtysh output, with some masking to make comparison easy
vrf_name = "{0}-cust1".format(rname)
current = topotest.ip6_route_zebra(tgen.gears[rname], vrf_name)
-
+
# Use just the 'O'spf lines of the output
linearr = []
for line in current.splitlines():
@@ -331,7 +328,11 @@ def test_linux_ipv6_kernel_routingTable():
for i in range(1, 5):
# Actual output from router
- actual = tgen.gears["r{}".format(i)].run("ip -6 route show vrf r{}-cust1".format(i)).rstrip()
+ actual = (
+ tgen.gears["r{}".format(i)]
+ .run("ip -6 route show vrf r{}-cust1".format(i))
+ .rstrip()
+ )
if "nhid" in actual:
refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i))
else:
@@ -362,9 +363,9 @@ def test_linux_ipv6_kernel_routingTable():
"unreachable fe80::/64 "
):
continue
- if 'anycast' in line:
+ if "anycast" in line:
continue
- if 'multicast' in line:
+ if "multicast" in line:
continue
filtered_lines.append(line)
actual = "\n".join(filtered_lines).splitlines(1)
@@ -398,6 +399,35 @@ def test_linux_ipv6_kernel_routingTable():
)
+def test_ospfv3_routingTable_write_multiplier():
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # For debugging, uncomment the next line
+ # tgen.mininet_cli()
+ # Modify R1 write muliplier and reset the interfaces
+ r1 = tgen.gears["r1"]
+
+ r1.vtysh_cmd("conf t\nrouter ospf6 vrf r1-cust1 \n write-multiplier 100")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5")
+
+ # Verify OSPFv3 Routing Table
+ for router, rnode in tgen.routers().iteritems():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/show_ipv6_vrf_route.ref".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6_vrf, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+ assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
+
+
def test_shutdown_check_stderr():
tgen = get_topogen()
diff --git a/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json
new file mode 100644
index 0000000000..28e890da50
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json
@@ -0,0 +1,199 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.3"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
index 88b549732c..41960ac79f 100644
--- a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
@@ -774,6 +774,2642 @@ def test_ospf_type5_summary_tc48_p0(request):
write_test_footer(tc_name)
+def test_ospf_type5_summary_tc42_p0(request):
+ """OSPF summarisation functionality."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route. with aggregate timer as 6 sec"
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ],
+ "aggr_timer": 6,
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "del_aggr_timer": True,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ dut = "r1"
+ step("All 5 routes are advertised after deletion of configured summary.")
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("configure the summary again and delete static routes .")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole", "delete": True}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ step("Verify that summary route is withdrawn from R1.")
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Add back static routes.")
+ input_dict_static_rtes = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1."
+ )
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show configure summaries.")
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure new static route which is matching configured summary.")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [{"network": NETWORK_11["ipv4"], "next_hop": "blackhole"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete one of the static route.")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK_11["ipv4"], "next_hop": "blackhole", "delete": True}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted static route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step(
+ "Configure redistribute connected and configure ospf external"
+ " summary address to summarise the connected routes."
+ )
+
+ dut = "r0"
+ red_connected(dut)
+ clear_ospf(tgen, dut)
+
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"]
+
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {"summary-address": [{"prefix": ip_net.split("/")[0], "mask": "8"}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured "
+ "summary address on R0 and only one route is sent to R1."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "10.0.0.0/8"}]}}
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Shut one of the interface")
+ intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted connected route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Un do shut the interface")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted connected route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete OSPF process.")
+ ospf_del = {"r0": {"ospf": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("Reconfigure ospf process with summary")
+ reset_config_on_routers(tgen)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+ red_connected(dut)
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 and only one route is sent to R1."
+ )
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete the redistribute command in ospf.")
+ dut = "r0"
+ red_connected(dut, config=False)
+ red_static(dut, config=False)
+
+ step("Verify that summary route is withdrawn from the peer.")
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "metric": "1234",
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc45_p0(request):
+ """OSPF summarisation with Tag option"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure OSPF on all the routers of the topology.")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": "1234",
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1 with configured tag."
+ )
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1234"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": "1234",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ step("Configure Min tag value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8", "tag": 1}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure Max Tag Value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "4294967295"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("configure new static route with different tag.")
+ input_dict_static_rtes_11 = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK_11["ipv4"], "next_hop": "blackhole", "tag": "88888"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes_11)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("New tag has not been used by summary address.")
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "88888"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict_summary,
+ protocol=protocol,
+ tag="88888",
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary address")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that 6 routes are advertised to neighbour with 5 routes"
+ " without any tag, 1 route with tag."
+ )
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that summary address is flushed from neighbor.")
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Configure summary first & then configure matching static route.")
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole", "delete": True},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole", "delete": True},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Repeat steps 1 to 10 of summarisation in non Back bone area.")
+ reset_config_on_routers(tgen)
+
+ step("Change the area id on the interface on R0")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the area id on the interface ")
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": "1234",
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1 with configured tag."
+ )
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1234"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ step("Configure Min tag value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8", "tag": 1}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure Max Tag Value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "4294967295"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("configure new static route with different tag.")
+ input_dict_static_rtes_11 = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK_11["ipv4"], "next_hop": "blackhole", "tag": "88888"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes_11)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("New tag has not been used by summary address.")
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "88888"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict_summary,
+ protocol=protocol,
+ tag="88888",
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary address")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that 6 routes are advertised to neighbour with 5 routes"
+ " without any tag, 1 route with tag."
+ )
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that summary address is flushed from neighbor.")
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Configure summary first & then configure matching static route.")
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole", "delete": True},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole", "delete": True},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc46_p0(request):
+ """OSPF summarisation with advertise and no advertise option"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure OSPF on all the routers of the topology.")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route with no advertise option."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "advertise": False,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and summary route is not advertised to neighbor as"
+ " no advertise is configured.."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the " "configured summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Summary has 5 sec delay timer, sleep 5 secs...")
+ sleep(5)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ step("Reconfigure summary with no advertise.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "advertise": False,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and summary route is not advertised to neighbor as"
+ " no advertise is configured.."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the " "configured summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Change summary address from no advertise to advertise "
+ "(summary-address 10.0.0.0 255.255.0.0)"
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "advertise": False,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc47_p0(request):
+ """OSPF summarisation with route map filtering."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "configure route map and add rule to permit configured static "
+ "routes, redistribute static & connected routes with the route map."
+ )
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the rule from permit to deny in configured route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "deny",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("summary route has 5 secs dealy, sleep 5 secs")
+ sleep(5)
+ step("Verify that advertised summary route is flushed from neighbor.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Delete the configured route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {"r0": {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Reconfigure the route map with denying configure summary address.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": SUMMARY["ipv4"][0], "action": "deny"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that advertised summary route is not flushed from neighbor.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Redistribute static/connected routes without route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Configure rule to deny all the routes in route map and configure"
+ " redistribute command in ospf using route map."
+ )
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "deny"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that no summary route is originated.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure cli in this order - 2 static routes, a route map to "
+ "permit those routes, summary address in ospf to match the "
+ "configured static route network, redistribute the static "
+ "routes with route map"
+ )
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [{"network": NETWORK2["ipv4"], "next_hop": "blackhole"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][1].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": "12.0.0.0/8"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ "12.0.0.0/8": {
+ "Summary address": "12.0.0.0/8",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change route map rule for 1 of the routes to deny.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": NETWORK2["ipv4"][0], "action": "deny"},
+ {"seqid": 20, "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that originated type 5 summary lsa is not refreshed because"
+ "of the route map events."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "12.0.0.0/8"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("add rule in route map to deny configured summary address.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "12.0.0.0/8", "action": "deny"},
+ {"seqid": 20, "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that summary route is not denied, summary route should be"
+ " originated if matching prefixes are present."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "12.0.0.0/8"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc51_p2(request):
+ """OSPF CLI Show.
+
+ verify ospf ASBR summary config and show commands behaviours.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Configure all the supported OSPF ASBR summary commands on DUT.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "16",
+ "advertise": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure and re configure all the commands 10 times in a loop.")
+
+ for itrate in range(0, 10):
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "16",
+ "advertise": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ "delete": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "16",
+ "advertise": True,
+ "delete": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ "delete": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ "delete": True,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify the show commands")
+
+ input_dict = {
+ SUMMARY["ipv4"][2]: {
+ "Summary address": SUMMARY["ipv4"][2],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc49_p2(request):
+ """OSPF summarisation Chaos."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r0")
+ start_router(tgen, "r0")
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Kill OSPFd daemon on R0.")
+ kill_router_daemons(tgen, "r0", ["ospfd"])
+
+ step("Bring up OSPFd daemon on R0.")
+ start_router_daemons(tgen, "r0", ["ospfd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R0")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("restart zebrad")
+ kill_router_daemons(tgen, "r0", ["zebra"])
+
+ step("Bring up zebra daemon on R0.")
+ start_router_daemons(tgen, "r0", ["zebra"])
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py
new file mode 100644
index 0000000000..393eb19a53
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py
@@ -0,0 +1,439 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 by VMware, Inc. ("VMware")
+# Used Copyright (c) 2018 by Network Device Education Foundation, Inc.
+# ("NetDEF") in this file.
+#
+# 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 VMWARE DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE 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.
+#
+
+
+"""OSPF Summarisation Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+from time import sleep
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ kill_router_daemons,
+ write_test_footer,
+ reset_config_on_routers,
+ stop_router,
+ start_router,
+ verify_rib,
+ create_static_routes,
+ step,
+ start_router_daemons,
+ create_route_maps,
+ shutdown_bringup_interface,
+ topo_daemons,
+ create_prefix_lists,
+ create_route_maps,
+ create_interfaces_cfg,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_rib,
+ create_router_ospf,
+ verify_ospf_summary,
+)
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospf_asbr_summary_type7_lsa.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+NETWORK2 = {
+ "ipv4": [
+ "12.0.20.1/32",
+ "12.0.20.2/32",
+ "12.0.20.3/32",
+ "12.0.20.4/32",
+ "12.0.20.5/32",
+ ]
+}
+NETWORK3 = {
+ "ipv4": [
+ "13.0.20.1/32",
+ "13.0.20.2/32",
+ "13.0.20.3/32",
+ "13.0.20.4/32",
+ "13.0.20.5/32",
+ ]
+}
+SUMMARY = {"ipv4": ["11.0.20.1/8", "12.0.0.0/8", "13.0.0.0/8", "11.0.0.0/8"]}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+
+
+TESTCASES =
+1. OSPF summarisation with type7 LSAs.
+
+"""
+
+
+class CreateTopo(Topo):
+ """
+ Test topology builder.
+
+ * `Topo`: Topology object
+ """
+
+ def build(self, *_args, **_opts):
+ """Build function."""
+ tgen = get_topogen(self)
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(CreateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """Local def for Redstribute connected routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf": {
+ "redistribute": [{"redist_type": "connected", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_type5_summary_tc44_p0(request):
+ """OSPF summarisation with type7 LSAs"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Bring up the base config as per the topology")
+ step("Configure area 1 as NSSA Area")
+
+ reset_config_on_routers(tgen)
+
+ dut = "r0"
+ protocol = "ospf"
+
+ red_static(dut)
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r0 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+
+ step(
+ "Configure summary & redistribute static/connected route with " "metric type 2"
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][3]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][3]: {
+ "Summary address": SUMMARY["ipv4"][3],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Learn type 7 lsa from neighbours")
+
+ dut = "r1"
+ protocol = "ospf"
+
+ red_static(dut)
+ input_dict_static_rtes = {
+ "r1": {
+ "static_routes": [{"network": NETWORK3["ipv4"], "next_hop": "blackhole"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that routes are learnt on R0.")
+ dut = "r0"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r0 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][2].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that type7 LSAs received from neighbor are not summarised.")
+ input_dict = {
+ "13.0.0.0/8": {
+ "Summary address": "13.0.0.0/8",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that already originated summary is intact.")
+ input_dict = {
+ SUMMARY["ipv4"][3]: {
+ "Summary address": SUMMARY["ipv4"][3],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ dut = "r1"
+ aggr_timer = {"r1": {"ospf": {"aggr_timer": 6}}}
+ result = create_router_ospf(tgen, topo, aggr_timer)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ ospf_summ_r0 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][2].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "wait for 6+1 seconds as ospf aggregation start after 6 secs as "
+ "per the above aggr_timer command"
+ )
+ sleep(7)
+ dut = "r1"
+ aggr_timer = {"r1": {"ospf": {"del_aggr_timer": 6}}}
+ result = create_router_ospf(tgen, topo, aggr_timer)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+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 0d64320488..732470f828 100644
--- a/tools/etc/frr/support_bundle_commands.conf
+++ b/tools/etc/frr/support_bundle_commands.conf
@@ -31,7 +31,29 @@ show bgp ipv6 statistics
show bgp martian next-hop
show bgp nexthop
+show bgp vrf all summary
+show bgp vrf all ipv4
+show bgp vrf all ipv6
+show bgp vrf all neighbors
+
show bgp evpn route
+show bgp l2vpn evpn route vni all
+show bgp l2vpn evpn vni
+show bgp l2vpn evpn import-rt
+show bgp l2vpn evpn vrf-import-rt
+show bgp l2vpn evpn all overlay
+show bgp l2vpn evpn summary
+show bgp l2vpn evpn route detail
+show bgp l2vpn evpn vni remote-ip-hash
+show bgp l2vpn evpn vni-svi-hash
+
+show evpn
+show evpn arp-cache vni all detail
+show evpn mac vni all detail
+show evpn next-hops vni all
+show evpn rmac vni all
+show evpn vni detail
+
CMD_LIST_END
# Zebra Support Bundle Command List
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index 7cb8a2e729..eb8753fd08 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -1391,6 +1391,53 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
lines_to_del_to_del.append((ctx_keys, line))
"""
+ Neighbor changes of route-maps need to be accounted for in that we
+ do not want to do a `no route-map...` `route-map ....` when changing
+ a route-map. This is bad mojo as that we will send/receive
+ data we don't want.
+ Additionally we need to ensure that if we have different afi/safi
+ variants that they actually match and if we are going from a very
+ old style command such that the neighbor command is under the
+ `router bgp ..` node that we need to handle that appropriately
+ """
+ re_nbr_rm = re.search("neighbor(.*)route-map(.*)(in|out)$", line)
+ if re_nbr_rm:
+ adjust_for_bgp_node = 0
+ neighbor_name = re_nbr_rm.group(1)
+ rm_name_del = re_nbr_rm.group(2)
+ dir = re_nbr_rm.group(3)
+ search = "neighbor%sroute-map(.*)%s" % (neighbor_name, dir)
+ save_line = "EMPTY"
+ for (ctx_keys_al, add_line) in lines_to_add:
+ if ctx_keys_al[0].startswith("router bgp"):
+ if add_line:
+ rm_match = re.search(search, add_line)
+ if rm_match:
+ rm_name_add = rm_match.group(1)
+ if rm_name_add == rm_name_del:
+ continue
+ if len(ctx_keys_al) == 1:
+ save_line = line
+ adjust_for_bgp_node = 1
+ else:
+ if (
+ len(ctx_keys) > 1
+ and len(ctx_keys_al) > 1
+ and ctx_keys[1] == ctx_keys_al[1]
+ ):
+ lines_to_del_to_del.append((ctx_keys_al, line))
+
+ if adjust_for_bgp_node == 1:
+ for (ctx_keys_dl, dl_line) in lines_to_del:
+ if (
+ ctx_keys_dl[0].startswith("router bgp")
+ and len(ctx_keys_dl) > 1
+ and ctx_keys_dl[1] == "address-family ipv4 unicast"
+ ):
+ if save_line == dl_line:
+ lines_to_del_to_del.append((ctx_keys_dl, save_line))
+
+ """
We changed how we display the neighbor interface command. Older
versions of frr would display the following:
neighbor swp1 interface
diff --git a/vrrpd/vrrp_vty.c b/vrrpd/vrrp_vty.c
index 6c3863132d..7af9148a8e 100644
--- a/vrrpd/vrrp_vty.c
+++ b/vrrpd/vrrp_vty.c
@@ -771,6 +771,7 @@ void vrrp_vty_init(void)
install_node(&debug_node);
install_node(&interface_node);
install_node(&vrrp_node);
+ vrf_cmd_init(NULL, &vrrp_privs);
if_cmd_init();
install_element(VIEW_NODE, &vrrp_vrid_show_cmd);
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 72835e7526..507c6ce882 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -1263,7 +1263,6 @@ static struct cmd_node pw_node = {
.prompt = "%s(config-pw)# ",
};
-#if defined(HAVE_PATHD)
static struct cmd_node segment_routing_node = {
.name = "segment-routing",
.node = SEGMENT_ROUTING_NODE,
@@ -1271,6 +1270,7 @@ static struct cmd_node segment_routing_node = {
.prompt = "%s(config-sr)# ",
};
+#if defined(HAVE_PATHD)
static struct cmd_node sr_traffic_eng_node = {
.name = "sr traffic-eng",
.node = SR_TRAFFIC_ENG_NODE,
@@ -2171,7 +2171,6 @@ DEFUNSH(VTYSH_FABRICD, router_openfabric, router_openfabric_cmd, "router openfab
}
#endif /* HAVE_FABRICD */
-#if defined(HAVE_PATHD)
DEFUNSH(VTYSH_SR, segment_routing, segment_routing_cmd,
"segment-routing",
"Configure segment routing\n")
@@ -2180,6 +2179,7 @@ DEFUNSH(VTYSH_SR, segment_routing, segment_routing_cmd,
return CMD_SUCCESS;
}
+#if defined (HAVE_PATHD)
DEFUNSH(VTYSH_PATHD, sr_traffic_eng, sr_traffic_eng_cmd,
"traffic-eng",
"Configure SR traffic engineering\n")
@@ -2664,6 +2664,18 @@ DEFUNSH(VTYSH_KEYS, vtysh_quit_keys, vtysh_quit_keys_cmd, "quit",
return vtysh_exit_keys(self, vty, argc, argv);
}
+DEFUNSH(VTYSH_SR, vtysh_exit_sr, vtysh_exit_sr_cmd, "exit",
+ "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit(vty);
+}
+
+DEFUNSH(VTYSH_SR, vtysh_quit_sr, vtysh_quit_sr_cmd, "quit",
+ "Exit current mode and down to previous mode\n")
+{
+ return vtysh_exit(vty);
+}
+
#if defined(HAVE_PATHD)
DEFUNSH(VTYSH_PATHD, vtysh_exit_pathd, vtysh_exit_pathd_cmd, "exit",
"Exit current mode and down to previous mode\n")
@@ -2839,7 +2851,6 @@ DEFUN (vtysh_show_poll,
return show_per_daemon(vty, argv, argc, "Thread statistics for %s:\n");
}
-#ifndef EXCLUDE_CPU_TIME
DEFUN (vtysh_show_thread,
vtysh_show_thread_cmd,
"show thread cpu [FILTER]",
@@ -2850,7 +2861,6 @@ DEFUN (vtysh_show_thread,
{
return show_per_daemon(vty, argv, argc, "Thread statistics for %s:\n");
}
-#endif
DEFUN (vtysh_show_work_queues,
vtysh_show_work_queues_cmd,
@@ -4329,15 +4339,17 @@ void vtysh_init_vty(void)
install_element(BFD_PROFILE_NODE, &vtysh_end_all_cmd);
#endif /* HAVE_BFDD */
-#if defined(HAVE_PATHD)
install_node(&segment_routing_node);
+ install_element(SEGMENT_ROUTING_NODE, &vtysh_exit_sr_cmd);
+ install_element(SEGMENT_ROUTING_NODE, &vtysh_quit_sr_cmd);
+ install_element(SEGMENT_ROUTING_NODE, &vtysh_end_all_cmd);
+
+#if defined(HAVE_PATHD)
install_node(&sr_traffic_eng_node);
install_node(&srte_segment_list_node);
install_node(&srte_policy_node);
install_node(&srte_candidate_dyn_node);
- install_element(SEGMENT_ROUTING_NODE, &vtysh_exit_pathd_cmd);
- install_element(SEGMENT_ROUTING_NODE, &vtysh_quit_pathd_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &vtysh_exit_pathd_cmd);
install_element(SR_TRAFFIC_ENG_NODE, &vtysh_quit_pathd_cmd);
install_element(SR_SEGMENT_LIST_NODE, &vtysh_exit_pathd_cmd);
@@ -4347,7 +4359,7 @@ void vtysh_init_vty(void)
install_element(SR_CANDIDATE_DYN_NODE, &vtysh_exit_pathd_cmd);
install_element(SR_CANDIDATE_DYN_NODE, &vtysh_quit_pathd_cmd);
- install_element(SEGMENT_ROUTING_NODE, &vtysh_end_all_cmd);
+
install_element(SR_TRAFFIC_ENG_NODE, &vtysh_end_all_cmd);
install_element(SR_SEGMENT_LIST_NODE, &vtysh_end_all_cmd);
install_element(SR_POLICY_NODE, &vtysh_end_all_cmd);
@@ -4553,9 +4565,7 @@ void vtysh_init_vty(void)
install_element(VIEW_NODE, &vtysh_show_modules_cmd);
install_element(VIEW_NODE, &vtysh_show_work_queues_cmd);
install_element(VIEW_NODE, &vtysh_show_work_queues_daemon_cmd);
-#ifndef EXCLUDE_CPU_TIME
install_element(VIEW_NODE, &vtysh_show_thread_cmd);
-#endif
install_element(VIEW_NODE, &vtysh_show_poll_cmd);
/* Logging */
diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h
index 87f1f67443..71f672554b 100644
--- a/vtysh/vtysh.h
+++ b/vtysh/vtysh.h
@@ -56,7 +56,7 @@ DECLARE_MGROUP(MVTYSH);
#define VTYSH_ACL VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA
#define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_EIGRPD|VTYSH_FABRICD
#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD
-#define VTYSH_VRF VTYSH_ZEBRA|VTYSH_PIMD|VTYSH_STATICD
+#define VTYSH_VRF VTYSH_INTERFACE|VTYSH_STATICD
#define VTYSH_KEYS VTYSH_RIPD|VTYSH_EIGRPD
/* Daemons who can process nexthop-group configs */
#define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD
diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang
index ca60c8f7b6..1c990b5ed9 100644
--- a/yang/frr-bgp-route-map.yang
+++ b/yang/frr-bgp-route-map.yang
@@ -300,6 +300,18 @@ module frr-bgp-route-map {
"Set BGP large community list (for deletion)";
}
+ identity set-evpn-gateway-ip-ipv4 {
+ base frr-route-map:rmap-set-type;
+ description
+ "Set EVPN gateway IP overlay index IPv4";
+ }
+
+ identity set-evpn-gateway-ip-ipv6 {
+ base frr-route-map:rmap-set-type;
+ description
+ "Set EVPN gateway IP overlay index IPv6";
+ }
+
grouping extcommunity-non-transitive-types {
leaf two-octet-as-specific {
type boolean;
@@ -816,5 +828,25 @@ module frr-bgp-route-map {
type bgp-filter:bgp-list-name;
}
}
+ case evpn-gateway-ip-ipv4 {
+ when
+ "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action,
+ 'frr-bgp-route-map:set-evpn-gateway-ip-ipv4')";
+ description
+ "Set EVPN gateway IP overlay index IPv4";
+ leaf evpn-gateway-ip-ipv4 {
+ type inet:ipv4-address;
+ }
+ }
+ case evpn-gateway-ip-ipv6 {
+ when
+ "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action,
+ 'frr-bgp-route-map:set-evpn-gateway-ip-ipv6')";
+ description
+ "Set EVPN gateway IP overlay index IPv6";
+ leaf evpn-gateway-ip-ipv6 {
+ type inet:ipv6-address;
+ }
+ }
}
}
diff --git a/zebra/rib.h b/zebra/rib.h
index 75d7ae1b67..957f38602a 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -401,7 +401,7 @@ extern void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type,
bool fromkernel);
extern struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
- union g_addr *addr,
+ const union g_addr *addr,
struct route_node **rn_out);
extern struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id,
struct in_addr addr,
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index a8df0c56ce..a5d672987d 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -157,12 +157,17 @@ struct dplane_pw_info {
int af;
int status;
uint32_t flags;
+ uint32_t nhg_id;
union g_addr dest;
mpls_label_t local_label;
mpls_label_t remote_label;
- /* Nexthops */
- struct nexthop_group nhg;
+ /* Nexthops that are valid and installed */
+ struct nexthop_group fib_nhg;
+
+ /* Primary and backup nexthop sets, copied from the resolving route. */
+ struct nexthop_group primary_nhg;
+ struct nexthop_group backup_nhg;
union pw_protocol_fields fields;
};
@@ -664,11 +669,21 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_PW_INSTALL:
case DPLANE_OP_PW_UNINSTALL:
/* Free allocated nexthops */
- if (ctx->u.pw.nhg.nexthop) {
+ if (ctx->u.pw.fib_nhg.nexthop) {
/* This deals with recursive nexthops too */
- nexthops_free(ctx->u.pw.nhg.nexthop);
+ nexthops_free(ctx->u.pw.fib_nhg.nexthop);
+
+ ctx->u.pw.fib_nhg.nexthop = NULL;
+ }
+ if (ctx->u.pw.primary_nhg.nexthop) {
+ nexthops_free(ctx->u.pw.primary_nhg.nexthop);
+
+ ctx->u.pw.primary_nhg.nexthop = NULL;
+ }
+ if (ctx->u.pw.backup_nhg.nexthop) {
+ nexthops_free(ctx->u.pw.backup_nhg.nexthop);
- ctx->u.pw.nhg.nexthop = NULL;
+ ctx->u.pw.backup_nhg.nexthop = NULL;
}
break;
@@ -1630,7 +1645,23 @@ dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
- return &(ctx->u.pw.nhg);
+ return &(ctx->u.pw.fib_nhg);
+}
+
+const struct nexthop_group *
+dplane_ctx_get_pw_primary_nhg(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return &(ctx->u.pw.primary_nhg);
+}
+
+const struct nexthop_group *
+dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return &(ctx->u.pw.backup_nhg);
}
/* Accessors for interface information */
@@ -2461,12 +2492,14 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
enum dplane_op_e op,
struct zebra_pw *pw)
{
+ int ret = EINVAL;
struct prefix p;
afi_t afi;
struct route_table *table;
struct route_node *rn;
struct route_entry *re;
const struct nexthop_group *nhg;
+ struct nexthop *nh, *newnh, *last_nh;
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u",
@@ -2509,31 +2542,83 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
afi = (pw->af == AF_INET) ? AFI_IP : AFI_IP6;
table = zebra_vrf_table(afi, SAFI_UNICAST, pw->vrf_id);
- if (table) {
- rn = route_node_match(table, &p);
- if (rn) {
- RNODE_FOREACH_RE(rn, re) {
- if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
- break;
+ if (table == NULL)
+ goto done;
+
+ rn = route_node_match(table, &p);
+ if (rn == NULL)
+ goto done;
+
+ re = NULL;
+ RNODE_FOREACH_RE(rn, re) {
+ if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED))
+ break;
+ }
+
+ if (re) {
+ /* We'll capture a 'fib' list of nexthops that meet our
+ * criteria: installed, and labelled.
+ */
+ nhg = rib_get_fib_nhg(re);
+ last_nh = NULL;
+
+ if (nhg && nhg->nexthop) {
+ for (ALL_NEXTHOPS_PTR(nhg, nh)) {
+ if (!CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)
+ || CHECK_FLAG(nh->flags,
+ NEXTHOP_FLAG_RECURSIVE)
+ || nh->nh_label == NULL)
+ continue;
+
+ newnh = nexthop_dup(nh, NULL);
+
+ if (last_nh)
+ NEXTHOP_APPEND(last_nh, newnh);
+ else
+ ctx->u.pw.fib_nhg.nexthop = newnh;
+ last_nh = newnh;
}
+ }
- if (re) {
- nhg = rib_get_fib_nhg(re);
- if (nhg && nhg->nexthop)
- copy_nexthops(&(ctx->u.pw.nhg.nexthop),
- nhg->nexthop, NULL);
-
- /* Include any installed backup nexthops */
- nhg = rib_get_fib_backup_nhg(re);
- if (nhg && nhg->nexthop)
- copy_nexthops(&(ctx->u.pw.nhg.nexthop),
- nhg->nexthop, NULL);
+ /* Include any installed backup nexthops also. */
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg && nhg->nexthop) {
+ for (ALL_NEXTHOPS_PTR(nhg, nh)) {
+ if (!CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ACTIVE)
+ || CHECK_FLAG(nh->flags,
+ NEXTHOP_FLAG_RECURSIVE)
+ || nh->nh_label == NULL)
+ continue;
+
+ newnh = nexthop_dup(nh, NULL);
+
+ if (last_nh)
+ NEXTHOP_APPEND(last_nh, newnh);
+ else
+ ctx->u.pw.fib_nhg.nexthop = newnh;
+ last_nh = newnh;
}
- route_unlock_node(rn);
+ }
+
+ /* Copy primary nexthops; recursive info is included too */
+ assert(re->nhe != NULL); /* SA warning */
+ copy_nexthops(&(ctx->u.pw.primary_nhg.nexthop),
+ re->nhe->nhg.nexthop, NULL);
+ ctx->u.pw.nhg_id = re->nhe->id;
+
+ /* Copy backup nexthop info, if present */
+ if (re->nhe->backup_info && re->nhe->backup_info->nhe) {
+ copy_nexthops(&(ctx->u.pw.backup_nhg.nexthop),
+ re->nhe->backup_info->nhe->nhg.nexthop,
+ NULL);
}
}
+ route_unlock_node(rn);
- return AOK;
+ ret = AOK;
+
+done:
+ return ret;
}
/**
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 3a8536dda5..e091655a48 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -437,6 +437,10 @@ const union pw_protocol_fields *dplane_ctx_get_pw_proto(
const struct zebra_dplane_ctx *ctx);
const struct nexthop_group *dplane_ctx_get_pw_nhg(
const struct zebra_dplane_ctx *ctx);
+const struct nexthop_group *
+dplane_ctx_get_pw_primary_nhg(const struct zebra_dplane_ctx *ctx);
+const struct nexthop_group *
+dplane_ctx_get_pw_backup_nhg(const struct zebra_dplane_ctx *ctx);
/* Accessors for interface information */
uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx);
diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c
index 30f4a44769..816f46bac9 100644
--- a/zebra/zebra_evpn.c
+++ b/zebra/zebra_evpn.c
@@ -134,6 +134,10 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt)
if (json == NULL) {
vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name);
vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex);
+ vty_out(vty, " SVI interface: %s\n",
+ (zevpn->svi_if ? zevpn->svi_if->name : ""));
+ vty_out(vty, " SVI ifIndex: %u\n",
+ (zevpn->svi_if ? zevpn->svi_if->ifindex : 0));
vty_out(vty, " Local VTEP IP: %pI4\n",
&zevpn->local_vtep_ip);
vty_out(vty, " Mcast group: %pI4\n",
@@ -142,6 +146,12 @@ void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt)
json_object_string_add(json, "vxlanInterface",
zevpn->vxlan_if->name);
json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex);
+ if (zevpn->svi_if) {
+ json_object_string_add(json, "sviInterface",
+ zevpn->svi_if->name);
+ json_object_int_add(json, "sviIfindex",
+ zevpn->svi_if->ifindex);
+ }
json_object_string_add(json, "vtepIp",
inet_ntop(AF_INET, &zevpn->local_vtep_ip,
buf, sizeof(buf)));
@@ -1048,6 +1058,8 @@ int zebra_evpn_del(zebra_evpn_t *zevpn)
zvrf = zebra_vrf_get_evpn();
assert(zvrf);
+ zevpn->svi_if = NULL;
+
/* Free the neighbor hash table. */
hash_free(zevpn->neigh_table);
zevpn->neigh_table = NULL;
@@ -1075,6 +1087,7 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn)
{
struct zserv *client;
struct stream *s;
+ ifindex_t svi_index;
int rc;
client = zserv_find_client(ZEBRA_ROUTE_BGP, 0);
@@ -1082,6 +1095,8 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn)
if (!client)
return 0;
+ svi_index = zevpn->svi_if ? zevpn->svi_if->ifindex : 0;
+
s = stream_new(ZEBRA_MAX_PACKET_SIZ);
zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id());
@@ -1089,15 +1104,18 @@ int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn)
stream_put_in_addr(s, &zevpn->local_vtep_ip);
stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */
stream_put_in_addr(s, &zevpn->mcast_grp);
+ stream_put(s, &svi_index, sizeof(ifindex_t));
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Send EVPN_ADD %u %pI4 tenant vrf %s to %s", zevpn->vni,
- &zevpn->local_vtep_ip,
- vrf_id_to_name(zevpn->vrf_id),
- zebra_route_string(client->proto));
+ zlog_debug(
+ "Send EVPN_ADD %u %pI4 tenant vrf %s(%u) SVI index %u to %s",
+ zevpn->vni, &zevpn->local_vtep_ip,
+ vrf_id_to_name(zevpn->vrf_id), zevpn->vrf_id,
+ (zevpn->svi_if ? zevpn->svi_if->ifindex : 0),
+ zebra_route_string(client->proto));
client->vniadd_cnt++;
rc = zserv_send_message(client, s);
diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h
index 27392ec85c..ee9e1406e4 100644
--- a/zebra/zebra_evpn.h
+++ b/zebra/zebra_evpn.h
@@ -98,6 +98,9 @@ struct zebra_evpn_t_ {
/* Corresponding VxLAN interface. */
struct interface *vxlan_if;
+ /* Corresponding SVI interface. */
+ struct interface *svi_if;
+
/* List of remote VTEPs */
zebra_vtep_t *vteps;
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index cebd576365..efbd078a52 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -42,6 +42,7 @@
#include "zebra/zebra_fpm_private.h"
#include "zebra/zebra_vxlan_private.h"
+#include "zebra/interface.h"
/*
* af_addr_size
@@ -164,7 +165,10 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri,
{
struct netlink_nh_info nhi;
union g_addr *src;
- zebra_l3vni_t *zl3vni = NULL;
+ struct zebra_vrf *zvrf = NULL;
+ struct interface *ifp = NULL, *link_if = NULL;
+ struct zebra_if *zif = NULL;
+ vni_t vni = 0;
memset(&nhi, 0, sizeof(nhi));
src = NULL;
@@ -199,12 +203,29 @@ static int netlink_route_info_add_nh(struct netlink_route_info *ri,
if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN;
- zl3vni = zl3vni_from_vrf(nexthop->vrf_id);
- if (zl3vni && is_l3vni_oper_up(zl3vni)) {
-
- /* Add VNI to VxLAN encap info */
- nhi.encap_info.vxlan_encap.vni = zl3vni->vni;
+ /* Extract VNI id for the nexthop SVI interface */
+ zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id);
+ if (zvrf) {
+ ifp = if_lookup_by_index_per_ns(zvrf->zns,
+ nexthop->ifindex);
+ if (ifp) {
+ zif = (struct zebra_if *)ifp->info;
+ if (zif) {
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ link_if = ifp;
+ else if (IS_ZEBRA_IF_VLAN(ifp))
+ link_if =
+ if_lookup_by_index_per_ns(
+ zvrf->zns,
+ zif->link_ifindex);
+ if (link_if)
+ vni = vni_id_from_svi(ifp,
+ link_if);
+ }
+ }
}
+
+ nhi.encap_info.vxlan_encap.vni = vni;
}
/*
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index 850ca17636..a4576b310e 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -54,6 +54,7 @@ DEFINE_MTYPE_STATIC(ZEBRA, FEC, "MPLS FEC object");
DEFINE_MTYPE_STATIC(ZEBRA, NHLFE, "MPLS nexthop object");
int mpls_enabled;
+bool mpls_pw_reach_strict; /* Strict reachability checking */
/* static function declarations */
@@ -3977,6 +3978,7 @@ void zebra_mpls_init_tables(struct zebra_vrf *zvrf)
void zebra_mpls_init(void)
{
mpls_enabled = 0;
+ mpls_pw_reach_strict = false;
if (mpls_kernel_init() < 0) {
flog_warn(EC_ZEBRA_MPLS_SUPPORT_DISABLED,
diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h
index dd2d2e5658..7059d393ed 100644
--- a/zebra/zebra_mpls.h
+++ b/zebra/zebra_mpls.h
@@ -576,6 +576,7 @@ static inline int mpls_should_lsps_be_processed(struct route_node *rn)
/* Global variables. */
extern int mpls_enabled;
+extern bool mpls_pw_reach_strict; /* Strict pseudowire reachability checking */
#ifdef __cplusplus
}
diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c
index b767929dc0..74b1e37278 100644
--- a/zebra/zebra_mpls_openbsd.c
+++ b/zebra/zebra_mpls_openbsd.c
@@ -458,6 +458,9 @@ int mpls_kernel_init(void)
kr_state.rtseq = 1;
+ /* Strict pseudowire reachability checking required for obsd */
+ mpls_pw_reach_strict = true;
+
return 0;
}
diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c
index 5afb3e5926..6b4a815151 100644
--- a/zebra/zebra_pw.c
+++ b/zebra/zebra_pw.c
@@ -48,7 +48,7 @@ static int zebra_pw_enabled(struct zebra_pw *);
static void zebra_pw_install(struct zebra_pw *);
static void zebra_pw_uninstall(struct zebra_pw *);
static int zebra_pw_install_retry(struct thread *);
-static int zebra_pw_check_reachability(struct zebra_pw *);
+static int zebra_pw_check_reachability(const struct zebra_pw *);
static void zebra_pw_update_status(struct zebra_pw *, int);
static inline int zebra_pw_compare(const struct zebra_pw *a,
@@ -243,14 +243,79 @@ static void zebra_pw_update_status(struct zebra_pw *pw, int status)
zsend_pw_update(pw->client, pw);
}
-static int zebra_pw_check_reachability(struct zebra_pw *pw)
+static int zebra_pw_check_reachability_strict(const struct zebra_pw *pw,
+ struct route_entry *re)
+{
+ const struct nexthop *nexthop;
+ const struct nexthop_group *nhg;
+ bool found_p = false;
+ bool fail_p = false;
+
+ /* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */
+
+ /* All active nexthops must be labelled; look at
+ * primary and backup fib lists, in case there's been
+ * a backup nexthop activation.
+ */
+ nhg = rib_get_fib_nhg(re);
+ if (nhg && nhg->nexthop) {
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
+ if (nexthop->nh_label != NULL)
+ found_p = true;
+ else {
+ fail_p = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (fail_p)
+ goto done;
+
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg && nhg->nexthop) {
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) {
+ if (nexthop->nh_label != NULL)
+ found_p = true;
+ else {
+ fail_p = true;
+ break;
+ }
+ }
+ }
+ }
+
+done:
+
+ if (fail_p || !found_p) {
+ if (IS_ZEBRA_DEBUG_PW)
+ zlog_debug("%s: unlabeled route for %s",
+ __func__, pw->ifname);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int zebra_pw_check_reachability(const struct zebra_pw *pw)
{
struct route_entry *re;
- struct nexthop *nexthop;
+ const struct nexthop *nexthop;
+ const struct nexthop_group *nhg;
+ bool found_p = false;
/* TODO: consider GRE/L2TPv3 tunnels in addition to MPLS LSPs */
- /* find route to the remote end of the pseudowire */
+ /* Find route to the remote end of the pseudowire */
re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,
&pw->nexthop, NULL);
if (!re) {
@@ -260,19 +325,52 @@ static int zebra_pw_check_reachability(struct zebra_pw *pw)
return -1;
}
- /*
- * Need to ensure that there's a label binding for all nexthops.
- * Otherwise, ECMP for this route could render the pseudowire unusable.
+ /* Stricter checking for some OSes (OBSD, e.g.) */
+ if (mpls_pw_reach_strict)
+ return zebra_pw_check_reachability_strict(pw, re);
+
+ /* There must be at least one installed labelled nexthop;
+ * look at primary and backup fib lists, in case there's been
+ * a backup nexthop activation.
*/
- for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) {
- if (!nexthop->nh_label) {
- if (IS_ZEBRA_DEBUG_PW)
- zlog_debug("%s: unlabeled route for %s",
- __func__, pw->ifname);
- return -1;
+ nhg = rib_get_fib_nhg(re);
+ if (nhg && nhg->nexthop) {
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) &&
+ nexthop->nh_label != NULL) {
+ found_p = true;
+ break;
+ }
+ }
+ }
+
+ if (found_p)
+ return 0;
+
+ nhg = rib_get_fib_backup_nhg(re);
+ if (nhg && nhg->nexthop) {
+ for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
+ continue;
+
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) &&
+ nexthop->nh_label != NULL) {
+ found_p = true;
+ break;
+ }
}
}
+ if (!found_p) {
+ if (IS_ZEBRA_DEBUG_PW)
+ zlog_debug("%s: unlabeled route for %s",
+ __func__, pw->ifname);
+ return -1;
+ }
+
return 0;
}
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 8afb5053c4..d0acf77936 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -332,7 +332,8 @@ void rib_handle_nhg_replace(struct nhg_hash_entry *old_entry,
}
struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,
- union g_addr *addr, struct route_node **rn_out)
+ const union g_addr *addr,
+ struct route_node **rn_out)
{
struct prefix p;
struct route_table *table;
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 09eb78917c..2f3ea7475a 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -1012,6 +1012,7 @@ static int zevpn_build_hash_table_zns(struct ns *ns,
vxl->access_vlan,
zif->brslave_info.br_if);
if (vlan_if) {
+ zevpn->svi_if = vlan_if;
zevpn->vrf_id = vlan_if->vrf_id;
zl3vni = zl3vni_from_vrf(
vlan_if->vrf_id);
@@ -1841,6 +1842,27 @@ static zebra_l3vni_t *zl3vni_from_svi(struct interface *ifp,
return zl3vni;
}
+vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if)
+{
+ vni_t vni = 0;
+ zebra_evpn_t *zevpn = NULL;
+ zebra_l3vni_t *zl3vni = NULL;
+
+ /* Check if an L3VNI belongs to this SVI interface.
+ * If not, check if an L2VNI belongs to this SVI interface.
+ */
+ zl3vni = zl3vni_from_svi(ifp, br_if);
+ if (zl3vni)
+ vni = zl3vni->vni;
+ else {
+ zevpn = zebra_evpn_from_svi(ifp, br_if);
+ if (zevpn)
+ vni = zevpn->vni;
+ }
+
+ return vni;
+}
+
static inline void zl3vni_get_vrr_rmac(zebra_l3vni_t *zl3vni,
struct ethaddr *rmac)
{
@@ -4527,6 +4549,7 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if)
zevpn = zebra_evpn_from_svi(ifp, link_if);
if (zevpn) {
+ zevpn->svi_if = NULL;
zevpn->vrf_id = VRF_DEFAULT;
/* update the tenant vrf in BGP */
@@ -4582,6 +4605,7 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if)
vrf_id_to_name(ifp->vrf_id));
/* update the vrf information for l2-vni and inform bgp */
+ zevpn->svi_if = ifp;
zevpn->vrf_id = ifp->vrf_id;
if (if_is_operative(zevpn->vxlan_if))
@@ -4792,6 +4816,7 @@ int zebra_vxlan_if_up(struct interface *ifp)
vlan_if = zvni_map_to_svi(vxl->access_vlan,
zif->brslave_info.br_if);
if (vlan_if) {
+ zevpn->svi_if = vlan_if;
zevpn->vrf_id = vlan_if->vrf_id;
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
if (zl3vni)
@@ -4894,6 +4919,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
struct zebra_l2info_vxlan *vxl = NULL;
zebra_evpn_t *zevpn = NULL;
zebra_l3vni_t *zl3vni = NULL;
+ struct interface *vlan_if = NULL;
/* Check if EVPN is enabled. */
if (!is_evpn_enabled())
@@ -4983,6 +5009,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
&& (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) {
/* Delete from client, remove all remote VTEPs */
/* Also, free up all MACs and neighbors. */
+ zevpn->svi_if = NULL;
zebra_evpn_send_del_to_client(zevpn);
zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH);
zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC);
@@ -5012,6 +5039,11 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags)
zebra_evpn_es_set_base_evpn(zevpn);
}
zevpn_vxlan_if_set(zevpn, ifp, true /* set */);
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if)
+ zevpn->svi_if = vlan_if;
+
/* Take further actions needed.
* Note that if we are here, there is a change of interest.
*/
@@ -5131,6 +5163,7 @@ int zebra_vxlan_if_add(struct interface *ifp)
vlan_if = zvni_map_to_svi(vxl->access_vlan,
zif->brslave_info.br_if);
if (vlan_if) {
+ zevpn->svi_if = vlan_if;
zevpn->vrf_id = vlan_if->vrf_id;
zl3vni = zl3vni_from_vrf(vlan_if->vrf_id);
if (zl3vni)
diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h
index 0556c4adce..84ac76b3b9 100644
--- a/zebra/zebra_vxlan_private.h
+++ b/zebra/zebra_vxlan_private.h
@@ -224,6 +224,7 @@ extern struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni);
extern struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni);
extern struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni);
extern zebra_l3vni_t *zl3vni_lookup(vni_t vni);
+extern vni_t vni_id_from_svi(struct interface *ifp, struct interface *br_if);
DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni,
bool delete, const char *reason), (rmac, zl3vni, delete, reason));