summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_vty.c2
-rw-r--r--doc/user/sharp.rst4
-rw-r--r--doc/user/zebra.rst11
-rw-r--r--include/linux/if_link.h13
-rw-r--r--lib/northbound_grpc.cpp4
-rw-r--r--lib/plist.c113
-rw-r--r--lib/plist_int.h7
-rw-r--r--ospfd/ospf_vty.c74
-rw-r--r--pimd/pim_cmd.c33
-rw-r--r--pimd/pim_iface.h1
-rw-r--r--pimd/pim_igmp.c5
-rw-r--r--pimd/pim_igmp_stats.c1
-rw-r--r--pimd/pim_igmp_stats.h1
-rw-r--r--pimd/pim_instance.h2
-rw-r--r--pimd/pim_nb_config.c36
-rw-r--r--pimd/pim_pim.c1
-rw-r--r--pimd/pim_register.c106
-rw-r--r--pimd/pim_ssmpingd.c242
-rw-r--r--pimd/pim_ssmpingd.h6
-rw-r--r--pimd/pim_vty.c6
-rw-r--r--sharpd/sharp_vty.c64
-rw-r--r--sharpd/sharp_zebra.c12
-rw-r--r--sharpd/sharp_zebra.h2
-rw-r--r--tests/topotests/lib/common_config.py7
-rw-r--r--tests/topotests/lib/ospf.py8
-rw-r--r--tests/topotests/lib/topogen.py8
-rw-r--r--tests/topotests/lib/topotest.py1
-rwxr-xr-xtests/topotests/ospf_multi_vrf_bgp_route_leak/__init__.py0
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf57
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-default.txt19
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-neno.txt12
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt9
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt6
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf62
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-default.txt20
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-ray.txt15
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt10
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt9
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf22
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/ospf-vrf-default.txt17
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt8
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf22
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/ospf-vrf-default.txt17
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt7
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py243
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_type7_lsa.json202
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_dual_stack.json312
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json36
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp_lan.json264
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_lan.json140
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_nssa2.json197
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json59
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json17
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py1200
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py400
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py482
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py416
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py375
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py957
-rw-r--r--tests/topotests/zebra_rib/test_zebra_rib.py57
-rwxr-xr-xvtysh/extract.pl.in7
-rw-r--r--zebra/debug_nl.c110
-rw-r--r--zebra/dplane_fpm_nl.c3
-rw-r--r--zebra/if_netlink.c280
-rw-r--r--zebra/if_netlink.h25
-rw-r--r--zebra/if_socket.c41
-rw-r--r--zebra/interface.c349
-rw-r--r--zebra/interface.h30
-rw-r--r--zebra/kernel_netlink.c10
-rw-r--r--zebra/kernel_socket.c6
-rw-r--r--zebra/rt.h3
-rw-r--r--zebra/rt_netlink.h2
-rw-r--r--zebra/subdir.am1
-rw-r--r--zebra/zapi_msg.c22
-rw-r--r--zebra/zebra_dplane.c204
-rw-r--r--zebra/zebra_dplane.h19
-rw-r--r--zebra/zebra_errors.c9
-rw-r--r--zebra/zebra_errors.h1
-rw-r--r--zebra/zebra_evpn_mh.c52
-rw-r--r--zebra/zebra_evpn_mh.h2
-rw-r--r--zebra/zebra_nhg.c3
-rw-r--r--zebra/zebra_rib.c8
-rw-r--r--zebra/zebra_router.h16
-rw-r--r--zebra/zebra_vty.c28
-rw-r--r--zebra/zebra_vxlan.c6
85 files changed, 7020 insertions, 656 deletions
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index d21e257cb5..dea1433f6d 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -1486,7 +1486,7 @@ DEFUN (no_router_bgp,
}
if (bgp->l3vni) {
- vty_out(vty, "%% Please unconfigure l3vni %u",
+ vty_out(vty, "%% Please unconfigure l3vni %u\n",
bgp->l3vni);
return CMD_WARNING_CONFIG_FAILED;
}
diff --git a/doc/user/sharp.rst b/doc/user/sharp.rst
index e9d4e2763f..8d201a3c06 100644
--- a/doc/user/sharp.rst
+++ b/doc/user/sharp.rst
@@ -296,3 +296,7 @@ keyword. At present, no sharp commands will be preserved in the config.
router# show sharp segment-routing srv6
(nothing)
+
+.. clicmd:: sharp interface IFNAME protodown
+
+ Set an interface protodown.
diff --git a/doc/user/zebra.rst b/doc/user/zebra.rst
index 221e9c6fe2..0244f7c583 100644
--- a/doc/user/zebra.rst
+++ b/doc/user/zebra.rst
@@ -255,6 +255,17 @@ Link Parameters Commands
for InterASv2 link in OSPF (RFC5392). Note that this option is not yet
supported for ISIS (RFC5316).
+Global Commands
+------------------------
+
+.. clicmd:: zebra protodown reason-bit (0-31)
+
+ This command is only supported for linux and a kernel > 5.1.
+ Change reason-bit frr uses for setting protodown. We default to 7, but
+ if another userspace app ever conflicts with this, you can change it here.
+ The descriptor for this bit should exist in :file:`/etc/iproute2/protodown_reasons.d/`
+ to display with :clicmd:`ip -d link show`.
+
Nexthop Tracking
================
diff --git a/include/linux/if_link.h b/include/linux/if_link.h
index 22a45914a2..e5cea27829 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -167,12 +167,25 @@ enum {
IFLA_NEW_IFINDEX,
IFLA_MIN_MTU,
IFLA_MAX_MTU,
+ IFLA_PROP_LIST,
+ IFLA_ALT_IFNAME, /* Alternative ifname */
+ IFLA_PERM_ADDRESS,
+ IFLA_PROTO_DOWN_REASON,
__IFLA_MAX
};
#define IFLA_MAX (__IFLA_MAX - 1)
+enum {
+ IFLA_PROTO_DOWN_REASON_UNSPEC,
+ IFLA_PROTO_DOWN_REASON_MASK, /* u32, mask for reason bits */
+ IFLA_PROTO_DOWN_REASON_VALUE, /* u32, reason bit value */
+
+ __IFLA_PROTO_DOWN_REASON_CNT,
+ IFLA_PROTO_DOWN_REASON_MAX = __IFLA_PROTO_DOWN_REASON_CNT - 1
+};
+
/* backwards compatibility for userspace */
#ifndef __KERNEL__
#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
diff --git a/lib/northbound_grpc.cpp b/lib/northbound_grpc.cpp
index dda1756214..9cb999110b 100644
--- a/lib/northbound_grpc.cpp
+++ b/lib/northbound_grpc.cpp
@@ -223,7 +223,7 @@ class RpcStateBase
pthread_mutex_t cmux = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
CallState state = CREATE;
- CallState entered_state;
+ CallState entered_state = CREATE;
public:
const char *name;
@@ -1153,7 +1153,9 @@ static void *grpc_pthread_start(void *arg)
std::unique_ptr<grpc::Server> server = builder.BuildAndStart();
s_server = server.get();
+ pthread_mutex_lock(&s_server_lock); // Make coverity happy
grpc_running = true;
+ pthread_mutex_unlock(&s_server_lock); // Make coverity happy
/* Schedule unary RPC handlers */
REQUEST_NEWRPC(GetCapabilities, NULL);
diff --git a/lib/plist.c b/lib/plist.c
index d6a63c1b0c..e7647fb2a7 100644
--- a/lib/plist.c
+++ b/lib/plist.c
@@ -31,6 +31,7 @@
#include "lib/json.h"
#include "libfrr.h"
+#include <typesafe.h>
#include "plist_int.h"
DEFINE_MTYPE_STATIC(LIB, PREFIX_LIST, "Prefix List");
@@ -58,17 +59,8 @@ struct pltrie_table {
struct pltrie_entry entries[PLC_LEN];
};
-/* List of struct prefix_list. */
-struct prefix_list_list {
- struct prefix_list *head;
- struct prefix_list *tail;
-};
-
/* Master structure of prefix_list. */
struct prefix_master {
- /* List of prefix_list which name is string. */
- struct prefix_list_list str;
-
/* The latest update. */
struct prefix_list *recent;
@@ -80,26 +72,32 @@ struct prefix_master {
/* number of bytes that have a trie level */
size_t trie_depth;
+
+ struct plist_head str;
};
+static int prefix_list_compare_func(const struct prefix_list *a,
+ const struct prefix_list *b);
+DECLARE_RBTREE_UNIQ(plist, struct prefix_list, plist_item,
+ prefix_list_compare_func);
/* Static structure of IPv4 prefix_list's master. */
static struct prefix_master prefix_master_ipv4 = {
- {NULL, NULL}, NULL, NULL, NULL, PLC_MAXLEVELV4,
+ NULL, NULL, NULL, PLC_MAXLEVELV4,
};
/* Static structure of IPv6 prefix-list's master. */
static struct prefix_master prefix_master_ipv6 = {
- {NULL, NULL}, NULL, NULL, NULL, PLC_MAXLEVELV6,
+ NULL, NULL, NULL, PLC_MAXLEVELV6,
};
/* Static structure of BGP ORF prefix_list's master. */
static struct prefix_master prefix_master_orf_v4 = {
- {NULL, NULL}, NULL, NULL, NULL, PLC_MAXLEVELV4,
+ NULL, NULL, NULL, PLC_MAXLEVELV4,
};
/* Static structure of BGP ORF prefix_list's master. */
static struct prefix_master prefix_master_orf_v6 = {
- {NULL, NULL}, NULL, NULL, NULL, PLC_MAXLEVELV6,
+ NULL, NULL, NULL, PLC_MAXLEVELV6,
};
static struct prefix_master *prefix_master_get(afi_t afi, int orf)
@@ -124,11 +122,17 @@ afi_t prefix_list_afi(struct prefix_list *plist)
return AFI_IP6;
}
+static int prefix_list_compare_func(const struct prefix_list *a,
+ const struct prefix_list *b)
+{
+ return strcmp(a->name, b->name);
+}
+
/* Lookup prefix_list from list of prefix_list by name. */
static struct prefix_list *prefix_list_lookup_do(afi_t afi, int orf,
const char *name)
{
- struct prefix_list *plist;
+ struct prefix_list *plist, lookup;
struct prefix_master *master;
if (name == NULL)
@@ -138,11 +142,10 @@ static struct prefix_list *prefix_list_lookup_do(afi_t afi, int orf,
if (master == NULL)
return NULL;
- for (plist = master->str.head; plist; plist = plist->next)
- if (strcmp(plist->name, name) == 0)
- return plist;
-
- return NULL;
+ lookup.name = XSTRDUP(MTYPE_TMP, name);
+ plist = plist_find(&master->str, &lookup);
+ XFREE(MTYPE_TMP, lookup.name);
+ return plist;
}
struct prefix_list *prefix_list_lookup(afi_t afi, const char *name)
@@ -188,8 +191,6 @@ static struct prefix_list *prefix_list_insert(afi_t afi, int orf,
const char *name)
{
struct prefix_list *plist;
- struct prefix_list *point;
- struct prefix_list_list *list;
struct prefix_master *master;
master = prefix_master_get(afi, orf);
@@ -203,43 +204,7 @@ static struct prefix_list *prefix_list_insert(afi_t afi, int orf,
plist->trie =
XCALLOC(MTYPE_PREFIX_LIST_TRIE, sizeof(struct pltrie_table));
- /* Set prefix_list to string list. */
- list = &master->str;
-
- /* Set point to insertion point. */
- for (point = list->head; point; point = point->next)
- if (strcmp(point->name, name) >= 0)
- break;
-
- /* In case of this is the first element of master. */
- if (list->head == NULL) {
- list->head = list->tail = plist;
- return plist;
- }
-
- /* In case of insertion is made at the tail of access_list. */
- if (point == NULL) {
- plist->prev = list->tail;
- list->tail->next = plist;
- list->tail = plist;
- return plist;
- }
-
- /* In case of insertion is made at the head of access_list. */
- if (point == list->head) {
- plist->next = list->head;
- list->head->prev = plist;
- list->head = plist;
- return plist;
- }
-
- /* Insertion is made at middle of the access_list. */
- plist->next = point;
- plist->prev = point->prev;
-
- if (point->prev)
- point->prev->next = plist;
- point->prev = plist;
+ plist_add(&master->str, plist);
return plist;
}
@@ -261,7 +226,6 @@ static void prefix_list_trie_del(struct prefix_list *plist,
/* Delete prefix-list from prefix_list_master and free it. */
void prefix_list_delete(struct prefix_list *plist)
{
- struct prefix_list_list *list;
struct prefix_master *master;
struct prefix_list_entry *pentry;
struct prefix_list_entry *next;
@@ -278,17 +242,7 @@ void prefix_list_delete(struct prefix_list *plist)
master = plist->master;
- list = &master->str;
-
- if (plist->next)
- plist->next->prev = plist->prev;
- else
- list->tail = plist->prev;
-
- if (plist->prev)
- plist->prev->next = plist->next;
- else
- list->head = plist->next;
+ plist_del(&master->str, plist);
XFREE(MTYPE_TMP, plist->desc);
@@ -1120,7 +1074,7 @@ static int vty_show_prefix_list(struct vty *vty, afi_t afi, const char *name,
master->recent->name);
}
- for (plist = master->str.head; plist; plist = plist->next)
+ frr_each (plist, &master->str, plist)
vty_show_prefix_entry(vty, json_proto, afi, plist,
master, dtype, seqnum);
}
@@ -1208,7 +1162,7 @@ static int vty_clear_prefix_list(struct vty *vty, afi_t afi, const char *name,
return CMD_WARNING;
if (name == NULL && prefix == NULL) {
- for (plist = master->str.head; plist; plist = plist->next)
+ frr_each (plist, &master->str, plist)
for (pentry = plist->head; pentry;
pentry = pentry->next)
pentry->hitcnt = 0;
@@ -1608,20 +1562,14 @@ int prefix_bgp_show_prefix_list(struct vty *vty, afi_t afi, char *name,
static void prefix_list_reset_afi(afi_t afi, int orf)
{
struct prefix_list *plist;
- struct prefix_list *next;
struct prefix_master *master;
master = prefix_master_get(afi, orf);
if (master == NULL)
return;
- for (plist = master->str.head; plist; plist = next) {
- next = plist->next;
+ while ((plist = plist_pop(&master->str)))
prefix_list_delete(plist);
- }
-
- assert(master->str.head == NULL);
- assert(master->str.tail == NULL);
master->recent = NULL;
}
@@ -1643,7 +1591,7 @@ static void plist_autocomplete_afi(afi_t afi, vector comps,
if (master == NULL)
return;
- for (plist = master->str.head; plist; plist = plist->next)
+ frr_each (plist, &master->str, plist)
vector_set(comps, XSTRDUP(MTYPE_COMPLETION, plist->name));
}
@@ -1696,6 +1644,11 @@ static void prefix_list_init_ipv6(void)
void prefix_list_init(void)
{
+ plist_init(&prefix_master_ipv4.str);
+ plist_init(&prefix_master_orf_v4.str);
+ plist_init(&prefix_master_ipv6.str);
+ plist_init(&prefix_master_orf_v6.str);
+
cmd_variable_handler_register(plist_var_handlers);
prefix_list_init_ipv4();
diff --git a/lib/plist_int.h b/lib/plist_int.h
index 571978a517..397557b37f 100644
--- a/lib/plist_int.h
+++ b/lib/plist_int.h
@@ -28,6 +28,8 @@ extern "C" {
struct pltrie_table;
+PREDECL_RBTREE_UNIQ(plist);
+
struct prefix_list {
char *name;
char *desc;
@@ -37,13 +39,12 @@ struct prefix_list {
int count;
int rangecount;
+ struct plist_item plist_item;
+
struct prefix_list_entry *head;
struct prefix_list_entry *tail;
struct pltrie_table *trie;
-
- struct prefix_list *next;
- struct prefix_list *prev;
};
/* Each prefix-list's entry. */
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index da90435c5b..d4245bde7f 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -3432,7 +3432,7 @@ DEFUN (show_ip_ospf,
if (uj)
vty_json(vty, json);
else if (!ospf_output)
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty, "%% OSPF is not enabled\n");
return ret;
}
ospf = ospf_lookup_by_inst_name(inst, vrf_name);
@@ -3440,7 +3440,9 @@ DEFUN (show_ip_ospf,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf %s\n",
+ vrf_name);
return CMD_SUCCESS;
}
@@ -3451,7 +3453,8 @@ DEFUN (show_ip_ospf,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf default\n");
return CMD_SUCCESS;
}
@@ -4164,7 +4167,7 @@ DEFUN (show_ip_ospf_interface,
if (uj)
vty_json(vty, json);
else if (!ospf)
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty, "%% OSPF is not enabled\n");
return ret;
}
@@ -4173,7 +4176,9 @@ DEFUN (show_ip_ospf_interface,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf %s\n",
+ vrf_name);
return CMD_SUCCESS;
}
@@ -4187,7 +4192,8 @@ DEFUN (show_ip_ospf_interface,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf default\n");
return CMD_SUCCESS;
}
@@ -4572,7 +4578,7 @@ DEFUN (show_ip_ospf_neighbor,
if (uj)
vty_json(vty, json);
else if (!ospf)
- vty_out(vty, "OSPF instance not found\n");
+ vty_out(vty, "OSPF is not enabled\n");
return ret;
}
@@ -4582,7 +4588,9 @@ DEFUN (show_ip_ospf_neighbor,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf %s\n",
+ vrf_name);
return CMD_SUCCESS;
}
@@ -4593,7 +4601,8 @@ DEFUN (show_ip_ospf_neighbor,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf default\n");
return CMD_SUCCESS;
}
@@ -7123,11 +7132,13 @@ DEFUN (show_ip_ospf_database_max,
}
if (!ospf_output)
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty, "%% OSPF is not enabled\n");
} else {
ospf = ospf_lookup_by_inst_name(inst, vrf_name);
if (ospf == NULL || !ospf->oi_running) {
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf %s\n",
+ vrf_name);
return CMD_SUCCESS;
}
ret = (show_ip_ospf_database_common(
@@ -7138,7 +7149,7 @@ DEFUN (show_ip_ospf_database_max,
/* Display default ospf (instance 0) info */
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
if (ospf == NULL || !ospf->oi_running) {
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty, "%% OSPF is not enabled in vrf default\n");
return CMD_SUCCESS;
}
@@ -7349,11 +7360,13 @@ DEFUN (show_ip_ospf_database_type_adv_router,
uj);
}
if (!ospf_output)
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty, "%% OSPF is not enabled\n");
} else {
ospf = ospf_lookup_by_inst_name(inst, vrf_name);
if ((ospf == NULL) || !ospf->oi_running) {
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf %s\n",
+ vrf_name);
return CMD_SUCCESS;
}
@@ -7364,7 +7377,7 @@ DEFUN (show_ip_ospf_database_type_adv_router,
/* Display default ospf (instance 0) info */
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
if (ospf == NULL || !ospf->oi_running) {
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty, "%% OSPF is not enabled on vrf default\n");
return CMD_SUCCESS;
}
@@ -10460,7 +10473,9 @@ DEFPY (show_ip_ospf_gr_helper,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf %s\n",
+ vrf_name);
return CMD_SUCCESS;
}
@@ -10474,7 +10489,8 @@ DEFPY (show_ip_ospf_gr_helper,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf default\n");
return CMD_SUCCESS;
}
@@ -11019,11 +11035,13 @@ DEFUN (show_ip_ospf_border_routers,
}
if (!ospf_output)
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty, "%% OSPF is not enabled\n");
} else {
ospf = ospf_lookup_by_inst_name(inst, vrf_name);
if (ospf == NULL || !ospf->oi_running) {
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf %s\n",
+ vrf_name);
return CMD_SUCCESS;
}
@@ -11034,7 +11052,7 @@ DEFUN (show_ip_ospf_border_routers,
/* Display default ospf (instance 0) info */
ospf = ospf_lookup_by_vrf_id(VRF_DEFAULT);
if (ospf == NULL || !ospf->oi_running) {
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty, "%% OSPF is not enabled in vrf default\n");
return CMD_SUCCESS;
}
@@ -11161,7 +11179,7 @@ DEFUN (show_ip_ospf_route,
/* Keep Non-pretty format */
vty_json(vty, json);
} else if (!ospf_output)
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty, "%% OSPF is not enabled\n");
return ret;
}
@@ -11170,7 +11188,9 @@ DEFUN (show_ip_ospf_route,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf %s\n",
+ vrf_name);
return CMD_SUCCESS;
}
@@ -11181,7 +11201,8 @@ DEFUN (show_ip_ospf_route,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf default\n");
return CMD_SUCCESS;
}
@@ -11602,7 +11623,9 @@ DEFUN (show_ip_ospf_external_aggregator,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf %s\n",
+ vrf_name);
return CMD_SUCCESS;
}
@@ -11615,7 +11638,8 @@ DEFUN (show_ip_ospf_external_aggregator,
if (uj)
vty_json(vty, json);
else
- vty_out(vty, "%% OSPF instance not found\n");
+ vty_out(vty,
+ "%% OSPF is not enabled in vrf default\n");
return CMD_SUCCESS;
}
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 78dbbf9326..2a6d03fb91 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -1285,9 +1285,14 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
{
struct interface *ifp;
struct igmp_stats igmp_stats;
+ bool found_ifname = false;
+ json_object *json = NULL;
igmp_stats_init(&igmp_stats);
+ if (uj)
+ json = json_object_new_object();
+
FOR_ALL_INTERFACES (pim->vrf, ifp) {
struct pim_interface *pim_ifp;
struct listnode *sock_node, *source_node, *group_node;
@@ -1303,12 +1308,16 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
if (ifname && strcmp(ifname, ifp->name))
continue;
+ found_ifname = true;
+
igmp_stats.joins_failed += pim_ifp->igmp_ifstat_joins_failed;
igmp_stats.joins_sent += pim_ifp->igmp_ifstat_joins_sent;
igmp_stats.total_groups +=
pim_ifp->gm_group_list
? listcount(pim_ifp->gm_group_list)
: 0;
+ igmp_stats.peak_groups += pim_ifp->igmp_peak_group_count;
+
for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, group_node,
group)) {
@@ -1326,15 +1335,20 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
igmp_stats_add(&igmp_stats, &igmp->igmp_stats);
}
}
- if (uj) {
- json_object *json = NULL;
- json_object *json_row = NULL;
- json = json_object_new_object();
- json_row = json_object_new_object();
+ if (!found_ifname) {
+ if (uj)
+ vty_json(vty, json);
+ else
+ vty_out(vty, "%% No such interface\n");
+ return;
+ }
+
+ if (uj) {
+ json_object *json_row = json_object_new_object();
- json_object_string_add(json_row, "name", ifname ? ifname :
- "global");
+ json_object_string_add(json_row, "name",
+ ifname ? ifname : "global");
json_object_int_add(json_row, "queryV1", igmp_stats.query_v1);
json_object_int_add(json_row, "queryV2", igmp_stats.query_v2);
json_object_int_add(json_row, "queryV3", igmp_stats.query_v3);
@@ -1350,6 +1364,8 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
igmp_stats.unsupported);
json_object_int_add(json_row, "totalReceivedMessages",
igmp_stats.total_recv_messages);
+ json_object_int_add(json_row, "peakGroups",
+ igmp_stats.peak_groups);
json_object_int_add(json_row, "totalGroups",
igmp_stats.total_groups);
json_object_int_add(json_row, "totalSourceGroups",
@@ -1399,6 +1415,8 @@ static void igmp_show_statistics(struct pim_instance *pim, struct vty *vty,
igmp_stats.general_queries_sent);
vty_out(vty, "group queries sent : %u\n",
igmp_stats.group_queries_sent);
+ vty_out(vty, "peak groups : %u\n",
+ igmp_stats.peak_groups);
vty_out(vty, "total groups : %u\n",
igmp_stats.total_groups);
vty_out(vty, "total source groups : %u\n",
@@ -4051,6 +4069,7 @@ DEFUN (clear_ip_pim_interface_traffic,
pim_ifp->pim_ifstat_bsm_tx = 0;
pim_ifp->igmp_ifstat_joins_sent = 0;
pim_ifp->igmp_ifstat_joins_failed = 0;
+ pim_ifp->igmp_peak_group_count = 0;
}
return CMD_SUCCESS;
diff --git a/pimd/pim_iface.h b/pimd/pim_iface.h
index 244e1aedd1..bab73eae86 100644
--- a/pimd/pim_iface.h
+++ b/pimd/pim_iface.h
@@ -191,6 +191,7 @@ struct pim_interface {
uint32_t igmp_ifstat_joins_sent;
uint32_t igmp_ifstat_joins_failed;
+ uint32_t igmp_peak_group_count;
struct {
bool enabled;
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index cfab9265fd..aa6b30c624 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -977,6 +977,8 @@ static void igmp_group_free(struct gm_group *group)
static void igmp_group_count_incr(struct pim_interface *pim_ifp)
{
+ uint32_t group_count = listcount(pim_ifp->gm_group_list);
+
++pim_ifp->pim->igmp_group_count;
if (pim_ifp->pim->igmp_group_count
== pim_ifp->pim->igmp_watermark_limit) {
@@ -985,6 +987,9 @@ static void igmp_group_count_incr(struct pim_interface *pim_ifp)
pim_ifp->pim->igmp_group_count,
VRF_LOGNAME(pim_ifp->pim->vrf));
}
+
+ if (pim_ifp->igmp_peak_group_count < group_count)
+ pim_ifp->igmp_peak_group_count = group_count;
}
static void igmp_group_count_decr(struct pim_interface *pim_ifp)
diff --git a/pimd/pim_igmp_stats.c b/pimd/pim_igmp_stats.c
index 6a5ce4dc8d..1d51104687 100644
--- a/pimd/pim_igmp_stats.c
+++ b/pimd/pim_igmp_stats.c
@@ -43,6 +43,7 @@ void igmp_stats_add(struct igmp_stats *a, struct igmp_stats *b)
a->mtrace_rsp += b->mtrace_rsp;
a->mtrace_req += b->mtrace_req;
a->unsupported += b->unsupported;
+ a->peak_groups += b->peak_groups;
a->total_groups += b->total_groups;
a->total_source_groups += b->total_source_groups;
a->joins_sent += b->joins_sent;
diff --git a/pimd/pim_igmp_stats.h b/pimd/pim_igmp_stats.h
index 560132a19a..8c66986e03 100644
--- a/pimd/pim_igmp_stats.h
+++ b/pimd/pim_igmp_stats.h
@@ -33,6 +33,7 @@ struct igmp_stats {
uint32_t mtrace_rsp;
uint32_t mtrace_req;
uint32_t unsupported;
+ uint32_t peak_groups;
uint32_t total_groups;
uint32_t total_source_groups;
uint32_t joins_sent;
diff --git a/pimd/pim_instance.h b/pimd/pim_instance.h
index 68c5b9167b..4ac9ef7f51 100644
--- a/pimd/pim_instance.h
+++ b/pimd/pim_instance.h
@@ -176,7 +176,7 @@ struct pim_instance {
struct pim_vxlan_instance vxlan;
struct list *ssmpingd_list;
- struct in_addr ssmpingd_group_addr;
+ pim_addr ssmpingd_group_addr;
unsigned int igmp_group_count;
unsigned int igmp_watermark_limit;
diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c
index 21f57e2d11..2fabee5dfd 100644
--- a/pimd/pim_nb_config.c
+++ b/pimd/pim_nb_config.c
@@ -930,7 +930,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss
struct vrf *vrf;
struct pim_instance *pim;
int result;
- struct ipaddr source_addr;
+ pim_addr source_addr;
switch (args->event) {
case NB_EV_VALIDATE:
@@ -940,16 +940,14 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
- yang_dnode_get_ip(&source_addr, args->dnode, NULL);
- result = pim_ssmpingd_start(pim, source_addr.ip._v4_addr);
+ yang_dnode_get_pimaddr(&source_addr, args->dnode,
+ "./source-addr");
+ result = pim_ssmpingd_start(pim, source_addr);
if (result) {
- char source_str[INET_ADDRSTRLEN];
-
- ipaddr2str(&source_addr, source_str,
- sizeof(source_str));
- snprintf(args->errmsg, args->errmsg_len,
- "%% Failure starting ssmpingd for source %s: %d",
- source_str, result);
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "%% Failure starting ssmpingd for source %pPA: %d",
+ &source_addr, result);
return NB_ERR_INCONSISTENCY;
}
}
@@ -963,7 +961,7 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss
struct vrf *vrf;
struct pim_instance *pim;
int result;
- struct ipaddr source_addr;
+ pim_addr source_addr;
switch (args->event) {
case NB_EV_VALIDATE:
@@ -973,16 +971,14 @@ int routing_control_plane_protocols_control_plane_protocol_pim_address_family_ss
case NB_EV_APPLY:
vrf = nb_running_get_entry(args->dnode, NULL, true);
pim = vrf->info;
- yang_dnode_get_ip(&source_addr, args->dnode, NULL);
- result = pim_ssmpingd_stop(pim, source_addr.ip._v4_addr);
+ yang_dnode_get_pimaddr(&source_addr, args->dnode,
+ "./source-addr");
+ result = pim_ssmpingd_stop(pim, source_addr);
if (result) {
- char source_str[INET_ADDRSTRLEN];
-
- ipaddr2str(&source_addr, source_str,
- sizeof(source_str));
- snprintf(args->errmsg, args->errmsg_len,
- "%% Failure stopping ssmpingd for source %s: %d",
- source_str, result);
+ snprintf(
+ args->errmsg, args->errmsg_len,
+ "%% Failure stopping ssmpingd for source %pPA: %d",
+ &source_addr, result);
return NB_ERR_INCONSISTENCY;
}
diff --git a/pimd/pim_pim.c b/pimd/pim_pim.c
index 616cad16c6..535448f013 100644
--- a/pimd/pim_pim.c
+++ b/pimd/pim_pim.c
@@ -469,6 +469,7 @@ void pim_ifstat_reset(struct interface *ifp)
pim_ifp->pim_ifstat_bsm_invalid_sz = 0;
pim_ifp->igmp_ifstat_joins_sent = 0;
pim_ifp->igmp_ifstat_joins_failed = 0;
+ pim_ifp->igmp_peak_group_count = 0;
}
void pim_sock_reset(struct interface *ifp)
diff --git a/pimd/pim_register.c b/pimd/pim_register.c
index 8313c8d4f6..7fa36e5a44 100644
--- a/pimd/pim_register.c
+++ b/pimd/pim_register.c
@@ -61,7 +61,7 @@ void pim_register_join(struct pim_upstream *up)
pim_channel_add_oif(up->channel_oil, pim->regiface,
PIM_OIF_FLAG_PROTO_PIM, __func__);
up->reg_state = PIM_REG_JOIN;
- pim_vxlan_update_sg_reg_state(pim, up, true /*reg_join*/);
+ pim_vxlan_update_sg_reg_state(pim, up, true);
}
void pim_register_stop_send(struct interface *ifp, pim_sgaddr *sg,
@@ -108,13 +108,39 @@ void pim_register_stop_send(struct interface *ifp, pim_sgaddr *sg,
++pinfo->pim_ifstat_reg_stop_send;
}
+static void pim_reg_stop_upstream(struct pim_instance *pim,
+ struct pim_upstream *up)
+{
+ switch (up->reg_state) {
+ case PIM_REG_NOINFO:
+ case PIM_REG_PRUNE:
+ return;
+ case PIM_REG_JOIN:
+ up->reg_state = PIM_REG_PRUNE;
+ pim_channel_del_oif(up->channel_oil, pim->regiface,
+ PIM_OIF_FLAG_PROTO_PIM, __func__);
+ pim_upstream_start_register_stop_timer(up, 0);
+ pim_vxlan_update_sg_reg_state(pim, up, false);
+ break;
+ case PIM_REG_JOIN_PENDING:
+ up->reg_state = PIM_REG_PRUNE;
+ pim_upstream_start_register_stop_timer(up, 0);
+ return;
+ }
+}
+
int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size)
{
struct pim_interface *pim_ifp = ifp->info;
struct pim_instance *pim = pim_ifp->pim;
- struct pim_upstream *upstream = NULL;
+ struct pim_upstream *up = NULL;
+ struct pim_rpf *rp;
+ pim_addr rpf_addr;
pim_sgaddr sg;
+ struct listnode *up_node;
+ struct pim_upstream *child;
bool wrong_af = false;
+ bool handling_star = false;
int l;
++pim_ifp->pim_ifstat_reg_stop_recv;
@@ -127,33 +153,65 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size)
if (wrong_af) {
zlog_err("invalid AF in Register-Stop on %s", ifp->name);
- return 0;
+ return -1;
}
- upstream = pim_upstream_find(pim, &sg);
- if (!upstream) {
- return 0;
- }
if (PIM_DEBUG_PIM_REG)
- zlog_debug("Received Register stop for %s", upstream->sg_str);
+ zlog_debug("Received Register stop for %pSG", &sg);
+
+ rp = RP(pim_ifp->pim, sg.grp);
+ if (rp) {
+ rpf_addr = pim_addr_from_prefix(&rp->rpf_addr);
+ if (pim_addr_cmp(sg.src, rpf_addr) == 0) {
+ handling_star = true;
+ sg.src = PIMADDR_ANY;
+ }
+ }
- switch (upstream->reg_state) {
- case PIM_REG_NOINFO:
- case PIM_REG_PRUNE:
- return 0;
- case PIM_REG_JOIN:
- upstream->reg_state = PIM_REG_PRUNE;
- pim_channel_del_oif(upstream->channel_oil, pim->regiface,
- PIM_OIF_FLAG_PROTO_PIM, __func__);
- pim_upstream_start_register_stop_timer(upstream, 0);
- pim_vxlan_update_sg_reg_state(pim, upstream,
- false/*reg_join*/);
- break;
- case PIM_REG_JOIN_PENDING:
- upstream->reg_state = PIM_REG_PRUNE;
- pim_upstream_start_register_stop_timer(upstream, 0);
- return 0;
+ /*
+ * RFC 7761 Sec 4.4.1
+ * Handling Register-Stop(*,G) Messages at the DR:
+ * A Register-Stop(*,G) should be treated as a
+ * Register-Stop(S,G) for all (S,G) Register state
+ * machines that are not in the NoInfo state.
+ */
+ up = pim_upstream_find(pim, &sg);
+ if (up) {
+ /*
+ * If the upstream find actually found a particular
+ * S,G then we *know* that the following for loop
+ * is not going to execute and this is ok
+ */
+ for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug("Executing Reg stop for %s",
+ child->sg_str);
+
+ pim_reg_stop_upstream(pim, child);
+ }
+
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug("Executing Reg stop for %s", up->sg_str);
+ pim_reg_stop_upstream(pim, up);
+ } else {
+ if (!handling_star)
+ return 0;
+ /*
+ * Unfortunately pim was unable to find a *,G
+ * but pim may still actually have individual
+ * S,G's that need to be processed. In that
+ * case pim must do the expensive walk to find
+ * and stop
+ */
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ if (pim_addr_cmp(up->sg.grp, sg.grp) == 0) {
+ if (PIM_DEBUG_PIM_REG)
+ zlog_debug("Executing Reg stop for %s",
+ up->sg_str);
+ pim_reg_stop_upstream(pim, up);
+ }
+ }
}
return 0;
diff --git a/pimd/pim_ssmpingd.c b/pimd/pim_ssmpingd.c
index e43b46604c..da903bd980 100644
--- a/pimd/pim_ssmpingd.c
+++ b/pimd/pim_ssmpingd.c
@@ -30,8 +30,13 @@
#include "pim_ssmpingd.h"
#include "pim_time.h"
#include "pim_sock.h"
+#include "network.h"
+#if PIM_IPV == 4
static const char *const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
+#else
+static const char *const PIM_SSMPINGD_REPLY_GROUP = "ff3e::4321:1234";
+#endif
enum { PIM_SSMPINGD_REQUEST = 'Q', PIM_SSMPINGD_REPLY = 'A' };
@@ -43,7 +48,7 @@ void pim_ssmpingd_init(struct pim_instance *pim)
assert(!pim->ssmpingd_list);
- result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP,
+ result = inet_pton(PIM_AF, PIM_SSMPINGD_REPLY_GROUP,
&pim->ssmpingd_group_addr);
assert(result > 0);
@@ -56,7 +61,7 @@ void pim_ssmpingd_destroy(struct pim_instance *pim)
}
static struct ssmpingd_sock *ssmpingd_find(struct pim_instance *pim,
- struct in_addr source_addr)
+ pim_addr source_addr)
{
struct listnode *node;
struct ssmpingd_sock *ss;
@@ -65,7 +70,7 @@ static struct ssmpingd_sock *ssmpingd_find(struct pim_instance *pim,
return 0;
for (ALL_LIST_ELEMENTS_RO(pim->ssmpingd_list, node, ss))
- if (source_addr.s_addr == ss->source_addr.s_addr)
+ if (!pim_addr_cmp(source_addr, ss->source_addr))
return ss;
return 0;
@@ -76,73 +81,50 @@ static void ssmpingd_free(struct ssmpingd_sock *ss)
XFREE(MTYPE_PIM_SSMPINGD, ss);
}
-static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
+#if PIM_IPV == 4
+static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
{
- struct sockaddr_in sockaddr;
- int fd;
-
- fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- if (fd < 0) {
- flog_err_sys(EC_LIB_SOCKET,
- "%s: could not create socket: errno=%d: %s",
- __func__, errno, safe_strerror(errno));
- return -1;
+ /* Needed to obtain destination address from recvmsg() */
+#if defined(HAVE_IP_PKTINFO)
+ /* Linux and Solaris IP_PKTINFO */
+ int opt = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
+ zlog_warn(
+ "%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
+ __func__, fd, errno, safe_strerror(errno));
}
+#elif defined(HAVE_IP_RECVDSTADDR)
+ /* BSD IP_RECVDSTADDR */
+ int opt = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
+ zlog_warn(
+ "%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
+ __func__, fd, errno, safe_strerror(errno));
+ }
+#else
+ flog_err(
+ EC_LIB_DEVELOPMENT,
+ "%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
+ __FILE__, __func__);
+ close(fd);
+ return -1;
+#endif
- sockaddr.sin_family = AF_INET;
- sockaddr.sin_addr = addr;
- sockaddr.sin_port = htons(port);
-
- if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
- char addr_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str));
+ if (setsockopt_ipv4_multicast_loop(fd, 0)) {
zlog_warn(
- "%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s",
- __func__, fd, addr_str, port, sizeof(sockaddr), errno,
- safe_strerror(errno));
+ "%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
+ __func__, fd, errno, safe_strerror(errno));
close(fd);
- return -1;
+ return PIM_SOCK_ERR_LOOP;
}
- /* Needed to obtain destination address from recvmsg() */
- {
-#if defined(HAVE_IP_PKTINFO)
- /* Linux and Solaris IP_PKTINFO */
- int opt = 1;
- if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
- zlog_warn(
- "%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
- __func__, fd, errno, safe_strerror(errno));
- }
-#elif defined(HAVE_IP_RECVDSTADDR)
- /* BSD IP_RECVDSTADDR */
- int opt = 1;
- if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt,
- sizeof(opt))) {
- zlog_warn(
- "%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
- __func__, fd, errno, safe_strerror(errno));
- }
-#else
- flog_err(
- EC_LIB_DEVELOPMENT,
- "%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
- __FILE__, __func__);
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&addr,
+ sizeof(addr))) {
+ zlog_warn(
+ "%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
+ __func__, fd, errno, safe_strerror(errno));
close(fd);
return -1;
-#endif
- }
-
- {
- int reuse = 1;
- if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&reuse,
- sizeof(reuse))) {
- zlog_warn(
- "%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
- __func__, fd, errno, safe_strerror(errno));
- close(fd);
- return -1;
- }
}
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&mttl,
@@ -154,7 +136,15 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
return -1;
}
- if (setsockopt_ipv4_multicast_loop(fd, 0)) {
+ return 0;
+}
+#else
+static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
+{
+ setsockopt_ipv6_pktinfo(fd, 1);
+ setsockopt_ipv6_multicast_hops(fd, mttl);
+
+ if (setsockopt_ipv6_multicast_loop(fd, 0)) {
zlog_warn(
"%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
__func__, fd, errno, safe_strerror(errno));
@@ -162,7 +152,7 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
return PIM_SOCK_ERR_LOOP;
}
- if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&addr,
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&addr,
sizeof(addr))) {
zlog_warn(
"%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
@@ -170,26 +160,45 @@ static int ssmpingd_socket(struct in_addr addr, int port, int mttl)
close(fd);
return -1;
}
+ return 0;
+}
+#endif
- {
- long flags;
- flags = fcntl(fd, F_GETFL, 0);
- if (flags < 0) {
- zlog_warn(
- "%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
- __func__, fd, errno, safe_strerror(errno));
- close(fd);
- return -1;
- }
+static int ssmpingd_socket(pim_addr addr, int port, int mttl)
+{
+ struct sockaddr_storage sockaddr;
+ int fd;
+ int ret;
+ socklen_t len = sizeof(sockaddr);
- if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
- zlog_warn(
- "%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
- __func__, fd, errno, safe_strerror(errno));
- close(fd);
- return -1;
- }
+ fd = socket(PIM_AF, SOCK_DGRAM, IPPROTO_UDP);
+ if (fd < 0) {
+ flog_err_sys(EC_LIB_SOCKET,
+ "%s: could not create socket: errno=%d: %s",
+ __func__, errno, safe_strerror(errno));
+ return -1;
+ }
+
+ pim_socket_getsockname(fd, (struct sockaddr *)&sockaddr, &len);
+
+ if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
+ zlog_warn(
+ "%s: bind(fd=%d,addr=%pSUp,port=%d,len=%zu) failure: errno=%d: %s",
+ __func__, fd, &sockaddr, port, sizeof(sockaddr), errno,
+ safe_strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ set_nonblocking(fd);
+ sockopt_reuseaddr(fd);
+
+ ret = ssmpingd_setsockopt(fd, addr, mttl);
+ if (ret) {
+ zlog_warn("ssmpingd_setsockopt failed");
+ close(fd);
+ return -1;
}
return fd;
@@ -202,12 +211,9 @@ static void ssmpingd_delete(struct ssmpingd_sock *ss)
THREAD_OFF(ss->t_sock_read);
if (close(ss->sock_fd)) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", ss->source_addr, source_str,
- sizeof(source_str));
zlog_warn(
- "%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
- __func__, ss->sock_fd, source_str, errno,
+ "%s: failure closing ssmpingd sock_fd=%d for source %pI4: errno=%d: %s",
+ __func__, ss->sock_fd, &ss->source_addr, errno,
safe_strerror(errno));
/* warning only */
}
@@ -217,7 +223,7 @@ static void ssmpingd_delete(struct ssmpingd_sock *ss)
}
static void ssmpingd_sendto(struct ssmpingd_sock *ss, const uint8_t *buf,
- int len, struct sockaddr_in to)
+ int len, struct sockaddr_storage to)
{
socklen_t tolen = sizeof(to);
int sent;
@@ -225,18 +231,15 @@ static void ssmpingd_sendto(struct ssmpingd_sock *ss, const uint8_t *buf,
sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
(struct sockaddr *)&to, tolen);
if (sent != len) {
- char to_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
if (sent < 0) {
zlog_warn(
- "%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
- __func__, to_str, ntohs(to.sin_port),
- ss->sock_fd, len, errno, safe_strerror(errno));
+ "%s: sendto() failure to %pSUp,fd=%d len=%d: errno=%d: %s",
+ __func__, &to, ss->sock_fd, len, errno,
+ safe_strerror(errno));
} else {
zlog_warn(
- "%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
- __func__, to_str, ntohs(to.sin_port),
- ss->sock_fd, len, sent);
+ "%s: sendto() partial to %pSUp, fd=%d len=%d: sent=%d",
+ __func__, &to, ss->sock_fd, len, sent);
}
}
}
@@ -285,14 +288,12 @@ static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
buf[0] = PIM_SSMPINGD_REPLY;
- struct sockaddr_in *from_addr = (struct sockaddr_in *)&from;
-
/* unicast reply */
- ssmpingd_sendto(ss, buf, len, *from_addr);
+ ssmpingd_sendto(ss, buf, len, from);
/* multicast reply */
- from_addr->sin_addr = ss->pim->ssmpingd_group_addr;
- ssmpingd_sendto(ss, buf, len, *from_addr);
+ memcpy(&from, &ss->pim->ssmpingd_group_addr, sizeof(pim_addr));
+ ssmpingd_sendto(ss, buf, len, from);
return 0;
}
@@ -316,7 +317,7 @@ static void ssmpingd_read_on(struct ssmpingd_sock *ss)
}
static struct ssmpingd_sock *ssmpingd_new(struct pim_instance *pim,
- struct in_addr source_addr)
+ pim_addr source_addr)
{
struct ssmpingd_sock *ss;
int sock_fd;
@@ -329,11 +330,8 @@ static struct ssmpingd_sock *ssmpingd_new(struct pim_instance *pim,
sock_fd =
ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
if (sock_fd < 0) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_warn("%s: ssmpingd_socket() failure for source %s",
- __func__, source_str);
+ zlog_warn("%s: ssmpingd_socket() failure for source %pI4",
+ __func__, &source_addr);
return 0;
}
@@ -353,7 +351,7 @@ static struct ssmpingd_sock *ssmpingd_new(struct pim_instance *pim,
return ss;
}
-int pim_ssmpingd_start(struct pim_instance *pim, struct in_addr source_addr)
+int pim_ssmpingd_start(struct pim_instance *pim, pim_addr source_addr)
{
struct ssmpingd_sock *ss;
@@ -364,47 +362,33 @@ int pim_ssmpingd_start(struct pim_instance *pim, struct in_addr source_addr)
}
{
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_info("%s: starting ssmpingd for source %s", __func__,
- source_str);
+ zlog_info("%s: starting ssmpingd for source %pPAs", __func__,
+ &source_addr);
}
ss = ssmpingd_new(pim, source_addr);
if (!ss) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_warn("%s: ssmpingd_new() failure for source %s", __func__,
- source_str);
+ zlog_warn("%s: ssmpingd_new() failure for source %pPAs",
+ __func__, &source_addr);
return -1;
}
return 0;
}
-int pim_ssmpingd_stop(struct pim_instance *pim, struct in_addr source_addr)
+int pim_ssmpingd_stop(struct pim_instance *pim, pim_addr source_addr)
{
struct ssmpingd_sock *ss;
ss = ssmpingd_find(pim, source_addr);
if (!ss) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_warn("%s: could not find ssmpingd for source %s", __func__,
- source_str);
+ zlog_warn("%s: could not find ssmpingd for source %pPAs",
+ __func__, &source_addr);
return -1;
}
- {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", source_addr, source_str,
- sizeof(source_str));
- zlog_info("%s: stopping ssmpingd for source %s", __func__,
- source_str);
- }
+ zlog_info("%s: stopping ssmpingd for source %pPAs", __func__,
+ &source_addr);
ssmpingd_delete(ss);
diff --git a/pimd/pim_ssmpingd.h b/pimd/pim_ssmpingd.h
index fafdd7ade1..c4376bd0e4 100644
--- a/pimd/pim_ssmpingd.h
+++ b/pimd/pim_ssmpingd.h
@@ -31,14 +31,14 @@ struct ssmpingd_sock {
int sock_fd; /* socket */
struct thread *t_sock_read; /* thread for reading socket */
- struct in_addr source_addr; /* source address */
+ pim_addr source_addr; /* source address */
int64_t creation; /* timestamp of socket creation */
int64_t requests; /* counter */
};
void pim_ssmpingd_init(struct pim_instance *pim);
void pim_ssmpingd_destroy(struct pim_instance *pim);
-int pim_ssmpingd_start(struct pim_instance *pim, struct in_addr source_addr);
-int pim_ssmpingd_stop(struct pim_instance *pim, struct in_addr source_addr);
+int pim_ssmpingd_start(struct pim_instance *pim, pim_addr source_addr);
+int pim_ssmpingd_stop(struct pim_instance *pim, pim_addr source_addr);
#endif /* PIM_SSMPINGD_H */
diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c
index 6de3a04b66..79deceee15 100644
--- a/pimd/pim_vty.c
+++ b/pimd/pim_vty.c
@@ -259,10 +259,8 @@ int pim_global_config_write_worker(struct pim_instance *pim, struct vty *vty)
struct ssmpingd_sock *ss;
++writes;
for (ALL_LIST_ELEMENTS_RO(pim->ssmpingd_list, node, ss)) {
- char source_str[INET_ADDRSTRLEN];
- pim_inet4_dump("<src?>", ss->source_addr, source_str,
- sizeof(source_str));
- vty_out(vty, "%sip ssmpingd %s\n", spaces, source_str);
+ vty_out(vty, "%sip ssmpingd %pI4\n", spaces,
+ &ss->source_addr);
++writes;
}
}
diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c
index 78cc57cc44..889643f65e 100644
--- a/sharpd/sharp_vty.c
+++ b/sharpd/sharp_vty.c
@@ -1258,6 +1258,67 @@ DEFPY (show_sharp_cspf,
return CMD_SUCCESS;
}
+static struct interface *if_lookup_vrf_all(const char *ifname)
+{
+ struct interface *ifp;
+ struct vrf *vrf;
+
+ RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) {
+ ifp = if_lookup_by_name(ifname, vrf->vrf_id);
+ if (ifp)
+ return ifp;
+ }
+
+ return NULL;
+}
+
+DEFPY (sharp_interface_protodown,
+ sharp_interface_protodown_cmd,
+ "sharp interface IFNAME$ifname protodown",
+ SHARP_STR
+ INTERFACE_STR
+ IFNAME_STR
+ "Set interface protodown\n")
+{
+ struct interface *ifp;
+
+ ifp = if_lookup_vrf_all(ifname);
+
+ if (!ifp) {
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+
+ if (sharp_zebra_send_interface_protodown(ifp, true) != 0)
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_sharp_interface_protodown,
+ no_sharp_interface_protodown_cmd,
+ "no sharp interface IFNAME$ifname protodown",
+ NO_STR
+ SHARP_STR
+ INTERFACE_STR
+ IFNAME_STR
+ "Set interface protodown\n")
+{
+ struct interface *ifp;
+
+ ifp = if_lookup_vrf_all(ifname);
+
+ if (!ifp) {
+ vty_out(vty, "%% Can't find interface %s\n", ifname);
+ return CMD_WARNING;
+ }
+
+ if (sharp_zebra_send_interface_protodown(ifp, false) != 0)
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
void sharp_vty_init(void)
{
install_element(ENABLE_NODE, &install_routes_data_dump_cmd);
@@ -1290,5 +1351,8 @@ void sharp_vty_init(void)
&sharp_srv6_manager_release_locator_chunk_cmd);
install_element(ENABLE_NODE, &show_sharp_segment_routing_srv6_cmd);
+ install_element(ENABLE_NODE, &sharp_interface_protodown_cmd);
+ install_element(ENABLE_NODE, &no_sharp_interface_protodown_cmd);
+
return;
}
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index 5304b17f06..52364bff41 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -969,6 +969,18 @@ static int sharp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS)
return 0;
}
+int sharp_zebra_send_interface_protodown(struct interface *ifp, bool down)
+{
+ zlog_debug("Sending zebra to set %s protodown %s", ifp->name,
+ down ? "on" : "off");
+
+ if (zclient_send_interface_protodown(zclient, ifp->vrf->vrf_id, ifp,
+ down) == ZCLIENT_SEND_FAILURE)
+ return -1;
+
+ return 0;
+}
+
static zclient_handler *const sharp_handlers[] = {
[ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add,
[ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete,
diff --git a/sharpd/sharp_zebra.h b/sharpd/sharp_zebra.h
index 49f11a67e8..d8ea679797 100644
--- a/sharpd/sharp_zebra.h
+++ b/sharpd/sharp_zebra.h
@@ -73,4 +73,6 @@ extern void sharp_install_seg6local_route_helper(struct prefix *p,
enum seg6local_action_t act,
struct seg6local_context *ctx);
+extern int sharp_zebra_send_interface_protodown(struct interface *ifp,
+ bool down);
#endif
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
index c0572fca4c..005896b01c 100644
--- a/tests/topotests/lib/common_config.py
+++ b/tests/topotests/lib/common_config.py
@@ -2536,6 +2536,7 @@ def create_route_maps(tgen, input_dict, build=False):
ipv6_data = set_data.setdefault("ipv6", {})
local_preference = set_data.setdefault("locPrf", None)
metric = set_data.setdefault("metric", None)
+ metric_type = set_data.setdefault("metric-type", None)
as_path = set_data.setdefault("path", {})
weight = set_data.setdefault("weight", None)
community = set_data.setdefault("community", {})
@@ -2559,7 +2560,11 @@ def create_route_maps(tgen, input_dict, build=False):
# Metric
if metric:
- rmap_data.append("set metric {} \n".format(metric))
+ del_comm = set_data.setdefault("delete", None)
+ if del_comm:
+ rmap_data.append("no set metric {}".format(metric))
+ else:
+ rmap_data.append("set metric {}".format(metric))
# Origin
if origin:
diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py
index 8d2bf12af2..e7ea7d32ba 100644
--- a/tests/topotests/lib/ospf.py
+++ b/tests/topotests/lib/ospf.py
@@ -1668,7 +1668,7 @@ def verify_ospf6_rib(
logger.info("Checking router %s RIB:", router)
# Verifying RIB routes
- command = "show ipv6 ospf route"
+ command = "show ipv6 ospf route detail"
found_routes = []
missing_routes = []
@@ -1710,6 +1710,8 @@ def verify_ospf6_rib(
# Generating IPs for verification
ip_list = generate_ips(network, no_of_ip)
+ if len(ip_list) == 1:
+ ip_list = [network]
st_found = False
nh_found = False
for st_rt in ip_list:
@@ -1846,7 +1848,7 @@ def verify_ospf6_rib(
return errormsg
if metric is not None:
- if "type2cost" not in ospf_rib_json[st_rt]:
+ if "metricCostE2" not in ospf_rib_json[st_rt]:
errormsg = (
"[DUT: {}]: metric is"
" not present for"
@@ -1854,7 +1856,7 @@ def verify_ospf6_rib(
)
return errormsg
- if metric != ospf_rib_json[st_rt]["type2cost"]:
+ if metric != ospf_rib_json[st_rt]["metricCostE2"]:
errormsg = (
"[DUT: {}]: metric value "
"{} is not matched for "
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
index a83ae7071f..4ed5b2f825 100644
--- a/tests/topotests/lib/topogen.py
+++ b/tests/topotests/lib/topogen.py
@@ -804,9 +804,11 @@ class TopoRouter(TopoGear):
for daemon in self.RD:
# This will not work for all daemons
daemonstr = self.RD.get(daemon).rstrip("d")
- result = self.run(
- "grep 'router {}' {}".format(daemonstr, source)
- ).strip()
+ if daemonstr == "pim":
+ grep_cmd = "grep 'ip {}' {}".format(daemonstr, source)
+ else:
+ grep_cmd = "grep 'router {}' {}".format(daemonstr, source)
+ result = self.run(grep_cmd).strip()
if result:
self.load_config(daemon)
else:
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
index 62b6a8a70e..e786ae02cd 100644
--- a/tests/topotests/lib/topotest.py
+++ b/tests/topotests/lib/topotest.py
@@ -737,6 +737,7 @@ def proto_name_to_number(protocol):
"sharp": "194",
"pbr": "195",
"static": "196",
+ "ospf6": "197",
}.get(
protocol, protocol
) # default return same as input
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/__init__.py b/tests/topotests/ospf_multi_vrf_bgp_route_leak/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/__init__.py
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf
new file mode 100644
index 0000000000..e365e25772
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf
@@ -0,0 +1,57 @@
+!
+hostname r1
+password zebra
+log file /tmp/r1-frr.log
+!
+interface r1-eth0
+ ip address 10.0.1.1/24
+!
+interface r1-eth1
+ ip address 10.0.20.1/24
+!
+interface r1-eth2 vrf neno
+ ip address 10.0.30.1/24
+!
+ip forwarding
+!
+router ospf
+ ospf router-id 10.0.255.1
+ redistribute bgp
+ network 10.0.1.0/24 area 0
+ network 10.0.20.0/24 area 0
+!
+router ospf vrf neno
+ ospf router-id 10.0.255.1
+ redistribute bgp
+ network 10.0.30.0/24 area 0
+!
+!
+router bgp 99
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf neno
+ !
+!
+router bgp 99 vrf neno
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf default
+ !
+!
+!!!!!!!!!!!!!!!!!!!!!
+! VRFs neno and ray subnets
+ip prefix-list nets seq 5 permit 10.0.3.0/24
+ip prefix-list nets seq 10 permit 10.0.30.0/24
+ip prefix-list nets seq 15 permit 10.0.4.0/24
+ip prefix-list nets seq 20 permit 10.0.40.0/24
+ip prefix-list nets seq 25 deny any
+!
+route-map rmap permit 10
+ match ip address prefix-list nets
+ exit
+!
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-default.txt
new file mode 100644
index 0000000000..45b7fad334
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-default.txt
@@ -0,0 +1,19 @@
+VRF Name: default
+============ OSPF network routing table ============
+N 10.0.1.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth0
+N 10.0.2.0/24 [20] area: 0.0.0.0
+ via 10.0.20.2, r1-eth1
+N 10.0.20.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.2 [10] area: 0.0.0.0, ASBR
+ via 10.0.20.2, r1-eth1
+
+============ OSPF external routing table ===========
+N E2 10.0.4.0/24 [10/20] tag: 0
+ via 10.0.20.2, r1-eth1
+N E2 10.0.40.0/24 [10/20] tag: 0
+ via 10.0.20.2, r1-eth1
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-neno.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-neno.txt
new file mode 100644
index 0000000000..cc2c1baa17
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-neno.txt
@@ -0,0 +1,12 @@
+VRF Name: neno
+============ OSPF network routing table ============
+N 10.0.3.0/24 [20] area: 0.0.0.0
+ via 10.0.30.3, r1-eth2
+N 10.0.30.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth2
+
+============ OSPF router routing table =============
+R 10.0.255.3 [10] area: 0.0.0.0, ASBR
+ via 10.0.30.3, r1-eth2
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt
new file mode 100644
index 0000000000..86c089ab3b
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt
@@ -0,0 +1,9 @@
+O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX
+C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX
+O>* 10.0.2.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX
+B>* 10.0.3.0/24 [20/20] via 10.0.30.3, r1-eth2 (vrf neno), weight 1, XX:XX:XX
+O>* 10.0.4.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX
+O 10.0.20.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX
+C>* 10.0.20.0/24 is directly connected, r1-eth1, XX:XX:XX
+B>* 10.0.30.0/24 [20/0] is directly connected, r1-eth2 (vrf neno), weight 1, XX:XX:XX
+O>* 10.0.40.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt
new file mode 100644
index 0000000000..4e818eb6f1
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt
@@ -0,0 +1,6 @@
+VRF neno:
+O>* 10.0.3.0/24 [110/20] via 10.0.30.3, r1-eth2, weight 1, XX:XX:XX
+B>* 10.0.4.0/24 [20/20] via 10.0.20.2, r1-eth1 (vrf default), weight 1, XX:XX:XX
+O 10.0.30.0/24 [110/10] is directly connected, r1-eth2, weight 1, XX:XX:XX
+C>* 10.0.30.0/24 is directly connected, r1-eth2, XX:XX:XX
+B>* 10.0.40.0/24 [20/20] via 10.0.20.2, r1-eth1 (vrf default), weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf
new file mode 100644
index 0000000000..e87899ca77
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf
@@ -0,0 +1,62 @@
+!
+hostname r2
+password zebra
+log file /tmp/r2-frr.log
+!
+interface r2-eth0
+ ip address 10.0.2.2/24
+!
+interface r2-eth1
+ ip address 10.0.20.2/24
+!
+ip route 0.0.0.0/0 10.0.20.1
+!
+interface r2-eth2 vrf ray
+ ip address 10.0.40.2/24
+!
+ip forwarding
+!
+vrf ray
+ ip protocol bgp route-map rmap
+ exit-vrf
+!
+router ospf
+ ospf router-id 10.0.255.2
+ redistribute bgp
+ network 10.0.2.0/24 area 0
+ network 10.0.20.0/24 area 0
+!
+router ospf vrf ray
+ ospf router-id 10.0.255.1
+ redistribute bgp
+ network 10.0.40.0/24 area 0
+!
+
+router bgp 99
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf ray
+ !
+!
+router bgp 99 vrf ray
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf default
+ !
+!
+!!!!!!!!!!!!!!!!!!!!!
+! VRFs neno and ray subnets
+ip prefix-list nets seq 5 permit 10.0.3.0/24
+ip prefix-list nets seq 10 permit 10.0.30.0/24
+ip prefix-list nets seq 15 permit 10.0.4.0/24
+ip prefix-list nets seq 20 permit 10.0.40.0/24
+ip prefix-list nets seq 25 deny any
+!
+route-map rmap permit 10
+ match ip address prefix-list nets
+ exit
+!
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-default.txt
new file mode 100644
index 0000000000..77b8038b70
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-default.txt
@@ -0,0 +1,20 @@
+VRF Name: default
+============ OSPF network routing table ============
+N 10.0.1.0/24 [20] area: 0.0.0.0
+ via 10.0.20.1, r2-eth1
+N 10.0.2.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth0
+N 10.0.20.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.20.1, r2-eth1
+
+============ OSPF external routing table ===========
+N E2 10.0.3.0/24 [10/20] tag: 0
+ via 10.0.20.1, r2-eth1
+N E2 10.0.30.0/24 [10/20] tag: 0
+ via 10.0.20.1, r2-eth1
+
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-ray.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-ray.txt
new file mode 100644
index 0000000000..b70ee9d5a6
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-ray.txt
@@ -0,0 +1,15 @@
+VRF Name: ray
+============ OSPF network routing table ============
+N 10.0.4.0/24 [20] area: 0.0.0.0
+ via 10.0.40.4, r2-eth2
+N 10.0.40.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth2
+
+============ OSPF router routing table =============
+R 10.0.255.4 [10] area: 0.0.0.0, ASBR
+ via 10.0.40.4, r2-eth2
+
+============ OSPF external routing table ===========
+
+
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt
new file mode 100644
index 0000000000..9681d8a04e
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt
@@ -0,0 +1,10 @@
+S>* 0.0.0.0/0 [1/0] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX
+O>* 10.0.1.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX
+O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX
+C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX
+O>* 10.0.3.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX
+B>* 10.0.4.0/24 [20/20] via 10.0.40.4, r2-eth2 (vrf ray), weight 1, XX:XX:XX
+O 10.0.20.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX
+C>* 10.0.20.0/24 is directly connected, r2-eth1, XX:XX:XX
+O>* 10.0.30.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX
+B>* 10.0.40.0/24 [20/0] is directly connected, r2-eth2 (vrf ray), weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt
new file mode 100644
index 0000000000..ce9903ae71
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt
@@ -0,0 +1,9 @@
+VRF ray:
+B 10.0.1.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX
+B 10.0.2.0/24 [20/0] is directly connected, r2-eth0 (vrf default) inactive, weight 1, XX:XX:XX
+B>* 10.0.3.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX
+O>* 10.0.4.0/24 [110/20] via 10.0.40.4, r2-eth2, weight 1, XX:XX:XX
+B 10.0.20.0/24 [20/0] is directly connected, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX
+B>* 10.0.30.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX
+O 10.0.40.0/24 [110/10] is directly connected, r2-eth2, weight 1, XX:XX:XX
+C>* 10.0.40.0/24 is directly connected, r2-eth2, XX:XX:XX
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf
new file mode 100644
index 0000000000..2657f589d8
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf
@@ -0,0 +1,22 @@
+!
+hostname r3
+password zebra
+log file /tmp/r3-frr.log
+!
+interface r3-eth0
+ ip address 10.0.3.3/24
+!
+interface r3-eth1
+ ip address 10.0.30.3/24
+!
+ip forwarding
+!
+!
+router ospf
+ ospf router-id 10.0.255.3
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.3.0/24 area 0
+ network 10.0.30.0/24 area 0
+!
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/ospf-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/ospf-vrf-default.txt
new file mode 100644
index 0000000000..3eb5690834
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/ospf-vrf-default.txt
@@ -0,0 +1,17 @@
+VRF Name: default
+============ OSPF network routing table ============
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth0
+N 10.0.30.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.30.1, r3-eth1
+
+============ OSPF external routing table ===========
+N E2 10.0.4.0/24 [10/20] tag: 0
+ via 10.0.30.1, r3-eth1
+N E2 10.0.40.0/24 [10/20] tag: 0
+ via 10.0.30.1, r3-eth1
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt
new file mode 100644
index 0000000000..f6f861b73b
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt
@@ -0,0 +1,8 @@
+O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, weight 1, XX:XX:XX
+C>* 10.0.3.0/24 is directly connected, r3-eth0, XX:XX:XX
+O>* 10.0.4.0/24 [110/20] via 10.0.30.1, r3-eth1, weight 1, XX:XX:XX
+O 10.0.30.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX
+C>* 10.0.30.0/24 is directly connected, r3-eth1, XX:XX:XX
+O>* 10.0.40.0/24 [110/20] via 10.0.30.1, r3-eth1, weight 1, XX:XX:XX
+
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf
new file mode 100644
index 0000000000..79d8077062
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf
@@ -0,0 +1,22 @@
+!
+hostname r4
+password zebra
+log file /tmp/r4-frr.log
+!
+interface r4-eth0
+ ip address 10.0.4.4/24
+!
+interface r4-eth1
+ ip address 10.0.40.4/24
+!
+ip forwarding
+!
+!
+router ospf
+ ospf router-id 10.0.255.4
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.4.0/24 area 0
+ network 10.0.40.0/24 area 0
+!
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/ospf-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/ospf-vrf-default.txt
new file mode 100644
index 0000000000..ad799af996
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/ospf-vrf-default.txt
@@ -0,0 +1,17 @@
+VRF Name: default
+============ OSPF network routing table ============
+N 10.0.4.0/24 [10] area: 0.0.0.0
+ directly attached to r4-eth0
+N 10.0.40.0/24 [10] area: 0.0.0.0
+ directly attached to r4-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.40.2, r4-eth1
+
+============ OSPF external routing table ===========
+N E2 10.0.3.0/24 [10/20] tag: 0
+ via 10.0.40.2, r4-eth1
+N E2 10.0.30.0/24 [10/20] tag: 0
+ via 10.0.40.2, r4-eth1
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt
new file mode 100644
index 0000000000..b6be5e7fdb
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt
@@ -0,0 +1,7 @@
+O>* 10.0.3.0/24 [110/20] via 10.0.40.2, r4-eth1, weight 1, XX:XX:XX
+O 10.0.4.0/24 [110/10] is directly connected, r4-eth0, weight 1, XX:XX:XX
+C>* 10.0.4.0/24 is directly connected, r4-eth0, XX:XX:XX
+O>* 10.0.30.0/24 [110/20] via 10.0.40.2, r4-eth1, weight 1, XX:XX:XX
+O 10.0.40.0/24 [110/10] is directly connected, r4-eth1, weight 1, XX:XX:XX
+C>* 10.0.40.0/24 is directly connected, r4-eth1, XX:XX:XX
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py b/tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py
new file mode 100644
index 0000000000..0b6b50ec06
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_multi_vrf_bgp_route_leak.py
+#
+# Copyright (c) 2022 ATCorp
+# Jafar Al-Gharaibeh
+#
+# 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.
+#
+
+import os
+import sys
+from functools import partial
+import pytest
+
+# 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
+
+
+"""
+test_ospf_multi_vrf_bgp_route_leak.py: Test OSPF with multi vrf setup and route leaking.
+"""
+
+TOPOLOGY = """
+ bgp route leaking (connected/ospf), vrfs: neno <==> default <==> ray
+ routes leaking to vrfs are limited to neno and ray routes.
+
+ 10.0.1.1/24
+ ^
+ |vrf:default
+ +---+---+
+ 10.0.30.0/24 .1| |.1
+ +-----------------+ R1 +
+ | vrf:neno | |
+ | +---+---+ ^
+ |.3 .1|vrf:default | 10.0.4.4/24
+ +---+---+ | +---+---+
+ | | 10.0.20.0/24 | |
+ | R3 | | | R4 |
+ | | |.2 | |
+ +---+---+ +---+---+ +---+---+
+ | | | vrf:ray |.4
+ v | R2 +----------------+
+10.0.3.3/24 | |.2 10.0.40.0/24
+ +---+---+
+ |
+ v
+ 10.0.2.2/24
+
+"""
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Create a empty network for router 2
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+
+ # Create a empty network for router 3
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"])
+
+ # Create a empty network for router 4
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"])
+
+ # Interconect router 1, 2
+ switch = tgen.add_switch("s1-2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 1, 3
+ switch = tgen.add_switch("s1-3")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ # Interconect router 2, 4
+ switch = tgen.add_switch("s2-4")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ logger.info("OSPF Multi VRF Topology with BGP route leaking:\n {}".format(TOPOLOGY))
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ r1_vrf_setup_cmds = [
+ "ip link add name neno type vrf table 11",
+ "ip link set dev neno up",
+ "ip link set dev r1-eth2 vrf neno up",
+ ]
+ r2_vrf_setup_cmds = [
+ "ip link add name ray type vrf table 11",
+ "ip link set dev ray up",
+ "ip link set dev r2-eth2 vrf ray up",
+ ]
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ # Create VRFs on r1/r2 and bind to interfaces
+ for cmd in r1_vrf_setup_cmds:
+ tgen.net["r1"].cmd(cmd)
+ for cmd in r2_vrf_setup_cmds:
+ tgen.net["r2"].cmd(cmd)
+
+ logger.info("Testing OSPF VRF support")
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ # Initialize all routers.
+ tgen.start_router()
+ for router in router_list.values():
+ if router.has_version("<", "4.0"):
+ tgen.set_error("unsupported version")
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+# Shared test function to validate expected output.
+def compare_show_ip_route_vrf(rname, expected, vrf_name):
+ """
+ Calls 'show ip route vrf [vrf_name] route' and compare the obtained
+ result with the expected output.
+ """
+ tgen = get_topogen()
+ current = topotest.ip4_route_zebra(tgen.gears[rname], vrf_name)
+ ret = topotest.difflines(
+ current, expected, title1="Current output", title2="Expected output"
+ )
+ return ret
+
+
+def test_ospf_convergence():
+ "Test OSPF daemon convergence"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ for rname, router in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', rname)
+
+ for vrf in ["default", "neno", "ray"]:
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/ospf-vrf-{}.txt".format(rname, vrf))
+ if vrf == "default" or os.path.exists(reffile):
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 80 seconds.
+ test_func = partial(
+ topotest.router_output_cmp,
+ router,
+ "show ip ospf vrf {} route".format(vrf),
+ expected,
+ )
+ result, diff = topotest.run_and_expect(
+ test_func, "", count=80, wait=1
+ )
+ assertmsg = "OSPF did not converge on {}:\n{}".format(rname, diff)
+ assert result, assertmsg
+
+
+def test_ospf_kernel_route():
+ "Test OSPF kernel route installation"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ rlist = tgen.routers().values()
+ for router in rlist:
+ logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name)
+ for vrf in ["default", "neno", "ray"]:
+ reffile = os.path.join(CWD, "{}/zebra-vrf-{}.txt".format(router.name, vrf))
+ if vrf == "default" or os.path.exists(reffile):
+ expected = open(reffile).read()
+ # Run test function until we get an result. Wait at most 80 seconds.
+ test_func = partial(
+ compare_show_ip_route_vrf, router.name, expected, vrf
+ )
+ result, diff = topotest.run_and_expect(
+ test_func, "", count=80, wait=1
+ )
+ assertmsg = 'OSPF IPv4 route mismatch in router "{}": {}'.format(
+ router.name, diff
+ )
+ assert result, assertmsg
+
+
+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/ospfv3_basic_functionality/ospfv3_asbr_summary_type7_lsa.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_type7_lsa.json
new file mode 100644
index 0000000000..27b36aea17
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_type7_lsa.json
@@ -0,0 +1,202 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf6": {
+ "area": "0.0.0.3"
+ }
+ }
+ },
+ "ospf6": {
+ "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/ospfv3_basic_functionality/ospfv3_dual_stack.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_dual_stack.json
new file mode 100644
index 0000000000..5555d9291e
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_dual_stack.json
@@ -0,0 +1,312 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 30,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf6": {
+ "area": "0.0.0.0"
+ },
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "1.0.4.17",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
index c928093925..22f46e2bf6 100644
--- a/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
@@ -2,6 +2,7 @@
"address_types": [
"ipv6"
],
+
"ipv6base": "fd00::",
"ipv6mask": 64,
"link_ip_start": {
@@ -9,6 +10,7 @@
"v6mask": 64
},
"lo_prefix": {
+
"ipv6": "2001:db8:f::",
"v6mask": 128
},
@@ -21,6 +23,7 @@
},
"r1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -29,6 +32,7 @@
},
"r1-link1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -37,6 +41,7 @@
},
"r1-link2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -45,6 +50,7 @@
},
"r1-link3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -53,6 +59,7 @@
},
"r1-link4": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -61,6 +68,7 @@
},
"r1-link5": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -69,6 +77,7 @@
},
"r1-link6": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -77,6 +86,7 @@
},
"r1-link7": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -85,6 +95,7 @@
},
"r2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -93,6 +104,7 @@
},
"r3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -101,6 +113,7 @@
}
}
},
+
"ospf6": {
"router_id": "100.1.1.0",
"neighbors": {
@@ -139,6 +152,7 @@
},
"r0": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -147,6 +161,7 @@
},
"r0-link1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -155,6 +170,7 @@
},
"r0-link2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -163,6 +179,7 @@
},
"r0-link3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -171,6 +188,7 @@
},
"r0-link4": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -179,6 +197,7 @@
},
"r0-link5": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -187,6 +206,7 @@
},
"r0-link6": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -195,6 +215,7 @@
},
"r0-link7": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -203,6 +224,7 @@
},
"r2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -211,6 +233,7 @@
},
"r3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -222,6 +245,7 @@
"description": "DummyIntftoR3"
}
},
+
"ospf6": {
"router_id": "100.1.1.1",
"neighbors": {
@@ -260,6 +284,7 @@
},
"r0": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -268,6 +293,7 @@
},
"r1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -276,6 +302,7 @@
},
"r3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -283,6 +310,7 @@
}
}
},
+
"ospf6": {
"router_id": "100.1.1.2",
"neighbors": {
@@ -300,6 +328,7 @@
},
"r0": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -309,6 +338,7 @@
},
"r1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -317,6 +347,7 @@
},
"r2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -326,14 +357,13 @@
"r1-link0": {
"ipv6": "auto",
"description": "DummyIntftoR1",
- "ospf": {
- "area": "0.0.0.0"
- },
+
"ospf6": {
"area": "0.0.0.0"
}
}
},
+
"ospf6": {
"router_id": "100.1.1.3",
"neighbors": {
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp_lan.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp_lan.json
new file mode 100644
index 0000000000..53b3f49e62
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp_lan.json
@@ -0,0 +1,264 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "switches": {
+ "s1": {
+ "links": {
+ "r0": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 98
+ }
+ },
+ "r1": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 99
+ }
+ },
+ "r2": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r3": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r4": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r5": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r6": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r7": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ }
+ }
+ }
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {},
+ "r4": {},
+ "r5": {},
+ "r6": {},
+ "r7": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3-link0": {
+
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {},
+ "r4": {},
+ "r5": {},
+ "r6": {},
+ "r7": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+
+ "ipv6": "auto",
+ "description": "DummyIntftoR1"
+
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.4",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.5",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r6": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.6",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r7": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.7",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_lan.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_lan.json
new file mode 100644
index 0000000000..3a2fc022e5
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_lan.json
@@ -0,0 +1,140 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "switches": {
+ "s1": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 98
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 99
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ }
+ }
+ }
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_nssa2.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_nssa2.json
new file mode 100644
index 0000000000..b1432b9bee
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_nssa2.json
@@ -0,0 +1,197 @@
+
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf6": {
+ "area": "0.0.0.3"
+ }
+ }
+ },
+ "ospf6": {
+ "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/ospfv3_basic_functionality/ospfv3_routemaps.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json
index 226f84f320..a1c7bd72d4 100644
--- a/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json
@@ -1,14 +1,26 @@
{
- "address_types": ["ipv6"],
+ "address_types": [
+ "ipv6"
+ ],
+
"ipv6base": "fd00::",
"ipv6mask": 64,
- "link_ip_start": {"ipv6": "fd00::", "v6mask": 64},
- "lo_prefix": {"ipv6": "2001:db8:f::", "v6mask": 128},
+ "link_ip_start": {
+
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
"routers": {
"r0": {
"links": {
"r1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -17,6 +29,7 @@
},
"r2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -25,6 +38,7 @@
},
"r3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -32,15 +46,21 @@
}
}
},
+
"ospf6": {
"router_id": "100.1.1.0",
- "neighbors": {"r1": {}, "r2": {}, "r3": {}}
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
}
},
"r1": {
"links": {
"r0": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -49,6 +69,7 @@
},
"r2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -57,6 +78,7 @@
},
"r3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -64,15 +86,21 @@
}
}
},
+
"ospf6": {
"router_id": "100.1.1.1",
- "neighbors": {"r0": {}, "r2": {}, "r3": {}}
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
}
},
"r2": {
"links": {
"r0": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -81,6 +109,7 @@
},
"r1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -89,6 +118,7 @@
},
"r3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -96,15 +126,21 @@
}
}
},
+
"ospf6": {
"router_id": "100.1.1.2",
- "neighbors": {"r1": {}, "r0": {}, "r3": {}}
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
}
},
"r3": {
"links": {
"r0": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -113,6 +149,7 @@
},
"r1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -121,6 +158,7 @@
},
"r2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -128,10 +166,15 @@
}
}
},
+
"ospf6": {
"router_id": "100.1.1.3",
- "neighbors": {"r0": {}, "r1": {}, "r2": {}}
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
}
}
}
-}
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json
index 3669b3a554..e70481ace9 100644
--- a/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json
@@ -5,13 +5,16 @@
"address_types": [
"ipv6"
],
+
"ipv6base": "fd00::",
"ipv6mask": 64,
"link_ip_start": {
+
"ipv6": "fd00::",
"v6mask": 64
},
"lo_prefix": {
+
"ipv6": "2001:db8:f::",
"v6mask": 128
},
@@ -24,6 +27,7 @@
},
"r1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -32,6 +36,7 @@
},
"r2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -40,6 +45,7 @@
},
"r3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -47,6 +53,7 @@
}
}
},
+
"ospf6": {
"router_id": "100.1.1.0",
"neighbors": {
@@ -63,6 +70,7 @@
},
"r0": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -71,6 +79,7 @@
},
"r2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -79,6 +88,7 @@
},
"r3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -86,6 +96,7 @@
}
}
},
+
"ospf6": {
"router_id": "100.1.1.1",
"neighbors": {
@@ -103,6 +114,7 @@
},
"r0": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -111,6 +123,7 @@
},
"r1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -119,6 +132,7 @@
},
"r3": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -126,6 +140,7 @@
}
}
},
+
"ospf6": {
"router_id": "100.1.1.2",
"neighbors": {
@@ -146,6 +161,7 @@
},
"r1": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
@@ -154,6 +170,7 @@
},
"r2": {
"ipv6": "auto",
+
"ospf6": {
"area": "0.0.0.0",
"hello_interval": 1,
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
index 47333fcb39..10c51a6784 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
@@ -56,6 +56,7 @@ from lib.common_config import (
create_prefix_lists,
create_route_maps,
topo_daemons,
+ create_interfaces_cfg,
)
from lib.topolog import logger
from lib.topojson import build_config_from_json
@@ -747,6 +748,1084 @@ def test_ospfv3_type5_summary_tc42_p0(request):
write_test_footer(tc_name)
+def test_ospfv3_type5_summary_tc43_p0(request):
+ """OSPF summarisation with metric type 2."""
+ 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["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "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_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", 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": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ 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["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change the summary address mask to lower match (ex - 16 to 8)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "16"},
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "delete": True,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ sleep(5)
+
+ input_dict = {
+ "2011::/16": {
+ "Summary address": "2011::/16",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "2011::0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Change the summary address mask to higher match (ex - 8 to 24)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "2011::/32": {
+ "Summary address": "2011::/32",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+ step("Configure 2 summary address with different mask of same network.")
+ step(
+ "Verify that external routes(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "2011::0/32"}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(" Un configure one of the summary address.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ sleep(5)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "2011::0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", 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": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "16"}
+ ]
+ }
+ }
+ }
+ 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(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": "2011::0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", 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 ospfv3_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["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "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_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", 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": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "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["ipv6"][0], "tag": "1234"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "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_ospf6_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, "ipv6", 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", 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": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32", "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["ipv6"][0], "tag": "1"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure Max Tag Value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "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["ipv6"][0], "tag": "4294967295"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ 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["ipv6"], "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["ipv6"][0], "tag": "88888"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_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,
+ "ipv6",
+ 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", 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": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "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_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", 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_ospf6_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, "ipv6", 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["ipv6"], "next_hop": "blackhole", "delete": True},
+ {"network": NETWORK2["ipv6"], "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": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ 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["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "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"],
+ "ospf6": {"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"],
+ "ospf6": {"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"],
+ "ospf6": {"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"],
+ "ospf6": {"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_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, 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["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "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_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", 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": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "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["ipv6"][0], "tag": "1234"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "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_ospf6_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, "ipv6", 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", 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": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32", "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["ipv6"][0], "tag": "1"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure Max Tag Value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "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["ipv6"][0], "tag": "4294967295"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ 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["ipv6"], "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["ipv6"][0], "tag": "88888"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_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,
+ "ipv6",
+ 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["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", 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": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "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_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", 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_ospf6_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, "ipv6", 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["ipv6"], "next_hop": "blackhole", "delete": True},
+ {"network": NETWORK2["ipv6"], "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": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ 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["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "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_ospfv3_type5_summary_tc46_p0(request):
"""OSPF summarisation with advertise and no advertise option"""
tc_name = request.node.name
@@ -1657,127 +2736,6 @@ def test_ospfv3_type5_summary_tc49_p2(request):
result is not True
), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
- step("Kill OSPF6d daemon on R0.")
- kill_router_daemons(tgen, "r0", ["ospf6d"])
-
- step("Bring up OSPF6d daemon on R0.")
- start_router_daemons(tgen, "r0", ["ospf6d"])
-
- step("Verify OSPF neighbors are up after bringing back ospf6d in R0")
- # Api call verify whether OSPF is converged
- ospf_covergence = verify_ospf6_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["ipv6"][0]}]}}
- dut = "r1"
-
- result = verify_ospf6_rib(tgen, dut, input_dict_summary)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- result = verify_rib(tgen, "ipv6", 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["ipv6"][0]: {
- "Summary address": SUMMARY["ipv6"][0],
- "Metric-type": "E2",
- "Metric": 20,
- "Tag": 0,
- "External route count": 5,
- }
- }
- dut = "r0"
- result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
- 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["ipv6"], "next_hop": "blackhole"}]}
- }
- dut = "r1"
- result = verify_ospf6_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, "ipv6", 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["ipv6"][0]}]}}
- dut = "r1"
-
- result = verify_ospf6_rib(tgen, dut, input_dict_summary)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
-
- result = verify_rib(tgen, "ipv6", 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["ipv6"][0]: {
- "Summary address": SUMMARY["ipv6"][0],
- "Metric-type": "E2",
- "Metric": 20,
- "Tag": 0,
- "External route count": 5,
- }
- }
- dut = "r0"
- result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
- 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["ipv6"], "next_hop": "blackhole"}]}
- }
- dut = "r1"
- result = verify_ospf6_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, "ipv6", 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)
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py
new file mode 100644
index 0000000000..fe8be0a4b3
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py
@@ -0,0 +1,400 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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 Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+from copy import deepcopy
+from ipaddress import IPv4Address
+from lib.topotest import frr_unicode
+import ipaddress
+
+# 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 lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ topo_daemons,
+ get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ config_ospf6_interface,
+)
+
+from ipaddress import IPv6Address
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+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",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+MASK = {"ipv6": "32", "ipv6": "128"}
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ Topo : Broadcast Networks
+ +---+ +---+ +---+ +---+
+ |R0 + +R1 + +R2 + +R3 |
+ +-+-+ +-+-+ +-+-+ +-+-+
+ | | | |
+ | | | |
+ --+-----------+--------------+---------------+-----
+ Ethernet Segment
+
+TESTCASES =
+1. Verify OSPF ECMP with max path configured as 8
+ (Edge having 1 uplink port as broadcast network,
+ connect to 8 TORs - LAN case)
+
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo, switch_name
+ 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...
+ json_file = "{}/ospfv3_ecmp_lan.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... 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_ospf6_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ switch_name = [k for k in topo["switches"].keys()][0]
+
+ 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
+
+ try:
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ except OSError:
+ # OSError exception is raised when mininet tries to stop switch
+ # though switch is stopped once but mininet tries to stop same
+ # switch again, where it ended up with exception
+ pass
+ 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: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {"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: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "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)
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a perticular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a perticular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_lan_ecmp_tc18_p0(request):
+ """
+ OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 8
+ (Edge having 1 uplink port as broadcast network,
+ connect to 8 TORs - LAN case)
+
+ """
+ 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 in all the routers on LAN interface.")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Configure a static route in all the routes and "
+ "redistribute static/connected in OSPF."
+ )
+
+ for rtr in topo["routers"]:
+ input_dict = {
+ rtr: {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = rtr
+ red_static(dut)
+
+ step(
+ "Verify that route in R0 in stalled with 8 hops. "
+ "Verify ospf route table and ip route table."
+ )
+
+ nh = []
+ for rtr in topo["routers"]:
+ llip = get_llip(rtr, switch_name)
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ nh.append(llip)
+
+ llip = get_llip("r1", switch_name)
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh.remove(llip)
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf6"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" clear ip ospf interface on DUT(r0)")
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step(
+ "Verify that after clearing the ospf interface all the "
+ "neighbours are up and routes are installed with 8 next hop "
+ "in ospf and ip route tables on R0"
+ )
+
+ dut = "r0"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" clear ip ospf interface on R2")
+ clear_ospf(tgen, "r2")
+
+ dut = "r2"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Delete static/connected cmd in ospf in all the routes one by one.")
+ for rtr in topo["routers"]:
+ input_dict = {
+ rtr: {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ 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/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py
new file mode 100644
index 0000000000..dc3b915d49
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py
@@ -0,0 +1,482 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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 Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+import ipaddress
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf6_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ create_router_ospf,
+)
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp_and_verify,
+ verify_bgp_rib,
+)
+from lib.topolog import logger
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ topo_daemons,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ check_router_status,
+)
+from ipaddress import IPv4Address
+from lib.topolog import logger
+from lib.topojson import build_config_from_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, "../"))
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+# Global variables
+topo = None
+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",
+ ],
+ "ipv6": [
+ "2011:0:20::1/128",
+ "2011:0:20::2/128",
+ "2011:0:20::3/128",
+ "2011:0:20::4/128",
+ "2011:0:20::5/128",
+ ],
+}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+
+
+TESTCASES =
+1. OSPF Learning - Verify OSPF can learn different types of LSA and
+ processes them.[Edge learning different types of LSAs]
+2. Verify that ospf non back bone area can be configured as NSSA area
+3. Verify that ospf NSSA area DUT is capable receiving & processing
+ Type7 N2 route.
+"""
+
+
+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...
+ json_file = "{}/ospfv3_nssa2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... 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_ospf6_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():
+ """Teardown the pytest environment."""
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {"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: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "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_ospfv3_nssa_tc26_p0(request):
+ """Verify that ospf non back bone area can be configured as NSSA area"""
+ 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():
+ check_router_status(tgen)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure ospf area 2 on r0 , r1 & r4, make the area 2 as NSSA area")
+
+ reset_config_on_routers(tgen)
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute static route in R2 ospf.")
+ dut = "r2"
+ red_static(dut)
+
+ step("Verify that Type 5 LSA is originated by R2.")
+ dut = "r0"
+ protocol = "ospf6"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Un configure redistribute command in R4")
+ dut = "r2"
+ red_static(dut, config=False)
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
+ }
+
+ step("Configure area 0 on interface of r2 connecting to r1")
+
+ input_dict = {
+ "r2": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ "ospf6": {"area": "0.0.0.2"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r2": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbor goes down between r2 and r1.")
+ result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result)
+
+ step("Now configure area 0 on interface of r1 connecting to r2.")
+
+ input_dict = {
+ "r1": {
+ "links": {
+ "r2": {
+ "interface": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "ospf6": {"area": "0.0.0.2"},
+ "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": {
+ "r2": {
+ "interface": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that ospf neighbour comes up between r2 and r1.")
+ result = verify_ospf6_neighbor(tgen, topo, dut="r2")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure area 2 on interface of r2 connecting to r1.")
+
+ input_dict = {
+ "r2": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ "ospf6": {"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 = {
+ "r2": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ "ospf6": {"area": "0.0.0.2"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbor goes down between r2 and r1.")
+ result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result)
+
+ step("Now configure area 2 on interface of r1 connecting to r2.")
+
+ input_dict = {
+ "r1": {
+ "links": {
+ "r2": {
+ "interface": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "ospf6": {"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": {
+ "r2": {
+ "interface": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "ospf6": {"area": "0.0.0.2"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that ospf neighbour comes up between r2 and r1.")
+ result = verify_ospf6_neighbor(tgen, topo, dut="r2")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+# As per internal discussion, this script has to be removed as translator
+# function is not supported, for more details kindly check this PR 2565570
+def ospfv3_nssa_tc27_p0(request):
+ """
+ OSPF NSSA.
+
+ Verify that ospf NSSA area DUT is capable receiving & processing
+ Type7 N2 route.
+ """
+ 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():
+ check_router_status(tgen)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure ospf area 2 on r0 , r1 & r4, make the area 2 as NSSA area")
+
+ reset_config_on_routers(tgen)
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute static route in R2 ospf.")
+ dut = "r2"
+ red_static(dut)
+
+ step("Verify that Type 5 LSA is originated by R2.")
+ dut = "r0"
+ protocol = "ospf6"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Un configure redistribute command in R4")
+ dut = "r2"
+ red_static(dut, config=False)
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
+ }
+
+ dut = "r0"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert result is not 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/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
index 461efbe979..d7cf951c5f 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
@@ -178,6 +178,312 @@ def teardown_module(mod):
# ##################################
+def test_ospfv3_routemaps_functionality_tc19_p0(request):
+ """
+ OSPF Route map - Verify OSPF route map support functionality.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Create static routes(10.0.20.1/32 and 10.0.20.2/32) in R0")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {"r0": {"ospf6": {"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)
+
+ dut = "r1"
+ lsid = NETWORK["ipv6"][0].split("/")[0]
+ rid = routerids[0]
+
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf6": {"redistribute": [{"redist_type": "static", "del_action": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32"
+ )
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv6"][0],
+ "action": "permit",
+ },
+ {"seqid": 11, "network": "any", "action": "deny"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n, prefix list creation failed. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure route map rmap1 and redistribute static routes to"
+ " ospf using route map rmap1"
+ )
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Verify that route map is activated in OSPF.")
+
+ step("Verify that route 10.0.20.1 is allowed and 10.0.20.2 is denied.")
+ dut = "r1"
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ lsid = NETWORK["ipv6"][1].split("/")[0]
+ rid = routerids[0]
+
+ step("Change prefix rules to permit 10.0.20.2 and deny 10.0.20.1")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv6"][1],
+ "action": "permit",
+ },
+ {"seqid": 11, "network": "any", "action": "deny"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route 10.0.20.2 is allowed and 10.0.20.1 is denied.")
+ dut = "r1"
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][1], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete and reconfigure prefix list.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv6"][1],
+ "action": "permit",
+ "delete": True,
+ },
+ {
+ "seqid": 11,
+ "network": "any",
+ "action": "deny",
+ "delete": True,
+ },
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv6"][1],
+ "action": "permit",
+ },
+ {"seqid": 11, "network": "any", "action": "deny"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route 10.0.20.2 is allowed and 10.0.20.1 is denied.")
+ dut = "r1"
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][1], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
def test_ospfv3_routemaps_functionality_tc20_p0(request):
"""
OSPF route map support functionality.
@@ -461,7 +767,7 @@ def test_ospfv3_routemaps_functionality_tc22_p0(request):
{
"action": "permit",
"seq_id": "20",
- "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv4"}},
+ "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv6"}},
},
]
}
@@ -474,7 +780,7 @@ def test_ospfv3_routemaps_functionality_tc22_p0(request):
input_dict_2 = {
"r0": {
"prefix_lists": {
- "ipv4": {
+ "ipv6": {
"pf_list_1_ipv6": [
{"seqid": 10, "network": NETWORK["ipv6"][0], "action": "permit"}
]
@@ -489,8 +795,8 @@ def test_ospfv3_routemaps_functionality_tc22_p0(request):
input_dict_2 = {
"r0": {
"prefix_lists": {
- "ipv4": {
- "pf_list_2_ipv4": [
+ "ipv6": {
+ "pf_list_2_ipv6": [
{"seqid": 10, "network": NETWORK["ipv6"][1], "action": "permit"}
]
}
@@ -567,7 +873,7 @@ def test_ospfv3_routemaps_functionality_tc22_p0(request):
{
"action": "deny",
"seq_id": "20",
- "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv4"}},
+ "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv6"}},
}
]
}
@@ -624,6 +930,106 @@ def test_ospfv3_routemaps_functionality_tc22_p0(request):
write_test_footer(tc_name)
+def test_ospfv3_routemaps_functionality_tc23_p0(request):
+ """
+ OSPF Route map - Multiple set clauses.
+
+ Verify OSPF route map support functionality when we add/remove route-maps
+ with multiple set clauses and without any match statement.(Set only)
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ " Create static routes(10.0.20.1/32) in R1 and "
+ "redistribute to OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure route map with set clause (set metric)")
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {"action": "permit", "seq_id": 10, "set": {"metric": 123}}
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that configured metric is applied to ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, metric=123)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, metric=123)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("un configure the set clause")
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "set": {"metric": 123, "delete": True},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, metric=20)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, metric=20)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
def test_ospfv3_routemaps_functionality_tc24_p0(request):
"""
OSPF Route map - Multiple set clauses.
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
index d8f659e5a9..21d03fadfb 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
@@ -48,17 +48,21 @@ from lib.common_config import (
create_interfaces_cfg,
topo_daemons,
get_frr_ipv6_linklocal,
+ check_router_status,
+ create_static_routes,
)
from lib.topolog import logger
from lib.topojson import build_config_from_json
-
+from lib.bgp import create_router_bgp, verify_bgp_convergence
from lib.ospf import (
verify_ospf6_neighbor,
+ clear_ospf,
verify_ospf6_rib,
+ verify_ospf_database,
create_router_ospf,
- verify_ospf6_interface,
config_ospf6_interface,
+ verify_ospf6_interface,
)
@@ -251,6 +255,8 @@ def red_connected(dut, config=True):
# ##################################
# Test cases start here.
# ##################################
+
+
def test_ospfv3_redistribution_tc5_p0(request):
"""Test OSPF intra area route calculations."""
tc_name = request.node.name
@@ -259,7 +265,7 @@ def test_ospfv3_redistribution_tc5_p0(request):
# Don't run this test if we have any failure.
if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
+ check_router_status(tgen)
global topo
step("Bring up the base config.")
@@ -280,7 +286,11 @@ def test_ospfv3_redistribution_tc5_p0(request):
nh = llip
input_dict = {
- "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]}
+ "r1": {
+ "static_routes": [
+ {"network": ip_net, "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
}
dut = "r1"
@@ -372,7 +382,7 @@ def test_ospfv3_redistribution_tc6_p0(request):
# Don't run this test if we have any failure.
if tgen.routers_have_failure():
- pytest.skip(tgen.errors)
+ check_router_status(tgen)
global topo
step("Bring up the base config.")
@@ -380,8 +390,8 @@ def test_ospfv3_redistribution_tc6_p0(request):
step("Verify that OSPF neighbors are FULL.")
ospf_covergence = verify_ospf6_neighbor(tgen, topo)
- assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
- ospf_covergence
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
)
step("verify intra area route is calculated for r0-r3 interface ip in R1")
@@ -391,7 +401,11 @@ def test_ospfv3_redistribution_tc6_p0(request):
assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
nh = llip
input_dict = {
- "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]}
+ "r1": {
+ "static_routes": [
+ {"network": ip_net, "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
}
dut = "r1"
@@ -460,9 +474,6 @@ def test_ospfv3_redistribution_tc6_p0(request):
intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
shutdown_bringup_interface(tgen, dut, intf, False)
- step("Verify that intraroute calculated for R1 intf on R0 is deleted.")
- dut = "r1"
-
step("un shut the OSPF interface on R0")
dut = "r0"
shutdown_bringup_interface(tgen, dut, intf, True)
@@ -478,6 +489,168 @@ def test_ospfv3_redistribution_tc6_p0(request):
write_test_footer(tc_name)
+def test_ospfv3_redistribution_tc8_p1(request):
+ """
+ Test OSPF redistribution of connected routes.
+
+ Verify OSPF redistribution of connected routes when bgp multi hop
+ neighbor is configured using ospf routes
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ step(
+ "Configure loopback interface on all routers, and redistribut"
+ "e connected routes into ospf"
+ )
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step(
+ "verify that connected routes -loopback is found in all routers"
+ "advertised/exchaged via ospf"
+ )
+ for rtr in topo["routers"]:
+ red_static(rtr)
+ red_connected(rtr)
+
+ for node in topo["routers"]:
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": topo["routers"][node]["links"]["lo"]["ipv6"],
+ "no_of_ip": 1,
+ }
+ ]
+ }
+ }
+ for rtr in topo["routers"]:
+ result = verify_rib(tgen, "ipv6", rtr, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure E BGP multi hop using the loopback addresses.")
+ as_num = 100
+ for node in topo["routers"]:
+ as_num += 1
+ topo["routers"][node].update(
+ {
+ "bgp": {
+ "local_as": as_num,
+ "address_family": {"ipv6": {"unicast": {"neighbor": {}}}},
+ }
+ }
+ )
+ for node in topo["routers"]:
+ for rtr in topo["routers"]:
+ if node is not rtr:
+ topo["routers"][node]["bgp"]["address_family"]["ipv6"]["unicast"][
+ "neighbor"
+ ].update(
+ {
+ rtr: {
+ "dest_link": {
+ "lo": {"source_link": "lo", "ebgp_multihop": 2}
+ }
+ }
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, topo["routers"])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Modify router id
+ input_dict = {
+ "r0": {"bgp": {"router_id": "11.11.11.11"}},
+ "r1": {"bgp": {"router_id": "22.22.22.22"}},
+ "r2": {"bgp": {"router_id": "33.33.33.33"}},
+ "r3": {"bgp": {"router_id": "44.44.44.44"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that BGP neighbor is ESTABLISHED")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Configure couple of static routes in R0 and "
+ "Redistribute static routes in R1 bgp."
+ )
+
+ for rtr in topo["routers"]:
+ ospf_red = {
+ rtr: {
+ "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r0 = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ protocol = "bgp"
+ for rtr in ["r1", "r2", "r3"]:
+ result = verify_rib(tgen, "ipv6", rtr, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Clear ospf neighbours in R0")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that OSPF neighbours are reset and forms new adjacencies.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that BGP neighbours are reset and forms new adjacencies.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "bgp"
+ for rtr in ["r1", "r2", "r3"]:
+ result = verify_rib(tgen, "ipv6", rtr, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
def test_ospfv3_cost_tc52_p0(request):
"""OSPF Cost - verifying ospf interface cost functionality"""
tc_name = request.node.name
@@ -485,6 +658,8 @@ def test_ospfv3_cost_tc52_p0(request):
tgen = get_topogen()
global topo
step("Bring up the base config.")
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
reset_config_on_routers(tgen)
step(
@@ -565,6 +740,184 @@ def test_ospfv3_cost_tc52_p0(request):
write_test_footer(tc_name)
+def test_ospfv3_def_rte_tc9_p0(request):
+ """OSPF default route - Verify OSPF default route origination."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ step("Configure OSPF on all the routers of the topology.")
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step(" Configure default-information originate always on R0.")
+ input_dict = {"r0": {"ospf6": {"default-information": {"originate": True}}}}
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ step(" Configure default-information originate always on R0.")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that default route is originated in area always.")
+ dut = "r1"
+
+ step(" Configure default-information originate metric type 1 on R0.")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ "metric-type": 1,
+ }
+ }
+ }
+ }
+
+ step(
+ "Verify that default route is originated in area when external "
+ "routes are present in R0 with metric type as 1."
+ )
+ dut = "r0"
+ step(
+ "Verify that on R1 default route with type 1 is installed"
+ " (R1 is DUT in this case)"
+ )
+ dut = "r1"
+ step("Configure default-information originate metric type 2 on R0.")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ "metric-type": 2,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that default route is originated in area when external"
+ " routes are present in R0 with metric type as 2."
+ )
+
+ dut = "r1"
+ step(" Configure default-information originate metric 100 on R0")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ "metric-type": 2,
+ "metric": 100,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that default route is originated with cost as 100 on R0.")
+
+ dut = "r1"
+
+ step("Delete the default-information command")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ "delete": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ step("Configure default-information originate always on R0.")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure default route originate with active def route in zebra")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": "0::0/0",
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that default route is originated by R0.")
+ dut = "r1"
+
+ step("Delete static route")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": "0::0/0",
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ 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/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
index ed70c09fae..9ec06ec36b 100644
--- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
@@ -26,6 +26,8 @@ import os
import sys
import time
import pytest
+import ipaddress
+
from copy import deepcopy
from lib.topotest import frr_unicode
@@ -47,6 +49,8 @@ from lib.common_config import (
step,
create_interfaces_cfg,
topo_daemons,
+ create_debug_log_config,
+ apply_raw_config,
)
from lib.topolog import logger
from lib.topojson import build_config_from_json
@@ -55,6 +59,9 @@ from lib.ospf import (
verify_ospf6_neighbor,
clear_ospf,
verify_ospf6_interface,
+ create_router_ospf,
+ config_ospf6_interface,
+ verify_ospf6_rib,
)
from ipaddress import IPv6Address
@@ -381,6 +388,956 @@ def test_ospfv3_p2p_tc3_p0(request):
write_test_footer(tc_name)
+def test_ospfv3_hello_tc10_p0(request):
+ """
+ OSPF timers.
+
+ Verify OSPF interface timer hello interval 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)
+
+ step("modify hello timer from default value to some other value on r1")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 11, "dead_interval": 12},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "verify that new timer value is configured and applied using "
+ "the show ip ospf interface command."
+ )
+ dut = "r1"
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 11,
+ "timerIntervalsConfigDead": 12,
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("modify hello timer from default value to r1 hello timer on r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 11, "dead_interval": 12},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 11,
+ "timerIntervalsConfigDead": 12,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("reconfigure the default hello timer value to default on r1 and r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 10,
+ "timerIntervalsConfigDead": 40,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("reconfigure the default hello timer value to default on r1 and r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 10,
+ "timerIntervalsConfigDead": 40,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("configure hello timer = 1 on r1 and r2")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 1, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 1, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 1,
+ "timerIntervalsConfigDead": 4,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" Configure hello timer = 65535")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 65535, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 65535, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 65535,
+ "timerIntervalsConfigDead": 4,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+ step(" Try configuring timer values outside range for example 65536")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 65536, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Create interface failed. Error: {}".format(
+ tc_name, result
+ )
+
+ step("Unconfigure the hello timer from the interface from r1 and r2.")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 65535},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that timer value is deleted from intf & " "set to default value 40 sec."
+ )
+ input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigHello": 10}}}}}
+ dut = "r1"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_dead_tc11_p0(request):
+ """
+ OSPF timers.
+
+ Verify OSPF interface timer dead interval 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)
+
+ step("modify dead interval from default value to some other value on r1")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 12, "dead_interval": 48},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "verify that new timer value is configured and applied using "
+ "the show ip ospf interface command."
+ )
+ dut = "r1"
+ input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigDead": 48}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("modify dead interval from default value to r1" "dead interval timer on r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"dead_interval": 48, "hello_interval": 12},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"timerIntervalsConfigDead": 48}}}}}
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+
+ step("remove ospf on R0")
+ ospf_del = {"r0": {"ospf6": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ # reconfiguring deleted ospf process by resetting the configs.
+ reset_config_on_routers(tgen)
+
+ step("reconfigure the default dead interval timer value to " "default on r1 and r2")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"timerIntervalsConfigDead": 40}}}}}
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+
+ step(" Configure dead timer = 65535 on r1 and r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"dead_interval": 65535},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"dead_interval": 65535},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {"links": {"r1": {"ospf6": {"timerIntervalsConfigDead": 65535}}}}
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+
+ step(" Try configuring timer values outside range for example 65536")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"dead_interval": 65536},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Create interface config failed. Error: {}".format(
+ tc_name, result
+ )
+
+ step("Unconfigure the dead timer from the interface from r1 and r2.")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"dead_interval": 65535},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that timer value is deleted from intf & " "set to default value 40 sec."
+ )
+ input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigDead": 40}}}}}
+ dut = "r1"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_tc4_mtu_ignore_p0(request):
+ """
+ OSPF NFSM - MTU change
+
+ Verify NFSM events when ospf nbr changes with different MTU values
+ """
+ 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.")
+ step("Verify that OSPF neighbors are FULL.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Modify the MTU to non default Value on R0 to R1 interface. "
+ "Reset ospf neighbors on R0."
+ )
+
+ rtr0 = tgen.routers()["r0"]
+ rtr1 = tgen.routers()["r1"]
+
+ r0_r1_intf = topo["routers"]["r0"]["links"]["r1"]["interface"]
+ r1_r0_intf = topo["routers"]["r1"]["links"]["r0"]["interface"]
+
+ rtr0.run("ifconfig {} mtu 1400".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+ clear_ospf(tgen, "r1", ospf="ospf6")
+
+ step(
+ "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
+ )
+ result = verify_ospf6_neighbor(tgen, topo, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n OSPF nbrs are Full "
+ "instead of Exstart. Error: {}".format(tc_name, result)
+ )
+
+ step(
+ "Verify that configured MTU value is updated in the show ip " "ospf interface."
+ )
+
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"interfaceMtu": 1400}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Modify the MTU to non default Value on R0 to R1 interface. "
+ "Reset ospf neighbors on R0."
+ )
+ rtr0.run("ifconfig {} mtu 1500".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure mtu ignore and change the value of the mtu to non default"
+ " on R0 to R1 interface. Reset ospf neighbors on R0."
+ )
+ r0_ospf_mtu = {"r0": {"links": {"r1": {"ospf6": {"mtu_ignore": True}}}}}
+ result = config_ospf6_interface(tgen, topo, r0_ospf_mtu)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"mtuMismatchDetection": True}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ r1_ospf_mtu = {"r1": {"links": {"r0": {"ospf6": {"mtu_ignore": True}}}}}
+ result = config_ospf6_interface(tgen, topo, r1_ospf_mtu)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ rtr0.run("ifconfig {} mtu 1400".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Unconfigure mtu-ignore command from the interface. "
+ "Reset ospf neighbors on R0."
+ )
+
+ r1_ospf_mtu = {
+ "r1": {"links": {"r0": {"ospf6": {"mtu_ignore": True, "delete": True}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf_mtu)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step(
+ "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
+ )
+ result = verify_ospf6_neighbor(tgen, topo, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n OSPF nbrs are Full "
+ "instead of Exstart. Error: {}".format(tc_name, result)
+ )
+
+ step("Modify the MTU to again default valaue on R0 to R1 interface.")
+
+ rtr0.run("ifconfig {} mtu 1500".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0."
+ )
+
+ rtr0.run("ifconfig {} mtu 9216".format(r0_r1_intf))
+ rtr1.run("ifconfig {} mtu 9216".format(r1_r0_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+ clear_ospf(tgen, "r1", ospf="ospf6")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that jumbo MTU is updated in the show ip ospf interface.")
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"interfaceMtu": 9216}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_show_p1(request):
+ """Verify ospf show commands with json output."""
+ 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)
+
+ input_dict = {"r2": {"debug": {"log_file": "debug.log", "enable": ["ospf6"]}}}
+
+ result = create_debug_log_config(tgen, input_dict)
+
+ # Code coverage steps #Do Not upstream
+ input_dict_config = {
+ "r1": {
+ "raw_config": [
+ "end",
+ "debug ospf6 event",
+ "debug ospf6 gr helper",
+ "debug ospf6 ism events",
+ "debug ospf6 ism status",
+ "debug ospf6 ism timers",
+ "debug ospf6 nsm events",
+ "debug ospf6 nsm status",
+ "debug ospf6 nsm timers ",
+ "debug ospf6 nssa",
+ "debug ospf6 lsa aggregate",
+ "debug ospf6 lsa flooding ",
+ "debug ospf6 lsa generate",
+ "debug ospf6 lsa install ",
+ "debug ospf6 lsa refresh",
+ "debug ospf6 packet all detail",
+ "debug ospf6 packet all recv",
+ "debug ospf6 packet all send",
+ "debug ospf6 packet dd detail",
+ "debug ospf6 packet dd recv",
+ "debug ospf6 packet dd send ",
+ "debug ospf6 packet hello detail",
+ "debug ospf6 packet hello recv",
+ "debug ospf6 packet hello send",
+ "debug ospf6 packet ls-ack detail",
+ "debug ospf6 packet ls-ack recv",
+ "debug ospf6 packet ls-ack send",
+ "debug ospf6 packet ls-request detail",
+ "debug ospf6 packet ls-request recv",
+ "debug ospf6 packet ls-request send",
+ "debug ospf6 packet ls-update detail",
+ "debug ospf6 packet ls-update recv",
+ "debug ospf6 packet ls-update send",
+ "debug ospf6 sr",
+ "debug ospf6 te ",
+ "debug ospf6 zebra interface",
+ "debug ospf6 zebra redistribute",
+ ]
+ }
+ }
+
+ apply_raw_config(tgen, input_dict_config)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ step(" Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ dut = "r1"
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "ospf6": {
+ "status": "up",
+ "type": "BROADCAST",
+ "ospf6Enabled": True,
+ "attachedToArea": True,
+ "instanceId": 0,
+ "interfaceMtu": 1500,
+ "autoDetect": 1500,
+ "mtuMismatchDetection": "enabled",
+ "areaId": "0.0.0.0",
+ "cost": 10,
+ "transmitDelaySec": 1,
+ "priority": 1,
+ "timerIntervalsConfigHello": 1,
+ "timerIntervalsConfigDead": 4,
+ "timerIntervalsConfigRetransmit": 5,
+ "dr": "0.0.0.0",
+ "bdr": "0.0.0.0",
+ "numberOfInterfaceScopedLsa": 2,
+ "pendingLsaLsUpdateCount": 0,
+ "lsUpdateSendThread": "off",
+ "pendingLsaLsAckCount": 0,
+ "lsAckSendThread": "off",
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ nh = topo["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0]
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": ip_net, "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
+ }
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def ospfv3_router_id_tc14_p2(request):
+ """OSPF Router ID - Verify OSPF router id changes."""
+ 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 system router id as 1.1.1.1 on R1 , clear ospf router")
+ ospf_rid = {"r0": {"ospf6": {"router_id": "1.1.1.1"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("configure ospf router id as 1.1.1.2 on R1, clear ospf router")
+ ospf_rid = {"r1": {"ospf6": {"router_id": "1.1.1.2"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ topo1 = deepcopy(topo)
+ step("Verify that OSPF takes system router ID as ospf router id.")
+
+ topo1["routers"]["r0"]["ospf6"]["router_id"] = "1.1.1.1"
+ topo1["routers"]["r1"]["ospf6"]["router_id"] = "1.1.1.2"
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo1)
+ assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" delete ospf router id and clear ospf process.")
+ ospf_rid = {"r0": {"ospf6": {"del_router_id": "1.1.1.1"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_rid = {"r1": {"ospf6": {"del_router_id": "1.1.1.2"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ reset_config_on_routers(tgen)
+
+ step(" Configure R0 R1 R2 with same router ids")
+ ospf_rid = {"r0": {"ospf6": {"router_id": "1.1.1.1"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("configure ospf router id as 1.1.1.2 on R1, reboot router")
+ ospf_rid = {"r1": {"ospf6": {"router_id": "1.1.1.1"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, expected=False)
+ assert (
+ ospf_covergence is not True
+ ), "OSPF NBRs are up.Failed \n Error:" " {}".format(ospf_covergence)
+ topo1 = {}
+ topo1 = deepcopy(topo)
+
+ for rtr in ["r1", "r2", "r3", "r0"]:
+ topo1["routers"][rtr]["ospf6"].pop("router_id")
+
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is not True, (
+ "Testcase {} :Failed \n Neighborship "
+ "should not up as no router id is configured. Error: {}".format(tc_name, result)
+ )
+
+ step("Clear ospf process and check nbrs should not be up.")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is not True, (
+ "Testcase {} :Failed \n Neighborship "
+ "should not up as no router id is configured. Error: {}".format(tc_name, result)
+ )
+
+ topo1 = deepcopy(topo)
+
+ step("Configure system router id on routers , clear ospf router")
+ ospf_rid = {
+ "r0": {"ospf6": {"router_id": "1.1.1.1"}},
+ "r1": {"ospf6": {"router_id": "1.1.1.2"}},
+ "r2": {"ospf6": {"router_id": "1.1.1.3"}},
+ "r3": {"ospf6": {"router_id": "1.1.1.4"}},
+ }
+ result = create_router_ospf(tgen, topo1, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ topo1["routers"]["r0"]["ospf6"]["router_id"] = "1.1.1.1"
+ topo1["routers"]["r1"]["ospf6"]["router_id"] = "1.1.1.2"
+ topo1["routers"]["r2"]["ospf6"]["router_id"] = "1.1.1.3"
+ topo1["routers"]["r3"]["ospf6"]["router_id"] = "1.1.1.4"
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo1)
+ assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py
index ae891d9067..b72691b71e 100644
--- a/tests/topotests/zebra_rib/test_zebra_rib.py
+++ b/tests/topotests/zebra_rib/test_zebra_rib.py
@@ -31,6 +31,7 @@ import sys
from functools import partial
import pytest
import json
+import platform
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
@@ -45,6 +46,20 @@ from time import sleep
pytestmark = [pytest.mark.sharpd]
+krel = platform.release()
+
+
+def config_macvlan(tgen, r_str, device, macvlan):
+ "Creates specified macvlan interace on physical device"
+
+ if topotest.version_cmp(krel, "5.1") < 0:
+ return
+
+ router = tgen.gears[r_str]
+ router.run(
+ "ip link add {} link {} type macvlan mode bridge".format(macvlan, device)
+ )
+ router.run("ip link set {} up".format(macvlan))
def setup_module(mod):
@@ -62,6 +77,8 @@ def setup_module(mod):
TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
)
+ # Macvlan interface for protodown func test */
+ config_macvlan(tgen, "r1", "r1-eth0", "r1-eth0-macvlan")
# Initialize all routers.
tgen.start_router()
@@ -269,6 +286,46 @@ def test_route_map_usage():
assert ok, result
+def test_protodown():
+ "Run protodown basic functionality test and report results."
+ pdown = False
+ count = 0
+ tgen = get_topogen()
+ if topotest.version_cmp(krel, "5.1") < 0:
+ tgen.errors = "kernel 5.1 needed for protodown tests"
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ # Set interface protodown on
+ r1.vtysh_cmd("sharp interface r1-eth0-macvlan protodown")
+
+ # Timeout to wait for dplane to handle it
+ while count < 10:
+ count += 1
+ output = r1.vtysh_cmd("show interface r1-eth0-macvlan")
+ if re.search(r"protodown reasons:.*sharp", output):
+ pdown = True
+ break
+ sleep(1)
+
+ assert pdown is True, "Interface r1-eth0-macvlan not set protodown"
+
+ # Set interface protodown off
+ r1.vtysh_cmd("no sharp interface r1-eth0-macvlan protodown")
+
+ # Timeout to wait for dplane to handle it
+ while count < 10:
+ count += 1
+ output = r1.vtysh_cmd("show interface r1-eth0-macvlan")
+ if not re.search(r"protodown reasons:.*sharp", output):
+ pdown = False
+ break
+ sleep(1)
+
+ assert pdown is False, "Interface r1-eth0-macvlan not set protodown off"
+
+
def test_memory_leak():
"Run the memory leak test and report results."
tgen = get_topogen()
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index d940e03e1c..07819ea76d 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -143,10 +143,9 @@ sub scan_file {
elsif ($fabricd) {
$protocol = "VTYSH_FABRICD";
}
-# Enable VTYSH_PIM6D once pim6_cmd.c is merged
-# elsif ($file =~ /pimd\/pim6_cmd\.c$/) {
-# $protocol = "VTYSH_PIM6D";
-# }
+ elsif ($file =~ /pimd\/pim6_.*\.c$/) {
+ $protocol = "VTYSH_PIM6D";
+ }
else {
($protocol) = ($file =~ /^(?:.*\/)?([a-z0-9]+)\/[a-zA-Z0-9_\-]+\.c$/);
$protocol = "VTYSH_" . uc $protocol;
diff --git a/zebra/debug_nl.c b/zebra/debug_nl.c
index 260ba30b3c..b7d12bf537 100644
--- a/zebra/debug_nl.c
+++ b/zebra/debug_nl.c
@@ -255,6 +255,40 @@ const char *ifi_type2str(int type)
}
}
+const char *ifla_pdr_type2str(int type)
+{
+ switch (type) {
+ case IFLA_PROTO_DOWN_REASON_UNSPEC:
+ return "UNSPEC";
+ case IFLA_PROTO_DOWN_REASON_MASK:
+ return "MASK";
+ case IFLA_PROTO_DOWN_REASON_VALUE:
+ return "VALUE";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+const char *ifla_info_type2str(int type)
+{
+ switch (type) {
+ case IFLA_INFO_UNSPEC:
+ return "UNSPEC";
+ case IFLA_INFO_KIND:
+ return "KIND";
+ case IFLA_INFO_DATA:
+ return "DATA";
+ case IFLA_INFO_XSTATS:
+ return "XSTATS";
+ case IFLA_INFO_SLAVE_KIND:
+ return "SLAVE_KIND";
+ case IFLA_INFO_SLAVE_DATA:
+ return "SLAVE_DATA";
+ default:
+ return "UNKNOWN";
+ }
+}
+
const char *rta_type2str(int type)
{
switch (type) {
@@ -358,6 +392,8 @@ const char *rta_type2str(int type)
case IFLA_EVENT:
return "EVENT";
#endif /* IFLA_EVENT */
+ case IFLA_PROTO_DOWN_REASON:
+ return "PROTO_DOWN_REASON";
default:
return "UNKNOWN";
}
@@ -838,6 +874,42 @@ const char *nh_flags2str(uint32_t flags, char *buf, size_t buflen)
/*
* Netlink abstractions.
*/
+static void nllink_pdr_dump(struct rtattr *rta, size_t msglen)
+{
+ size_t plen;
+ uint32_t u32v;
+
+next_rta:
+ /* Check the header for valid length and for outbound access. */
+ if (RTA_OK(rta, msglen) == 0)
+ return;
+
+ plen = RTA_PAYLOAD(rta);
+ zlog_debug(" linkinfo [len=%d (payload=%zu) type=(%d) %s]",
+ rta->rta_len, plen, rta->rta_type,
+ ifla_pdr_type2str(rta->rta_type));
+ switch (rta->rta_type) {
+ case IFLA_PROTO_DOWN_REASON_MASK:
+ case IFLA_PROTO_DOWN_REASON_VALUE:
+ if (plen < sizeof(uint32_t)) {
+ zlog_debug(" invalid length");
+ break;
+ }
+
+ u32v = *(uint32_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u32v);
+ break;
+
+ default:
+ /* NOTHING: unhandled. */
+ break;
+ }
+
+ /* Get next pointer and start iteration again. */
+ rta = RTA_NEXT(rta, msglen);
+ goto next_rta;
+}
+
static void nllink_linkinfo_dump(struct rtattr *rta, size_t msglen)
{
size_t plen;
@@ -851,7 +923,7 @@ next_rta:
plen = RTA_PAYLOAD(rta);
zlog_debug(" linkinfo [len=%d (payload=%zu) type=(%d) %s]",
rta->rta_len, plen, rta->rta_type,
- rta_type2str(rta->rta_type));
+ ifla_info_type2str(rta->rta_type));
switch (rta->rta_type) {
case IFLA_INFO_KIND:
if (plen == 0) {
@@ -888,8 +960,10 @@ static void nllink_dump(struct ifinfomsg *ifi, size_t msglen)
struct rtattr *rta;
size_t plen, it;
uint32_t u32v;
+ uint8_t u8v;
char bytestr[16];
char dbuf[128];
+ unsigned short rta_type;
/* Get the first attribute and go from there. */
rta = IFLA_RTA(ifi);
@@ -899,10 +973,10 @@ next_rta:
return;
plen = RTA_PAYLOAD(rta);
+ rta_type = rta->rta_type & ~NLA_F_NESTED;
zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
- plen, rta->rta_type, rta_type2str(rta->rta_type));
- switch (rta->rta_type) {
- case IFLA_IFNAME:
+ plen, rta_type, rta_type2str(rta_type));
+ switch (rta_type) {
case IFLA_IFALIAS:
if (plen == 0) {
zlog_debug(" invalid length");
@@ -927,6 +1001,7 @@ next_rta:
#endif /* IFLA_GSO_MAX_SIZE */
case IFLA_CARRIER_CHANGES:
case IFLA_MASTER:
+ case IFLA_LINK:
if (plen < sizeof(uint32_t)) {
zlog_debug(" invalid length");
break;
@@ -936,6 +1011,15 @@ next_rta:
zlog_debug(" %u", u32v);
break;
+ case IFLA_PROTO_DOWN:
+ if (plen < sizeof(uint8_t)) {
+ zlog_debug(" invalid length");
+ break;
+ }
+
+ u8v = *(uint8_t *)RTA_DATA(rta);
+ zlog_debug(" %u", u8v);
+ break;
case IFLA_ADDRESS:
datap = RTA_DATA(rta);
dbuf[0] = 0;
@@ -952,7 +1036,11 @@ next_rta:
break;
case IFLA_LINKINFO:
- nllink_linkinfo_dump(RTA_DATA(rta), msglen);
+ nllink_linkinfo_dump(RTA_DATA(rta), plen);
+ break;
+
+ case IFLA_PROTO_DOWN_REASON:
+ nllink_pdr_dump(RTA_DATA(rta), plen);
break;
default:
@@ -1027,6 +1115,7 @@ static void nlneigh_dump(struct ndmsg *ndm, size_t msglen)
uint16_t vid;
char bytestr[16];
char dbuf[128];
+ unsigned short rta_type;
#ifndef NDA_RTA
#define NDA_RTA(ndm) \
@@ -1043,9 +1132,10 @@ next_rta:
return;
plen = RTA_PAYLOAD(rta);
+ rta_type = rta->rta_type & ~NLA_F_NESTED;
zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
- plen, rta->rta_type, neigh_rta2str(rta->rta_type));
- switch (rta->rta_type & ~ NLA_F_NESTED) {
+ plen, rta->rta_type, neigh_rta2str(rta_type));
+ switch (rta_type) {
case NDA_LLADDR:
datap = RTA_DATA(rta);
dbuf[0] = 0;
@@ -1153,6 +1243,7 @@ static void nlnh_dump(struct nhmsg *nhm, size_t msglen)
uint32_t u32v;
unsigned long count, i;
struct nexthop_grp *nhgrp;
+ unsigned short rta_type;
rta = RTM_NHA(nhm);
@@ -1162,9 +1253,10 @@ next_rta:
return;
plen = RTA_PAYLOAD(rta);
+ rta_type = rta->rta_type & ~NLA_F_NESTED;
zlog_debug(" rta [len=%d (payload=%zu) type=(%d) %s]", rta->rta_len,
- plen, rta->rta_type, nhm_rta2str(rta->rta_type));
- switch (rta->rta_type & ~NLA_F_NESTED) {
+ plen, rta->rta_type, nhm_rta2str(rta_type));
+ switch (rta_type) {
case NHA_ID:
u32v = *(uint32_t *)RTA_DATA(rta);
zlog_debug(" %u", u32v);
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c
index 4cd2bef307..ec4ea372f1 100644
--- a/zebra/dplane_fpm_nl.c
+++ b/zebra/dplane_fpm_nl.c
@@ -812,6 +812,9 @@ static int fpm_nl_enqueue(struct fpm_nl_ctx *fnc, struct zebra_dplane_ctx *ctx)
case DPLANE_OP_INTF_ADDR_ADD:
case DPLANE_OP_INTF_ADDR_DEL:
case DPLANE_OP_INTF_NETCONFIG:
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
case DPLANE_OP_NONE:
break;
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index a75b165270..fca03e55bf 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -77,6 +77,7 @@
#include "zebra/netconf_netlink.h"
extern struct zebra_privs_t zserv_privs;
+uint8_t frr_protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT;
/* Note: on netlink systems, there should be a 1-to-1 mapping between interface
names and ifindex values. */
@@ -814,33 +815,90 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id,
return 0;
}
-/* If the interface is an es bond member then it must follow EVPN's
- * protodown setting
+static bool is_if_protodown_reason_only_frr(uint32_t rc_bitfield)
+{
+ /* This shouldn't be possible */
+ assert(frr_protodown_r_bit < 32);
+ return (rc_bitfield == (((uint32_t)1) << frr_protodown_r_bit));
+}
+
+/*
+ * Process interface protodown dplane update.
+ *
+ * If the interface is an es bond member then it must follow EVPN's
+ * protodown setting.
*/
static void netlink_proc_dplane_if_protodown(struct zebra_if *zif,
- bool protodown)
+ struct rtattr **tb)
{
- bool zif_protodown;
+ bool protodown;
+ bool old_protodown;
+ uint32_t rc_bitfield = 0;
+ struct rtattr *pd_reason_info[IFLA_MAX + 1];
+
+ protodown = !!*(uint8_t *)RTA_DATA(tb[IFLA_PROTO_DOWN]);
+
+ if (tb[IFLA_PROTO_DOWN_REASON]) {
+ netlink_parse_rtattr_nested(pd_reason_info, IFLA_INFO_MAX,
+ tb[IFLA_PROTO_DOWN_REASON]);
- zif_protodown = !!(zif->flags & ZIF_FLAG_PROTODOWN);
- if (protodown == zif_protodown)
+ if (pd_reason_info[IFLA_PROTO_DOWN_REASON_VALUE])
+ rc_bitfield = *(uint32_t *)RTA_DATA(
+ pd_reason_info[IFLA_PROTO_DOWN_REASON_VALUE]);
+ }
+
+ /*
+ * Set our reason code to note it wasn't us.
+ * If the reason we got from the kernel is ONLY frr though, don't
+ * set it.
+ */
+ COND_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_EXTERNAL,
+ protodown && rc_bitfield &&
+ !is_if_protodown_reason_only_frr(rc_bitfield));
+
+
+ old_protodown = !!ZEBRA_IF_IS_PROTODOWN(zif);
+ if (protodown == old_protodown)
return;
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("interface %s dplane change, protdown %s",
zif->ifp->name, protodown ? "on" : "off");
+ /* Set protodown, respectively */
+ COND_FLAG(zif->flags, ZIF_FLAG_PROTODOWN, protodown);
+
if (zebra_evpn_is_es_bond_member(zif->ifp)) {
+ /* Check it's not already being sent to the dplane first */
+ if (protodown &&
+ CHECK_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN)) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "bond mbr %s protodown on recv'd but already sent protodown on to the dplane",
+ zif->ifp->name);
+ return;
+ }
+
+ if (!protodown &&
+ CHECK_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN)) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "bond mbr %s protodown off recv'd but already sent protodown off to the dplane",
+ zif->ifp->name);
+ return;
+ }
+
if (IS_ZEBRA_DEBUG_EVPN_MH_ES || IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
- "bond mbr %s re-instate protdown %s in the dplane",
- zif->ifp->name, zif_protodown ? "on" : "off");
- netlink_protodown(zif->ifp, zif_protodown);
- } else {
- if (protodown)
- zif->flags |= ZIF_FLAG_PROTODOWN;
+ "bond mbr %s reinstate protodown %s in the dplane",
+ zif->ifp->name, old_protodown ? "on" : "off");
+
+ if (old_protodown)
+ SET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN);
else
- zif->flags &= ~ZIF_FLAG_PROTODOWN;
+ SET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN);
+
+ dplane_intf_update(zif->ifp);
}
}
@@ -859,6 +917,29 @@ static uint8_t netlink_parse_lacp_bypass(struct rtattr **linkinfo)
}
/*
+ * Only called at startup to cleanup leftover protodown reasons we may
+ * have not cleaned up. We leave protodown set though.
+ */
+static void if_sweep_protodown(struct zebra_if *zif)
+{
+ bool protodown;
+
+ protodown = !!ZEBRA_IF_IS_PROTODOWN(zif);
+
+ if (!protodown)
+ return;
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("interface %s sweeping protodown %s reason 0x%x",
+ zif->ifp->name, protodown ? "on" : "off",
+ zif->protodown_rc);
+
+ /* Only clear our reason codes, leave external if it was set */
+ UNSET_FLAG(zif->protodown_rc, ZEBRA_PROTODOWN_ALL);
+ dplane_intf_update(zif->ifp);
+}
+
+/*
* Called from interface_lookup_netlink(). This function is only used
* during bootstrap.
*/
@@ -905,7 +986,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
/* Looking up interface name. */
memset(linkinfo, 0, sizeof(linkinfo));
- netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+ netlink_parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len,
+ NLA_F_NESTED);
/* check for wireless messages to ignore */
if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) {
@@ -1020,10 +1102,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass);
if (tb[IFLA_PROTO_DOWN]) {
- uint8_t protodown;
-
- protodown = *(uint8_t *)RTA_DATA(tb[IFLA_PROTO_DOWN]);
- netlink_proc_dplane_if_protodown(zif, !!protodown);
+ netlink_proc_dplane_if_protodown(zif, tb);
+ if_sweep_protodown(zif);
}
return 0;
@@ -1244,6 +1324,41 @@ netlink_put_address_update_msg(struct nl_batch *bth,
false);
}
+static ssize_t netlink_intf_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
+ size_t buflen)
+{
+ enum dplane_op_e op;
+ int cmd = 0;
+
+ op = dplane_ctx_get_op(ctx);
+
+ switch (op) {
+ case DPLANE_OP_INTF_UPDATE:
+ cmd = RTM_SETLINK;
+ break;
+ case DPLANE_OP_INTF_INSTALL:
+ cmd = RTM_NEWLINK;
+ break;
+ case DPLANE_OP_INTF_DELETE:
+ cmd = RTM_DELLINK;
+ break;
+ default:
+ flog_err(
+ EC_ZEBRA_NHG_FIB_UPDATE,
+ "Context received for kernel interface update with incorrect OP code (%u)",
+ op);
+ return -1;
+ }
+
+ return netlink_intf_msg_encode(cmd, ctx, buf, buflen);
+}
+
+enum netlink_msg_status
+netlink_put_intf_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
+{
+ return netlink_batch_add_msg(bth, ctx, netlink_intf_msg_encoder, false);
+}
+
int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
{
int len;
@@ -1716,7 +1831,8 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
/* Looking up interface name. */
memset(linkinfo, 0, sizeof(linkinfo));
- netlink_parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len);
+ netlink_parse_rtattr_flags(tb, IFLA_MAX, IFLA_RTA(ifi), len,
+ NLA_F_NESTED);
/* check for wireless messages to ignore */
if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) {
@@ -1834,7 +1950,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
ZEBRA_INTERFACE_VRF_LOOPBACK);
/* Update link. */
- zebra_if_update_link(ifp, link_ifindex, ns_id);
+ zebra_if_update_link(ifp, link_ifindex, link_nsid);
ifp->ll_type =
netlink_to_zebra_link_type(ifi->ifi_type);
@@ -1856,14 +1972,9 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
zebra_l2if_update_bond_slave(ifp, bond_ifindex,
!!bypass);
- if (tb[IFLA_PROTO_DOWN]) {
- uint8_t protodown;
+ if (tb[IFLA_PROTO_DOWN])
+ netlink_proc_dplane_if_protodown(ifp->info, tb);
- protodown = *(uint8_t *)RTA_DATA(
- tb[IFLA_PROTO_DOWN]);
- netlink_proc_dplane_if_protodown(ifp->info,
- !!protodown);
- }
} else if (ifp->vrf->vrf_id != vrf_id) {
/* VRF change for an interface. */
if (IS_ZEBRA_DEBUG_KERNEL)
@@ -1904,20 +2015,14 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
memcpy(old_hw_addr, ifp->hw_addr, INTERFACE_HWADDR_MAX);
/* Update link. */
- zebra_if_update_link(ifp, link_ifindex, ns_id);
+ zebra_if_update_link(ifp, link_ifindex, link_nsid);
ifp->ll_type =
netlink_to_zebra_link_type(ifi->ifi_type);
netlink_interface_update_hw_addr(tb, ifp);
- if (tb[IFLA_PROTO_DOWN]) {
- uint8_t protodown;
-
- protodown = *(uint8_t *)RTA_DATA(
- tb[IFLA_PROTO_DOWN]);
- netlink_proc_dplane_if_protodown(zif,
- !!protodown);
- }
+ if (tb[IFLA_PROTO_DOWN])
+ netlink_proc_dplane_if_protodown(ifp->info, tb);
if (if_is_no_ptm_operative(ifp)) {
bool is_up = if_is_operative(ifp);
@@ -2049,30 +2154,72 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
return 0;
}
-int netlink_protodown(struct interface *ifp, bool down)
-{
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+/**
+ * Interface encoding helper function.
+ *
+ * \param[in] cmd netlink command.
+ * \param[in] ctx dataplane context (information snapshot).
+ * \param[out] buf buffer to hold the packet.
+ * \param[in] buflen amount of buffer bytes.
+ */
+ssize_t netlink_intf_msg_encode(uint16_t cmd,
+ const struct zebra_dplane_ctx *ctx, void *buf,
+ size_t buflen)
+{
struct {
struct nlmsghdr n;
struct ifinfomsg ifa;
- char buf[NL_PKT_BUF_SIZE];
- } req;
+ char buf[];
+ } *req = buf;
- memset(&req, 0, sizeof(req));
+ struct rtattr *nest_protodown_reason;
+ ifindex_t ifindex = dplane_ctx_get_ifindex(ctx);
+ bool down = dplane_ctx_intf_is_protodown(ctx);
+ bool pd_reason_val = dplane_ctx_get_intf_pd_reason_val(ctx);
+ struct nlsock *nl =
+ kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
- req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
- req.n.nlmsg_flags = NLM_F_REQUEST;
- req.n.nlmsg_type = RTM_SETLINK;
- req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid;
+ if (buflen < sizeof(*req))
+ return 0;
- req.ifa.ifi_index = ifp->ifindex;
+ memset(req, 0, sizeof(*req));
- nl_attr_put(&req.n, sizeof(req), IFLA_PROTO_DOWN, &down, sizeof(down));
- nl_attr_put32(&req.n, sizeof(req), IFLA_LINK, ifp->ifindex);
+ if (cmd != RTM_SETLINK)
+ flog_err(
+ EC_ZEBRA_INTF_UPDATE_FAILURE,
+ "Only RTM_SETLINK message type currently supported in dplane pthread");
- return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
- false);
+ req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req->n.nlmsg_flags = NLM_F_REQUEST;
+ req->n.nlmsg_type = cmd;
+ req->n.nlmsg_pid = nl->snl.nl_pid;
+
+ req->ifa.ifi_index = ifindex;
+
+ nl_attr_put8(&req->n, buflen, IFLA_PROTO_DOWN, down);
+ nl_attr_put32(&req->n, buflen, IFLA_LINK, ifindex);
+
+ /* Reason info nest */
+ nest_protodown_reason =
+ nl_attr_nest(&req->n, buflen, IFLA_PROTO_DOWN_REASON);
+
+ if (!nest_protodown_reason)
+ return -1;
+
+ nl_attr_put32(&req->n, buflen, IFLA_PROTO_DOWN_REASON_MASK,
+ (1 << frr_protodown_r_bit));
+ nl_attr_put32(&req->n, buflen, IFLA_PROTO_DOWN_REASON_VALUE,
+ ((int)pd_reason_val) << frr_protodown_r_bit);
+
+ nl_attr_nest_end(&req->n, nest_protodown_reason);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s, protodown=%d reason_val=%d ifindex=%u",
+ __func__, nl_msg_type_to_str(cmd), down,
+ pd_reason_val, ifindex);
+
+ return NLMSG_ALIGN(req->n.nlmsg_len);
}
/* Interface information read by netlink. */
@@ -2088,4 +2235,35 @@ void interface_list(struct zebra_ns *zns)
interface_addr_lookup_netlink(zns);
}
+void if_netlink_set_frr_protodown_r_bit(uint8_t bit)
+{
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Protodown reason bit index changed: bit-index %u -> bit-index %u",
+ frr_protodown_r_bit, bit);
+
+ frr_protodown_r_bit = bit;
+}
+
+void if_netlink_unset_frr_protodown_r_bit(void)
+{
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Protodown reason bit index changed: bit-index %u -> bit-index %u",
+ frr_protodown_r_bit, FRR_PROTODOWN_REASON_DEFAULT_BIT);
+
+ frr_protodown_r_bit = FRR_PROTODOWN_REASON_DEFAULT_BIT;
+}
+
+
+bool if_netlink_frr_protodown_r_bit_is_set(void)
+{
+ return (frr_protodown_r_bit != FRR_PROTODOWN_REASON_DEFAULT_BIT);
+}
+
+uint8_t if_netlink_get_frr_protodown_r_bit(void)
+{
+ return frr_protodown_r_bit;
+}
+
#endif /* GNU_LINUX */
diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h
index a1ce7af8c7..46eac25377 100644
--- a/zebra/if_netlink.h
+++ b/zebra/if_netlink.h
@@ -40,6 +40,9 @@ int netlink_interface_addr_dplane(struct nlmsghdr *h, ns_id_t ns_id,
extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup);
extern int interface_lookup_netlink(struct zebra_ns *zns);
+extern ssize_t netlink_intf_msg_encode(uint16_t cmd,
+ const struct zebra_dplane_ctx *ctx,
+ void *buf, size_t buflen);
extern enum netlink_msg_status
netlink_put_gre_set_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
@@ -47,19 +50,19 @@ extern enum netlink_msg_status
netlink_put_address_update_msg(struct nl_batch *bth,
struct zebra_dplane_ctx *ctx);
-/*
- * Set protodown status of interface.
- *
- * ifp
- * Interface to set protodown on.
- *
- * down
- * If true, set protodown on. If false, set protodown off.
+extern enum netlink_msg_status
+netlink_put_intf_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx);
+
+#define FRR_PROTODOWN_REASON_DEFAULT_BIT 7
+/* Protodown bit setter/getter
*
- * Returns:
- * 0
+ * Allow users to change the bit if it conflicts with another
+ * on their system.
*/
-int netlink_protodown(struct interface *ifp, bool down);
+extern void if_netlink_set_frr_protodown_r_bit(uint8_t bit);
+extern void if_netlink_unset_frr_protodown_r_bit(void);
+extern bool if_netlink_frr_protodown_r_bit_is_set(void);
+extern uint8_t if_netlink_get_frr_protodown_r_bit(void);
#ifdef __cplusplus
}
diff --git a/zebra/if_socket.c b/zebra/if_socket.c
new file mode 100644
index 0000000000..309d5a3f3e
--- /dev/null
+++ b/zebra/if_socket.c
@@ -0,0 +1,41 @@
+/*
+ * Zebra Interface interaction with the kernel using socket.
+ * Copyright (C) 2022 NVIDIA CORPORATION & AFFILIATES
+ * Stephen Worley
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#ifndef HAVE_NETLINK
+
+#include "lib_errors.h"
+
+#include "zebra/rt.h"
+#include "zebra/zebra_dplane.h"
+#include "zebra/zebra_errors.h"
+
+enum zebra_dplane_result kernel_intf_update(struct zebra_dplane_ctx *ctx)
+{
+ flog_err(EC_LIB_UNAVAILABLE, "%s not Implemented for this platform",
+ __func__);
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
+}
+
+#endif
diff --git a/zebra/interface.c b/zebra/interface.c
index a76f8741e0..69d611e583 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -261,6 +261,13 @@ static int if_zebra_delete_hook(struct interface *ifp)
if (ifp->info) {
zebra_if = ifp->info;
+ /* If we set protodown, clear our reason now from the kernel */
+ if (ZEBRA_IF_IS_PROTODOWN(zebra_if) && zebra_if->protodown_rc &&
+ !ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zebra_if))
+ zebra_if_update_protodown_rc(ifp, true,
+ (zebra_if->protodown_rc &
+ ~ZEBRA_PROTODOWN_ALL));
+
/* Free installed address chains tree. */
if (zebra_if->ipv4_subnets)
route_table_finish(zebra_if->ipv4_subnets);
@@ -1229,58 +1236,130 @@ void zebra_if_update_all_links(struct zebra_ns *zns)
}
}
-void zebra_if_set_protodown(struct interface *ifp, bool down)
+static bool if_ignore_set_protodown(const struct interface *ifp, bool new_down,
+ uint32_t new_protodown_rc)
+{
+ struct zebra_if *zif;
+ bool old_down, old_set_down, old_unset_down;
+
+ zif = ifp->info;
+
+ /* Current state as we know it */
+ old_down = !!(ZEBRA_IF_IS_PROTODOWN(zif));
+ old_set_down = !!CHECK_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN);
+ old_unset_down = !!CHECK_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN);
+
+ if (new_protodown_rc == zif->protodown_rc) {
+ /* Early return if already down & reason bitfield matches */
+ if (new_down == old_down) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Ignoring request to set protodown %s for interface %s (%u): protodown %s is already set (reason bitfield: old 0x%x new 0x%x)",
+ new_down ? "on" : "off", ifp->name,
+ ifp->ifindex, new_down ? "on" : "off",
+ zif->protodown_rc, new_protodown_rc);
+
+ return true;
+ }
+
+ /* Early return if already set queued & reason bitfield matches
+ */
+ if (new_down && old_set_down) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Ignoring request to set protodown %s for interface %s (%u): protodown %s is already queued to dplane (reason bitfield: old 0x%x new 0x%x)",
+ new_down ? "on" : "off", ifp->name,
+ ifp->ifindex, new_down ? "on" : "off",
+ zif->protodown_rc, new_protodown_rc);
+
+ return true;
+ }
+
+ /* Early return if already unset queued & reason bitfield
+ * matches */
+ if (!new_down && old_unset_down) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "Ignoring request to set protodown %s for interface %s (%u): protodown %s is already queued to dplane (reason bitfield: old 0x%x new 0x%x)",
+ new_down ? "on" : "off", ifp->name,
+ ifp->ifindex, new_down ? "on" : "off",
+ zif->protodown_rc, new_protodown_rc);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int zebra_if_update_protodown_rc(struct interface *ifp, bool new_down,
+ uint32_t new_protodown_rc)
{
+ struct zebra_if *zif;
+
+ zif = ifp->info;
+
+ /* Check if we already have this state or it's queued */
+ if (if_ignore_set_protodown(ifp, new_down, new_protodown_rc))
+ return 1;
+
+ zlog_info(
+ "Setting protodown %s - interface %s (%u): reason bitfield change from 0x%x --> 0x%x",
+ new_down ? "on" : "off", ifp->name, ifp->ifindex,
+ zif->protodown_rc, new_protodown_rc);
+
+ zif->protodown_rc = new_protodown_rc;
+
+ if (new_down)
+ SET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN);
+ else
+ SET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN);
+
#ifdef HAVE_NETLINK
- netlink_protodown(ifp, down);
+ dplane_intf_update(ifp);
#else
zlog_warn("Protodown is not supported on this platform");
#endif
+ return 0;
+}
+
+int zebra_if_set_protodown(struct interface *ifp, bool new_down,
+ enum protodown_reasons new_reason)
+{
+ struct zebra_if *zif;
+ uint32_t new_protodown_rc;
+
+ zif = ifp->info;
+
+ if (new_down)
+ new_protodown_rc = zif->protodown_rc | new_reason;
+ else
+ new_protodown_rc = zif->protodown_rc & ~new_reason;
+
+ return zebra_if_update_protodown_rc(ifp, new_down, new_protodown_rc);
}
/*
- * Handle an interface addr event based on info in a dplane context object.
+ * Handle an interface events based on info in a dplane context object.
* This runs in the main pthread, using the info in the context object to
* modify an interface.
*/
-void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx)
+static void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp)
{
- struct interface *ifp;
uint8_t flags = 0;
const char *label = NULL;
- ns_id_t ns_id;
- struct zebra_ns *zns;
uint32_t metric = METRIC_MAX;
- ifindex_t ifindex;
const struct prefix *addr, *dest = NULL;
enum dplane_op_e op;
op = dplane_ctx_get_op(ctx);
- ns_id = dplane_ctx_get_ns_id(ctx);
-
- zns = zebra_ns_lookup(ns_id);
- if (zns == NULL) {
- /* No ns - deleted maybe? */
- if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("%s: can't find zns id %u", __func__, ns_id);
- goto done;
- }
-
- ifindex = dplane_ctx_get_ifindex(ctx);
-
- ifp = if_lookup_by_index_per_ns(zns, ifindex);
- if (ifp == NULL) {
- if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("%s: can't find ifp at nsid %u index %d",
- __func__, ns_id, ifindex);
- goto done;
- }
-
addr = dplane_ctx_get_intf_addr(ctx);
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("%s: %s: ifindex %u, addr %pFX", __func__,
- dplane_op2str(op), ifindex, addr);
+ zlog_debug("%s: %s: ifindex %s(%u), addr %pFX", __func__,
+ dplane_op2str(dplane_ctx_get_op(ctx)), ifp->name,
+ ifp->ifindex, addr);
/* Is there a peer or broadcast address? */
dest = dplane_ctx_get_intf_dest(ctx);
@@ -1335,41 +1414,66 @@ void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx)
*/
if (op != DPLANE_OP_INTF_ADDR_ADD)
rib_update(RIB_UPDATE_KERNEL);
+}
+
+static void zebra_if_update_ctx(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp)
+{
+ enum zebra_dplane_result dp_res;
+ struct zebra_if *zif;
+ bool pd_reason_val;
+ bool down;
+
+ dp_res = dplane_ctx_get_status(ctx);
+ pd_reason_val = dplane_ctx_get_intf_pd_reason_val(ctx);
+ down = dplane_ctx_intf_is_protodown(ctx);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: %s: if %s(%u) ctx-protodown %s ctx-reason %d",
+ __func__, dplane_op2str(dplane_ctx_get_op(ctx)),
+ ifp->name, ifp->ifindex, down ? "on" : "off",
+ pd_reason_val);
+
+ zif = ifp->info;
+ if (!zif) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: if %s(%u) zebra info pointer is NULL",
+ __func__, ifp->name, ifp->ifindex);
+ return;
+ }
+
+ if (dp_res != ZEBRA_DPLANE_REQUEST_SUCCESS) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: if %s(%u) dplane update failed",
+ __func__, ifp->name, ifp->ifindex);
+ goto done;
+ }
+
+ /* Update our info */
+ COND_FLAG(zif->flags, ZIF_FLAG_PROTODOWN, down);
done:
- /* We're responsible for the ctx object */
- dplane_ctx_fini(&ctx);
+ /* Clear our dplane flags */
+ UNSET_FLAG(zif->flags, ZIF_FLAG_SET_PROTODOWN);
+ UNSET_FLAG(zif->flags, ZIF_FLAG_UNSET_PROTODOWN);
}
/*
* Handle netconf change from a dplane context object; runs in the main
* pthread so it can update zebra data structs.
*/
-int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx)
+static void zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx,
+ struct interface *ifp)
{
- struct zebra_ns *zns;
- struct interface *ifp;
struct zebra_if *zif;
enum dplane_netconf_status_e mpls;
- int ret = 0;
-
- zns = zebra_ns_lookup(dplane_ctx_get_netconf_ns_id(ctx));
- if (zns == NULL) {
- ret = -1;
- goto done;
- }
-
- ifp = if_lookup_by_index_per_ns(zns,
- dplane_ctx_get_netconf_ifindex(ctx));
- if (ifp == NULL) {
- ret = -1;
- goto done;
- }
zif = ifp->info;
- if (zif == NULL) {
- ret = -1;
- goto done;
+ if (!zif) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: if %s(%u) zebra info pointer is NULL",
+ __func__, ifp->name, ifp->ifindex);
+ return;
}
mpls = dplane_ctx_get_netconf_mpls(ctx);
@@ -1383,12 +1487,105 @@ int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx)
zlog_debug("%s: if %s, ifindex %d, mpls %s",
__func__, ifp->name, ifp->ifindex,
(zif->mpls ? "ON" : "OFF"));
+}
+void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx)
+{
+ struct zebra_ns *zns;
+ struct interface *ifp;
+ ns_id_t ns_id;
+ enum dplane_op_e op;
+ enum zebra_dplane_result dp_res;
+ ifindex_t ifindex;
+
+ ns_id = dplane_ctx_get_ns_id(ctx);
+ dp_res = dplane_ctx_get_status(ctx);
+ op = dplane_ctx_get_op(ctx);
+ ifindex = dplane_ctx_get_ifindex(ctx);
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL || IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("Intf dplane ctx %p, op %s, ifindex (%u), result %s",
+ ctx, dplane_op2str(op), ifindex,
+ dplane_res2str(dp_res));
+
+ zns = zebra_ns_lookup(ns_id);
+ if (zns == NULL) {
+ /* No ns - deleted maybe? */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: can't find zns id %u", __func__, ns_id);
+
+ goto done;
+ }
+
+ ifp = if_lookup_by_index_per_ns(zns, ifindex);
+ if (ifp == NULL) {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("%s: can't find ifp at nsid %u index %d",
+ __func__, ns_id, ifindex);
+
+ goto done;
+ }
+
+ switch (op) {
+ case DPLANE_OP_INTF_ADDR_ADD:
+ case DPLANE_OP_INTF_ADDR_DEL:
+ zebra_if_addr_update_ctx(ctx, ifp);
+ break;
+
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
+ zebra_if_update_ctx(ctx, ifp);
+ break;
+
+ case DPLANE_OP_INTF_NETCONFIG:
+ zebra_if_netconf_update_ctx(ctx, ifp);
+ break;
+
+ case DPLANE_OP_ROUTE_INSTALL:
+ case DPLANE_OP_ROUTE_UPDATE:
+ case DPLANE_OP_ROUTE_DELETE:
+ case DPLANE_OP_NH_DELETE:
+ case DPLANE_OP_NH_INSTALL:
+ case DPLANE_OP_NH_UPDATE:
+ case DPLANE_OP_ROUTE_NOTIFY:
+ case DPLANE_OP_LSP_INSTALL:
+ case DPLANE_OP_LSP_UPDATE:
+ case DPLANE_OP_LSP_DELETE:
+ case DPLANE_OP_LSP_NOTIFY:
+ case DPLANE_OP_PW_INSTALL:
+ case DPLANE_OP_PW_UNINSTALL:
+ case DPLANE_OP_SYS_ROUTE_ADD:
+ case DPLANE_OP_SYS_ROUTE_DELETE:
+ case DPLANE_OP_ADDR_INSTALL:
+ case DPLANE_OP_ADDR_UNINSTALL:
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ case DPLANE_OP_NEIGH_INSTALL:
+ case DPLANE_OP_NEIGH_UPDATE:
+ case DPLANE_OP_NEIGH_DELETE:
+ case DPLANE_OP_NEIGH_IP_INSTALL:
+ case DPLANE_OP_NEIGH_IP_DELETE:
+ case DPLANE_OP_VTEP_ADD:
+ case DPLANE_OP_VTEP_DELETE:
+ case DPLANE_OP_RULE_ADD:
+ case DPLANE_OP_RULE_DELETE:
+ case DPLANE_OP_RULE_UPDATE:
+ case DPLANE_OP_NEIGH_DISCOVER:
+ case DPLANE_OP_BR_PORT_UPDATE:
+ case DPLANE_OP_NONE:
+ case DPLANE_OP_IPTABLE_ADD:
+ case DPLANE_OP_IPTABLE_DELETE:
+ case DPLANE_OP_IPSET_ADD:
+ case DPLANE_OP_IPSET_DELETE:
+ case DPLANE_OP_IPSET_ENTRY_ADD:
+ case DPLANE_OP_IPSET_ENTRY_DELETE:
+ case DPLANE_OP_NEIGH_TABLE_UPDATE:
+ case DPLANE_OP_GRE_SET:
+ break; /* should never hit here */
+ }
done:
- /* Free the context */
dplane_ctx_fini(&ctx);
-
- return ret;
}
/* Dump if address information to vty. */
@@ -1651,28 +1848,34 @@ static void ifs_dump_brief_vty_json(json_object *json, struct vrf *vrf)
}
}
-const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc,
- char *pd_buf, uint32_t pd_buf_len)
+const char *zebra_protodown_rc_str(uint32_t protodown_rc, char *pd_buf,
+ uint32_t pd_buf_len)
{
- bool first = true;
-
pd_buf[0] = '\0';
+ size_t len;
strlcat(pd_buf, "(", pd_buf_len);
- if (protodown_rc & ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY) {
- if (first)
- first = false;
- else
- strlcat(pd_buf, ",", pd_buf_len);
- strlcat(pd_buf, "startup-delay", pd_buf_len);
- }
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EXTERNAL))
+ strlcat(pd_buf, "external,", pd_buf_len);
- if (protodown_rc & ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN) {
- if (!first)
- strlcat(pd_buf, ",", pd_buf_len);
- strlcat(pd_buf, "uplinks-down", pd_buf_len);
- }
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY))
+ strlcat(pd_buf, "startup-delay,", pd_buf_len);
+
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN))
+ strlcat(pd_buf, "uplinks-down,", pd_buf_len);
+
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_VRRP))
+ strlcat(pd_buf, "vrrp,", pd_buf_len);
+
+ if (CHECK_FLAG(protodown_rc, ZEBRA_PROTODOWN_SHARP))
+ strlcat(pd_buf, "sharp,", pd_buf_len);
+
+ len = strnlen(pd_buf, pd_buf_len);
+
+ /* Remove trailing comma */
+ if (pd_buf[len - 1] == ',')
+ pd_buf[len - 1] = '\0';
strlcat(pd_buf, ")", pd_buf_len);
@@ -1878,7 +2081,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
zebra_evpn_if_es_print(vty, NULL, zebra_if);
vty_out(vty, " protodown: %s %s\n",
- (zebra_if->flags & ZIF_FLAG_PROTODOWN) ? "on" : "off",
+ (ZEBRA_IF_IS_PROTODOWN(zebra_if)) ? "on" : "off",
if_is_protodown_applicable(ifp) ? "" : "(n/a)");
if (zebra_if->protodown_rc)
vty_out(vty, " protodown reasons: %s\n",
@@ -2229,7 +2432,7 @@ static void if_dump_vty_json(struct vty *vty, struct interface *ifp,
if (if_is_protodown_applicable(ifp)) {
json_object_string_add(
json_if, "protodown",
- (zebra_if->flags & ZIF_FLAG_PROTODOWN) ? "on" : "off");
+ (ZEBRA_IF_IS_PROTODOWN(zebra_if)) ? "on" : "off");
if (zebra_if->protodown_rc)
json_object_string_add(
json_if, "protodownReason",
diff --git a/zebra/interface.h b/zebra/interface.h
index 315a3170d8..4b06603e7f 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -308,14 +308,22 @@ enum zebra_if_flags {
/* Dataplane protodown-on */
ZIF_FLAG_PROTODOWN = (1 << 2),
+ /* Dataplane protodown-on Queued to the dplane */
+ ZIF_FLAG_SET_PROTODOWN = (1 << 3),
+ /* Dataplane protodown-off Queued to the dplane */
+ ZIF_FLAG_UNSET_PROTODOWN = (1 << 4),
/* LACP bypass state is set by the dataplane on a bond member
* and inherited by the bond (if one or more bond members are in
* a bypass state the bond is placed in a bypass state)
*/
- ZIF_FLAG_LACP_BYPASS = (1 << 3)
+ ZIF_FLAG_LACP_BYPASS = (1 << 5)
};
+#define ZEBRA_IF_IS_PROTODOWN(zif) ((zif)->flags & ZIF_FLAG_PROTODOWN)
+#define ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zif) \
+ ((zif)->protodown_rc == ZEBRA_PROTODOWN_EXTERNAL)
+
/* `zebra' daemon local interface structure. */
struct zebra_if {
/* back pointer to the interface */
@@ -403,7 +411,7 @@ struct zebra_if {
* in the dataplane. This results in a carrier/L1 down on the
* physical device.
*/
- enum protodown_reasons protodown_rc;
+ uint32_t protodown_rc;
/* list of zebra_mac entries using this interface as destination */
struct list *mac_list;
@@ -497,7 +505,16 @@ extern void if_handle_vrf_change(struct interface *ifp, vrf_id_t vrf_id);
extern void zebra_if_update_link(struct interface *ifp, ifindex_t link_ifindex,
ns_id_t ns_id);
extern void zebra_if_update_all_links(struct zebra_ns *zns);
-extern void zebra_if_set_protodown(struct interface *ifp, bool down);
+/**
+ * Directly update entire protodown & reason code bitfield.
+ */
+extern int zebra_if_update_protodown_rc(struct interface *ifp, bool new_down,
+ uint32_t new_protodown_rc);
+/**
+ * Set protodown with single reason.
+ */
+extern int zebra_if_set_protodown(struct interface *ifp, bool down,
+ enum protodown_reasons new_reason);
extern int if_ip_address_install(struct interface *ifp, struct prefix *prefix,
const char *label, struct prefix *pp);
extern int if_ipv6_address_install(struct interface *ifp, struct prefix *prefix,
@@ -521,10 +538,9 @@ extern bool if_nhg_dependents_is_empty(const struct interface *ifp);
extern void vrf_add_update(struct vrf *vrfp);
extern void zebra_l2_map_slave_to_bond(struct zebra_if *zif, vrf_id_t vrf);
extern void zebra_l2_unmap_slave_from_bond(struct zebra_if *zif);
-extern const char *zebra_protodown_rc_str(enum protodown_reasons protodown_rc,
- char *pd_buf, uint32_t pd_buf_len);
-void zebra_if_addr_update_ctx(struct zebra_dplane_ctx *ctx);
-int zebra_if_netconf_update_ctx(struct zebra_dplane_ctx *ctx);
+extern const char *zebra_protodown_rc_str(uint32_t protodown_rc, char *pd_buf,
+ uint32_t pd_buf_len);
+void zebra_if_dplane_result(struct zebra_dplane_ctx *ctx);
#ifdef HAVE_PROC_NET_DEV
extern void ifstat_update_proc(void);
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index d84b0c1325..0dd76e3253 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -94,6 +94,7 @@ static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},
{RTM_DELROUTE, "RTM_DELROUTE"},
{RTM_GETROUTE, "RTM_GETROUTE"},
{RTM_NEWLINK, "RTM_NEWLINK"},
+ {RTM_SETLINK, "RTM_SETLINK"},
{RTM_DELLINK, "RTM_DELLINK"},
{RTM_GETLINK, "RTM_GETLINK"},
{RTM_NEWADDR, "RTM_NEWADDR"},
@@ -209,6 +210,10 @@ int netlink_config_write_helper(struct vty *vty)
vty_out(vty, "zebra kernel netlink batch-tx-buf %u %u\n", size,
threshold);
+ if (if_netlink_frr_protodown_r_bit_is_set())
+ vty_out(vty, "zebra protodown reason-bit %u\n",
+ if_netlink_get_frr_protodown_r_bit());
+
return 0;
}
@@ -1491,6 +1496,11 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
case DPLANE_OP_INTF_NETCONFIG:
case DPLANE_OP_NONE:
return FRR_NETLINK_ERROR;
+
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
+ return netlink_put_intf_update_msg(bth, ctx);
}
return FRR_NETLINK_ERROR;
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index ce1f17111b..6583af0a54 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1577,6 +1577,12 @@ void kernel_update_multi(struct dplane_ctx_q *ctx_list)
res = kernel_pbr_rule_update(ctx);
break;
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
+ res = kernel_intf_update(ctx);
+ break;
+
/* Ignore 'notifications' - no-op */
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
diff --git a/zebra/rt.h b/zebra/rt.h
index 5e626928d9..4952c3eb1a 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -66,6 +66,9 @@ enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx);
extern enum zebra_dplane_result
kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx);
+extern enum zebra_dplane_result
+kernel_intf_update(struct zebra_dplane_ctx *ctx);
+
#endif /* !HAVE_NETLINK */
extern int kernel_neigh_update(int cmd, int ifindex, void *addr, char *lla,
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index 0a79771708..b1af4b20e1 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -128,6 +128,8 @@ const char *af_type2str(int type);
const char *ifi_type2str(int type);
const char *rta_type2str(int type);
const char *rtm_type2str(int type);
+const char *ifla_pdr_type2str(int type);
+const char *ifla_info_type2str(int type);
const char *rtm_protocol2str(int type);
const char *rtm_scope2str(int type);
const char *rtm_rta2str(int type);
diff --git a/zebra/subdir.am b/zebra/subdir.am
index 77e0898d81..8cb1237c22 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -60,6 +60,7 @@ zebra_zebra_SOURCES = \
zebra/debug.c \
zebra/if_ioctl.c \
zebra/if_netlink.c \
+ zebra/if_socket.c \
zebra/if_sysctl.c \
zebra/interface.c \
zebra/ioctl.c \
diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c
index 1b6f37ec6a..e94aee5c1a 100644
--- a/zebra/zapi_msg.c
+++ b/zebra/zapi_msg.c
@@ -1487,6 +1487,7 @@ static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS)
ifindex_t ifindex;
struct interface *ifp;
char down;
+ enum protodown_reasons reason;
STREAM_GETL(msg, ifindex);
STREAM_GETC(msg, down);
@@ -1494,16 +1495,27 @@ static void zread_interface_set_protodown(ZAPI_HANDLER_ARGS)
/* set ifdown */
ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), ifindex);
- if (ifp) {
- zlog_info("Setting interface %s (%u): protodown %s", ifp->name,
- ifindex, down ? "on" : "off");
- zebra_if_set_protodown(ifp, down);
- } else {
+ if (!ifp) {
zlog_warn(
"Cannot set protodown %s for interface %u; does not exist",
down ? "on" : "off", ifindex);
+
+ return;
+ }
+
+ switch (client->proto) {
+ case ZEBRA_ROUTE_VRRP:
+ reason = ZEBRA_PROTODOWN_VRRP;
+ break;
+ case ZEBRA_ROUTE_SHARP:
+ reason = ZEBRA_PROTODOWN_SHARP;
+ break;
+ default:
+ reason = 0;
+ break;
}
+ zebra_if_set_protodown(ifp, down, reason);
stream_failure:
return;
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index 6de2be3ab8..d034c8f306 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -193,6 +193,9 @@ struct dplane_intf_info {
uint32_t metric;
uint32_t flags;
+ bool protodown;
+ bool pd_reason_val;
+
#define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */
#define DPLANE_INTF_SECONDARY (1 << 1)
#define DPLANE_INTF_BROADCAST (1 << 2)
@@ -526,6 +529,9 @@ static struct zebra_dplane_globals {
_Atomic uint32_t dg_gre_set_in;
_Atomic uint32_t dg_gre_set_errors;
+ _Atomic uint32_t dg_intfs_in;
+ _Atomic uint32_t dg_intf_errors;
+
/* Dataplane pthread */
struct frr_pthread *dg_pthread;
@@ -760,6 +766,9 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_NONE:
case DPLANE_OP_IPSET_ADD:
case DPLANE_OP_IPSET_DELETE:
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
break;
case DPLANE_OP_IPSET_ENTRY_ADD:
@@ -1073,6 +1082,16 @@ const char *dplane_op2str(enum dplane_op_e op)
case DPLANE_OP_INTF_NETCONFIG:
return "INTF_NETCONFIG";
+
+ case DPLANE_OP_INTF_INSTALL:
+ ret = "INTF_INSTALL";
+ break;
+ case DPLANE_OP_INTF_UPDATE:
+ ret = "INTF_UPDATE";
+ break;
+ case DPLANE_OP_INTF_DELETE:
+ ret = "INTF_DELETE";
+ break;
}
return ret;
@@ -1771,6 +1790,27 @@ void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric)
ctx->u.intf.metric = metric;
}
+uint32_t dplane_ctx_get_intf_pd_reason_val(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.intf.pd_reason_val;
+}
+
+void dplane_ctx_set_intf_pd_reason_val(struct zebra_dplane_ctx *ctx, bool val)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ ctx->u.intf.pd_reason_val = val;
+}
+
+bool dplane_ctx_intf_is_protodown(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->u.intf.protodown;
+}
+
/* Is interface addr p2p? */
bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx)
{
@@ -2638,6 +2678,73 @@ done:
return ret;
}
+/**
+ * dplane_ctx_intf_init() - Initialize a context block for a inteface update
+ *
+ * @ctx: Dataplane context to init
+ * @op: Operation being performed
+ * @ifp: Interface
+ *
+ * Return: Result status
+ */
+int dplane_ctx_intf_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
+ const struct interface *ifp)
+{
+ struct zebra_ns *zns;
+ struct zebra_if *zif;
+ int ret = EINVAL;
+ bool set_pdown, unset_pdown;
+
+ if (!ctx || !ifp)
+ goto done;
+
+ ctx->zd_op = op;
+ ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS;
+ ctx->zd_vrf_id = ifp->vrf->vrf_id;
+
+ strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname));
+ ctx->zd_ifindex = ifp->ifindex;
+
+ zns = zebra_ns_lookup(ifp->vrf->vrf_id);
+ dplane_ctx_ns_init(ctx, zns, false);
+
+
+ /* Copy over ifp info */
+ ctx->u.intf.metric = ifp->metric;
+ ctx->u.intf.flags = ifp->flags;
+
+ /* Copy over extra zebra info, if available */
+ zif = (struct zebra_if *)ifp->info;
+
+ if (zif) {
+ set_pdown = !!(zif->flags & ZIF_FLAG_SET_PROTODOWN);
+ unset_pdown = !!(zif->flags & ZIF_FLAG_UNSET_PROTODOWN);
+
+ if (zif->protodown_rc &&
+ ZEBRA_IF_IS_PROTODOWN_ONLY_EXTERNAL(zif) == false)
+ ctx->u.intf.pd_reason_val = true;
+
+ /*
+ * See if we have new protodown state to set, otherwise keep
+ * current state
+ */
+ if (set_pdown)
+ ctx->u.intf.protodown = true;
+ else if (unset_pdown)
+ ctx->u.intf.protodown = false;
+ else
+ ctx->u.intf.protodown = !!ZEBRA_IF_IS_PROTODOWN(zif);
+ }
+
+ dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_INTF_UPDATE));
+ ctx->zd_is_update = (op == DPLANE_OP_INTF_UPDATE);
+
+ ret = AOK;
+
+done:
+ return ret;
+}
+
/*
* Capture information for an LSP update in a dplane context.
*/
@@ -3824,6 +3931,85 @@ static enum zebra_dplane_result intf_addr_update_internal(
return result;
}
+/**
+ * dplane_intf_update_internal() - Helper for enqueuing interface changes
+ *
+ * @ifp: Interface where the change occured
+ * @op: The operation to be enqued
+ *
+ * Return: Result of the change
+ */
+static enum zebra_dplane_result
+dplane_intf_update_internal(const struct interface *ifp, enum dplane_op_e op)
+{
+ enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE;
+ int ret = EINVAL;
+ struct zebra_dplane_ctx *ctx = NULL;
+
+ /* Obtain context block */
+ ctx = dplane_ctx_alloc();
+ if (!ctx) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = dplane_ctx_intf_init(ctx, op, ifp);
+ if (ret == AOK)
+ ret = dplane_update_enqueue(ctx);
+
+done:
+ /* Update counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_intfs_in, 1,
+ memory_order_relaxed);
+
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else {
+ atomic_fetch_add_explicit(&zdplane_info.dg_intf_errors, 1,
+ memory_order_relaxed);
+ if (ctx)
+ dplane_ctx_free(&ctx);
+ }
+
+ return result;
+}
+
+/*
+ * Enqueue a interface add for the dataplane.
+ */
+enum zebra_dplane_result dplane_intf_add(const struct interface *ifp)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (ifp)
+ ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_INSTALL);
+ return ret;
+}
+
+/*
+ * Enqueue a interface update for the dataplane.
+ */
+enum zebra_dplane_result dplane_intf_update(const struct interface *ifp)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (ifp)
+ ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_UPDATE);
+ return ret;
+}
+
+/*
+ * Enqueue a interface delete for the dataplane.
+ */
+enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp)
+{
+ enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE;
+
+ if (ifp)
+ ret = dplane_intf_update_internal(ifp, DPLANE_OP_INTF_DELETE);
+ return ret;
+}
+
/*
* Enqueue vxlan/evpn mac add (or update).
*/
@@ -5241,6 +5427,15 @@ static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx)
dplane_ctx_get_netconf_mpls(ctx),
dplane_ctx_get_netconf_mcast(ctx));
break;
+
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
+ zlog_debug("Dplane intf %s, idx %u, protodown %d",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ dplane_ctx_get_ifindex(ctx),
+ dplane_ctx_intf_is_protodown(ctx));
+ break;
}
}
@@ -5375,6 +5570,15 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx)
&zdplane_info.dg_gre_set_errors, 1,
memory_order_relaxed);
break;
+
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_intf_errors,
+ 1, memory_order_relaxed);
+ break;
+
/* Ignore 'notifications' - no-op */
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 29555d5b56..334d440a2f 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -188,6 +188,11 @@ enum dplane_op_e {
/* Incoming interface config events */
DPLANE_OP_INTF_NETCONFIG,
+
+ /* Interface update */
+ DPLANE_OP_INTF_INSTALL,
+ DPLANE_OP_INTF_UPDATE,
+ DPLANE_OP_INTF_DELETE,
};
/*
@@ -480,6 +485,9 @@ 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);
void dplane_ctx_set_intf_metric(struct zebra_dplane_ctx *ctx, uint32_t metric);
+uint32_t dplane_ctx_get_intf_pd_reason_val(const struct zebra_dplane_ctx *ctx);
+void dplane_ctx_set_intf_pd_reason_val(struct zebra_dplane_ctx *ctx, bool val);
+bool dplane_ctx_intf_is_protodown(const struct zebra_dplane_ctx *ctx);
/* Is interface addr p2p? */
bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_intf_set_connected(struct zebra_dplane_ctx *ctx);
@@ -677,6 +685,13 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp,
const struct connected *ifc);
/*
+ * Enqueue interface link changes for the dataplane.
+ */
+enum zebra_dplane_result dplane_intf_add(const struct interface *ifp);
+enum zebra_dplane_result dplane_intf_update(const struct interface *ifp);
+enum zebra_dplane_result dplane_intf_delete(const struct interface *ifp);
+
+/*
* Link layer operations for the dataplane.
*/
enum zebra_dplane_result dplane_neigh_ip_update(enum dplane_op_e op,
@@ -814,6 +829,10 @@ int dplane_ctx_route_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
struct nhg_hash_entry *nhe);
+/* Encode interface information into data plane context. */
+int dplane_ctx_intf_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op,
+ const struct interface *ifp);
+
/* Retrieve the limit on the number of pending, unprocessed updates. */
uint32_t dplane_get_in_queue_limit(void);
diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c
index c3890f7220..7549a3d5c0 100644
--- a/zebra/zebra_errors.c
+++ b/zebra/zebra_errors.c
@@ -792,6 +792,15 @@ static struct log_ref ferr_zebra_err[] = {
.suggestion = "Ignore this error.",
},
{
+ .code = EC_ZEBRA_INTF_UPDATE_FAILURE,
+ .title =
+ "Zebra failed to update interface in the kernel",
+ .description =
+ "Zebra made an attempt to update an interfce in the kernel, but it was not successful.",
+ .suggestion =
+ "Wait for Zebra to reattempt update.",
+ },
+ {
.code = END_FERR,
}
};
diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h
index 540c6dd7d0..5164de09d6 100644
--- a/zebra/zebra_errors.h
+++ b/zebra/zebra_errors.h
@@ -136,6 +136,7 @@ enum zebra_log_refs {
EC_ZEBRA_ES_CREATE,
EC_ZEBRA_GRE_SET_UPDATE,
EC_ZEBRA_SRV6M_UNRELEASED_LOCATOR_CHUNK,
+ EC_ZEBRA_INTF_UPDATE_FAILURE,
};
void zebra_error_init(void);
diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c
index 50eecd31d5..9099c066b1 100644
--- a/zebra/zebra_evpn_mh.c
+++ b/zebra/zebra_evpn_mh.c
@@ -3463,11 +3463,13 @@ void zebra_evpn_mh_json(json_object *json)
if (zmh_info->protodown_rc) {
json_array = json_object_new_array();
- if (zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY)
+ if (CHECK_FLAG(zmh_info->protodown_rc,
+ ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY))
json_object_array_add(
json_array,
json_object_new_string("startupDelay"));
- if (zmh_info->protodown_rc & ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN)
+ if (CHECK_FLAG(zmh_info->protodown_rc,
+ ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN))
json_object_array_add(
json_array,
json_object_new_string("uplinkDown"));
@@ -3623,10 +3625,10 @@ bool zebra_evpn_is_es_bond_member(struct interface *ifp)
void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif, bool clear,
const char *caller)
{
- bool old_protodown;
bool new_protodown;
- enum protodown_reasons old_protodown_rc = 0;
- enum protodown_reasons protodown_rc = 0;
+ uint32_t old_protodown_rc = 0;
+ uint32_t new_protodown_rc = 0;
+ uint32_t protodown_rc = 0;
if (!clear) {
struct zebra_if *bond_zif;
@@ -3635,32 +3637,23 @@ void zebra_evpn_mh_update_protodown_bond_mbr(struct zebra_if *zif, bool clear,
protodown_rc = bond_zif->protodown_rc;
}
- old_protodown = !!(zif->flags & ZIF_FLAG_PROTODOWN);
old_protodown_rc = zif->protodown_rc;
- zif->protodown_rc &= ~ZEBRA_PROTODOWN_EVPN_ALL;
- zif->protodown_rc |= (protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL);
- new_protodown = !!zif->protodown_rc;
+ new_protodown_rc = (old_protodown_rc & ~ZEBRA_PROTODOWN_EVPN_ALL);
+ new_protodown_rc |= (protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL);
+ new_protodown = !!new_protodown_rc;
- if (IS_ZEBRA_DEBUG_EVPN_MH_ES
- && (zif->protodown_rc != old_protodown_rc))
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES && (new_protodown_rc != old_protodown_rc))
zlog_debug(
"%s bond mbr %s protodown_rc changed; old 0x%x new 0x%x",
caller, zif->ifp->name, old_protodown_rc,
- zif->protodown_rc);
-
- if (old_protodown == new_protodown)
- return;
+ new_protodown_rc);
- if (new_protodown)
- zif->flags |= ZIF_FLAG_PROTODOWN;
- else
- zif->flags &= ~ZIF_FLAG_PROTODOWN;
-
- if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
- zlog_debug("%s protodown %s", zif->ifp->name,
- new_protodown ? "on" : "off");
-
- zebra_if_set_protodown(zif->ifp, new_protodown);
+ if (zebra_if_update_protodown_rc(zif->ifp, new_protodown,
+ new_protodown_rc) == 0) {
+ if (IS_ZEBRA_DEBUG_EVPN_MH_ES)
+ zlog_debug("%s protodown %s", zif->ifp->name,
+ new_protodown ? "on" : "off");
+ }
}
/* The bond members inherit the protodown reason code from the bond */
@@ -3683,7 +3676,7 @@ static void zebra_evpn_mh_update_protodown_es(struct zebra_evpn_es *es,
bool resync_dplane)
{
struct zebra_if *zif;
- enum protodown_reasons old_protodown_rc;
+ uint32_t old_protodown_rc;
zif = es->zif;
/* if the reason code is the same bail unless it is a new
@@ -3714,7 +3707,7 @@ static void zebra_evpn_mh_update_protodown_es(struct zebra_evpn_es *es,
static void zebra_evpn_mh_clear_protodown_es(struct zebra_evpn_es *es)
{
struct zebra_if *zif;
- enum protodown_reasons old_protodown_rc;
+ uint32_t old_protodown_rc;
zif = es->zif;
if (!(zif->protodown_rc & ZEBRA_PROTODOWN_EVPN_ALL))
@@ -3742,10 +3735,9 @@ static void zebra_evpn_mh_update_protodown_es_all(void)
zebra_evpn_mh_update_protodown_es(es, false /*resync_dplane*/);
}
-static void zebra_evpn_mh_update_protodown(enum protodown_reasons protodown_rc,
- bool set)
+static void zebra_evpn_mh_update_protodown(uint32_t protodown_rc, bool set)
{
- enum protodown_reasons old_protodown_rc = zmh_info->protodown_rc;
+ uint32_t old_protodown_rc = zmh_info->protodown_rc;
if (set) {
if ((protodown_rc & zmh_info->protodown_rc) == protodown_rc)
diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h
index af6832092b..ce7b920de1 100644
--- a/zebra/zebra_evpn_mh.h
+++ b/zebra/zebra_evpn_mh.h
@@ -263,7 +263,7 @@ struct zebra_evpn_mh_info {
uint32_t uplink_oper_up_cnt;
/* These protodown bits are inherited by all ES bonds */
- enum protodown_reasons protodown_rc;
+ uint32_t protodown_rc;
};
/* returns TRUE if the EVPN is ready to be sent to BGP */
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 469a94a65b..1b926dba5f 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -2993,6 +2993,9 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx)
case DPLANE_OP_INTF_ADDR_ADD:
case DPLANE_OP_INTF_ADDR_DEL:
case DPLANE_OP_INTF_NETCONFIG:
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
break;
}
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index e376d4b2af..c6840a503c 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -4318,11 +4318,11 @@ static void rib_process_dplane_results(struct thread *thread)
case DPLANE_OP_INTF_ADDR_ADD:
case DPLANE_OP_INTF_ADDR_DEL:
- zebra_if_addr_update_ctx(ctx);
- break;
-
+ case DPLANE_OP_INTF_INSTALL:
+ case DPLANE_OP_INTF_UPDATE:
+ case DPLANE_OP_INTF_DELETE:
case DPLANE_OP_INTF_NETCONFIG:
- zebra_if_netconf_update_ctx(ctx);
+ zebra_if_dplane_result(ctx);
break;
/* Some op codes not handled here */
diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h
index 63a61d5293..7aca91959c 100644
--- a/zebra/zebra_router.h
+++ b/zebra/zebra_router.h
@@ -68,19 +68,27 @@ enum multicast_mode {
* physical device.
*/
enum protodown_reasons {
+ /* A process outside of FRR's control protodowned the interface */
+ ZEBRA_PROTODOWN_EXTERNAL = (1 << 0),
/* On startup local ESs are held down for some time to
* allow the underlay to converge and EVPN routes to
* get learnt
*/
- ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY = (1 << 0),
+ ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY = (1 << 1),
/* If all the uplinks are down the switch has lost access
* to the VxLAN overlay and must shut down the access
* ports to allow servers to re-direct their traffic to
* other switches on the Ethernet Segment
*/
- ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN = (1 << 1),
- ZEBRA_PROTODOWN_EVPN_ALL = (ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN
- | ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY)
+ ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN = (1 << 2),
+ ZEBRA_PROTODOWN_EVPN_ALL = (ZEBRA_PROTODOWN_EVPN_UPLINK_DOWN |
+ ZEBRA_PROTODOWN_EVPN_STARTUP_DELAY),
+ ZEBRA_PROTODOWN_VRRP = (1 << 3),
+ /* This reason used exclusively for testing */
+ ZEBRA_PROTODOWN_SHARP = (1 << 4),
+ /* Just used to clear our fields on shutdown, externel not included */
+ ZEBRA_PROTODOWN_ALL = (ZEBRA_PROTODOWN_EVPN_ALL | ZEBRA_PROTODOWN_VRRP |
+ ZEBRA_PROTODOWN_SHARP)
};
#define ZEBRA_PROTODOWN_RC_STR_LEN 80
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index cc1ba3d8f8..32bbfd6654 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -60,6 +60,7 @@
#include "northbound_cli.h"
#include "zebra/zebra_nb.h"
#include "zebra/kernel_netlink.h"
+#include "zebra/if_netlink.h"
#include "zebra/table_manager.h"
#include "zebra/zebra_script.h"
#include "zebra/rtadv.h"
@@ -4357,6 +4358,31 @@ DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf,
return CMD_SUCCESS;
}
+DEFPY (zebra_protodown_bit,
+ zebra_protodown_bit_cmd,
+ "zebra protodown reason-bit (0-31)$bit",
+ ZEBRA_STR
+ "Protodown Configuration\n"
+ "Reason Bit used in the kernel for application\n"
+ "Reason Bit range\n")
+{
+ if_netlink_set_frr_protodown_r_bit(bit);
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_zebra_protodown_bit,
+ no_zebra_protodown_bit_cmd,
+ "no zebra protodown reason-bit [(0-31)$bit]",
+ NO_STR
+ ZEBRA_STR
+ "Protodown Configuration\n"
+ "Reason Bit used in the kernel for setting protodown\n"
+ "Reason Bit Range\n")
+{
+ if_netlink_unset_frr_protodown_r_bit();
+ return CMD_SUCCESS;
+}
+
#endif /* HAVE_NETLINK */
DEFUN(ip_table_range, ip_table_range_cmd,
@@ -4562,6 +4588,8 @@ void zebra_vty_init(void)
#ifdef HAVE_NETLINK
install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd);
install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd);
+ install_element(CONFIG_NODE, &zebra_protodown_bit_cmd);
+ install_element(CONFIG_NODE, &no_zebra_protodown_bit_cmd);
#endif /* HAVE_NETLINK */
#ifdef HAVE_SCRIPTING
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 676b92d429..6a3b1bfbe3 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -1068,13 +1068,11 @@ static void zebra_evpn_vxlan_cleanup_all(struct hash_bucket *bucket, void *arg)
{
struct zebra_evpn *zevpn = NULL;
struct zebra_l3vni *zl3vni = NULL;
- struct zebra_vrf *zvrf = (struct zebra_vrf *)arg;
zevpn = (struct zebra_evpn *)bucket->data;
- /* remove from l3-vni list */
- if (zvrf->l3vni)
- zl3vni = zl3vni_lookup(zvrf->l3vni);
+ /* remove l2vni from l2vni's tenant-vrf l3-vni list */
+ zl3vni = zl3vni_from_vrf(zevpn->vrf_id);
if (zl3vni)
listnode_delete(zl3vni->l2vnis, zevpn);