diff options
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); |
