]> git.proxmox.com Git - mirror_frr.git/commitdiff
Merge pull request #9104 from idryzhov/topotest-multiline
authorDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 28 Jul 2021 15:22:26 +0000 (11:22 -0400)
committerGitHub <noreply@github.com>
Wed, 28 Jul 2021 15:22:26 +0000 (11:22 -0400)
tests: fix invalid multiline format

166 files changed:
Makefile.am
bgpd/bgp_route.c
bgpd/bgp_routemap.c
bgpd/bgp_routemap_nb.c
bgpd/bgp_routemap_nb.h
bgpd/bgp_routemap_nb_config.c
bgpd/bgp_rpki.c
bgpd/bgp_zebra.c
bgpd/subdir.am
configure.ac
doc/user/bgp.rst
doc/user/filter.rst
doc/user/ospf6d.rst
doc/user/pbr.rst
doc/user/pim.rst
fpm/subdir.am
grpc/subdir.am
isisd/subdir.am
ldpd/subdir.am
lib/command.c
lib/pbr.h
lib/plist.c
lib/plist.h
lib/routemap.h
lib/routemap_cli.c
lib/subdir.am
mlag/subdir.am
ospf6d/ospf6_abr.c
ospf6d/ospf6_asbr.c
ospf6d/ospf6_flood.c
ospf6d/ospf6_interface.c
ospf6d/ospf6_interface.h
ospf6d/ospf6_intra.c
ospf6d/ospf6_nssa.c
ospf6d/ospf6_route.c
ospf6d/ospf6_route.h
ospf6d/ospf6_top.c
ospf6d/ospf6_top.h
ospf6d/ospf6_zebra.c
ospf6d/subdir.am
ospfclient/subdir.am
ospfd/ospf_network.c
ospfd/ospf_vty.c
ospfd/subdir.am
pathd/subdir.am
pbrd/pbr_map.h
pbrd/pbr_vty.c
pbrd/pbr_zebra.c
pimd/pim_cmd.c
pimd/pim_iface.c
pimd/pim_igmp.c
pimd/pim_igmp.h
pimd/pim_nb_config.c
pimd/pim_neighbor.c
pimd/pim_rp.c
pimd/pim_sock.c
qpb/subdir.am
ripd/subdir.am
tests/.gitignore
tests/lib/cli/common_cli.c
tests/lib/cli/common_cli.h
tests/lib/test_plist.c [new file with mode: 0644]
tests/subdir.am
tests/topotests/bgp_community_alias/r1/bgpd.conf
tests/topotests/bgp_community_alias/r2/bgpd.conf
tests/topotests/bgp_community_alias/r2/zebra.conf
tests/topotests/bgp_community_alias/test_bgp-community-alias.py
tests/topotests/evpn_pim_1/leaf1/pimd.conf
tests/topotests/evpn_pim_1/leaf2/pimd.conf
tests/topotests/evpn_pim_1/spine/pimd.conf
tests/topotests/lib/common_config.py
tests/topotests/lib/ospf.py
tests/topotests/lib/topotest.py
tests/topotests/msdp_mesh_topo1/r1/pimd.conf
tests/topotests/msdp_mesh_topo1/r2/pimd.conf
tests/topotests/msdp_mesh_topo1/r3/pimd.conf
tests/topotests/msdp_topo1/r1/pimd.conf
tests/topotests/msdp_topo1/r2/pimd.conf
tests/topotests/msdp_topo1/r3/pimd.conf
tests/topotests/msdp_topo1/r4/pimd.conf
tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json [new file with mode: 0644]
tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json [new file with mode: 0644]
tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py [new file with mode: 0644]
tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py [new file with mode: 0644]
tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
tests/topotests/pim_acl/h1/zebra.conf [new file with mode: 0644]
tests/topotests/pim_acl/h2/zebra.conf [new file with mode: 0644]
tests/topotests/pim_acl/r1/acl_1_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r1/acl_2_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r1/acl_3_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r1/acl_4_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r1/acl_5_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r1/acl_6_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r1/ospf_neighbor.json [new file with mode: 0644]
tests/topotests/pim_acl/r1/ospfd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r1/pim_neighbor.json [new file with mode: 0644]
tests/topotests/pim_acl/r1/pimd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r1/zebra.conf [new file with mode: 0644]
tests/topotests/pim_acl/r11/acl_1_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r11/ospfd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r11/pimd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r11/zebra.conf [new file with mode: 0644]
tests/topotests/pim_acl/r12/acl_2_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r12/ospfd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r12/pimd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r12/zebra.conf [new file with mode: 0644]
tests/topotests/pim_acl/r13/acl_3_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r13/ospfd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r13/pimd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r13/zebra.conf [new file with mode: 0644]
tests/topotests/pim_acl/r14/acl_4_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r14/acl_5_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r14/ospfd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r14/pimd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r14/zebra.conf [new file with mode: 0644]
tests/topotests/pim_acl/r15/acl_6_pim_join.json [new file with mode: 0644]
tests/topotests/pim_acl/r15/ospfd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r15/pimd.conf [new file with mode: 0644]
tests/topotests/pim_acl/r15/zebra.conf [new file with mode: 0644]
tests/topotests/pim_acl/test_pim_acl.py [new file with mode: 0755]
tests/topotests/pim_basic/r1/pimd.conf
tests/topotests/pim_basic/rp/pimd.conf
tests/topotests/pim_basic_topo2/r2/pimd.conf
tests/topotests/pim_igmp_vrf/h1/zebra.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/h2/zebra.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/h3/zebra.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/h4/zebra.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/ospfd.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/pim_red_join.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/pimd.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r1/zebra.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r11/ospfd.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r11/pimd.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r11/zebra.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r12/ospfd.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r12/pim_red_join.json [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r12/pimd.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/r12/zebra.conf [new file with mode: 0644]
tests/topotests/pim_igmp_vrf/test_pim_vrf.py [new file with mode: 0755]
vtysh/vtysh.c
yang/frr-bgp-route-map.yang
yang/frr-pim.yang
zebra/if_netlink.c
zebra/kernel_netlink.c
zebra/kernel_netlink.h
zebra/redistribute.c
zebra/rule_netlink.c
zebra/subdir.am
zebra/zapi_msg.c
zebra/zebra_dplane.c
zebra/zebra_dplane.h
zebra/zebra_mpls.c
zebra/zebra_mpls.h
zebra/zebra_pbr.c
zebra/zebra_vrf.h
zebra/zebra_vxlan.c
zebra/zserv.c

index 9bc5dd7d2241f1b0bdc0613f8a334dc371c13f8a..ce0f70a1a294043f17f989b15b75cdb80f5ce760 100644 (file)
@@ -29,11 +29,33 @@ AM_CPPFLAGS = \
        -I$(top_srcdir)/lib/assert \
        $(CPPFLAGS_BASE) \
        # end
+
+# AM_LDFLAGS is used for executables (daemons).  LDFLAGS can be left alone,
+# but if it is changed it should include $(AM_LDFLAGS)
 AM_LDFLAGS = \
+       -export-dynamic \
+       $(AC_LDFLAGS) \
+       $(AC_LDFLAGS_EXEC) \
+       $(SAN_FLAGS) \
+       # end
+
+# libraries need to use libxxx_LDFLAGS = $(LIB_LDFLAGS) -version-info X:Y:Z
+LIB_LDFLAGS = \
        -export-dynamic \
        $(AC_LDFLAGS) \
        $(SAN_FLAGS) \
        # end
+
+# modules need to use xxx_LDFLAGS = $(MODULE_LDFLAGS)
+MODULE_LDFLAGS = \
+       -export-dynamic \
+       -avoid-version \
+       -module \
+       -shared \
+       $(AC_LDFLAGS) \
+       $(SAN_FLAGS) \
+       # end
+
 DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DCONFDATE=$(CONFDATE)
 
 AR_FLAGS = @AR_FLAGS@
index d5bb53ad8dad6c6987cf7297e8fa30c7551002e0..66ff16d53aab2294d8274585c376789f7fbe1fba 100644 (file)
@@ -10862,8 +10862,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
                                                const char *com2alias =
                                                        bgp_community2alias(
                                                                communities[i]);
-                                               if (strncmp(alias, com2alias,
-                                                           strlen(com2alias))
+                                               if (strcmp(alias, com2alias)
                                                    == 0) {
                                                        found = true;
                                                        break;
@@ -10878,8 +10877,7 @@ static int bgp_show_table(struct vty *vty, struct bgp *bgp, safi_t safi,
                                                const char *com2alias =
                                                        bgp_community2alias(
                                                                communities[i]);
-                                               if (strncmp(alias, com2alias,
-                                                           strlen(com2alias))
+                                               if (strcmp(alias, com2alias)
                                                    == 0) {
                                                        found = true;
                                                        break;
index 529abcbea07b35a393c578e71be6f5448be38c73..61f57d04753132b5a558c6331a7b816bdfd61a83 100644 (file)
@@ -52,6 +52,7 @@
 #include "bgpd/bgp_zebra.h"
 #include "bgpd/bgp_regex.h"
 #include "bgpd/bgp_community.h"
+#include "bgpd/bgp_community_alias.h"
 #include "bgpd/bgp_clist.h"
 #include "bgpd/bgp_filter.h"
 #include "bgpd/bgp_mplsvpn.h"
@@ -1179,6 +1180,55 @@ static const struct route_map_rule_cmd route_match_vrl_source_vrf_cmd = {
        route_match_vrl_source_vrf_free
 };
 
+/* `match alias` */
+static enum route_map_cmd_result_t
+route_match_alias(void *rule, const struct prefix *prefix, void *object)
+{
+       char *alias = rule;
+       struct bgp_path_info *path = object;
+       char **communities;
+       int num;
+
+       if (path->attr->community) {
+               frrstr_split(path->attr->community->str, " ", &communities,
+                            &num);
+               for (int i = 0; i < num; i++) {
+                       const char *com2alias =
+                               bgp_community2alias(communities[i]);
+                       if (strcmp(alias, com2alias) == 0)
+                               return RMAP_MATCH;
+               }
+       }
+
+       if (path->attr->lcommunity) {
+               frrstr_split(path->attr->lcommunity->str, " ", &communities,
+                            &num);
+               for (int i = 0; i < num; i++) {
+                       const char *com2alias =
+                               bgp_community2alias(communities[i]);
+                       if (strcmp(alias, com2alias) == 0)
+                               return RMAP_MATCH;
+               }
+       }
+
+       return RMAP_NOMATCH;
+}
+
+static void *route_match_alias_compile(const char *arg)
+{
+
+       return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_alias_free(void *rule)
+{
+       XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_alias_cmd = {
+       "alias", route_match_alias, route_match_alias_compile,
+       route_match_alias_free};
+
 /* `match local-preference LOCAL-PREF' */
 
 /* Match function return 1 if match is success else return zero. */
@@ -4597,6 +4647,58 @@ DEFUN_YANG (no_match_local_pref,
        return nb_cli_apply_changes(vty, NULL);
 }
 
+DEFUN_YANG(match_alias, match_alias_cmd, "match alias ALIAS_NAME",
+          MATCH_STR
+          "Match BGP community alias name\n"
+          "BGP community alias name\n")
+{
+       const char *alias = argv[2]->arg;
+       struct community_alias ca1;
+       struct community_alias *lookup_alias;
+
+       const char *xpath =
+               "./match-condition[condition='frr-bgp-route-map:match-alias']";
+       char xpath_value[XPATH_MAXLEN];
+
+       memset(&ca1, 0, sizeof(ca1));
+       strlcpy(ca1.alias, alias, sizeof(ca1.alias));
+       lookup_alias = bgp_ca_alias_lookup(&ca1);
+       if (!lookup_alias) {
+               vty_out(vty, "%% BGP alias name '%s' does not exist\n", alias);
+               return CMD_WARNING_CONFIG_FAILED;
+       }
+
+       nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+       snprintf(xpath_value, sizeof(xpath_value),
+                "%s/rmap-match-condition/frr-bgp-route-map:alias", xpath);
+       nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, alias);
+
+       return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG(no_match_alias, no_match_alias_cmd, "no match alias [ALIAS_NAME]",
+          NO_STR MATCH_STR
+          "Match BGP community alias name\n"
+          "BGP community alias name\n")
+{
+       int idx_alias = 3;
+       const char *xpath =
+               "./match-condition[condition='frr-bgp-route-map:match-alias']";
+       char xpath_value[XPATH_MAXLEN];
+
+       nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+       if (argc <= idx_alias)
+               return nb_cli_apply_changes(vty, NULL);
+
+       snprintf(xpath_value, sizeof(xpath_value),
+                "%s/rmap-match-condition/frr-bgp-route-map:alias", xpath);
+       nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY,
+                             argv[idx_alias]->arg);
+
+       return nb_cli_apply_changes(vty, NULL);
+}
 
 DEFPY_YANG (match_community,
        match_community_cmd,
@@ -6286,6 +6388,7 @@ void bgp_route_map_init(void)
        route_map_no_set_tag_hook(generic_set_delete);
 
        route_map_install_match(&route_match_peer_cmd);
+       route_map_install_match(&route_match_alias_cmd);
        route_map_install_match(&route_match_local_pref_cmd);
 #ifdef HAVE_SCRIPTING
        route_map_install_match(&route_match_script_cmd);
@@ -6370,6 +6473,8 @@ void bgp_route_map_init(void)
        install_element(RMAP_NODE, &no_match_aspath_cmd);
        install_element(RMAP_NODE, &match_local_pref_cmd);
        install_element(RMAP_NODE, &no_match_local_pref_cmd);
+       install_element(RMAP_NODE, &match_alias_cmd);
+       install_element(RMAP_NODE, &no_match_alias_cmd);
        install_element(RMAP_NODE, &match_community_cmd);
        install_element(RMAP_NODE, &no_match_community_cmd);
        install_element(RMAP_NODE, &match_lcommunity_cmd);
index 1254591b87d26b5694f8db6b501591cc76e51522..92164269682aa3f2e4fcbfe357bb31ff7c512c75 100644 (file)
@@ -38,6 +38,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
                                .destroy = lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy,
                        }
                },
+               {
+                       .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:alias",
+                       .cbs = {
+                               .modify = lib_route_map_entry_match_condition_rmap_match_condition_alias_modify,
+                               .destroy = lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy,
+                       }
+               },
                {
                        .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script",
                        .cbs = {
index f0e492eb617da0de9af206d3dcf41c2ca58deb9b..069345b1a48d9d6f6ebf1e6481a0acb4f154e63c 100644 (file)
@@ -29,6 +29,10 @@ extern const struct frr_yang_module_info frr_bgp_route_map_info;
 /* prototypes */
 int lib_route_map_entry_match_condition_rmap_match_condition_local_preference_modify(struct nb_cb_modify_args *args);
 int lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_modify(
+       struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy(
+       struct nb_cb_destroy_args *args);
 int lib_route_map_entry_match_condition_rmap_match_condition_script_modify(struct nb_cb_modify_args *args);
 int lib_route_map_entry_match_condition_rmap_match_condition_script_destroy(struct nb_cb_destroy_args *args);
 int lib_route_map_entry_match_condition_rmap_match_condition_origin_modify(struct nb_cb_modify_args *args);
index 88e3f6438f547299887441b85406f2a3ad6afc05..45f5c8f4bcd73e665dbd1435087a78aa485a9000 100644 (file)
@@ -159,6 +159,62 @@ lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destro
        return NB_OK;
 }
 
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:alias
+ */
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_modify(
+       struct nb_cb_modify_args *args)
+{
+       struct routemap_hook_context *rhc;
+       const char *alias;
+       enum rmap_compile_rets ret;
+
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               /* Add configuration. */
+               rhc = nb_running_get_entry(args->dnode, NULL, true);
+               alias = yang_dnode_get_string(args->dnode, NULL);
+
+               /* Set destroy information. */
+               rhc->rhc_mhook = bgp_route_match_delete;
+               rhc->rhc_rule = "alias";
+               rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+               ret = bgp_route_match_add(rhc->rhc_rmi, "alias", alias,
+                                         RMAP_EVENT_MATCH_ADDED, args->errmsg,
+                                         args->errmsg_len);
+
+               if (ret != RMAP_COMPILE_SUCCESS) {
+                       rhc->rhc_mhook = NULL;
+                       return NB_ERR_VALIDATION;
+               }
+
+               break;
+       }
+
+       return NB_OK;
+}
+
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy(
+       struct nb_cb_destroy_args *args)
+{
+       switch (args->event) {
+       case NB_EV_VALIDATE:
+       case NB_EV_PREPARE:
+       case NB_EV_ABORT:
+               break;
+       case NB_EV_APPLY:
+               return lib_route_map_entry_match_destroy(args);
+       }
+
+       return NB_OK;
+}
+
 /*
  * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script
  */
index 816ed88eecd01b99808d7e2bb7ddda05c48f88ae..a8bccecacf01d122f45766a196d315bcabc4fe0c 100644 (file)
@@ -908,7 +908,7 @@ static int config_write(struct vty *vty)
 
        vty_out(vty, "!\n");
        vty_out(vty, "rpki\n");
-       vty_out(vty, "  rpki polling_period %d\n", polling_period);
+       vty_out(vty, " rpki polling_period %d\n", polling_period);
        for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
                switch (cache->type) {
                        struct tr_tcp_config *tcp_config;
@@ -917,13 +917,13 @@ static int config_write(struct vty *vty)
 #endif
                case TCP:
                        tcp_config = cache->tr_config.tcp_config;
-                       vty_out(vty, "  rpki cache %s %s ", tcp_config->host,
+                       vty_out(vty, " rpki cache %s %s ", tcp_config->host,
                                tcp_config->port);
                        break;
 #if defined(FOUND_SSH)
                case SSH:
                        ssh_config = cache->tr_config.ssh_config;
-                       vty_out(vty, "  rpki cache %s %u %s %s %s ",
+                       vty_out(vty, " rpki cache %s %u %s %s %s ",
                                ssh_config->host, ssh_config->port,
                                ssh_config->username,
                                ssh_config->client_privkey_path,
@@ -938,7 +938,7 @@ static int config_write(struct vty *vty)
 
                vty_out(vty, "preference %hhu\n", cache->preference);
        }
-       vty_out(vty, "  exit\n");
+       vty_out(vty, " exit\n");
 
        return 1;
 }
index 2c7c0878553df20b1441d8be56df1212a71a579b..24652ee93a7f098d04b2bebc7a31cf19fd74b7dc 100644 (file)
@@ -2576,6 +2576,7 @@ static void bgp_encode_pbr_rule_action(struct stream *s,
                stream_putl(s, pbr->unique);
        else
                stream_putl(s, pbra->unique);
+       stream_putc(s, 0); /* ip protocol being used */
        if (pbr && pbr->flags & MATCH_IP_SRC_SET)
                memcpy(&pfx, &(pbr->src), sizeof(struct prefix));
        else {
index 53225192f2c7c854fcbecd117278bcd51a32b6d8..cb9af08c95a84ba4b76d4628a87059fa3dea742b 100644 (file)
@@ -219,17 +219,17 @@ bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBYANG_LIBS) $(
 
 bgpd_bgpd_snmp_la_SOURCES = bgpd/bgp_snmp.c  bgpd/bgp_mplsvpn_snmp.c
 bgpd_bgpd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-bgpd_bgpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+bgpd_bgpd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
 bgpd_bgpd_snmp_la_LIBADD = lib/libfrrsnmp.la
 
 bgpd_bgpd_rpki_la_SOURCES = bgpd/bgp_rpki.c
 bgpd_bgpd_rpki_la_CFLAGS = $(AM_CFLAGS) $(RTRLIB_CFLAGS)
-bgpd_bgpd_rpki_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+bgpd_bgpd_rpki_la_LDFLAGS = $(MODULE_LDFLAGS)
 bgpd_bgpd_rpki_la_LIBADD = $(RTRLIB_LIBS)
 
 bgpd_bgpd_bmp_la_SOURCES = bgpd/bgp_bmp.c
 bgpd_bgpd_bmp_la_LIBADD = lib/libfrrcares.la
-bgpd_bgpd_bmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+bgpd_bgpd_bmp_la_LDFLAGS = $(MODULE_LDFLAGS)
 
 clippy_scan += \
        bgpd/bgp_bmp.c \
index 1ad87d9435f147e51edbb98f1a9920e7ac772a55..c86f47d07305b668005f6b6f06e9a0fe275c3323 100644 (file)
@@ -264,11 +264,11 @@ AC_C_FLAG([-std=gnu11], [CC="$ac_cc"], [CC="$CC -std=gnu11"])
 dnl if the user has specified any CFLAGS, override our settings
 if test "$enable_gcov" = "yes"; then
    if test "$orig_cflags" = ""; then
-      AC_C_FLAG([-coverage])
+      AC_C_FLAG([--coverage])
       AC_C_FLAG([-O0])
    fi
 
-   AC_LDFLAGS="${AC_LDFLAGS} -lgcov"
+   AC_LDFLAGS="${AC_LDFLAGS} --coverage"
 fi
 
 if test "$enable_clang_coverage" = "yes"; then
@@ -492,7 +492,7 @@ _LT_CONFIG_LIBTOOL([
   sed -e 's%func_warning ".*has not been installed in%true #\0%' -i libtool || true
 ])
 if test "$enable_static_bin" = "yes"; then
-  AC_LDFLAGS="-static"
+  AC_LDFLAGS_EXEC="-static"
   if test "$enable_static" != "yes"; then
     AC_MSG_ERROR([The --enable-static-bin option must be combined with --enable-static.])
   fi
@@ -501,6 +501,7 @@ if test "$enable_shared" != "yes"; then
   AC_MSG_ERROR([FRR cannot be built with --disable-shared.  If you want statically linked daemons, use --enable-shared --enable-static --enable-static-bin])
 fi
 AC_SUBST([AC_LDFLAGS])
+AC_SUBST([AC_LDFLAGS_EXEC])
 AM_CONDITIONAL([STATIC_BIN], [test "$enable_static_bin" = "yes"])
 
 AC_ARG_ENABLE([rpath],
index 6b5a0087614b3503af6523a8e9f3b525b71ab3b7..88f04839012dc438708453f07c447177f525592a 100644 (file)
@@ -2194,6 +2194,12 @@ communities attribute.
 
 The following commands can be used in route maps:
 
+.. clicmd:: match alias WORD
+
+   This command performs match to BGP updates using community alias WORD. When
+   the one of BGP communities value match to the one of community alias value in
+   community alias, it is match.
+
 .. clicmd:: match community WORD exact-match [exact-match]
 
    This command perform match to BGP updates using community list WORD. When
index 1fb9beccdc1794f3a527fe65183c93d8ca732e3e..cbbcd47dc37639501682ca29f1b3e9ce659a4d8c 100644 (file)
@@ -137,6 +137,15 @@ Showing ip prefix-list
 .. clicmd:: show ip prefix-list detail
 .. clicmd:: show ip prefix-list detail NAME
 
+.. clicmd:: debug prefix-list NAME match <A.B.C.D/M|X:X::X:X/M> [address-mode]
+
+   Execute the prefix list matching code for the specified list and prefix.
+   Shows which entry matched, if any.  (``address-mode`` is used for
+   PIM RP lookups and skips prefix length checks.)
+
+   The return value from this command is success only if the prefix-list
+   result is to permit the prefix, so the command can be used in scripting.
+
 Clear counter of ip prefix-list
 -------------------------------
 
index c4a1bc381eeb94b767b4085e54e42d0d72503331..5344f4cb05274025692f0d2dbbf25e28ccf9394f 100644 (file)
@@ -387,13 +387,3 @@ Larger example with policy and various options set:
     ipv6 access-class access6
     exec-timeout 0 0
    !
-
-
-Configuration Limits
-====================
-
-Ospf6d currently supports 100 interfaces addresses if MTU is set to
-default value, and 200 interface addresses if MTU is set to jumbo
-packet size or larger.
-
-  
index 77134a77042a550a91f56ab165dbbf326c955293..e59ed10896a0f8433ed982dda23ceb7e85caad9e 100644 (file)
@@ -117,6 +117,21 @@ end destination.
    both v4 and v6 prefixes.  This command is used in conjunction of the
    :clicmd:`match src-ip PREFIX` command for matching.
 
+.. clicmd:: match src-port (1-65535)
+
+   When a incoming packet matches the source port specified, take the
+   packet and forward according to the nexthops specified.
+
+.. clicmd:: match dst-port (1-65535)
+
+   When a incoming packet matches the destination port specified, take the
+   packet and forward according to the nexthops specified.
+
+.. clicmd:: match ip-protocol [tcp|udp]
+
+   When a incoming packet matches the specified ip protocol, take the
+   packet and forward according to the nexthops specified.
+
 .. clicmd:: match mark (1-4294967295)
 
    Select the mark to match.  This is a linux only command and if attempted
index 83d19d61880e768763fe65000b6074ce95d2ee4e..6f9aa289b4e992527fd4f2ea5525d972ff80f43c 100644 (file)
@@ -93,11 +93,13 @@ Certain signals have special meanings to *pimd*.
    down. This command is vrf aware, to configure for a vrf, enter the vrf
    submode.
 
-.. clicmd:: ip pim join-prune-interval (60-600)
+.. clicmd:: ip pim join-prune-interval (5-600)
 
    Modify the join/prune interval that pim uses to the new value. Time is
    specified in seconds. This command is vrf aware, to configure for a vrf,
-   enter the vrf submode.
+   enter the vrf submode.  The default time is 60 seconds.  If you enter
+   a value smaller than 60 seconds be aware that this can and will affect
+   convergence at scale.
 
 .. clicmd:: ip pim keep-alive-timer (31-60000)
 
@@ -199,7 +201,7 @@ is in a vrf, enter the interface command with the vrf keyword at the end.
    Set the DR Priority for the interface. This command is useful to allow the
    user to influence what node becomes the DR for a lan segment.
 
-.. clicmd:: ip pim hello (1-180) (1-180)
+.. clicmd:: ip pim hello (1-180) (1-630)
 
    Set the pim hello and hold interval for a interface.
 
index a645ca2b035f852e07548f85a5718d0e4f58eacf..b5988137e095943855c6a396f96b2f2215413366 100644 (file)
@@ -4,7 +4,7 @@ lib_LTLIBRARIES += fpm/libfrrfpm_pb.la
 endif
 endif
 
-fpm_libfrrfpm_pb_la_LDFLAGS = -version-info 0:0:0
+fpm_libfrrfpm_pb_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
 fpm_libfrrfpm_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS)
 fpm_libfrrfpm_pb_la_SOURCES = \
        fpm/fpm.h \
index d9ec365ba841ef37ad16899506dde7142f6e2945..cbebd72323d5e979a7ff8009f8fd42b191f59988 100644 (file)
@@ -2,7 +2,7 @@ if GRPC
 lib_LTLIBRARIES += grpc/libfrrgrpc_pb.la
 endif
 
-grpc_libfrrgrpc_pb_la_LDFLAGS = -version-info 0:0:0
+grpc_libfrrgrpc_pb_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
 grpc_libfrrgrpc_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(GRPC_CXXFLAGS)
 
 if GRPC
index 4243bd60cf560246acae7e7bd55c8e09a33afac3..3e5816c16b9a04156f71ff05b29a6a4e92d0f0dc 100644 (file)
@@ -139,7 +139,7 @@ nodist_isisd_isisd_SOURCES = \
 
 isisd_isisd_snmp_la_SOURCES = isisd/isis_snmp.c
 isisd_isisd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-isisd_isisd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+isisd_isisd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
 isisd_isisd_snmp_la_LIBADD = lib/libfrrsnmp.la
 
 # Building fabricd
index b7e2ab72d6b33fbd74bfdb10c4a73d04bec3c518..083effb703ad80831de2816f951cdf9addff6193 100644 (file)
@@ -65,5 +65,5 @@ ldpd_ldpd_LDADD = ldpd/libldp.a lib/libfrr.la $(LIBCAP)
 
 ldpd_ldpd_snmp_la_SOURCES = ldpd/ldp_snmp.c
 ldpd_ldpd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-ldpd_ldpd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+ldpd_ldpd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
 ldpd_ldpd_snmp_la_LIBADD = lib/libfrrsnmp.la
index 9dac60599c27ad0c18609d1120ea0919422f4dbc..fe17c68a8ba57df739f4aca8f2b3223c8a0bef9a 100644 (file)
@@ -160,6 +160,9 @@ static bool vty_check_node_for_xpath_decrement(enum node_type target_node,
                || node == BGP_FLOWSPECV6_NODE))
                return false;
 
+       if (target_node == INTERFACE_NODE && node == LINK_PARAMS_NODE)
+               return false;
+
        return true;
 }
 
index e365888662e193dde6e8d14d3c349a447bb965b4..cef1d9d380a44cf2632ed1e880ef0a7120ea2de9 100644 (file)
--- a/lib/pbr.h
+++ b/lib/pbr.h
@@ -49,7 +49,8 @@ struct pbr_filter {
 #define PBR_FILTER_PROTO               (1 << 5)
 #define PBR_FILTER_SRC_PORT_RANGE      (1 << 6)
 #define PBR_FILTER_DST_PORT_RANGE      (1 << 7)
-#define PBR_FILTER_DSFIELD                     (1 << 8)
+#define PBR_FILTER_DSFIELD             (1 << 8)
+#define PBR_FILTER_IP_PROTOCOL         (1 << 9)
 
 #define PBR_DSFIELD_DSCP (0xfc) /* Upper 6 bits of DS field: DSCP */
 #define PBR_DSFIELD_ECN (0x03) /* Lower 2 bits of DS field: BCN */
@@ -67,6 +68,9 @@ struct pbr_filter {
 
        /* Filter with fwmark */
        uint32_t fwmark;
+
+       /* Filter with the ip protocol */
+       uint8_t ip_proto;
 };
 
 /*
index 0ee02f8a0b5f09bb00a82feddd095c5ce6ef76ee..2b42c43764ec5b6eb4d674675ea8b49b96d59246 100644 (file)
@@ -750,7 +750,7 @@ static const char *prefix_list_type_str(struct prefix_list_entry *pentry)
 }
 
 static int prefix_list_entry_match(struct prefix_list_entry *pentry,
-                                  const struct prefix *p)
+                                  const struct prefix *p, bool address_mode)
 {
        int ret;
 
@@ -761,6 +761,9 @@ static int prefix_list_entry_match(struct prefix_list_entry *pentry,
        if (!ret)
                return 0;
 
+       if (address_mode)
+               return 1;
+
        /* In case of le nor ge is specified, exact match is performed. */
        if (!pentry->le && !pentry->ge) {
                if (pentry->prefix.prefixlen != p->prefixlen)
@@ -777,14 +780,15 @@ static int prefix_list_entry_match(struct prefix_list_entry *pentry,
        return 1;
 }
 
-enum prefix_list_type prefix_list_apply_which_prefix(
+enum prefix_list_type prefix_list_apply_ext(
        struct prefix_list *plist,
-       const struct prefix **which,
-       const void *object)
+       const struct prefix_list_entry **which,
+       union prefixconstptr object,
+       bool address_mode)
 {
        struct prefix_list_entry *pentry, *pbest = NULL;
 
-       const struct prefix *p = (const struct prefix *)object;
+       const struct prefix *p = object.p;
        const uint8_t *byte = p->u.val;
        size_t depth;
        size_t validbits = p->prefixlen;
@@ -809,7 +813,7 @@ enum prefix_list_type prefix_list_apply_which_prefix(
                     pentry = pentry->next_best) {
                        if (pbest && pbest->seq < pentry->seq)
                                continue;
-                       if (prefix_list_entry_match(pentry, p))
+                       if (prefix_list_entry_match(pentry, p, address_mode))
                                pbest = pentry;
                }
 
@@ -830,7 +834,7 @@ enum prefix_list_type prefix_list_apply_which_prefix(
                     pentry = pentry->next_best) {
                        if (pbest && pbest->seq < pentry->seq)
                                continue;
-                       if (prefix_list_entry_match(pentry, p))
+                       if (prefix_list_entry_match(pentry, p, address_mode))
                                pbest = pentry;
                }
                break;
@@ -838,7 +842,7 @@ enum prefix_list_type prefix_list_apply_which_prefix(
 
        if (which) {
                if (pbest)
-                       *which = &pbest->prefix;
+                       *which = pbest;
                else
                        *which = NULL;
        }
@@ -1296,6 +1300,51 @@ DEFPY (clear_ipv6_prefix_list,
        return vty_clear_prefix_list(vty, AFI_IP6, prefix_list, prefix_str);
 }
 
+DEFPY (debug_prefix_list_match,
+       debug_prefix_list_match_cmd,
+       "debug prefix-list WORD$prefix-list match <A.B.C.D/M|X:X::X:X/M>"
+       " [address-mode$addr_mode]",
+       DEBUG_STR
+       "Prefix-list test access\n"
+       "Name of a prefix list\n"
+       "Test prefix for prefix list result\n"
+       "Prefix to test in ip prefix-list\n"
+       "Prefix to test in ipv6 prefix-list\n"
+       "Use address matching mode (PIM RP)\n")
+{
+       struct prefix_list *plist;
+       const struct prefix_list_entry *entry = NULL;
+       enum prefix_list_type ret;
+
+       plist = prefix_list_lookup(family2afi(match->family), prefix_list);
+       if (!plist) {
+               vty_out(vty, "%% no prefix list named %s for AFI %s\n",
+                       prefix_list, afi2str(family2afi(match->family)));
+               return CMD_WARNING;
+       }
+
+       ret = prefix_list_apply_ext(plist, &entry, match, !!addr_mode);
+
+       vty_out(vty, "%s prefix list %s yields %s for %pFX, ",
+               afi2str(family2afi(match->family)), prefix_list,
+               ret == PREFIX_DENY ? "DENY" : "PERMIT", match);
+
+       if (!entry)
+               vty_out(vty, "no match found\n");
+       else {
+               vty_out(vty, "matching entry #%"PRId64": %pFX", entry->seq,
+                       &entry->prefix);
+               if (entry->ge)
+                       vty_out(vty, " ge %d", entry->ge);
+               if (entry->le)
+                       vty_out(vty, " le %d", entry->le);
+               vty_out(vty, "\n");
+       }
+
+       /* allow using this in scripts for quick prefix-list member tests */
+       return (ret == PREFIX_PERMIT) ? CMD_SUCCESS : CMD_WARNING;
+}
+
 struct stream *prefix_bgp_orf_entry(struct stream *s, struct prefix_list *plist,
                                    uint8_t init_flag, uint8_t permit_flag,
                                    uint8_t deny_flag)
@@ -1537,6 +1586,7 @@ static void prefix_list_init_ipv6(void)
        install_element(VIEW_NODE, &show_ipv6_prefix_list_prefix_cmd);
        install_element(VIEW_NODE, &show_ipv6_prefix_list_summary_cmd);
        install_element(VIEW_NODE, &show_ipv6_prefix_list_detail_cmd);
+       install_element(VIEW_NODE, &debug_prefix_list_match_cmd);
 
        install_element(ENABLE_NODE, &clear_ipv6_prefix_list_cmd);
 }
index 57eb763a6838e051a76f443435d1763204bef12e..c9507df57c88235a0448402265d67a36a32fef12 100644 (file)
@@ -37,6 +37,7 @@ enum prefix_list_type {
 };
 
 struct prefix_list;
+struct prefix_list_entry;
 
 struct orf_prefix {
        uint32_t seq;
@@ -63,12 +64,18 @@ extern struct prefix_list *prefix_list_lookup(afi_t, const char *);
  *
  * If no pointer is sent in, do not return anything.
  * If it is a empty plist return a NULL pointer.
+ *
+ * address_mode = the "prefix" being passed in is really an address, match
+ * regardless of prefix length (i.e. ge/le are ignored.)  prefix->prefixlen
+ * must be /32.
  */
 extern enum prefix_list_type
-prefix_list_apply_which_prefix(struct prefix_list *plist,
-                              const struct prefix **which,
-                              const void *object);
-#define prefix_list_apply(A, B) prefix_list_apply_which_prefix((A), NULL, (B))
+prefix_list_apply_ext(struct prefix_list *plist,
+                     const struct prefix_list_entry **matches,
+                     union prefixconstptr prefix,
+                     bool address_mode);
+#define prefix_list_apply(A, B) \
+       prefix_list_apply_ext((A), NULL, (B), false)
 
 extern struct prefix_list *prefix_bgp_orf_lookup(afi_t, const char *);
 extern struct stream *prefix_bgp_orf_entry(struct stream *,
index 4a40ec08b9ffdd639c51279070b87b2f8ad531ee..8af3b2c3c03c4ed7b85887e4057f63d69ba604bc 100644 (file)
@@ -270,6 +270,7 @@ DECLARE_QOBJ_TYPE(route_map);
 /* BGP route-map match conditions */
 #define IS_MATCH_LOCAL_PREF(C)                                                 \
        (strmatch(C, "frr-bgp-route-map:match-local-preference"))
+#define IS_MATCH_ALIAS(C) (strmatch(C, "frr-bgp-route-map:match-alias"))
 #define IS_MATCH_ORIGIN(C)                                                     \
        (strmatch(C, "frr-bgp-route-map:match-origin"))
 #define IS_MATCH_RPKI(C) (strmatch(C, "frr-bgp-route-map:rpki"))
index bf982cfa2ba2c0ab4dac52a5234a76976f444536..ec9033b3aaeb3fd881d0cbf2dc74c936bb5347ab 100644 (file)
@@ -634,6 +634,11 @@ void route_map_condition_show(struct vty *vty, struct lyd_node *dnode,
                        yang_dnode_get_string(
                                dnode,
                                "./rmap-match-condition/frr-bgp-route-map:local-preference"));
+       } else if (IS_MATCH_ALIAS(condition)) {
+               vty_out(vty, " match alias %s\n",
+                       yang_dnode_get_string(
+                               dnode,
+                               "./rmap-match-condition/frr-bgp-route-map:alias"));
        } else if (IS_MATCH_ORIGIN(condition)) {
                vty_out(vty, " match origin %s\n",
                        yang_dnode_get_string(
index 90301d800aecc5106ac3abc81af593cad53e329a..714af43238594ee0617c48934e2e358265d924c0 100644 (file)
@@ -2,7 +2,7 @@
 # libfrr
 #
 lib_LTLIBRARIES += lib/libfrr.la
-lib_libfrr_la_LDFLAGS = -version-info 0:0:0 -Xlinker -e_libfrr_version
+lib_libfrr_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0 -Xlinker -e_libfrr_version
 lib_libfrr_la_LIBADD = $(LIBCAP) $(UNWIND_LIBS) $(LIBYANG_LIBS) $(LUA_LIB) $(UST_LIBS) $(LIBM)
 
 lib_libfrr_la_SOURCES = \
@@ -322,7 +322,7 @@ lib_LTLIBRARIES += lib/libfrrsnmp.la
 endif
 
 lib_libfrrsnmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-lib_libfrrsnmp_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrsnmp_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
 lib_libfrrsnmp_la_LIBADD = $(SNMP_LIBS)
 lib_libfrrsnmp_la_SOURCES = \
        lib/agentx.c \
@@ -338,7 +338,7 @@ pkginclude_HEADERS += lib/resolver.h
 endif
 
 lib_libfrrcares_la_CFLAGS = $(AM_CFLAGS) $(CARES_CFLAGS)
-lib_libfrrcares_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrcares_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
 lib_libfrrcares_la_LIBADD = $(CARES_LIBS)
 lib_libfrrcares_la_SOURCES = \
        lib/resolver.c \
@@ -353,7 +353,7 @@ pkginclude_HEADERS += lib/frr_zmq.h
 endif
 
 lib_libfrrzmq_la_CFLAGS = $(AM_CFLAGS) $(ZEROMQ_CFLAGS)
-lib_libfrrzmq_la_LDFLAGS = -version-info 0:0:0
+lib_libfrrzmq_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
 lib_libfrrzmq_la_LIBADD = $(ZEROMQ_LIBS)
 lib_libfrrzmq_la_SOURCES = \
        lib/frr_zmq.c \
@@ -367,7 +367,7 @@ module_LTLIBRARIES += lib/confd.la
 endif
 
 lib_confd_la_CFLAGS = $(AM_CFLAGS) $(CONFD_CFLAGS)
-lib_confd_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_confd_la_LDFLAGS = $(MODULE_LDFLAGS)
 lib_confd_la_LIBADD = lib/libfrr.la $(CONFD_LIBS)
 lib_confd_la_SOURCES = lib/northbound_confd.c
 
@@ -379,7 +379,7 @@ module_LTLIBRARIES += lib/sysrepo.la
 endif
 
 lib_sysrepo_la_CFLAGS = $(AM_CFLAGS) $(SYSREPO_CFLAGS)
-lib_sysrepo_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_sysrepo_la_LDFLAGS = $(MODULE_LDFLAGS)
 lib_sysrepo_la_LIBADD = lib/libfrr.la $(SYSREPO_LIBS)
 lib_sysrepo_la_SOURCES = lib/northbound_sysrepo.c
 
@@ -391,7 +391,7 @@ module_LTLIBRARIES += lib/grpc.la
 endif
 
 lib_grpc_la_CXXFLAGS = $(WERROR) $(GRPC_CFLAGS)
-lib_grpc_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+lib_grpc_la_LDFLAGS = $(MODULE_LDFLAGS)
 lib_grpc_la_LIBADD = lib/libfrr.la grpc/libfrrgrpc_pb.la $(GRPC_LIBS)
 lib_grpc_la_SOURCES = lib/northbound_grpc.cpp
 
@@ -419,7 +419,8 @@ lib_grammar_sandbox_LDADD = \
 lib_clippy_CPPFLAGS = $(CPPFLAGS_BASE) -D_GNU_SOURCE -DBUILDING_CLIPPY
 lib_clippy_CFLAGS = $(AC_CFLAGS) $(PYTHON_CFLAGS)
 lib_clippy_LDADD = $(PYTHON_LIBS) $(UST_LIBS) -lelf
-lib_clippy_LDFLAGS = -export-dynamic
+# no $(SAN_FLAGS) here
+lib_clippy_LDFLAGS = -export-dynamic $(AC_LDFLAGS) $(AC_LDFLAGS_EXEC)
 lib_clippy_SOURCES = \
        lib/jhash.c \
        lib/clippy.c \
index 49d176150557a81a4a3bccc01e8a1c1fe69d1a7b..376eea8bc9b5831ad59d3207781996fb340835a2 100644 (file)
@@ -2,7 +2,7 @@ if HAVE_PROTOBUF3
 lib_LTLIBRARIES += mlag/libmlag_pb.la
 endif
 
-mlag_libmlag_pb_la_LDFLAGS = -version-info 0:0:0
+mlag_libmlag_pb_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
 mlag_libmlag_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS)
 mlag_libmlag_pb_la_SOURCES = \
        # end
index cc99d7c38727ed1d51302c902858dfab48fdbed9..9dd232dae5cea71009efecc55eabece71308b752 100644 (file)
@@ -477,11 +477,11 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
                monotime(&summary->changed);
        }
 
+       summary->prefix_options = route->prefix_options;
        summary->path.router_bits = route->path.router_bits;
        summary->path.options[0] = route->path.options[0];
        summary->path.options[1] = route->path.options[1];
        summary->path.options[2] = route->path.options[2];
-       summary->path.prefix_options = route->path.prefix_options;
        summary->path.area_id = area->area_id;
        summary->path.type = OSPF6_PATH_TYPE_INTER;
        summary->path.subtype = route->path.subtype;
@@ -514,7 +514,7 @@ int ospf6_abr_originate_summary_to_area(struct ospf6_route *route,
                /* Fill Inter-Area-Prefix-LSA */
                OSPF6_ABR_SUMMARY_METRIC_SET(prefix_lsa, route->path.cost);
                prefix_lsa->prefix.prefix_length = route->prefix.prefixlen;
-               prefix_lsa->prefix.prefix_options = route->path.prefix_options;
+               prefix_lsa->prefix.prefix_options = route->prefix_options;
 
                /* set Prefix */
                memcpy(p, &route->prefix.u.prefix6,
@@ -1154,6 +1154,7 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
 
        route->type = type;
        route->prefix = prefix;
+       route->prefix_options = prefix_options;
        route->path.origin.type = lsa->header->type;
        route->path.origin.id = lsa->header->id;
        route->path.origin.adv_router = lsa->header->adv_router;
@@ -1161,7 +1162,6 @@ void ospf6_abr_examin_summary(struct ospf6_lsa *lsa, struct ospf6_area *oa)
        route->path.options[0] = options[0];
        route->path.options[1] = options[1];
        route->path.options[2] = options[2];
-       route->path.prefix_options = prefix_options;
        route->path.area_id = oa->area_id;
        route->path.type = OSPF6_PATH_TYPE_INTER;
        route->path.cost = abr_entry->path.cost + cost;
index 3e911a743a6813c68cfb25df3a1d1b9b9d6bb32c..84111e4b7dbcfc53b65a44c07519a4e131273378 100644 (file)
@@ -121,7 +121,7 @@ void ospf6_as_external_lsa_originate(struct ospf6_route *route,
        as_external_lsa->prefix.prefix_length = route->prefix.prefixlen;
 
        /* PrefixOptions */
-       as_external_lsa->prefix.prefix_options = route->path.prefix_options;
+       as_external_lsa->prefix.prefix_options = route->prefix_options;
 
        /* don't use refer LS-type */
        as_external_lsa->prefix.prefix_refer_lstype = htons(0);
@@ -589,12 +589,12 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
        route->prefix.prefixlen = external->prefix.prefix_length;
        ospf6_prefix_in6_addr(&route->prefix.u.prefix6, external,
                              &external->prefix);
+       route->prefix_options = external->prefix.prefix_options;
 
        route->path.area_id = asbr_entry->path.area_id;
        route->path.origin.type = lsa->header->type;
        route->path.origin.id = lsa->header->id;
        route->path.origin.adv_router = lsa->header->adv_router;
-       route->path.prefix_options = external->prefix.prefix_options;
        memcpy(&route->path.ls_prefix, &asbr_id, sizeof(struct prefix));
 
        if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) {
index 738c2218fa43a7c6e0aa4d719749278a06a60fcb..0a384a98e696176e5efc0f6838ad89100fc05ad0 100644 (file)
@@ -89,6 +89,16 @@ void ospf6_lsa_originate(struct ospf6_lsa *lsa)
        struct ospf6_lsa *old;
        struct ospf6_lsdb *lsdb_self;
 
+       if (lsa->header->adv_router == INADDR_ANY) {
+               if (IS_OSPF6_DEBUG_ORIGINATE_TYPE(lsa->header->type))
+                       zlog_debug(
+                               "Refusing to originate LSA (zero router ID): %s",
+                               lsa->name);
+
+               ospf6_lsa_delete(lsa);
+               return;
+       }
+
        /* find previous LSA */
        old = ospf6_lsdb_lookup(lsa->header->type, lsa->header->id,
                                lsa->header->adv_router, lsa->lsdb);
index b52d6af90ec4c424ecfaabc523c84967bcd80067..468a4b8e81d27f351b9b1d0840eebfc578271e45 100644 (file)
@@ -385,7 +385,6 @@ void ospf6_interface_connected_route_update(struct interface *ifp)
        struct connected *c;
        struct listnode *node, *nnode;
        struct in6_addr nh_addr;
-       int count = 0, max_addr_count;
 
        oi = (struct ospf6_interface *)ifp->info;
        if (oi == NULL)
@@ -404,22 +403,10 @@ void ospf6_interface_connected_route_update(struct interface *ifp)
        /* update "route to advertise" interface route table */
        ospf6_route_remove_all(oi->route_connected);
 
-       if (oi->ifmtu >= OSPF6_JUMBO_MTU)
-               max_addr_count = OSPF6_MAX_IF_ADDRS_JUMBO;
-       else
-               max_addr_count = OSPF6_MAX_IF_ADDRS;
-
        for (ALL_LIST_ELEMENTS(oi->interface->connected, node, nnode, c)) {
                if (c->address->family != AF_INET6)
                        continue;
 
-               /* number of interface addresses supported is based on MTU
-                * size of OSPFv3 packet
-                */
-               count++;
-               if (count >= max_addr_count)
-                       break;
-
                CONTINUE_IF_ADDRESS_LINKLOCAL(IS_OSPF6_DEBUG_INTERFACE,
                                              c->address);
                CONTINUE_IF_ADDRESS_UNSPECIFIED(IS_OSPF6_DEBUG_INTERFACE,
@@ -821,7 +808,9 @@ int interface_up(struct thread *thread)
        }
 
        /* decide next interface state */
-       if (oi->type == OSPF_IFTYPE_POINTOPOINT) {
+       if (oi->type == OSPF_IFTYPE_LOOPBACK) {
+               ospf6_interface_state_change(OSPF6_INTERFACE_LOOPBACK, oi);
+       } else if (oi->type == OSPF_IFTYPE_POINTOPOINT) {
                ospf6_interface_state_change(OSPF6_INTERFACE_POINTTOPOINT, oi);
        } else if (oi->priority == 0)
                ospf6_interface_state_change(OSPF6_INTERFACE_DROTHER, oi);
@@ -1728,7 +1717,6 @@ DEFUN (ipv6_ospf6_area,
        int idx_ipv4 = 3;
        uint32_t area_id;
        int format;
-       int ipv6_count = 0;
 
        assert(ifp);
 
@@ -1743,23 +1731,6 @@ DEFUN (ipv6_ospf6_area,
                return CMD_SUCCESS;
        }
 
-       /* if more than OSPF6_MAX_IF_ADDRS are configured on this interface
-        * then don't allow ospfv3 to be configured
-        */
-       ipv6_count = connected_count_by_family(ifp, AF_INET6);
-       if (oi->ifmtu == OSPF6_DEFAULT_MTU && ipv6_count > OSPF6_MAX_IF_ADDRS) {
-               vty_out(vty,
-                       "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
-                       ifp->name, OSPF6_MAX_IF_ADDRS, ipv6_count);
-               return CMD_WARNING_CONFIG_FAILED;
-       } else if (oi->ifmtu >= OSPF6_JUMBO_MTU
-                  && ipv6_count > OSPF6_MAX_IF_ADDRS_JUMBO) {
-               vty_out(vty,
-                       "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
-                       ifp->name, OSPF6_MAX_IF_ADDRS_JUMBO, ipv6_count);
-               return CMD_WARNING_CONFIG_FAILED;
-       }
-
        if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) {
                vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg);
                return CMD_WARNING_CONFIG_FAILED;
index c9cd74b6919a95b1e60c5a6f7c08599ed139546b..b5efca743e74a7fb7c33a8bca133fd24a7aa6419 100644 (file)
@@ -201,7 +201,6 @@ extern void ospf6_interface_disable(struct ospf6_interface *);
 
 extern void ospf6_interface_state_update(struct interface *);
 extern void ospf6_interface_connected_route_update(struct interface *);
-extern void ospf6_interface_connected_route_add(struct connected *);
 extern struct in6_addr *
 ospf6_interface_get_global_address(struct interface *ifp);
 
index c971c6180e8536cfe5e6889d27869b8e43909266..06f64bbc40d3a9d95f8090512a5b88949d9d6cd7 100644 (file)
@@ -767,7 +767,6 @@ int ospf6_link_lsa_originate(struct thread *thread)
        struct ospf6_link_lsa *link_lsa;
        struct ospf6_route *route;
        struct ospf6_prefix *op;
-       int count, max_addr_count;
 
        oi = (struct ospf6_interface *)THREAD_ARG(thread);
        oi->thread_link_lsa = NULL;
@@ -811,30 +810,22 @@ int ospf6_link_lsa_originate(struct thread *thread)
        memcpy(link_lsa->options, oi->area->options, 3);
        memcpy(&link_lsa->linklocal_addr, oi->linklocal_addr,
               sizeof(struct in6_addr));
+       link_lsa->prefix_num = htonl(oi->route_connected->count);
 
        op = (struct ospf6_prefix *)((caddr_t)link_lsa
                                     + sizeof(struct ospf6_link_lsa));
 
-       /* connected prefix to advertise, number of interface addresses
-        * supported is based on MTU size of OSPFv3 packets
-        */
-       if (oi->ifmtu >= OSPF6_JUMBO_MTU)
-               max_addr_count = OSPF6_MAX_IF_ADDRS_JUMBO;
-       else
-               max_addr_count = OSPF6_MAX_IF_ADDRS;
-       for (route = ospf6_route_head(oi->route_connected), count = 0;
-            route && count < max_addr_count;
-            route = ospf6_route_next(route), count++) {
+       /* connected prefix to advertise */
+       for (route = ospf6_route_head(oi->route_connected); route;
+            route = ospf6_route_next(route)) {
                op->prefix_length = route->prefix.prefixlen;
-               op->prefix_options = route->path.prefix_options;
+               op->prefix_options = route->prefix_options;
                op->prefix_metric = htons(0);
                memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6,
                       OSPF6_PREFIX_SPACE(op->prefix_length));
                op = OSPF6_PREFIX_NEXT(op);
        }
 
-       link_lsa->prefix_num = htonl(count);
-
        /* Fill LSA Header */
        lsa_header->age = 0;
        lsa_header->type = htons(OSPF6_LSTYPE_LINK);
@@ -1014,7 +1005,6 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
        unsigned short prefix_num = 0;
        struct ospf6_route_table *route_advertise;
        int ls_id = 0;
-       int count, max_addr_count;
 
        oa = (struct ospf6_area *)THREAD_ARG(thread);
        oa->thread_intra_prefix_lsa = NULL;
@@ -1060,8 +1050,6 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
        intra_prefix_lsa->ref_adv_router = oa->ospf6->router_id;
 
        route_advertise = ospf6_route_table_create(0, 0);
-       route_advertise->hook_add = NULL;
-       route_advertise->hook_remove = NULL;
 
        for (ALL_LIST_ELEMENTS_RO(oa->if_list, i, oi)) {
                if (oi->state == OSPF6_INTERFACE_DOWN) {
@@ -1090,14 +1078,8 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
                        zlog_debug("  Interface %s:", oi->interface->name);
 
                /* connected prefix to advertise */
-               if (oi->ifmtu >= OSPF6_JUMBO_MTU)
-                       max_addr_count = OSPF6_MAX_IF_ADDRS_JUMBO;
-               else
-                       max_addr_count = OSPF6_MAX_IF_ADDRS;
-
-               for (route = ospf6_route_head(oi->route_connected), count = 0;
-                    route && count < max_addr_count;
-                    route = ospf6_route_best_next(route), count++) {
+               for (route = ospf6_route_head(oi->route_connected); route;
+                    route = ospf6_route_best_next(route)) {
                        if (IS_OSPF6_DEBUG_ORIGINATE(INTRA_PREFIX))
                                zlog_debug("    include %pFX", &route->prefix);
                        ospf6_route_add(ospf6_route_copy(route),
@@ -1193,7 +1175,7 @@ int ospf6_intra_prefix_lsa_originate_stub(struct thread *thread)
                }
 
                op->prefix_length = route->prefix.prefixlen;
-               op->prefix_options = route->path.prefix_options;
+               op->prefix_options = route->prefix_options;
                op->prefix_metric = htons(route->path.cost);
                memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6,
                       OSPF6_PREFIX_SPACE(op->prefix_length));
@@ -1312,8 +1294,6 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
 
        /* connected prefix to advertise */
        route_advertise = ospf6_route_table_create(0, 0);
-       route_advertise->hook_add = NULL;
-       route_advertise->hook_remove = NULL;
 
        type = ntohs(OSPF6_LSTYPE_LINK);
        for (ALL_LSDB_TYPED(oi->lsdb, type, lsa)) {
@@ -1356,6 +1336,7 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
                               sizeof(struct in6_addr));
                        memcpy(&route->prefix.u.prefix6, OSPF6_PREFIX_BODY(op),
                               OSPF6_PREFIX_SPACE(op->prefix_length));
+                       route->prefix_options = op->prefix_options;
 
                        route->path.origin.type = lsa->header->type;
                        route->path.origin.id = lsa->header->id;
@@ -1363,7 +1344,6 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
                        route->path.options[0] = link_lsa->options[0];
                        route->path.options[1] = link_lsa->options[1];
                        route->path.options[2] = link_lsa->options[2];
-                       route->path.prefix_options = op->prefix_options;
                        route->path.area_id = oi->area->area_id;
                        route->path.type = OSPF6_PATH_TYPE_INTRA;
 
@@ -1384,7 +1364,7 @@ int ospf6_intra_prefix_lsa_originate_transit(struct thread *thread)
        for (route = ospf6_route_head(route_advertise); route;
             route = ospf6_route_best_next(route)) {
                op->prefix_length = route->prefix.prefixlen;
-               op->prefix_options = route->path.prefix_options;
+               op->prefix_options = route->prefix_options;
                op->prefix_metric = htons(0);
                memcpy(OSPF6_PREFIX_BODY(op), &route->prefix.u.prefix6,
                       OSPF6_PREFIX_SPACE(op->prefix_length));
@@ -1817,12 +1797,12 @@ void ospf6_intra_prefix_lsa_add(struct ospf6_lsa *lsa)
                route->prefix.prefixlen = op->prefix_length;
                ospf6_prefix_in6_addr(&route->prefix.u.prefix6,
                                      intra_prefix_lsa, op);
+               route->prefix_options = op->prefix_options;
 
                route->type = OSPF6_DEST_TYPE_NETWORK;
                route->path.origin.type = lsa->header->type;
                route->path.origin.id = lsa->header->id;
                route->path.origin.adv_router = lsa->header->adv_router;
-               route->path.prefix_options = op->prefix_options;
                route->path.area_id = oa->area_id;
                route->path.type = OSPF6_PATH_TYPE_INTRA;
                route->path.metric_type = 1;
index 9f8cdf8fb7be657736b329263c5cafab020d94c8..bd6fb308dd8297a80469a8031071000f6e17bd69 100644 (file)
@@ -1296,7 +1296,7 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route,
        as_external_lsa->prefix.prefix_length = route->prefix.prefixlen;
 
        /* PrefixOptions */
-       as_external_lsa->prefix.prefix_options = route->path.prefix_options;
+       as_external_lsa->prefix.prefix_options = route->prefix_options;
 
        /* Set the P bit */
        as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P;
index 0a026785f47ca689d842de4fbc4ac3181424ebb0..80f0e7d26bf791891b2f2d28577b6b56a8fd2bd8 100644 (file)
@@ -436,6 +436,7 @@ struct ospf6_route *ospf6_route_copy(struct ospf6_route *route)
        new = ospf6_route_create();
        new->type = route->type;
        memcpy(&new->prefix, &route->prefix, sizeof(struct prefix));
+       new->prefix_options = route->prefix_options;
        new->installed = route->installed;
        new->changed = route->changed;
        new->flag = route->flag;
@@ -1137,6 +1138,7 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route,
 {
        char destination[PREFIX2STR_BUFFER], nexthop[64];
        char area_id[16], id[16], adv_router[16], capa[16], options[16];
+       char pfx_options[16];
        struct timeval now, res;
        char duration[64];
        struct listnode *node;
@@ -1264,10 +1266,13 @@ void ospf6_route_show_detail(struct vty *vty, struct ospf6_route *route,
                vty_out(vty, "Router Bits: %s\n", capa);
 
        /* Prefix Options */
+       ospf6_prefix_options_printbuf(route->prefix_options, pfx_options,
+                                     sizeof(pfx_options));
        if (use_json)
-               json_object_string_add(json_route, "prefixOptions", "xxx");
+               json_object_string_add(json_route, "prefixOptions",
+                                      pfx_options);
        else
-               vty_out(vty, "Prefix Options: xxx\n");
+               vty_out(vty, "Prefix Options: %s\n", pfx_options);
 
        /* Metrics */
        if (use_json) {
index a791a82cd4b03260e56a80c3c7c2650f14c6973b..ecfb45d1ea0d023ed7859ca3b4215630d2ac2cdb 100644 (file)
@@ -79,9 +79,6 @@ struct ospf6_path {
        /* Optional Capabilities */
        uint8_t options[3];
 
-       /* Prefix Options */
-       uint8_t prefix_options;
-
        /* Associated Area */
        in_addr_t area_id;
 
@@ -147,6 +144,9 @@ struct ospf6_route {
        /* flag */
        uint8_t flag;
 
+       /* Prefix Options */
+       uint8_t prefix_options;
+
        /* route option */
        void *route_option;
 
index 6f40989efddc9ffcb025d4d0dab91ba1f0700e41..92f1e50c65dd037900d95099cde4a72750991617 100644 (file)
@@ -989,7 +989,6 @@ DEFUN_HIDDEN (ospf6_interface_area,
        struct ospf6_interface *oi;
        struct interface *ifp;
        vrf_id_t vrf_id = VRF_DEFAULT;
-       int ipv6_count = 0;
        uint32_t area_id;
        int format;
 
@@ -1012,23 +1011,6 @@ DEFUN_HIDDEN (ospf6_interface_area,
                return CMD_SUCCESS;
        }
 
-       /* if more than OSPF6_MAX_IF_ADDRS are configured on this interface
-        * then don't allow ospfv3 to be configured
-        */
-       ipv6_count = connected_count_by_family(ifp, AF_INET6);
-       if (oi->ifmtu == OSPF6_DEFAULT_MTU && ipv6_count > OSPF6_MAX_IF_ADDRS) {
-               vty_out(vty,
-                       "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
-                       ifp->name, OSPF6_MAX_IF_ADDRS, ipv6_count);
-               return CMD_WARNING_CONFIG_FAILED;
-       } else if (oi->ifmtu >= OSPF6_JUMBO_MTU
-                  && ipv6_count > OSPF6_MAX_IF_ADDRS_JUMBO) {
-               vty_out(vty,
-                       "can not configure OSPFv3 on if %s, must have less than %d interface addresses but has %d addresses\n",
-                       ifp->name, OSPF6_MAX_IF_ADDRS_JUMBO, ipv6_count);
-               return CMD_WARNING_CONFIG_FAILED;
-       }
-
        if (str2area_id(argv[idx_ipv4]->arg, &area_id, &format)) {
                vty_out(vty, "Malformed Area-ID: %s\n", argv[idx_ipv4]->arg);
                return CMD_WARNING_CONFIG_FAILED;
index 3eb423f68155816e6f80cc3f8e73bdd5748f9553..b546ee87ae469c1542991c82442433f416d1b1ca 100644 (file)
@@ -164,10 +164,6 @@ DECLARE_QOBJ_TYPE(ospf6);
 
 #define OSPF6_DISABLED    0x01
 #define OSPF6_STUB_ROUTER 0x02
-#define OSPF6_MAX_IF_ADDRS 100
-#define OSPF6_MAX_IF_ADDRS_JUMBO 200
-#define OSPF6_DEFAULT_MTU 1500
-#define OSPF6_JUMBO_MTU 9000
 
 /* global pointer for OSPF top data structure */
 extern struct ospf6 *ospf6;
index 72bc3a2f3ac052d6f2d8d01d877854bd54e9bff4..5403e643dc664b542d7ed1b13a71695355f576f0 100644 (file)
@@ -131,38 +131,17 @@ void ospf6_zebra_no_redistribute(int type, vrf_id_t vrf_id)
 static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS)
 {
        struct connected *c;
-       struct ospf6_interface *oi;
-       int ipv6_count = 0;
 
        c = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD,
                                         zclient->ibuf, vrf_id);
        if (c == NULL)
                return 0;
 
-       oi = (struct ospf6_interface *)c->ifp->info;
-       if (oi == NULL)
-               oi = ospf6_interface_create(c->ifp);
-       assert(oi);
-
        if (IS_OSPF6_DEBUG_ZEBRA(RECV))
                zlog_debug("Zebra Interface address add: %s %5s %pFX",
                           c->ifp->name, prefix_family_str(c->address),
                           c->address);
 
-       ipv6_count = connected_count_by_family(c->ifp, AF_INET6);
-       if (oi->ifmtu == OSPF6_DEFAULT_MTU && ipv6_count > OSPF6_MAX_IF_ADDRS) {
-               zlog_warn(
-                       "Zebra Interface : %s has too many interface addresses %d only support %d, increase MTU",
-                       c->ifp->name, ipv6_count, OSPF6_MAX_IF_ADDRS);
-               return 0;
-       } else if (oi->ifmtu >= OSPF6_JUMBO_MTU
-                  && ipv6_count > OSPF6_MAX_IF_ADDRS_JUMBO) {
-               zlog_warn(
-                       "Zebra Interface : %s has too many interface addresses %d only support %d",
-                       c->ifp->name, ipv6_count, OSPF6_MAX_IF_ADDRS_JUMBO);
-               return 0;
-       }
-
        if (c->address->family == AF_INET6) {
                ospf6_interface_state_update(c->ifp);
                ospf6_interface_connected_route_update(c->ifp);
index 2b7bce53924a100059c94c58f44cd1bd43833c73..3f9ff76f3bad1921c1d05b0bfc9033e71aadf17d 100644 (file)
@@ -84,7 +84,7 @@ ospf6d_ospf6d_SOURCES = \
 
 ospf6d_ospf6d_snmp_la_SOURCES = ospf6d/ospf6_snmp.c
 ospf6d_ospf6d_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-ospf6d_ospf6d_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+ospf6d_ospf6d_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
 ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la
 
 clippy_scan += \
index 756ad88f151359753d7c4fe5dc6539116c7a6b6a..1f9547ab8765d200c1680e9946d151320c3970e7 100644 (file)
@@ -8,7 +8,7 @@ noinst_PROGRAMS += ospfclient/ospfclient
 #man8 += $(MANBUILD)/frr-ospfclient.8
 endif
 
-ospfclient_libfrrospfapiclient_la_LDFLAGS = -version-info 0:0:0
+ospfclient_libfrrospfapiclient_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
 ospfclient_libfrrospfapiclient_la_LIBADD = lib/libfrr.la
 ospfclient_libfrrospfapiclient_la_SOURCES = \
        ospfclient/ospf_apiclient.c \
index 00fbdc21a1dff4721f5f425f8d508865454b2691..be06afe532620e162dbde3d4bc82809ce34df9ae 100644 (file)
@@ -190,7 +190,7 @@ int ospf_sock_init(struct ospf *ospf)
                        flog_err(EC_LIB_SOCKET,
                                 "ospf_read_sock_init: socket: %s",
                                 safe_strerror(errno));
-                       exit(1);
+                       return -1;
                }
 
 #ifdef IP_HDRINCL
index bf2a8564f095011d47cbee5629dba963d127cf61..3819478cfc2c16a0070ab4a428529f26f041f6ae 100644 (file)
@@ -286,7 +286,7 @@ DEFPY (ospf_router_id,
        for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
                if (area->full_nbrs) {
                        vty_out(vty,
-                               "For this router-id change to take effect, use “clear ip ospf process” command\n");
+                               "For this router-id change to take effect, use \"clear ip ospf process\" command\n");
                        return CMD_SUCCESS;
                }
 
@@ -319,7 +319,7 @@ DEFUN_HIDDEN (ospf_router_id_old,
        for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
                if (area->full_nbrs) {
                        vty_out(vty,
-                               "For this router-id change to take effect, use “clear ip ospf process” command\n");
+                               "For this router-id change to take effect, use \"clear ip ospf process\" command\n");
                        return CMD_SUCCESS;
                }
 
@@ -352,7 +352,7 @@ DEFPY (no_ospf_router_id,
        for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area))
                if (area->full_nbrs) {
                        vty_out(vty,
-                               "For this router-id change to take effect, use “clear ip ospf process” command\n");
+                               "For this router-id change to take effect, use \"clear ip ospf process\" command\n");
                        return CMD_SUCCESS;
                }
 
index 2c4cc262c17106ca2a5c48cabc258e3b02ce70e3..4f9cbc7b1ee691c3efa2c323065bb3ed3e674cf4 100644 (file)
@@ -119,7 +119,7 @@ ospfd_ospfd_SOURCES = ospfd/ospf_main.c
 
 ospfd_ospfd_snmp_la_SOURCES = ospfd/ospf_snmp.c
 ospfd_ospfd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-ospfd_ospfd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+ospfd_ospfd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
 ospfd_ospfd_snmp_la_LIBADD = lib/libfrrsnmp.la
 
 EXTRA_DIST += \
index 693afabb39ac852038d6bbeec76440ee52b8c7d6..f339c792256fa54a38d9ead11733ee5b88f57f0b 100644 (file)
@@ -82,4 +82,4 @@ endif
 
 
 #pathd_pathd_pcep_la_CFLAGS = $(AM_CFLAGS)
-pathd_pathd_pcep_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+pathd_pathd_pcep_la_LDFLAGS = $(MODULE_LDFLAGS)
index caeadb064413837f35f3e1837edfb2450aa6ec07..694b915f484de2297d469abddcc98a5a68aac09c 100644 (file)
@@ -84,6 +84,17 @@ struct pbr_map_sequence {
         */
        uint32_t ruleno;
 
+       /*
+        * src and dst ports
+        */
+       uint16_t src_prt;
+       uint16_t dst_prt;
+
+       /*
+        * The ip protocol we want to match on
+        */
+       uint8_t ip_proto;
+
        /*
         * Our policy Catchers
         */
index 3d56fc3daa14ee09d89cc853b20b0e700a4becb3..730f965cd08990cfd5c97d790da80a942eecab68 100644 (file)
@@ -193,6 +193,76 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,
        return CMD_SUCCESS;
 }
 
+DEFPY(pbr_map_match_ip_proto, pbr_map_match_ip_proto_cmd,
+      "[no] match ip-protocol [tcp|udp]$ip_proto",
+      NO_STR
+      "Match the rest of the command\n"
+      "Choose an ip-protocol\n"
+      "Match on tcp flows\n"
+      "Match on udp flows\n")
+{
+       struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+       struct protoent *p;
+
+       if (!no) {
+               p = getprotobyname(ip_proto);
+               if (!p) {
+                       vty_out(vty, "Unable to convert %s to proto id\n",
+                               ip_proto);
+                       return CMD_WARNING;
+               }
+
+               pbrms->ip_proto = p->p_proto;
+       } else
+               pbrms->ip_proto = 0;
+
+       return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_src_port, pbr_map_match_src_port_cmd,
+      "[no] match src-port (1-65535)$port",
+      NO_STR
+      "Match the rest of the command\n"
+      "Choose the source port to use\n"
+      "The Source Port\n")
+{
+       struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+       if (!no) {
+               if (pbrms->src_prt == port)
+                       return CMD_SUCCESS;
+               else
+                       pbrms->src_prt = port;
+       } else
+               pbrms->src_prt = 0;
+
+       pbr_map_check(pbrms, true);
+
+       return CMD_SUCCESS;
+}
+
+DEFPY(pbr_map_match_dst_port, pbr_map_match_dst_port_cmd,
+      "[no] match dst-port (1-65535)$port",
+      NO_STR
+      "Match the rest of the command\n"
+      "Choose the destination port to use\n"
+      "The Destination Port\n")
+{
+       struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence);
+
+       if (!no) {
+               if (pbrms->dst_prt == port)
+                       return CMD_SUCCESS;
+               else
+                       pbrms->dst_prt = port;
+       } else
+               pbrms->dst_prt = 0;
+
+       pbr_map_check(pbrms, true);
+
+       return CMD_SUCCESS;
+}
+
 DEFPY(pbr_map_match_dscp, pbr_map_match_dscp_cmd,
       "[no] match dscp DSCP$dscp",
       NO_STR
@@ -674,6 +744,13 @@ static void vty_show_pbrms(struct vty *vty,
                        pbrms->installed ? "yes" : "no",
                        pbrms->reason ? rbuf : "Valid");
 
+       if (pbrms->ip_proto) {
+               struct protoent *p;
+
+               p = getprotobynumber(pbrms->ip_proto);
+               vty_out(vty, "        IP Protocol Match: %s\n", p->p_name);
+       }
+
        if (pbrms->src)
                vty_out(vty, "        SRC Match: %pFX\n", pbrms->src);
        if (pbrms->dst)
@@ -1079,6 +1156,18 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty,
        if (pbrms->dst)
                vty_out(vty, " match dst-ip %pFX\n", pbrms->dst);
 
+       if (pbrms->src_prt)
+               vty_out(vty, " match src-port %u\n", pbrms->src_prt);
+       if (pbrms->dst_prt)
+               vty_out(vty, " match dst-port %u\n", pbrms->dst_prt);
+
+       if (pbrms->ip_proto) {
+               struct protoent *p;
+
+               p = getprotobynumber(pbrms->ip_proto);
+               vty_out(vty, " match ip-protocol %s\n", p->p_name);
+       }
+
        if (pbrms->dsfield & PBR_DSFIELD_DSCP)
                vty_out(vty, " match dscp %u\n",
                        (pbrms->dsfield & PBR_DSFIELD_DSCP) >> 2);
@@ -1169,6 +1258,9 @@ void pbr_vty_init(void)
        install_element(CONFIG_NODE, &pbr_set_table_range_cmd);
        install_element(CONFIG_NODE, &no_pbr_set_table_range_cmd);
        install_element(INTERFACE_NODE, &pbr_policy_cmd);
+       install_element(PBRMAP_NODE, &pbr_map_match_ip_proto_cmd);
+       install_element(PBRMAP_NODE, &pbr_map_match_src_port_cmd);
+       install_element(PBRMAP_NODE, &pbr_map_match_dst_port_cmd);
        install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);
        install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd);
        install_element(PBRMAP_NODE, &pbr_map_match_dscp_cmd);
index fc5303c9d81ee01bb7f9b62ff752f3ddc9065e06..28def509d5eb518c626bb8aeb829edab8cbbdec7 100644 (file)
@@ -534,10 +534,11 @@ static void pbr_encode_pbr_map_sequence(struct stream *s,
        stream_putl(s, pbrms->seqno);
        stream_putl(s, pbrms->ruleno);
        stream_putl(s, pbrms->unique);
+       stream_putc(s, pbrms->ip_proto); /* The ip_proto */
        pbr_encode_pbr_map_sequence_prefix(s, pbrms->src, family);
-       stream_putw(s, 0);  /* src port */
+       stream_putw(s, pbrms->src_prt);
        pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);
-       stream_putw(s, 0);  /* dst port */
+       stream_putw(s, pbrms->dst_prt);
        stream_putc(s, pbrms->dsfield);
        stream_putl(s, pbrms->mark);
 
index cc11a3cc172b01d5345cdfa80544625a538ab023..37d206cc1179a24ec4dd004cde361c69bb5004ef 100644 (file)
@@ -497,6 +497,7 @@ static void igmp_show_interfaces(struct pim_instance *pim, struct vty *vty,
        struct interface *ifp;
        time_t now;
        char buf[PREFIX_STRLEN];
+       char quer_buf[PREFIX_STRLEN];
        json_object *json = NULL;
        json_object *json_row = NULL;
 
@@ -506,7 +507,7 @@ static void igmp_show_interfaces(struct pim_instance *pim, struct vty *vty,
                json = json_object_new_object();
        else
                vty_out(vty,
-                       "Interface         State          Address  V  Querier  Query Timer    Uptime\n");
+                       "Interface         State          Address  V  Querier          QuerierIp  Query Timer    Uptime\n");
 
        FOR_ALL_INTERFACES (pim->vrf, ifp) {
                struct pim_interface *pim_ifp;
@@ -544,6 +545,10 @@ static void igmp_show_interfaces(struct pim_instance *pim, struct vty *vty,
                                                               "queryTimer",
                                                               query_hhmmss);
                                }
+                               json_object_string_add(
+                                       json_row, "querierIp",
+                                       inet_ntop(AF_INET, &igmp->querier_addr,
+                                                 quer_buf, sizeof(quer_buf)));
 
                                json_object_object_add(json, ifp->name,
                                                       json_row);
@@ -554,18 +559,19 @@ static void igmp_show_interfaces(struct pim_instance *pim, struct vty *vty,
                                }
                        } else {
                                vty_out(vty,
-                                       "%-16s  %5s  %15s  %d  %7s  %11s  %8s\n",
+                                       "%-16s  %5s  %15s  %d  %7s  %17pI4  %11s  %8s\n",
                                        ifp->name,
                                        if_is_up(ifp)
-                                       ? (igmp->mtrace_only ? "mtrc"
-                                          : "up")
-                                       : "down",
-                                       inet_ntop(AF_INET, &igmp->ifaddr,
-                                                 buf, sizeof(buf)),
+                                               ? (igmp->mtrace_only ? "mtrc"
+                                                                    : "up")
+                                               : "down",
+                                       inet_ntop(AF_INET, &igmp->ifaddr, buf,
+                                                 sizeof(buf)),
                                        pim_ifp->igmp_version,
                                        igmp->t_igmp_query_timer ? "local"
-                                       : "other",
-                                       query_hhmmss, uptime);
+                                                                : "other",
+                                       &igmp->querier_addr, query_hhmmss,
+                                       uptime);
                        }
                }
        }
@@ -586,6 +592,7 @@ static void igmp_show_interfaces_single(struct pim_instance *pim,
        struct listnode *sock_node;
        struct pim_interface *pim_ifp;
        char uptime[10];
+       char quer_buf[PREFIX_STRLEN];
        char query_hhmmss[10];
        char other_hhmmss[10];
        int found_ifname = 0;
@@ -670,6 +677,10 @@ static void igmp_show_interfaces_single(struct pim_instance *pim,
                                                       igmp->t_igmp_query_timer
                                                       ? "local"
                                                       : "other");
+                               json_object_string_add(
+                                       json_row, "querierIp",
+                                       inet_ntop(AF_INET, &igmp->querier_addr,
+                                                 quer_buf, sizeof(quer_buf)));
                                json_object_int_add(json_row, "queryStartCount",
                                                    igmp->startup_query_count);
                                json_object_string_add(json_row,
@@ -739,6 +750,14 @@ static void igmp_show_interfaces_single(struct pim_instance *pim,
                                vty_out(vty, "Querier     : %s\n",
                                        igmp->t_igmp_query_timer ? "local"
                                        : "other");
+                               vty_out(vty, "QuerierIp   : %pI4",
+                                       &igmp->querier_addr);
+                               if (pim_ifp->primary_address.s_addr
+                                   == igmp->querier_addr.s_addr)
+                                       vty_out(vty, " (this router)\n");
+                               else
+                                       vty_out(vty, "\n");
+
                                vty_out(vty, "Start Count : %d\n",
                                        igmp->startup_query_count);
                                vty_out(vty, "Query Timer : %s\n",
@@ -1135,6 +1154,12 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
                        json_object_int_add(
                                json_row, "overrideIntervalHighest",
                                pim_ifp->pim_neighbors_highest_override_interval_msec);
+                       if (pim_ifp->bsm_enable)
+                               json_object_boolean_true_add(json_row,
+                                                            "bsmEnabled");
+                       if (pim_ifp->ucast_bsm_accept)
+                               json_object_boolean_true_add(json_row,
+                                                            "ucastBsmEnabled");
                        json_object_object_add(json, ifp->name, json_row);
 
                } else {
@@ -1289,6 +1314,15 @@ static void pim_show_interfaces_single(struct pim_instance *pim,
                                pim_ifp->pim_neighbors_highest_override_interval_msec);
                        vty_out(vty, "\n");
                        vty_out(vty, "\n");
+
+                       vty_out(vty, "BSM Status\n");
+                       vty_out(vty, "----------\n");
+                       vty_out(vty, "Bsm Enabled          : %s\n",
+                               pim_ifp->bsm_enable ? "yes" : "no");
+                       vty_out(vty, "Unicast Bsm Enabled  : %s\n",
+                               pim_ifp->ucast_bsm_accept ? "yes" : "no");
+                       vty_out(vty, "\n");
+                       vty_out(vty, "\n");
                }
        }
 
@@ -7153,7 +7187,7 @@ DEFPY (pim_register_accept_list,
 
 DEFUN (ip_pim_joinprune_time,
        ip_pim_joinprune_time_cmd,
-       "ip pim join-prune-interval (60-600)",
+       "ip pim join-prune-interval (5-600)",
        IP_STR
        "pim multicast routing\n"
        "Join Prune Send Interval\n"
@@ -7167,7 +7201,7 @@ DEFUN (ip_pim_joinprune_time,
 
 DEFUN (no_ip_pim_joinprune_time,
        no_ip_pim_joinprune_time_cmd,
-       "no ip pim join-prune-interval (60-600)",
+       "no ip pim join-prune-interval (5-600)",
        NO_STR
        IP_STR
        "pim multicast routing\n"
@@ -8752,7 +8786,7 @@ DEFUN (interface_no_ip_mroute,
 
 DEFUN (interface_ip_pim_hello,
        interface_ip_pim_hello_cmd,
-       "ip pim hello (1-180) [(1-180)]",
+       "ip pim hello (1-180) [(1-630)]",
        IP_STR
        PIM_STR
        IFACE_PIM_HELLO_STR
@@ -8787,7 +8821,7 @@ DEFUN (interface_ip_pim_hello,
 
 DEFUN (interface_no_ip_pim_hello,
        interface_no_ip_pim_hello_cmd,
-       "no ip pim hello [(1-180) [(1-180)]]",
+       "no ip pim hello [(1-180) [(1-630)]]",
        NO_STR
        IP_STR
        PIM_STR
index 48b019c8c8877a85c13f61292a01e338f10ce7af..0b28a3e84c36e6d2d2a95e0d4fbe6ed1a6867d3b 100644 (file)
@@ -1512,10 +1512,15 @@ struct prefix *pim_if_connected_to_source(struct interface *ifp, struct in_addr
        p.prefixlen = IPV4_MAX_BITLEN;
 
        for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
-               if ((c->address->family == AF_INET)
-                   && prefix_match(CONNECTED_PREFIX(c), &p)) {
-                       return CONNECTED_PREFIX(c);
-               }
+               if (c->address->family != AF_INET)
+                       continue;
+               if (prefix_match(c->address, &p))
+                       return c->address;
+               if (CONNECTED_PEER(c) && prefix_match(c->destination, &p))
+                       /* this is not a typo, on PtP we need to return the
+                        * *local* address that lines up with src.
+                        */
+                       return c->address;
        }
 
        return NULL;
index 477cf991b4684c6119c1264989918d3622e4e43a..71b2d9187ae19815d7491a8143e1e31d0a485d0e 100644 (file)
@@ -167,6 +167,8 @@ static int pim_igmp_other_querier_expire(struct thread *t)
                               sizeof(ifaddr_str));
                zlog_debug("%s: Querier %s resuming", __func__, ifaddr_str);
        }
+       /* Mark the interface address as querier address */
+       igmp->querier_addr = igmp->ifaddr;
 
        /*
          We are the current querier, then
@@ -397,6 +399,8 @@ static int igmp_recv_query(struct igmp_sock *igmp, int query_version,
                                ntohl(igmp->ifaddr.s_addr), from_str,
                                ntohl(from.s_addr));
                }
+               if (ntohl(from.s_addr) < ntohl(igmp->querier_addr.s_addr))
+                       igmp->querier_addr.s_addr = from.s_addr;
 
                pim_igmp_other_querier_timer_on(igmp);
        }
@@ -469,15 +473,33 @@ static int igmp_v1_recv_report(struct igmp_sock *igmp, struct in_addr from,
        return 0;
 }
 
-bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, int igmp_msg_len,
-                           int msg_type)
+bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
 {
+       char *igmp_msg;
+       int igmp_msg_len;
+       int msg_type;
+       size_t ip_hlen; /* ip header length in bytes */
+
        if (len < sizeof(*ip_hdr)) {
                zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
                          sizeof(*ip_hdr));
                return false;
        }
 
+       ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
+       *hlen = ip_hlen;
+
+       if (ip_hlen > len) {
+               zlog_warn(
+                       "IGMP packet header claims size %zu, but we only have %zu bytes",
+                       ip_hlen, len);
+               return false;
+       }
+
+       igmp_msg = (char *)ip_hdr + ip_hlen;
+       igmp_msg_len = len - ip_hlen;
+       msg_type = *igmp_msg;
+
        if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
                zlog_warn("IGMP message size=%d shorter than minimum=%d",
                          igmp_msg_len, PIM_IGMP_MIN_LEN);
@@ -490,7 +512,7 @@ bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, int igmp_msg_len,
                        zlog_warn(
                                "Recv IGMP packet with invalid ttl=%u, discarding the packet",
                                ip_hdr->ip_ttl);
-                       return -1;
+                       return false;
                }
        }
 
@@ -501,7 +523,7 @@ bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, int igmp_msg_len,
                if (ip_hdr->ip_tos != IPTOS_PREC_INTERNETCONTROL) {
                        zlog_warn("Received IGMP Packet with invalid TOS %u",
                                  ip_hdr->ip_tos);
-                       return -1;
+                       return false;
                }
        }
 
@@ -510,7 +532,7 @@ bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, int igmp_msg_len,
 
 int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
 {
-       struct ip *ip_hdr;
+       struct ip *ip_hdr = (struct ip *)buf;
        size_t ip_hlen; /* ip header length in bytes */
        char *igmp_msg;
        int igmp_msg_len;
@@ -518,16 +540,8 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
        char from_str[INET_ADDRSTRLEN];
        char to_str[INET_ADDRSTRLEN];
 
-       ip_hdr = (struct ip *)buf;
-
-       ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
-
-       if (ip_hlen > len) {
-               zlog_warn(
-                       "IGMP packet header claims size %zu, but we only have %zu bytes",
-                       ip_hlen, len);
+       if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
                return -1;
-       }
 
        igmp_msg = buf + ip_hlen;
        igmp_msg_len = len - ip_hlen;
@@ -543,14 +557,6 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
                        msg_type, igmp_msg_len);
        }
 
-       if (!pim_igmp_verify_header(ip_hdr, len, igmp_msg_len, msg_type)) {
-               zlog_warn(
-                       "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%u msg_type=%d msg_size=%d",
-                       from_str, to_str, igmp->interface->name, len,
-                       ip_hdr->ip_ttl, msg_type, igmp_msg_len);
-               return -1;
-       }
-
        switch (msg_type) {
        case PIM_IGMP_MEMBERSHIP_QUERY: {
                int max_resp_code = igmp_msg[1];
@@ -969,6 +975,7 @@ static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
        igmp->fd = fd;
        igmp->interface = ifp;
        igmp->ifaddr = ifaddr;
+       igmp->querier_addr = ifaddr;
        igmp->t_igmp_read = NULL;
        igmp->t_igmp_query_timer = NULL;
        igmp->t_other_querier_timer = NULL; /* no other querier present */
index 88324b7937545c5b7263e50b9a51132b86d0971d..abb8af836b49802ba1a5f7ab69de321aaa9c16b6 100644 (file)
@@ -92,8 +92,8 @@ struct igmp_sock {
        struct thread
                *t_igmp_query_timer; /* timer: issue IGMP general queries */
        struct thread *t_other_querier_timer; /* timer: other querier present */
-
-       int querier_query_interval;      /* QQI */
+       struct in_addr querier_addr;      /* IP address of the querier */
+       int querier_query_interval;        /* QQI */
        int querier_robustness_variable; /* QRV */
        int startup_query_count;
 
@@ -116,8 +116,7 @@ void igmp_sock_delete(struct igmp_sock *igmp);
 void igmp_sock_free(struct igmp_sock *igmp);
 void igmp_sock_delete_all(struct interface *ifp);
 int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len);
-bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, int igmp_msg_len,
-                           int msg_type);
+bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *ip_hlen);
 void pim_igmp_general_query_on(struct igmp_sock *igmp);
 void pim_igmp_general_query_off(struct igmp_sock *igmp);
 void pim_igmp_other_querier_timer_on(struct igmp_sock *igmp);
index dfdbd6dee204e1fea5108d154a74d5673cdd5162..bd5e2150274c601cf73920e642eb1d4a980537ac 100644 (file)
@@ -1613,7 +1613,7 @@ int lib_interface_pim_hello_holdtime_modify(struct nb_cb_modify_args *args)
                ifp = nb_running_get_entry(args->dnode, NULL, true);
                pim_ifp = ifp->info;
                pim_ifp->pim_default_holdtime =
-                       yang_dnode_get_uint8(args->dnode, NULL);
+                       yang_dnode_get_uint16(args->dnode, NULL);
                break;
        }
 
index 48b1a30f2d8c4bb9f6a6d78c69ef9c0a8ad0aaa7..571173c62a2478e5cbddacca7e4b15351aa5d292 100644 (file)
@@ -350,8 +350,8 @@ pim_neighbor_new(struct interface *ifp, struct in_addr source_addr,
                           __func__, src_str, ifp->name);
        }
 
-       zlog_info("PIM NEIGHBOR UP: neighbor %s on interface %s", src_str,
-                 ifp->name);
+       zlog_notice("PIM NEIGHBOR UP: neighbor %s on interface %s", src_str,
+                   ifp->name);
 
        if (neigh->propagation_delay_msec
            > pim_ifp->pim_neighbors_highest_propagation_delay_msec) {
@@ -616,8 +616,8 @@ void pim_neighbor_delete(struct interface *ifp, struct pim_neighbor *neigh,
        assert(pim_ifp);
 
        pim_inet4_dump("<src?>", neigh->source_addr, src_str, sizeof(src_str));
-       zlog_info("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s", src_str,
-                 ifp->name, delete_message);
+       zlog_notice("PIM NEIGHBOR DOWN: neighbor %s on interface %s: %s",
+                   src_str, ifp->name, delete_message);
 
        THREAD_OFF(neigh->t_expire_timer);
 
index b6521132f70fe0fca585ecf64cef22b1df36b34d..56e192752819c28ecc677939c8ca2b97e68a7698 100644 (file)
@@ -203,6 +203,26 @@ static struct rp_info *pim_rp_find_exact(struct pim_instance *pim,
        return NULL;
 }
 
+/*
+ * XXX: long-term issue:  we don't actually have a good "ip address-list"
+ * implementation.  ("access-list XYZ" is the closest but honestly it's
+ * kinda garbage.)
+ *
+ * So it's using a prefix-list to match an address here, which causes very
+ * unexpected results for the user since prefix-lists by default only match
+ * when the prefix length is an exact match too.  i.e. you'd have to add the
+ * "le 32" and do "ip prefix-list foo permit 10.0.0.0/24 le 32"
+ *
+ * To avoid this pitfall, this code uses "address_mode = true" for the prefix
+ * list match (this is the only user for that.)
+ *
+ * In the long run, we need to add a "ip address-list", but that's a wholly
+ * separate bag of worms, and existing configs using ip prefix-list would
+ * drop into the UX pitfall.
+ */
+
+#include "lib/plist_int.h"
+
 /*
  * Given a group, return the rp_info for that group
  */
@@ -213,7 +233,8 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
        struct rp_info *best = NULL;
        struct rp_info *rp_info;
        struct prefix_list *plist;
-       const struct prefix *p, *bp;
+       const struct prefix *bp;
+       const struct prefix_list_entry *entry;
        struct route_node *rn;
 
        bp = NULL;
@@ -221,19 +242,19 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
                if (rp_info->plist) {
                        plist = prefix_list_lookup(AFI_IP, rp_info->plist);
 
-                       if (prefix_list_apply_which_prefix(plist, &p, group)
-                           == PREFIX_DENY)
+                       if (prefix_list_apply_ext(plist, &entry, group, true)
+                           == PREFIX_DENY || !entry)
                                continue;
 
                        if (!best) {
                                best = rp_info;
-                               bp = p;
+                               bp = &entry->prefix;
                                continue;
                        }
 
-                       if (bp && bp->prefixlen < p->prefixlen) {
+                       if (bp && bp->prefixlen < entry->prefix.prefixlen) {
                                best = rp_info;
-                               bp = p;
+                               bp = &entry->prefix;
                        }
                }
        }
index 504519c8a41006bda36cde59f105ea7106d1d001..05b0f92a4b65fce0fc5e353f8043ba87749344b1 100644 (file)
@@ -112,17 +112,15 @@ int pim_socket_mcast(int protocol, struct in_addr ifaddr, struct interface *ifp,
        }
 
 #ifdef SO_BINDTODEVICE
-       if (protocol == IPPROTO_PIM) {
-               int ret;
+       int ret;
 
-               ret = pim_socket_bind(fd, ifp);
-               if (ret) {
-                       close(fd);
-                       zlog_warn(
-                               "Could not set fd: %d for interface: %s to device",
-                               fd, ifp->name);
-                       return PIM_SOCK_ERR_BIND;
-               }
+       ret = pim_socket_bind(fd, ifp);
+       if (ret) {
+               close(fd);
+               zlog_warn(
+                       "Could not set fd: %d for interface: %s to device",
+                       fd, ifp->name);
+               return PIM_SOCK_ERR_BIND;
        }
 #else
 /* XXX: use IP_PKTINFO / IP_RECVIF to emulate behaviour?  Or change to
index 704efc5930f54d986408c5ff4391e28473ef8fc2..e897822ecc1205dcf43a3145038d986b895fc075 100644 (file)
@@ -4,7 +4,7 @@ endif
 
 qpb_libfrr_pb_la_CPPFLAGS = $(AM_CPPFLAGS) $(PROTOBUF_C_CFLAGS)
 qpb_libfrr_pb_la_LIBADD = $(PROTOBUF_C_LIBS)
-qpb_libfrr_pb_la_LDFLAGS = -version-info 0:0:0
+qpb_libfrr_pb_la_LDFLAGS = $(LIB_LDFLAGS) -version-info 0:0:0
 
 qpb_libfrr_pb_la_SOURCES = \
        qpb/qpb.c \
index 8de0fc4b5a10d2dbb3f03b34f8a8a88456d62237..b43e369ab28fba63fea1d481d61fcedd74f8ac72 100644 (file)
@@ -57,5 +57,5 @@ nodist_ripd_ripd_SOURCES = \
 
 ripd_ripd_snmp_la_SOURCES = ripd/rip_snmp.c
 ripd_ripd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-ripd_ripd_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+ripd_ripd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
 ripd_ripd_snmp_la_LIBADD = lib/libfrrsnmp.la
index 3fad1b081320e3f96e87394a7d87a4783496c6b3..498d7dd0b7810a1b7185c82d0e03504b74dbd866 100644 (file)
@@ -36,6 +36,7 @@
 /lib/test_nexthop
 /lib/test_nexthop_iter
 /lib/test_ntop
+/lib/test_plist
 /lib/test_prefix2str
 /lib/test_printfrr
 /lib/test_privs
index 49bc0f4fb23abbf72bfff23167428d3731683b86..8be81cc4cb1ece3d326f85c17548cabda1019829 100644 (file)
@@ -59,10 +59,13 @@ static void vty_do_exit(int isexit)
                exit(0);
 }
 
+const struct frr_yang_module_info *const *test_yang_modules = NULL;
+
 /* main routine. */
 int main(int argc, char **argv)
 {
        struct thread thread;
+       size_t yangcount;
 
        /* Set umask before anything for security */
        umask(0027);
@@ -79,7 +82,11 @@ int main(int argc, char **argv)
 
        vty_init(master, false);
        lib_cmd_init();
-       nb_init(master, NULL, 0, false);
+
+       for (yangcount = 0; test_yang_modules && test_yang_modules[yangcount];
+            yangcount++)
+               ;
+       nb_init(master, test_yang_modules, yangcount, false);
 
        test_init(argc, argv);
 
index 15abe3b855905c745fd9ef492f0f095e6d8132aa..3042ff5b12696320f1415434ad22bf25ffa6aa7c 100644 (file)
@@ -25,6 +25,9 @@
 #include "zebra.h"
 #include "vty.h"
 #include "command.h"
+#include "northbound.h"
+
+extern const struct frr_yang_module_info *const *test_yang_modules;
 
 /* function to be implemented by test */
 extern void test_init(int argc, char **argv);
diff --git a/tests/lib/test_plist.c b/tests/lib/test_plist.c
new file mode 100644 (file)
index 0000000..ee7a9eb
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Simple prefix list querying tool
+ *
+ * Copyright (C) 2021 by David Lamparter,
+ *                   for Open Source Routing / NetDEF, Inc.
+ *
+ * Quagga 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.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "lib/plist.h"
+#include "lib/filter.h"
+#include "tests/lib/cli/common_cli.h"
+
+static const struct frr_yang_module_info *const my_yang_modules[] = {
+       &frr_filter_info,
+       NULL,
+};
+
+__attribute__((_CONSTRUCTOR(2000)))
+static void test_yang_modules_set(void)
+{
+       test_yang_modules = my_yang_modules;
+}
+
+void test_init(int argc, char **argv)
+{
+       prefix_list_init();
+       filter_cli_init();
+
+       /* nothing else to do here, giving stand-alone access to the prefix
+        * list code's "debug prefix-list ..." command is the only purpose of
+        * this "test".
+        */
+}
index c2153140f5403e2c3db29518599de7f0066ba64c..86c1aa42842db8ad28f537814370b063bab1b1ac 100644 (file)
@@ -87,6 +87,7 @@ check_PROGRAMS = \
        tests/lib/test_nexthop_iter \
        tests/lib/test_nexthop \
        tests/lib/test_ntop \
+       tests/lib/test_plist \
        tests/lib/test_prefix2str \
        tests/lib/test_printfrr \
        tests/lib/test_privs \
@@ -344,6 +345,10 @@ tests_lib_test_ntop_CFLAGS = $(TESTS_CFLAGS)
 tests_lib_test_ntop_CPPFLAGS = $(CPPFLAGS_BASE) # no assert override
 tests_lib_test_ntop_LDADD = # none
 tests_lib_test_ntop_SOURCES = tests/lib/test_ntop.c tests/helpers/c/prng.c
+tests_lib_test_plist_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_plist_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_lib_test_plist_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_plist_SOURCES = tests/lib/test_plist.c tests/lib/cli/common_cli.c
 tests_lib_test_prefix2str_CFLAGS = $(TESTS_CFLAGS)
 tests_lib_test_prefix2str_CPPFLAGS = $(TESTS_CPPFLAGS)
 tests_lib_test_prefix2str_LDADD = $(ALL_TESTS_LDADD)
index 06113bdd2a34486713291158a174f30d785f9085..a6366204e8aeacb08d90c89f72690ff262b2d3a5 100644 (file)
@@ -6,4 +6,17 @@ bgp community alias 65001:1:1 large-community-r2-1
 router bgp 65001
  no bgp ebgp-requires-policy
  neighbor 192.168.1.2 remote-as external
+ address-family ipv4 unicast
+  redistribute connected
+  neighbor 192.168.1.2 route-map r2 in
+ exit-address-family
+!
+route-map r2 permit 10
+ match alias community-r2-1
+ set tag 10
+route-map r2 permit 20
+ match alias community-r2-2
+ set tag 20
+route-map r2 permit 30
+ set tag 100
 !
index fc67ff2ad2262a86b051ba6b4094118debe8f373..9276fe592dda9341bcdca0924fe56d7d5d9a24e4 100644 (file)
@@ -8,6 +8,7 @@ router bgp 65002
 !
 ip prefix-list p1 permit 172.16.16.1/32
 ip prefix-list p2 permit 172.16.16.2/32
+ip prefix-list p3 permit 172.16.16.3/32
 !
 route-map r1 permit 10
  match ip address prefix-list p1
@@ -16,4 +17,6 @@ route-map r1 permit 10
 route-map r1 permit 20
  match ip address prefix-list p2
  set community 65002:1 65002:2
+route-map r1 permit 30
+ match ip address prefix-list p3
 !
index a806628a8e5f91f0f82a037f0edd95c2f49f46c1..b8cb9baf3c3a21c3e33efa4d7ef110929c810b12 100644 (file)
@@ -2,6 +2,7 @@
 int lo
  ip address 172.16.16.1/32
  ip address 172.16.16.2/32
+ ip address 172.16.16.3/32
 !
 int r2-eth0
  ip address 192.168.1.2/24
index 90eeaaa7318f4331134366770a0006bbd330a5e0..c41ba810f18e956b1a0391b9e4d00529dfcebb28 100644 (file)
@@ -84,39 +84,57 @@ def test_bgp_community_alias():
     router = tgen.gears["r1"]
 
     def _bgp_converge(router):
-        output = json.loads(
-            router.vtysh_cmd("show bgp ipv4 unicast 172.16.16.1/32 json")
-        )
+        output = json.loads(router.vtysh_cmd("show ip route json"))
         expected = {
-            "paths": [
+            "172.16.16.1/32": [
+                {
+                    "tag": 10,
+                    "communities": "community-r2-1 65001:2",
+                    "largeCommunities": "large-community-r2-1 65001:1:2",
+                }
+            ],
+            "172.16.16.2/32": [
+                {
+                    "tag": 20,
+                    "communities": "65002:1 community-r2-2",
+                    "largeCommunities": "",
+                }
+            ],
+            "172.16.16.3/32": [
                 {
-                    "community": {"string": "community-r2-1 65001:2"},
-                    "largeCommunity": {"string": "large-community-r2-1 65001:1:2"},
+                    "tag": 100,
+                    "communities": "",
+                    "largeCommunities": "",
                 }
-            ]
+            ],
         }
         return topotest.json_cmp(output, expected)
 
     test_func = functools.partial(_bgp_converge, router)
     success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
-    assert result is None, 'Cannot see BGP community aliases "{}"'.format(router)
+    assert result is None, "Cannot see BGP community aliases at r1"
 
     def _bgp_show_prefixes_by_alias(router):
         output = json.loads(
-            router.vtysh_cmd("show bgp ipv4 unicast alias community-r2-2 json detail")
+            router.vtysh_cmd(
+                "show bgp ipv4 unicast alias large-community-r2-1 json detail"
+            )
         )
         expected = {
             "routes": {
-                "172.16.16.2/32": [{"community": {"string": "65002:1 community-r2-2"}}]
+                "172.16.16.1/32": [
+                    {
+                        "community": {"string": "community-r2-1 65001:2"},
+                        "largeCommunity": {"string": "large-community-r2-1 65001:1:2"},
+                    }
+                ]
             }
         }
         return topotest.json_cmp(output, expected)
 
     test_func = functools.partial(_bgp_show_prefixes_by_alias, router)
     success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
-    assert result is None, 'Cannot see BGP prefixes by community alias "{}"'.format(
-        router
-    )
+    assert result is None, "Cannot see BGP prefixes by community alias at r1"
 
 
 if __name__ == "__main__":
index 293e25208629cd13728db0fe91d1ac0d2267abd2..d85f33d1fc2972fedfb4b0ab6127286136640708 100644 (file)
@@ -2,6 +2,7 @@ debug pim events
 debug pim nht
 debug pim zebra
 ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
 !
 int lo
   ip pim
index 08d5a19a2acf4bd316eb5d3e2c07cea6ea79cb99..d775b800b3337508238ac1da01cd723b9ec75da2 100644 (file)
@@ -1,4 +1,5 @@
 ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
 !
 int lo
   ip pim
index 56adda5cc47de46e73290e39013030b5c112e296..12c6d6f85c0fa015d3bd8f4bab705e5ab473c17a 100644 (file)
@@ -1,4 +1,5 @@
 ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
 !
 int lo
   ip pim
index 6a02e501270257b7c5ab0de66a2c0fcf81a4e07d..07bb5153ab8f9ab54cfb841a436b7baf0c485246 100644 (file)
@@ -1859,7 +1859,7 @@ def create_interfaces_cfg(tgen, topo, build=False):
                     )
                 if "ospf6" in data:
                     interface_data += _create_interfaces_ospf_cfg(
-                        "ospf6", c_data, data, ospf_keywords
+                        "ospf6", c_data, data, ospf_keywords + ["area"]
                     )
 
             result = create_common_configuration(
index dc9fe0fccaeb008e328e7d55cbb7a3e19b05f3e4..6aa7a2c0a94c22140d0278bd0f77e432efe160f4 100644 (file)
@@ -28,6 +28,7 @@ from time import sleep
 from lib.topolog import logger
 from lib.topotest import frr_unicode
 from ipaddress import IPv6Address
+
 # Import common_config to use commomnly used APIs
 from lib.common_config import (
     create_common_configuration,
@@ -89,8 +90,7 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru
             logger.debug("Router %s: 'ospf' not present in input_dict", router)
             continue
 
-        result = __create_ospf_global(
-            tgen, input_dict, router, build, load_config)
+        result = __create_ospf_global(tgen, input_dict, router, build, load_config)
         if result is True:
             ospf_data = input_dict[router]["ospf"]
 
@@ -100,7 +100,8 @@ def create_router_ospf(tgen, topo, input_dict=None, build=False, load_config=Tru
             continue
 
         result = __create_ospf_global(
-            tgen, input_dict, router, build, load_config, ospf='ospf6')
+            tgen, input_dict, router, build, load_config, ospf="ospf6"
+        )
         if result is True:
             ospf_data = input_dict[router]["ospf6"]
 
@@ -172,7 +173,6 @@ def __create_ospf_global(
 
         config_data.append(cmd)
 
-
         # router id
         router_id = ospf_data.setdefault("router_id", None)
         del_router_id = ospf_data.setdefault("del_router_id", False)
@@ -187,8 +187,7 @@ def __create_ospf_global(
         if del_log_adj_changes:
             config_data.append("no log-adjacency-changes detail")
         if log_adj_changes:
-            config_data.append("log-adjacency-changes {}".format(
-                log_adj_changes))
+            config_data.append("log-adjacency-changes {}".format(log_adj_changes))
 
         # aggregation timer
         aggr_timer = ospf_data.setdefault("aggr_timer", None)
@@ -196,8 +195,7 @@ def __create_ospf_global(
         if del_aggr_timer:
             config_data.append("no aggregation timer")
         if aggr_timer:
-            config_data.append("aggregation timer {}".format(
-                aggr_timer))
+            config_data.append("aggregation timer {}".format(aggr_timer))
 
         # maximum path information
         ecmp_data = ospf_data.setdefault("maximum-paths", {})
@@ -245,12 +243,13 @@ def __create_ospf_global(
                         cmd = "no {}".format(cmd)
                     config_data.append(cmd)
 
-        #def route information
+        # def route information
         def_rte_data = ospf_data.setdefault("default-information", {})
         if def_rte_data:
             if "originate" not in def_rte_data:
-                logger.debug("Router %s: 'originate key' not present in "
-                            "input_dict", router)
+                logger.debug(
+                    "Router %s: 'originate key' not present in " "input_dict", router
+                )
             else:
                 cmd = "default-information originate"
 
@@ -261,12 +260,10 @@ def __create_ospf_global(
                     cmd = cmd + " metric {}".format(def_rte_data["metric"])
 
                 if "metric-type" in def_rte_data:
-                    cmd = cmd + " metric-type {}".format(def_rte_data[
-                        "metric-type"])
+                    cmd = cmd + " metric-type {}".format(def_rte_data["metric-type"])
 
                 if "route-map" in def_rte_data:
-                    cmd = cmd + " route-map {}".format(def_rte_data[
-                        "route-map"])
+                    cmd = cmd + " route-map {}".format(def_rte_data["route-map"])
 
                 del_action = def_rte_data.setdefault("delete", False)
                 if del_action:
@@ -288,19 +285,19 @@ def __create_ospf_global(
                         config_data.append(cmd)
 
                     try:
-                        if "area" in input_dict[router]['links'][neighbor][
-                            'ospf6']:
+                        if "area" in input_dict[router]["links"][neighbor]["ospf6"]:
                             iface = input_dict[router]["links"][neighbor]["interface"]
                             cmd = "interface {} area {}".format(
-                                iface, input_dict[router]['links'][neighbor][
-                            'ospf6']['area'])
-                            if input_dict[router]['links'][neighbor].setdefault(
-                                "delete", False):
+                                iface,
+                                input_dict[router]["links"][neighbor]["ospf6"]["area"],
+                            )
+                            if input_dict[router]["links"][neighbor].setdefault(
+                                "delete", False
+                            ):
                                 cmd = "no {}".format(cmd)
                             config_data.append(cmd)
                     except KeyError:
-                            pass
-
+                        pass
 
         # summary information
         summary_data = ospf_data.setdefault("summary-address", {})
@@ -420,6 +417,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=
     True or False
     """
     logger.debug("Enter lib config_ospf_interface")
+    result = False
     if not input_dict:
         input_dict = deepcopy(topo)
     else:
@@ -502,7 +500,7 @@ def config_ospf_interface(tgen, topo, input_dict=None, build=False, load_config=
             # interface ospf mtu
             if data_ospf_mtu:
                 cmd = "ip ospf mtu-ignore"
-                if 'del_action' in ospf_data:
+                if "del_action" in ospf_data:
                     cmd = "no {}".format(cmd)
                 config_data.append(cmd)
 
@@ -543,8 +541,7 @@ def clear_ospf(tgen, router, ospf=None):
         version = "ip"
 
     cmd = "clear {} ospf interface".format(version)
-    logger.info(
-        "Clearing ospf process on router %s.. using command '%s'", router, cmd)
+    logger.info("Clearing ospf process on router %s.. using command '%s'", router, cmd)
     run_frr_cmd(rnode, cmd)
 
     logger.debug("Exiting lib API: clear_ospf()")
@@ -774,7 +771,7 @@ def verify_ospf_neighbor(tgen, topo, dut=None, input_dict=None, lan=False, expec
 ################################
 # Verification procs
 ################################
-@retry(retry_timeout=20)
+@retry(retry_timeout=50)
 def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
     """
     This API is to verify ospf neighborship by running
@@ -825,105 +822,133 @@ def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
 
     if input_dict:
         for router, rnode in tgen.routers().items():
-            if 'ospf6' not in topo['routers'][router]:
+            if "ospf6" not in topo["routers"][router]:
                 continue
 
             if dut is not None and dut != router:
                 continue
 
             logger.info("Verifying OSPF neighborship on router %s:", router)
-            show_ospf_json = run_frr_cmd(rnode,
-                "show ipv6 ospf neighbor json", isjson=True)
+            show_ospf_json = run_frr_cmd(
+                rnode, "show ipv6 ospf neighbor json", isjson=True
+            )
             # Verifying output dictionary show_ospf_json is empty or not
             if not bool(show_ospf_json):
                 errormsg = "OSPF6 is not running"
                 return errormsg
 
             ospf_data_list = input_dict[router]["ospf6"]
-            ospf_nbr_list = ospf_data_list['neighbors']
+            ospf_nbr_list = ospf_data_list["neighbors"]
 
             for ospf_nbr, nbr_data in ospf_nbr_list.items():
-                data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id']
+
+                try:
+                    data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
+                except KeyError:
+                    data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][
+                        "router_id"
+                    ]
+
                 if ospf_nbr in data_ip:
                     nbr_details = nbr_data[ospf_nbr]
                 elif lan:
-                    for switch in topo['switches']:
-                        if 'ospf6' in topo['switches'][switch]['links'][router]:
+                    for switch in topo["switches"]:
+                        if "ospf6" in topo["switches"][switch]["links"][router]:
                             neighbor_ip = data_ip
                         else:
                             continue
                 else:
-                    neighbor_ip = data_ip[router]['ipv6'].split("/")[0]
+                    neighbor_ip = data_ip[router]["ipv6"].split("/")[0]
 
                 nh_state = None
                 neighbor_ip = neighbor_ip.lower()
                 nbr_rid = data_rid
-                get_index_val = dict((d['neighborId'], dict( \
-                        d, index=index)) for (index, d) in enumerate( \
-                            show_ospf_json['neighbors']))
+                get_index_val = dict(
+                    (d["neighborId"], dict(d, index=index))
+                    for (index, d) in enumerate(show_ospf_json["neighbors"])
+                )
                 try:
-                    nh_state =  get_index_val.get(neighbor_ip)['state']
-                    intf_state = get_index_val.get(neighbor_ip)['ifState']
+                    nh_state = get_index_val.get(neighbor_ip)["state"]
+                    intf_state = get_index_val.get(neighbor_ip)["ifState"]
                 except TypeError:
-                    errormsg = "[DUT: {}] OSPF peer {} missing,from "\
-                        "{} ".format(router,
-                    nbr_rid, ospf_nbr)
+                    errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
+                        router, nbr_rid, ospf_nbr
+                    )
                     return errormsg
 
-                nbr_state = nbr_data.setdefault("state",None)
-                nbr_role = nbr_data.setdefault("role",None)
+                nbr_state = nbr_data.setdefault("state", None)
+                nbr_role = nbr_data.setdefault("role", None)
 
                 if nbr_state:
                     if nbr_state == nh_state:
-                        logger.info("[DUT: {}] OSPF6 Nbr is {}:{} State {}".format
-                        (router, ospf_nbr, nbr_rid, nh_state))
+                        logger.info(
+                            "[DUT: {}] OSPF6 Nbr is {}:{} State {}".format(
+                                router, ospf_nbr, nbr_rid, nh_state
+                            )
+                        )
                         result = True
                     else:
-                        errormsg = ("[DUT: {}] OSPF6 is not Converged, neighbor"
-                        " state is {} , Expected state is {}".format(router,
-                        nh_state, nbr_state))
+                        errormsg = (
+                            "[DUT: {}] OSPF6 is not Converged, neighbor"
+                            " state is {} , Expected state is {}".format(
+                                router, nh_state, nbr_state
+                            )
+                        )
                         return errormsg
                 if nbr_role:
                     if nbr_role == intf_state:
-                        logger.info("[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format(
-                        router, ospf_nbr, nbr_rid, nbr_role))
+                        logger.info(
+                            "[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format(
+                                router, ospf_nbr, nbr_rid, nbr_role
+                            )
+                        )
                     else:
-                        errormsg = ("[DUT: {}] OSPF6 is not Converged with rid"
-                        "{}, role is {}, Expected role is {}".format(router,
-                        nbr_rid, intf_state, nbr_role))
+                        errormsg = (
+                            "[DUT: {}] OSPF6 is not Converged with rid"
+                            "{}, role is {}, Expected role is {}".format(
+                                router, nbr_rid, intf_state, nbr_role
+                            )
+                        )
                         return errormsg
                 continue
     else:
 
         for router, rnode in tgen.routers().items():
-            if 'ospf6' not in topo['routers'][router]:
+            if "ospf6" not in topo["routers"][router]:
                 continue
 
             if dut is not None and dut != router:
                 continue
 
             logger.info("Verifying OSPF6 neighborship on router %s:", router)
-            show_ospf_json = run_frr_cmd(rnode,
-                "show ipv6 ospf neighbor json", isjson=True)
+            show_ospf_json = run_frr_cmd(
+                rnode, "show ipv6 ospf neighbor json", isjson=True
+            )
             # Verifying output dictionary show_ospf_json is empty or not
             if not bool(show_ospf_json):
                 errormsg = "OSPF6 is not running"
                 return errormsg
 
             ospf_data_list = topo["routers"][router]["ospf6"]
-            ospf_neighbors = ospf_data_list['neighbors']
+            ospf_neighbors = ospf_data_list["neighbors"]
             total_peer = 0
             total_peer = len(ospf_neighbors.keys())
             no_of_ospf_nbr = 0
-            ospf_nbr_list = ospf_data_list['neighbors']
+            ospf_nbr_list = ospf_data_list["neighbors"]
             no_of_peer = 0
             for ospf_nbr, nbr_data in ospf_nbr_list.items():
-                data_ip = data_rid = topo['routers'][ospf_nbr]['ospf6']['router_id']
+                try:
+                    data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
+                except KeyError:
+                    data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][
+                        "router_id"
+                    ]
+
                 if ospf_nbr in data_ip:
                     nbr_details = nbr_data[ospf_nbr]
                 elif lan:
-                    for switch in topo['switches']:
-                        if 'ospf6' in topo['switches'][switch]['links'][router]:
+                    for switch in topo["switches"]:
+                        if "ospf6" in topo["switches"][switch]["links"][router]:
                             neighbor_ip = data_ip
                         else:
                             continue
@@ -933,26 +958,27 @@ def verify_ospf6_neighbor(tgen, topo, dut=None, input_dict=None, lan=False):
                 nh_state = None
                 neighbor_ip = neighbor_ip.lower()
                 nbr_rid = data_rid
-                get_index_val = dict((d['neighborId'], dict( \
-                        d, index=index)) for (index, d) in enumerate( \
-                            show_ospf_json['neighbors']))
+                get_index_val = dict(
+                    (d["neighborId"], dict(d, index=index))
+                    for (index, d) in enumerate(show_ospf_json["neighbors"])
+                )
                 try:
-                    nh_state =  get_index_val.get(neighbor_ip)['state']
-                    intf_state = get_index_val.get(neighbor_ip)['ifState']
+                    nh_state = get_index_val.get(neighbor_ip)["state"]
+                    intf_state = get_index_val.get(neighbor_ip)["ifState"]
                 except TypeError:
-                    errormsg = "[DUT: {}] OSPF peer {} missing,from "\
-                        "{} ".format(router,
-                    nbr_rid, ospf_nbr)
+                    errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
+                        router, nbr_rid, ospf_nbr
+                    )
                     return errormsg
 
-                if nh_state == 'Full':
+                if nh_state == "Full":
                     no_of_peer += 1
 
             if no_of_peer == total_peer:
                 logger.info("[DUT: {}] OSPF6 is Converged".format(router))
                 result = True
             else:
-                errormsg = ("[DUT: {}] OSPF6 is not Converged".format(router))
+                errormsg = "[DUT: {}] OSPF6 is not Converged".format(router)
                 return errormsg
 
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -1627,31 +1653,34 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
             found_routes = []
             missing_routes = []
 
-            if "static_routes" in input_dict[routerInput] or \
-                "prefix" in input_dict[routerInput]:
+            if (
+                "static_routes" in input_dict[routerInput]
+                or "prefix" in input_dict[routerInput]
+            ):
                 if "prefix" in input_dict[routerInput]:
                     static_routes = input_dict[routerInput]["prefix"]
                 else:
                     static_routes = input_dict[routerInput]["static_routes"]
 
-
                 for static_route in static_routes:
                     cmd = "{}".format(command)
 
                     cmd = "{} json".format(cmd)
 
-                    ospf_rib_json =  run_frr_cmd(rnode, cmd, isjson=True)
+                    ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True)
 
                     # Fix for PR 2644182
                     try:
-                        ospf_rib_json = ospf_rib_json['routes']
+                        ospf_rib_json = ospf_rib_json["routes"]
                     except KeyError:
                         pass
 
                     # Verifying output dictionary ospf_rib_json is not empty
                     if bool(ospf_rib_json) is False:
-                        errormsg = "[DUT: {}] No routes found in OSPF6 route " \
+                        errormsg = (
+                            "[DUT: {}] No routes found in OSPF6 route "
                             "table".format(router)
+                        )
                         return errormsg
 
                     network = static_route["network"]
@@ -1659,7 +1688,6 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
                     _tag = static_route.setdefault("tag", None)
                     _rtype = static_route.setdefault("routeType", None)
 
-
                     # Generating IPs for verification
                     ip_list = generate_ips(network, no_of_ip)
                     st_found = False
@@ -1668,7 +1696,7 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
                         st_rt = str(ipaddress.ip_network(frr_unicode(st_rt)))
 
                         _addr_type = validate_ip_address(st_rt)
-                        if _addr_type != 'ipv6':
+                        if _addr_type != "ipv6":
                             continue
 
                         if st_rt in ospf_rib_json:
@@ -1681,17 +1709,26 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
                                     next_hop = [next_hop]
 
                                 for mnh in range(0, len(ospf_rib_json[st_rt])):
-                                    if 'fib' in ospf_rib_json[st_rt][
-                                        mnh]["nextHops"][0]:
-                                        found_hops.append([rib_r[
-                                            "ip"] for rib_r in ospf_rib_json[
-                                                st_rt][mnh]["nextHops"]])
+                                    if (
+                                        "fib"
+                                        in ospf_rib_json[st_rt][mnh]["nextHops"][0]
+                                    ):
+                                        found_hops.append(
+                                            [
+                                                rib_r["ip"]
+                                                for rib_r in ospf_rib_json[st_rt][mnh][
+                                                    "nextHops"
+                                                ]
+                                            ]
+                                        )
 
                                 if found_hops[0]:
-                                    missing_list_of_nexthops = \
-                                        set(found_hops[0]).difference(next_hop)
-                                    additional_nexthops_in_required_nhs = \
-                                        set(next_hop).difference(found_hops[0])
+                                    missing_list_of_nexthops = set(
+                                        found_hops[0]
+                                    ).difference(next_hop)
+                                    additional_nexthops_in_required_nhs = set(
+                                        next_hop
+                                    ).difference(found_hops[0])
 
                                     if additional_nexthops_in_required_nhs:
                                         logger.info(
@@ -1699,13 +1736,18 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
                                             "%s is not active for route %s in "
                                             "RIB of router %s\n",
                                             additional_nexthops_in_required_nhs,
-                                            st_rt, dut)
+                                            st_rt,
+                                            dut,
+                                        )
                                         errormsg = (
                                             "Nexthop {} is not active"
                                             " for route {} in RIB of router"
                                             " {}\n".format(
-                                            additional_nexthops_in_required_nhs,
-                                            st_rt, dut))
+                                                additional_nexthops_in_required_nhs,
+                                                st_rt,
+                                                dut,
+                                            )
+                                        )
                                         return errormsg
                                     else:
                                         nh_found = True
@@ -1713,98 +1755,118 @@ def verify_ospf6_rib(tgen, dut, input_dict, next_hop=None,
                             elif next_hop and fib is None:
                                 if type(next_hop) is not list:
                                     next_hop = [next_hop]
-                                found_hops = [rib_r['nextHop'] for rib_r in
-                                              ospf_rib_json[st_rt][
-                                    "nextHops"]]
+                                found_hops = [
+                                    rib_r["nextHop"]
+                                    for rib_r in ospf_rib_json[st_rt]["nextHops"]
+                                ]
 
                                 if found_hops:
-                                    missing_list_of_nexthops = \
-                                        set(found_hops).difference(next_hop)
-                                    additional_nexthops_in_required_nhs = \
-                                        set(next_hop).difference(found_hops)
+                                    missing_list_of_nexthops = set(
+                                        found_hops
+                                    ).difference(next_hop)
+                                    additional_nexthops_in_required_nhs = set(
+                                        next_hop
+                                    ).difference(found_hops)
                                     if additional_nexthops_in_required_nhs:
                                         logger.info(
-                                            "Missing nexthop %s for route"\
-                                        " %s in RIB of router %s\n", \
-                                        additional_nexthops_in_required_nhs,  \
-                                        st_rt, dut)
-                                        errormsg=("Nexthop {} is Missing for "\
-                                        "route {} in RIB of router {}\n".format(
+                                            "Missing nexthop %s for route"
+                                            " %s in RIB of router %s\n",
                                             additional_nexthops_in_required_nhs,
-                                            st_rt, dut))
+                                            st_rt,
+                                            dut,
+                                        )
+                                        errormsg = (
+                                            "Nexthop {} is Missing for "
+                                            "route {} in RIB of router {}\n".format(
+                                                additional_nexthops_in_required_nhs,
+                                                st_rt,
+                                                dut,
+                                            )
+                                        )
                                         return errormsg
                                     else:
                                         nh_found = True
                             if _rtype:
-                                if "destinationType" not in ospf_rib_json[
-                                    st_rt]:
-                                    errormsg = ("[DUT: {}]: destinationType missing"
-                                                "for route {} in OSPF RIB \n".\
-                                                format(dut, st_rt))
+                                if "destinationType" not in ospf_rib_json[st_rt]:
+                                    errormsg = (
+                                        "[DUT: {}]: destinationType missing"
+                                        "for route {} in OSPF RIB \n".format(dut, st_rt)
+                                    )
                                     return errormsg
-                                elif _rtype != ospf_rib_json[st_rt][
-                                    "destinationType"]:
-                                    errormsg = ("[DUT: {}]: destinationType mismatch"
-                                                "for route {} in OSPF RIB \n".\
-                                                format(dut, st_rt))
+                                elif _rtype != ospf_rib_json[st_rt]["destinationType"]:
+                                    errormsg = (
+                                        "[DUT: {}]: destinationType mismatch"
+                                        "for route {} in OSPF RIB \n".format(dut, st_rt)
+                                    )
                                     return errormsg
                                 else:
-                                    logger.info("DUT: {}]: Found destinationType {}"
-                                                "for route {}".\
-                                                format(dut, _rtype, st_rt))
+                                    logger.info(
+                                        "DUT: {}]: Found destinationType {}"
+                                        "for route {}".format(dut, _rtype, st_rt)
+                                    )
                             if tag:
-                                if "tag" not in ospf_rib_json[
-                                    st_rt]:
-                                    errormsg = ("[DUT: {}]: tag is not"
-                                                " present for"
-                                                " route {} in RIB \n".\
-                                                format(dut, st_rt
-                                                ))
+                                if "tag" not in ospf_rib_json[st_rt]:
+                                    errormsg = (
+                                        "[DUT: {}]: tag is not"
+                                        " present for"
+                                        " route {} in RIB \n".format(dut, st_rt)
+                                    )
                                     return errormsg
 
-                                if _tag != ospf_rib_json[
-                                    st_rt]["tag"]:
-                                    errormsg = ("[DUT: {}]: tag value {}"
-                                                " is not matched for"
-                                                " route {} in RIB \n".\
-                                                format(dut, _tag, st_rt,
-                                                ))
+                                if _tag != ospf_rib_json[st_rt]["tag"]:
+                                    errormsg = (
+                                        "[DUT: {}]: tag value {}"
+                                        " is not matched for"
+                                        " route {} in RIB \n".format(
+                                            dut,
+                                            _tag,
+                                            st_rt,
+                                        )
+                                    )
                                     return errormsg
 
                             if metric is not None:
-                                if "type2cost" not in ospf_rib_json[
-                                    st_rt]:
-                                    errormsg = ("[DUT: {}]: metric is"
-                                                " not present for"
-                                                " route {} in RIB \n".\
-                                                format(dut, st_rt))
+                                if "type2cost" not in ospf_rib_json[st_rt]:
+                                    errormsg = (
+                                        "[DUT: {}]: metric is"
+                                        " not present for"
+                                        " route {} in RIB \n".format(dut, st_rt)
+                                    )
                                     return errormsg
 
-                                if metric != ospf_rib_json[
-                                    st_rt]["type2cost"]:
-                                    errormsg = ("[DUT: {}]: metric value "
-                                                "{} is not matched for "
-                                                "route {} in RIB \n".\
-                                                format(dut, metric, st_rt,
-                                                ))
+                                if metric != ospf_rib_json[st_rt]["type2cost"]:
+                                    errormsg = (
+                                        "[DUT: {}]: metric value "
+                                        "{} is not matched for "
+                                        "route {} in RIB \n".format(
+                                            dut,
+                                            metric,
+                                            st_rt,
+                                        )
+                                    )
                                     return errormsg
 
                         else:
                             missing_routes.append(st_rt)
 
                 if nh_found:
-                    logger.info("[DUT: {}]: Found next_hop {} for all OSPF"
-                                " routes in RIB".format(router, next_hop))
+                    logger.info(
+                        "[DUT: {}]: Found next_hop {} for all OSPF"
+                        " routes in RIB".format(router, next_hop)
+                    )
 
                 if len(missing_routes) > 0:
-                    errormsg = ("[DUT: {}]: Missing route in RIB, "
-                                "routes: {}".\
-                                    format(dut, missing_routes))
+                    errormsg = "[DUT: {}]: Missing route in RIB, " "routes: {}".format(
+                        dut, missing_routes
+                    )
                     return errormsg
 
                 if found_routes:
-                    logger.info("[DUT: %s]: Verified routes in RIB, found"
-                                " routes are: %s\n", dut, found_routes)
+                    logger.info(
+                        "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n",
+                        dut,
+                        found_routes,
+                    )
                     result = True
 
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -1855,15 +1917,16 @@ def verify_ospf6_interface(tgen, topo, dut=None,lan=False, input_dict=None):
     result = False
 
     for router, rnode in tgen.routers().iteritems():
-        if 'ospf6' not in topo['routers'][router]:
+        if "ospf6" not in topo["routers"][router]:
             continue
 
         if dut is not None and dut != router:
             continue
 
         logger.info("Verifying OSPF interface on router %s:", router)
-        show_ospf_json = run_frr_cmd(rnode, "show ipv6 ospf interface json",
-                                    isjson=True)
+        show_ospf_json = run_frr_cmd(
+            rnode, "show ipv6 ospf interface json", isjson=True
+        )
 
         # Verifying output dictionary show_ospf_json is empty or not
         if not bool(show_ospf_json):
@@ -1873,32 +1936,49 @@ def verify_ospf6_interface(tgen, topo, dut=None,lan=False, input_dict=None):
         # To find neighbor ip type
         ospf_intf_data = input_dict[router]["links"]
         for ospf_intf, intf_data in ospf_intf_data.items():
-            intf = topo['routers'][router]['links'][ospf_intf]['interface']
-            if  intf in show_ospf_json:
-                for intf_attribute in intf_data['ospf6']:
-                    if intf_data['ospf6'][intf_attribute] is not list:
-                        if intf_data['ospf6'][intf_attribute] ==  show_ospf_json[
-                            intf][intf_attribute]:
-                            logger.info("[DUT: %s] OSPF6 interface %s: %s is %s",
-                            router, intf, intf_attribute, intf_data['ospf6'][
-                                intf_attribute])
-                    elif intf_data['ospf6'][intf_attribute] is list:
+            intf = topo["routers"][router]["links"][ospf_intf]["interface"]
+            if intf in show_ospf_json:
+                for intf_attribute in intf_data["ospf6"]:
+                    if intf_data["ospf6"][intf_attribute] is not list:
+                        if (
+                            intf_data["ospf6"][intf_attribute]
+                            == show_ospf_json[intf][intf_attribute]
+                        ):
+                            logger.info(
+                                "[DUT: %s] OSPF6 interface %s: %s is %s",
+                                router,
+                                intf,
+                                intf_attribute,
+                                intf_data["ospf6"][intf_attribute],
+                            )
+                    elif intf_data["ospf6"][intf_attribute] is list:
                         for addr_list in len(show_ospf_json[intf][intf_attribute]):
-                            if show_ospf_json[intf][intf_attribute][addr_list][
-                                'address'].split('/')[0] == intf_data['ospf6'][
-                                    'internetAddress'][0]['address']:
-                                    break
+                            if (
+                                show_ospf_json[intf][intf_attribute][addr_list][
+                                    "address"
+                                ].split("/")[0]
+                                == intf_data["ospf6"]["internetAddress"][0]["address"]
+                            ):
+                                break
                             else:
-                                errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \
-                                    Expected is {}".format(router, intf, intf_attribute,
-                                    intf_data['ospf6'][intf_attribute], intf_data['ospf6'][
-                                    intf_attribute])
+                                errormsg = "[DUT: {}] OSPF6 interface {}: {} is {}, \
+                                    Expected is {}".format(
+                                    router,
+                                    intf,
+                                    intf_attribute,
+                                    intf_data["ospf6"][intf_attribute],
+                                    intf_data["ospf6"][intf_attribute],
+                                )
                                 return errormsg
                     else:
-                        errormsg= "[DUT: {}] OSPF6 interface {}: {} is {}, \
-                        Expected is {}".format(router, intf, intf_attribute,
-                        intf_data['ospf6'][intf_attribute], intf_data['ospf6'][
-                            intf_attribute])
+                        errormsg = "[DUT: {}] OSPF6 interface {}: {} is {}, \
+                        Expected is {}".format(
+                            router,
+                            intf,
+                            intf_attribute,
+                            intf_data["ospf6"][intf_attribute],
+                            intf_data["ospf6"][intf_attribute],
+                        )
                         return errormsg
         result = True
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
@@ -1956,16 +2036,14 @@ def verify_ospf6_database(tgen, topo, dut, input_dict):
     router = dut
     logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
 
-    if 'ospf' not in topo['routers'][dut]:
-        errormsg = "[DUT: {}] OSPF is not configured on the router.".format(
-            dut)
+    if "ospf" not in topo["routers"][dut]:
+        errormsg = "[DUT: {}] OSPF is not configured on the router.".format(dut)
         return errormsg
 
     rnode = tgen.routers()[dut]
 
     logger.info("Verifying OSPF interface on router %s:", dut)
-    show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json",
-                                isjson=True)
+    show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", isjson=True)
     # Verifying output dictionary show_ospf_json is empty or not
     if not bool(show_ospf_json):
         errormsg = "OSPF is not running"
@@ -1973,167 +2051,209 @@ def verify_ospf6_database(tgen, topo, dut, input_dict):
 
     # for inter and inter lsa's
     ospf_db_data = input_dict.setdefault("areas", None)
-    ospf_external_lsa = input_dict.setdefault(
-        'asExternalLinkStates', None)
+    ospf_external_lsa = input_dict.setdefault("asExternalLinkStates", None)
 
     if ospf_db_data:
-            for ospf_area, area_lsa in ospf_db_data.items():
-                if ospf_area in show_ospf_json['areas']:
-                    if 'routerLinkStates' in area_lsa:
-                        for lsa in area_lsa['routerLinkStates']:
-                            for rtrlsa in show_ospf_json['areas'][ospf_area][
-                                'routerLinkStates']:
-                                if lsa['lsaId'] == rtrlsa['lsaId'] and \
-                                    lsa['advertisedRouter'] == rtrlsa[
-                                        'advertisedRouter']:
-                                                result = True
-                                                break
-                            if result:
-                                logger.info(
-                                    "[DUT: %s]  OSPF LSDB area %s:Router "
-                                    "LSA %s", router, ospf_area, lsa)
+        for ospf_area, area_lsa in ospf_db_data.items():
+            if ospf_area in show_ospf_json["areas"]:
+                if "routerLinkStates" in area_lsa:
+                    for lsa in area_lsa["routerLinkStates"]:
+                        for rtrlsa in show_ospf_json["areas"][ospf_area][
+                            "routerLinkStates"
+                        ]:
+                            if (
+                                lsa["lsaId"] == rtrlsa["lsaId"]
+                                and lsa["advertisedRouter"]
+                                == rtrlsa["advertisedRouter"]
+                            ):
+                                result = True
                                 break
-                        else:
-                            errormsg = \
-                            "[DUT: {}]  OSPF LSDB area {}: expected" \
+                        if result:
+                            logger.info(
+                                "[DUT: %s]  OSPF LSDB area %s:Router " "LSA %s",
+                                router,
+                                ospf_area,
+                                lsa,
+                            )
+                            break
+                    else:
+                        errormsg = (
+                            "[DUT: {}]  OSPF LSDB area {}: expected"
                             " Router LSA is {}".format(router, ospf_area, lsa)
-                            return errormsg
+                        )
+                        return errormsg
 
-                    if 'networkLinkStates' in area_lsa:
-                        for lsa in area_lsa['networkLinkStates']:
-                            for netlsa in show_ospf_json['areas'][ospf_area][
-                                'networkLinkStates']:
-                                if lsa in show_ospf_json['areas'][ospf_area][
-                                    'networkLinkStates']:
-                                    if lsa['lsaId'] == netlsa['lsaId'] and \
-                                    lsa['advertisedRouter'] == netlsa[
-                                        'advertisedRouter']:
-                                                result = True
-                                                break
-                            if result:
-                                logger.info(
-                                    "[DUT: %s]  OSPF LSDB area %s:Network "
-                                    "LSA %s", router, ospf_area, lsa)
-                                break
-                            else:
-                                errormsg = \
-                                "[DUT: {}]  OSPF LSDB area {}: expected" \
+                if "networkLinkStates" in area_lsa:
+                    for lsa in area_lsa["networkLinkStates"]:
+                        for netlsa in show_ospf_json["areas"][ospf_area][
+                            "networkLinkStates"
+                        ]:
+                            if (
+                                lsa
+                                in show_ospf_json["areas"][ospf_area][
+                                    "networkLinkStates"
+                                ]
+                            ):
+                                if (
+                                    lsa["lsaId"] == netlsa["lsaId"]
+                                    and lsa["advertisedRouter"]
+                                    == netlsa["advertisedRouter"]
+                                ):
+                                    result = True
+                                    break
+                        if result:
+                            logger.info(
+                                "[DUT: %s]  OSPF LSDB area %s:Network " "LSA %s",
+                                router,
+                                ospf_area,
+                                lsa,
+                            )
+                            break
+                        else:
+                            errormsg = (
+                                "[DUT: {}]  OSPF LSDB area {}: expected"
                                 " Network LSA is {}".format(router, ospf_area, lsa)
-                                return errormsg
+                            )
+                            return errormsg
 
-                    if 'summaryLinkStates' in area_lsa:
-                        for lsa in area_lsa['summaryLinkStates']:
-                            for t3lsa in show_ospf_json['areas'][ospf_area][
-                                'summaryLinkStates']:
-                                if lsa['lsaId'] == t3lsa['lsaId'] and \
-                                lsa['advertisedRouter'] == t3lsa[
-                                    'advertisedRouter']:
-                                            result = True
-                                            break
-                            if result:
-                                logger.info(
-                                    "[DUT: %s]  OSPF LSDB area %s:Summary "
-                                    "LSA %s", router, ospf_area, lsa)
+                if "summaryLinkStates" in area_lsa:
+                    for lsa in area_lsa["summaryLinkStates"]:
+                        for t3lsa in show_ospf_json["areas"][ospf_area][
+                            "summaryLinkStates"
+                        ]:
+                            if (
+                                lsa["lsaId"] == t3lsa["lsaId"]
+                                and lsa["advertisedRouter"] == t3lsa["advertisedRouter"]
+                            ):
+                                result = True
                                 break
-                            else:
-                                errormsg = \
-                                "[DUT: {}]  OSPF LSDB area {}: expected" \
+                        if result:
+                            logger.info(
+                                "[DUT: %s]  OSPF LSDB area %s:Summary " "LSA %s",
+                                router,
+                                ospf_area,
+                                lsa,
+                            )
+                            break
+                        else:
+                            errormsg = (
+                                "[DUT: {}]  OSPF LSDB area {}: expected"
                                 " Summary LSA is {}".format(router, ospf_area, lsa)
-                                return errormsg
+                            )
+                            return errormsg
 
-                    if 'nssaExternalLinkStates' in area_lsa:
-                        for lsa in area_lsa['nssaExternalLinkStates']:
-                            for t7lsa in show_ospf_json['areas'][ospf_area][
-                                'nssaExternalLinkStates']:
-                                if lsa['lsaId'] == t7lsa['lsaId'] and \
-                                lsa['advertisedRouter'] == t7lsa[
-                                    'advertisedRouter']:
-                                            result = True
-                                            break
-                            if result:
-                                logger.info(
-                                    "[DUT: %s]  OSPF LSDB area %s:Type7 "
-                                    "LSA %s", router, ospf_area, lsa)
+                if "nssaExternalLinkStates" in area_lsa:
+                    for lsa in area_lsa["nssaExternalLinkStates"]:
+                        for t7lsa in show_ospf_json["areas"][ospf_area][
+                            "nssaExternalLinkStates"
+                        ]:
+                            if (
+                                lsa["lsaId"] == t7lsa["lsaId"]
+                                and lsa["advertisedRouter"] == t7lsa["advertisedRouter"]
+                            ):
+                                result = True
                                 break
-                            else:
-                                errormsg = \
-                                "[DUT: {}]  OSPF LSDB area {}: expected" \
+                        if result:
+                            logger.info(
+                                "[DUT: %s]  OSPF LSDB area %s:Type7 " "LSA %s",
+                                router,
+                                ospf_area,
+                                lsa,
+                            )
+                            break
+                        else:
+                            errormsg = (
+                                "[DUT: {}]  OSPF LSDB area {}: expected"
                                 " Type7 LSA is {}".format(router, ospf_area, lsa)
-                                return errormsg
+                            )
+                            return errormsg
 
-                    if 'asbrSummaryLinkStates' in area_lsa:
-                        for lsa in area_lsa['asbrSummaryLinkStates']:
-                            for t4lsa in show_ospf_json['areas'][ospf_area][
-                                'asbrSummaryLinkStates']:
-                                if lsa['lsaId'] == t4lsa['lsaId'] and \
-                                lsa['advertisedRouter'] == t4lsa[
-                                    'advertisedRouter']:
-                                            result = True
-                                            break
-                            if result:
-                                logger.info(
-                                    "[DUT: %s]  OSPF LSDB area %s:ASBR Summary "
-                                    "LSA %s", router, ospf_area, lsa)
+                if "asbrSummaryLinkStates" in area_lsa:
+                    for lsa in area_lsa["asbrSummaryLinkStates"]:
+                        for t4lsa in show_ospf_json["areas"][ospf_area][
+                            "asbrSummaryLinkStates"
+                        ]:
+                            if (
+                                lsa["lsaId"] == t4lsa["lsaId"]
+                                and lsa["advertisedRouter"] == t4lsa["advertisedRouter"]
+                            ):
                                 result = True
-                            else:
-                                errormsg = \
-                                    "[DUT: {}]  OSPF LSDB area {}: expected" \
-                                    " ASBR Summary LSA is {}".format(
-                                        router, ospf_area, lsa)
-                                return errormsg
+                                break
+                        if result:
+                            logger.info(
+                                "[DUT: %s]  OSPF LSDB area %s:ASBR Summary " "LSA %s",
+                                router,
+                                ospf_area,
+                                lsa,
+                            )
+                            result = True
+                        else:
+                            errormsg = (
+                                "[DUT: {}]  OSPF LSDB area {}: expected"
+                                " ASBR Summary LSA is {}".format(router, ospf_area, lsa)
+                            )
+                            return errormsg
 
-                    if 'linkLocalOpaqueLsa' in area_lsa:
-                        for lsa in area_lsa['linkLocalOpaqueLsa']:
-                            try:
-                                for lnklsa in show_ospf_json['areas'][ospf_area][
-                                    'linkLocalOpaqueLsa']:
-                                    if lsa['lsaId'] in lnklsa['lsaId'] and \
-                                        'linkLocalOpaqueLsa' in show_ospf_json[
-                                            'areas'][ospf_area]:
-                                        logger.info((
-                                        "[DUT: FRR]  OSPF LSDB area %s:Opaque-LSA"
-                                        "%s", ospf_area, lsa))
-                                        result = True
-                                    else:
-                                        errormsg = ("[DUT: FRR] OSPF LSDB area: {} "
-                                    "expected Opaque-LSA is {}, Found is {}".format(
-                                        ospf_area, lsa, show_ospf_json))
-                                        raise ValueError (errormsg)
-                                        return errormsg
-                            except KeyError:
-                                errormsg = ("[DUT: FRR] linkLocalOpaqueLsa Not "
-                                                "present")
-                                return errormsg
+                if "linkLocalOpaqueLsa" in area_lsa:
+                    for lsa in area_lsa["linkLocalOpaqueLsa"]:
+                        try:
+                            for lnklsa in show_ospf_json["areas"][ospf_area][
+                                "linkLocalOpaqueLsa"
+                            ]:
+                                if (
+                                    lsa["lsaId"] in lnklsa["lsaId"]
+                                    and "linkLocalOpaqueLsa"
+                                    in show_ospf_json["areas"][ospf_area]
+                                ):
+                                    logger.info(
+                                        (
+                                            "[DUT: FRR]  OSPF LSDB area %s:Opaque-LSA"
+                                            "%s",
+                                            ospf_area,
+                                            lsa,
+                                        )
+                                    )
+                                    result = True
+                                else:
+                                    errormsg = (
+                                        "[DUT: FRR] OSPF LSDB area: {} "
+                                        "expected Opaque-LSA is {}, Found is {}".format(
+                                            ospf_area, lsa, show_ospf_json
+                                        )
+                                    )
+                                    raise ValueError(errormsg)
+                                    return errormsg
+                        except KeyError:
+                            errormsg = "[DUT: FRR] linkLocalOpaqueLsa Not " "present"
+                            return errormsg
 
     if ospf_external_lsa:
-            for lsa in ospf_external_lsa:
-                try:
-                    for t5lsa in show_ospf_json['asExternalLinkStates']:
-                        if lsa['lsaId'] == t5lsa['lsaId'] and \
-                            lsa['advertisedRouter'] == t5lsa[
-                                        'advertisedRouter']:
-                            result = True
-                            break
-                except KeyError:
-                        result = False
-                if result:
-                    logger.info(
-                            "[DUT: %s]  OSPF LSDB:External LSA %s",
-                            router, lsa)
-                    result = True
-                else:
-                    errormsg = \
-                            "[DUT: {}]  OSPF LSDB : expected" \
-                            " External LSA is {}".format(router, lsa)
-                    return errormsg
+        for lsa in ospf_external_lsa:
+            try:
+                for t5lsa in show_ospf_json["asExternalLinkStates"]:
+                    if (
+                        lsa["lsaId"] == t5lsa["lsaId"]
+                        and lsa["advertisedRouter"] == t5lsa["advertisedRouter"]
+                    ):
+                        result = True
+                        break
+            except KeyError:
+                result = False
+            if result:
+                logger.info("[DUT: %s]  OSPF LSDB:External LSA %s", router, lsa)
+                result = True
+            else:
+                errormsg = (
+                    "[DUT: {}]  OSPF LSDB : expected"
+                    " External LSA is {}".format(router, lsa)
+                )
+                return errormsg
 
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
     return result
 
 
-
-def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
-        load_config=True):
+def config_ospf6_interface(tgen, topo, input_dict=None, build=False, load_config=True):
     """
     API to configure ospf on router.
 
@@ -2180,17 +2300,17 @@ def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
                              "input_dict, passed input_dict %s", router,
                              str(input_dict))
                 continue
-            ospf_data = input_dict[router]['links'][lnk]['ospf6']
+            ospf_data = input_dict[router]["links"][lnk]["ospf6"]
             data_ospf_area = ospf_data.setdefault("area", None)
-            data_ospf_auth = ospf_data.setdefault("authentication", None)
+            data_ospf_auth = ospf_data.setdefault("hash-algo", None)
             data_ospf_dr_priority = ospf_data.setdefault("priority", None)
             data_ospf_cost = ospf_data.setdefault("cost", None)
             data_ospf_mtu = ospf_data.setdefault("mtu_ignore", None)
 
             try:
-                intf = topo['routers'][router]['links'][lnk]['interface']
+                intf = topo["routers"][router]["links"][lnk]["interface"]
             except KeyError:
-                intf = topo['switches'][router]['links'][lnk]['interface']
+                intf = topo["switches"][router]["links"][lnk]["interface"]
 
             # interface
             cmd = "interface {}".format(intf)
@@ -2201,34 +2321,50 @@ def config_ospf6_interface (tgen, topo, input_dict=None, build=False,
                 cmd = "ipv6 ospf area {}".format(data_ospf_area)
                 config_data.append(cmd)
 
+            # interface ospf auth
+            if data_ospf_auth:
+                cmd = "ipv6 ospf6 authentication"
+
+                if "del_action" in ospf_data:
+                    cmd = "no {}".format(cmd)
+
+                if "hash-algo" in ospf_data:
+                    cmd = "{} key-id {} hash-algo {} key {}".format(
+                        cmd,
+                        ospf_data["key-id"],
+                        ospf_data["hash-algo"],
+                        ospf_data["key"],
+                    )
+                    if "del_action" in ospf_data:
+                        cmd = "no {}".format(cmd)
+                    config_data.append(cmd)
+
             # interface ospf dr priority
             if data_ospf_dr_priority:
-                cmd = "ipv6 ospf priority {}".format(
-                    ospf_data["priority"])
-                if 'del_action' in ospf_data:
+                cmd = "ipv6 ospf priority {}".format(ospf_data["priority"])
+                if "del_action" in ospf_data:
                     cmd = "no {}".format(cmd)
                 config_data.append(cmd)
 
             # interface ospf cost
             if data_ospf_cost:
-                cmd = "ipv6 ospf cost {}".format(
-                    ospf_data["cost"])
-                if 'del_action' in ospf_data:
+                cmd = "ipv6 ospf cost {}".format(ospf_data["cost"])
+                if "del_action" in ospf_data:
                     cmd = "no {}".format(cmd)
                 config_data.append(cmd)
 
             # interface ospf mtu
             if data_ospf_mtu:
                 cmd = "ipv6 ospf mtu-ignore"
-                if 'del_action' in ospf_data:
+                if "del_action" in ospf_data:
                     cmd = "no {}".format(cmd)
                 config_data.append(cmd)
 
             if build:
                 return config_data
             else:
-                result = create_common_configuration(tgen, router, config_data,
-                                             "interface_config",
-                                             build=build)
+                result = create_common_configuration(
+                    tgen, router, config_data, "interface_config", build=build
+                )
     logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
     return result
index 23dcced2bf125c867569be5e18ac70f5d8ba5b5a..b516a67d5c712c22ab7b33c5fbe699d6e60c67a2 100644 (file)
@@ -1235,25 +1235,28 @@ class Router(Node):
             dmns = rundaemons.split("\n")
             # Exclude empty string at end of list
             for d in dmns[:-1]:
-                daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
-                if daemonpid.isdigit() and pid_exists(int(daemonpid)):
-                    daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0])
-                    logger.info("{}: stopping {}".format(self.name, daemonname))
-                    try:
-                        os.kill(int(daemonpid), signal.SIGTERM)
-                    except OSError as err:
-                        if err.errno == errno.ESRCH:
-                            logger.error(
-                                "{}: {} left a dead pidfile (pid={})".format(
-                                    self.name, daemonname, daemonpid
+                # Only check if daemonfilepath starts with /
+                # Avoids hang on "-> Connection closed" in above self.cmd()
+                if d[0] == '/':
+                    daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
+                    if daemonpid.isdigit() and pid_exists(int(daemonpid)):
+                        daemonname = os.path.basename(d.rstrip().rsplit(".", 1)[0])
+                        logger.info("{}: stopping {}".format(self.name, daemonname))
+                        try:
+                            os.kill(int(daemonpid), signal.SIGTERM)
+                        except OSError as err:
+                            if err.errno == errno.ESRCH:
+                                logger.error(
+                                    "{}: {} left a dead pidfile (pid={})".format(
+                                        self.name, daemonname, daemonpid
+                                    )
                                 )
-                            )
-                        else:
-                            logger.info(
-                                "{}: {} could not kill pid {}: {}".format(
-                                    self.name, daemonname, daemonpid, str(err)
+                            else:
+                                logger.info(
+                                    "{}: {} could not kill pid {}: {}".format(
+                                        self.name, daemonname, daemonpid, str(err)
+                                    )
                                 )
-                            )
 
             if not wait:
                 return errors
index 30cecee9e1ecf55722a158fe55708ad8724fc7dc..c2ffed4762348a15601e1651c0858829909c8022 100644 (file)
@@ -10,6 +10,7 @@ interface r1-eth1
  ip igmp
 !
 ip pim rp 10.254.254.1
+ip pim join-prune-interval 5
 ip msdp timers 10 20 3
 ip msdp mesh-group mg-1 source 10.254.254.1
 ip msdp mesh-group mg-1 member 10.254.254.2
index a51c6d58c759da09f896457eb86797a9842d20a3..1719a17007ffed0a91825edff266609ee87672ae 100644 (file)
@@ -9,6 +9,7 @@ interface r2-eth1
  ip pim
 !
 ip pim rp 10.254.254.2
+ip pim join-prune-interval 5
 ip msdp timers 10 20 3
 ip msdp mesh-group mg-1 source 10.254.254.2
 ip msdp mesh-group mg-1 member 10.254.254.1
index 663f78620e1334eb26dca55f3297c0b01328c336..2748a55d83a585fcf3cbd06556ff62c27d66afab 100644 (file)
@@ -9,6 +9,7 @@ interface r3-eth1
  ip pim
  ip igmp
 !
+ip pim join-prune-interval 5
 ip pim rp 10.254.254.3
 ip msdp timers 10 20 3
 ip msdp mesh-group mg-1 source 10.254.254.3
index fc289031f44e161656dcc81d4d21e217f61cfd1a..42743152714e2282e1908c8c2892c81a0d27a624 100644 (file)
@@ -19,3 +19,4 @@ ip msdp timers 10 20 3
 ip msdp peer 192.168.0.2 source 192.168.0.1
 ip msdp peer 192.168.1.2 source 192.168.1.1
 ip pim rp 10.254.254.1
+ip pim join-prune-interval 5
index ffa80b12d30dcd73eb1119ea2bffd1113644fe2c..a4a69bf05c62ab5d2106e5dad2528af67b0b02fe 100644 (file)
@@ -15,3 +15,4 @@ ip msdp timers 10 20 3
 ip msdp peer 192.168.0.1 source 192.168.0.2
 ip msdp peer 192.168.2.2 source 192.168.2.1
 ip pim rp 10.254.254.2
+ip pim join-prune-interval 5
index ab12f0573a2b82a91b1c45408631a95ae2a6f17b..db94447c767de6adcc7ba61ba56ab90ecc6cd4e5 100644 (file)
@@ -15,3 +15,4 @@ ip msdp timers 10 20 3
 ip msdp peer 192.168.1.1 source 192.168.1.2
 ip msdp peer 192.168.3.2 source 192.168.3.1
 ip pim rp 10.254.254.3
+ip pim join-prune-interval 5
index b2e05cb3cb63ca5e5f58a6ac4f4e46ac50e01e0a..e9bb59054ccef428c7e72cfd1ff52ba60c327932 100644 (file)
@@ -19,3 +19,4 @@ ip msdp timers 10 20 3
 ip msdp peer 192.168.2.1 source 192.168.2.2
 ip msdp peer 192.168.3.1 source 192.168.3.2
 ip pim rp 10.254.254.4
+ip pim join-prune-interval 5
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
new file mode 100644 (file)
index 0000000..c928093
--- /dev/null
@@ -0,0 +1,347 @@
+{
+    "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
+                    }
+                },
+                "r1-link1": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r1-link2": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r1-link3": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r1-link4": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r1-link5": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r1-link6": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r1-link7": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r2": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "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": {},
+                    "r1-link1": {
+                        "nbr": "r1"
+                    },
+                    "r1-link2": {
+                        "nbr": "r1"
+                    },
+                    "r1-link3": {
+                        "nbr": "r1"
+                    },
+                    "r1-link4": {
+                        "nbr": "r1"
+                    },
+                    "r1-link5": {
+                        "nbr": "r1"
+                    },
+                    "r1-link6": {
+                        "nbr": "r1"
+                    },
+                    "r1-link7": {
+                        "nbr": "r1"
+                    },
+                    "r2": {},
+                    "r3": {}
+                }
+            }
+        },
+        "r1": {
+            "links": {
+                "lo": {
+                    "ipv6": "auto",
+                    "type": "loopback"
+                },
+                "r0": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r0-link1": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r0-link2": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r0-link3": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r0-link4": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r0-link5": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r0-link6": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r0-link7": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r2": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r3": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r3-link0": {
+                    "ipv6": "auto",
+                    "description": "DummyIntftoR3"
+                }
+            },
+            "ospf6": {
+                "router_id": "100.1.1.1",
+                "neighbors": {
+                    "r0": {},
+                    "r0-link1": {
+                        "nbr": "r0"
+                    },
+                    "r0-link2": {
+                        "nbr": "r0"
+                    },
+                    "r0-link3": {
+                        "nbr": "r0"
+                    },
+                    "r0-link4": {
+                        "nbr": "r0"
+                    },
+                    "r0-link5": {
+                        "nbr": "r0"
+                    },
+                    "r0-link6": {
+                        "nbr": "r0"
+                    },
+                    "r0-link7": {
+                        "nbr": "r0"
+                    },
+                    "r2": {},
+                    "r3": {}
+                }
+            }
+        },
+        "r2": {
+            "links": {
+                "lo": {
+                    "ipv6": "auto",
+                    "type": "loopback"
+                },
+                "r0": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r1": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r3": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                }
+            },
+            "ospf6": {
+                "router_id": "100.1.1.2",
+                "neighbors": {
+                    "r1": {},
+                    "r0": {},
+                    "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.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r2": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "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": {
+                    "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
new file mode 100644 (file)
index 0000000..226f84f
--- /dev/null
@@ -0,0 +1,137 @@
+{
+    "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": {
+                "r1": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r2": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r3": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                }
+            },
+            "ospf6": {
+                "router_id": "100.1.1.0",
+                "neighbors": {"r1": {}, "r2": {}, "r3": {}}
+            }
+        },
+        "r1": {
+            "links": {
+                "r0": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r2": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r3": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                }
+            },
+            "ospf6": {
+                "router_id": "100.1.1.1",
+                "neighbors": {"r0": {}, "r2": {}, "r3": {}}
+            }
+        },
+        "r2": {
+            "links": {
+                "r0": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r1": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r3": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                }
+            },
+            "ospf6": {
+                "router_id": "100.1.1.2",
+                "neighbors": {"r1": {}, "r0": {}, "r3": {}}
+            }
+        },
+        "r3": {
+            "links": {
+                "r0": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r1": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                },
+                "r2": {
+                    "ipv6": "auto",
+                    "ospf6": {
+                        "area": "0.0.0.0",
+                        "hello_interval": 1,
+                        "dead_interval": 4
+                    }
+                }
+            },
+            "ospf6": {
+                "router_id": "100.1.1.3",
+                "neighbors": {"r0": {}, "r1": {}, "r2": {}}
+            }
+        }
+    }
+}
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
new file mode 100644 (file)
index 0000000..a439375
--- /dev/null
@@ -0,0 +1,520 @@
+#!/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
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# 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_topo_from_json, 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
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_ecmp.json".format(CWD)
+try:
+    with open(jsonFile, "r") as topoJson:
+        topo = json.load(topoJson)
+except IOError:
+    assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+    "ipv4": [
+        "11.0.20.1/32",
+        "11.0.20.2/32",
+        "11.0.20.3/32",
+        "11.0.20.4/32",
+        "11.0.20.5/32",
+    ],
+    "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+"""
+TOPOLOGY :
+      Please view in a fixed-width font such as Courier.
+      +---+  A1       +---+
+      +R1 +------------+R2 |
+      +-+-+-           +--++
+        |  --        --  |
+        |    -- A0 --    |
+      A0|      ----      |
+        |      ----      | A2
+        |    --    --    |
+        |  --        --  |
+      +-+-+-            +-+-+
+      +R0 +-------------+R3 |
+      +---+     A3     +---+
+
+TESTCASES :
+1. Verify OSPF ECMP with max path configured as 8 (ECMPconfigured at FRR level)
+2. Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+ """
+
+
+class CreateTopo(Topo):
+    """
+    Test topology builder.
+
+    * `Topo`: Topology object
+    """
+
+    def build(self, *_args, **_opts):
+        """Build function."""
+        tgen = get_topogen(self)
+
+        # Building topology from json file
+        build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+    """
+    Sets up the pytest environment
+
+    * `mod`: module name
+    """
+    global topo
+    testsuite_run_time = time.asctime(time.localtime(time.time()))
+    logger.info("Testsuite start time: {}".format(testsuite_run_time))
+    logger.info("=" * 40)
+
+    logger.info("Running setup_module to create topology")
+
+    # This function initiates the topology build with Topogen...
+    tgen = Topogen(CreateTopo, mod.__name__)
+    # ... and here it calls Mininet initialization functions.
+
+    # get list of daemons needs to be started for this suite.
+    daemons = topo_daemons(tgen, topo)
+
+    # Starting topology, create tmp files which are loaded to routers
+    #  to start deamons and then start routers
+    start_topology(tgen, daemons)
+
+    # Creating configuration from JSON
+    build_config_from_json(tgen, topo)
+
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    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(mod):
+    """
+    Teardown the pytest environment.
+
+    * `mod`: module name
+    """
+
+    logger.info("Running teardown_module to delete topology")
+
+    tgen = get_topogen()
+
+    # Stop toplogy and Remove tmp files
+    tgen.stop_topology()
+
+    logger.info(
+        "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+    )
+    logger.info("=" * 40)
+
+
+def red_static(dut, config=True):
+    """Local def for Redstribute static routes inside ospf."""
+    global topo
+    tgen = get_topogen()
+    if config:
+        ospf_red = {dut: {"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_ecmp_tc16_p0(request):
+    """
+    Verify OSPF ECMP.
+
+    Verify OSPF ECMP with max path configured as 8 (ECMP
+    configured at FRR level)
+    """
+    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 8 interfaces between R1 and R2 and enable ospf in area 0.")
+
+    reset_config_on_routers(tgen)
+
+    step("Verify that OSPF is up with 8 neighborship sessions.")
+    dut = "r1"
+    ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+        ospf_covergence
+    )
+
+    step("Configure a static route in R0 and redistribute in OSPF.")
+
+    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)
+
+    dut = "r0"
+    red_static(dut)
+
+    llip = get_llip("r0", "r1-link1")
+    assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Verify that route in R2 in stalled with 8 next hops.")
+    nh = []
+    for item in range(1, 7):
+        nh.append(llip)
+
+    llip = get_llip("r0", "r1")
+    assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    nh2 = llip
+
+    nh.append(nh2)
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("shut no shut all the interfaces on the remote router - R2")
+    dut = "r1"
+    for intfr in range(1, 7):
+        intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+        shutdown_bringup_interface(tgen, dut, intf, False)
+
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Route present in OSPF RIB.  Error: {}".format(
+        tc_name, result
+    )
+
+    protocol = "ospf"
+    result = verify_rib(
+        tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+    )
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+    for intfr in range(1, 7):
+        intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+        shutdown_bringup_interface(tgen, dut, intf, True)
+
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("shut no shut on all the interfaces on DUT (r1)")
+    for intfr in range(1, 7):
+        intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+        shutdown_bringup_interface(tgen, dut, intf, False)
+
+    for intfr in range(1, 7):
+        intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+        shutdown_bringup_interface(tgen, dut, intf, True)
+
+    step(
+        "Verify that all the neighbours are up and routes are installed"
+        " with 8 next hop in ospf and ip route tables on R1."
+    )
+
+    step("Verify that OSPF is up with 8 neighborship sessions.")
+    dut = "r1"
+    ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+        ospf_covergence
+    )
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    write_test_footer(tc_name)
+
+
+def test_ospfv3_ecmp_tc17_p0(request):
+    """
+    Verify OSPF ECMP.
+
+    Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+    """
+    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 2 interfaces between R1 and R2 & enable ospf in area 0.")
+
+    reset_config_on_routers(tgen)
+
+    step("Verify that OSPF is up with 2 neighborship sessions.")
+    dut = "r1"
+    ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+    assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+        ospf_covergence
+    )
+
+    step("Configure a static route in R0 and redistribute in OSPF.")
+
+    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)
+
+    dut = "r0"
+    red_static(dut)
+
+    step("Verify that route in R2 in stalled with 2 next hops.")
+
+    llip = get_llip("r0", "r1-link1")
+    assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    nh1 = llip
+
+    llip = get_llip("r0", "r1")
+    assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    nh2 = llip
+
+    nh = [nh1, nh2]
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Configure ECMP value as 1.")
+    max_path = {"r1": {"ospf6": {"maximum-paths": 1}}}
+    result = create_router_ospf(tgen, topo, max_path)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    dut = "r1"
+    max_path = {"r1": {"ospf6": {"maximum-paths": 2}}}
+    result = create_router_ospf(tgen, topo, max_path)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Configure cost on R0 as 100")
+    r0_ospf_cost = {"r0": {"links": {"r1": {"ospf6": {"cost": 100}}}}}
+    result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+    assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    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_routemaps.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
new file mode 100644 (file)
index 0000000..9ca460e
--- /dev/null
@@ -0,0 +1,872 @@
+#!/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
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# 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,
+    create_prefix_lists,
+    verify_rib,
+    create_static_routes,
+    step,
+    create_route_maps,
+    verify_prefix_lists,
+    get_frr_ipv6_linklocal,
+    topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, 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
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_routemaps.json".format(CWD)
+try:
+    with open(jsonFile, "r") as topoJson:
+        topo = json.load(topoJson)
+except IOError:
+    assert False, "Could not read file {}".format(jsonFile)
+
+NETWORK = {
+    "ipv4": [
+        "11.0.20.1/32",
+        "11.0.20.2/32",
+        "11.0.20.3/32",
+        "11.0.20.4/32",
+        "11.0.20.5/32",
+    ],
+    "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+
+routerids = ["100.1.1.0", "100.1.1.1", "100.1.1.2", "100.1.1.3"]
+
+"""
+TOPOOLOGY =
+      Please view in a fixed-width font such as Courier.
+      +---+  A1       +---+
+      +R1 +------------+R2 |
+      +-+-+-           +--++
+        |  --        --  |
+        |    -- A0 --    |
+      A0|      ----      |
+        |      ----      | A2
+        |    --    --    |
+        |  --        --  |
+      +-+-+-            +-+-+
+      +R0 +-------------+R3 |
+      +---+     A3     +---+
+
+TESTCASES =
+2. Verify OSPF route map support functionality when route map is not
+    configured at system level but configured in OSPF
+4. Verify OSPF route map support functionality
+    when route map actions are toggled.
+5. Verify OSPF route map support  functionality with multiple sequence
+    numbers in a single  route-map for different match/set clauses.
+6. Verify OSPF route map support functionality when we add/remove route-maps
+    with multiple set clauses and without any match statement.(Set only)
+7.  Verify OSPF route map support functionality when we
+    add/remove route-maps with multiple match clauses and without
+    any set statement.(Match only)
+8. Verify OSPF route map applied to ospf redistribution with  ipv6 prefix list
+ """
+
+
+class CreateTopo(Topo):
+    """
+    Test topology builder.
+
+    * `Topo`: Topology object
+    """
+
+    def build(self, *_args, **_opts):
+        """Build function."""
+        tgen = get_topogen(self)
+
+        # Building topology from json file
+        build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+    """
+    Sets up the pytest environment
+
+    * `mod`: module name
+    """
+    global topo
+    testsuite_run_time = time.asctime(time.localtime(time.time()))
+    logger.info("Testsuite start time: {}".format(testsuite_run_time))
+    logger.info("=" * 40)
+
+    logger.info("Running setup_module to create topology")
+
+    # This function initiates the topology build with Topogen...
+    tgen = Topogen(CreateTopo, mod.__name__)
+    # ... and here it calls Mininet initialization functions.
+
+    # get list of daemons needs to be started for this suite.
+    daemons = topo_daemons(tgen, topo)
+
+    # Starting topology, create tmp files which are loaded to routers
+    #  to start deamons and then start routers
+    start_topology(tgen, daemons)
+
+    # Creating configuration from JSON
+    build_config_from_json(tgen, topo)
+
+    # Don't run this test if we have any failure.
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    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(mod):
+    """
+    Teardown the pytest environment.
+
+    * `mod`: module name
+    """
+
+    logger.info("Running teardown_module to delete topology")
+
+    tgen = get_topogen()
+
+    # Stop toplogy and Remove tmp files
+    tgen.stop_topology()
+
+    logger.info(
+        "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+    )
+    logger.info("=" * 40)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_routemaps_functionality_tc20_p0(request):
+    """
+    OSPF route map support functionality.
+
+    Verify OSPF route map support functionality when route map is not
+    configured at system level but configured in OSPF
+
+    """
+    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)
+
+    step("Redistribute to ospf using route map ( non existent route map)")
+    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 routes are not allowed in OSPF even tough no "
+        "matching routing map is configured."
+    )
+
+    dut = "r1"
+    protocol = "ospf"
+    result = verify_ospf6_rib(tgen, dut, input_dict)
+    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)
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+        tc_name, result
+    )
+
+    step(
+        "configure the route map with the same name that is used "
+        "in the ospf with deny rule."
+    )
+
+    # Create route map
+    routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "deny"}]}}}
+    result = create_route_maps(tgen, routemaps)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("verify that now route map is activated & routes are denied in OSPF.")
+    dut = "r1"
+    protocol = "ospf"
+    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
+    )
+
+    # Create route map
+    routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "deny"}]}}}
+    result = create_route_maps(tgen, routemaps)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("verify that now route map is activated & routes are denied in OSPF.")
+    dut = "r1"
+    protocol = "ospf"
+    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 the route map.")
+    # Create route map
+    routemaps = {
+        "r0": {"route_maps": {"rmap_ipv6": [{"action": "deny", "delete": True}]}}
+    }
+    result = create_route_maps(tgen, routemaps)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "Verify that routes are allowed in OSPF even tough "
+        "no matching routing map is configured."
+    )
+    dut = "r1"
+    protocol = "ospf"
+    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_tc25_p0(request):
+    """
+    OSPF route map support functionality.
+
+    Verify OSPF route map support functionality
+    when route map actions are toggled.
+
+    """
+    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 permit rule")
+    # Create route map
+    routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "permit"}]}}}
+    result = create_route_maps(tgen, routemaps)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Verify that route is advertised to R1.")
+    dut = "r1"
+    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)
+    step("Configure route map with deny rule")
+    # Create route map
+    routemaps = {
+        "r0": {"route_maps": {"rmap_ipv6": [{"seq_id": 10, "action": "deny"}]}}
+    }
+    result = create_route_maps(tgen, routemaps)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    # 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 route is not advertised to R1.")
+    dut = "r1"
+    protocol = "ospf"
+    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_tc22_p0(request):
+    """
+    OSPF Route map - Multiple sequence numbers.
+
+    Verify OSPF route map support  functionality with multiple sequence
+    numbers in a single  route-map for different match/set clauses.
+
+    """
+    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(
+        "Configure route map with seq number 10 to with ip prefix"
+        " permitting route 10.0.20.1/32 in R1"
+    )
+    step(
+        "Configure route map with seq number 20 to with  ip prefix"
+        "  permitting route 10.0.20.2/32 in R1"
+    )
+
+    # Create route map
+    input_dict_3 = {
+        "r0": {
+            "route_maps": {
+                "rmap_ipv6": [
+                    {
+                        "action": "permit",
+                        "seq_id": "10",
+                        "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+                    },
+                    {
+                        "action": "permit",
+                        "seq_id": "20",
+                        "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv4"}},
+                    },
+                ]
+            }
+        }
+    }
+    result = create_route_maps(tgen, input_dict_3)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    # Create ip prefix list
+    input_dict_2 = {
+        "r0": {
+            "prefix_lists": {
+                "ipv4": {
+                    "pf_list_1_ipv6": [
+                        {"seqid": 10, "network": NETWORK["ipv6"][0], "action": "permit"}
+                    ]
+                }
+            }
+        }
+    }
+    result = create_prefix_lists(tgen, input_dict_2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    # Create ip prefix list
+    input_dict_2 = {
+        "r0": {
+            "prefix_lists": {
+                "ipv4": {
+                    "pf_list_2_ipv4": [
+                        {"seqid": 10, "network": NETWORK["ipv6"][1], "action": "permit"}
+                    ]
+                }
+            }
+        }
+    }
+    result = create_prefix_lists(tgen, input_dict_2)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Configure static routes 10.0.20.1/32 and 10.0.20.2 in R1")
+    # 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)
+
+    step("Configure redistribute static route with route map.")
+    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)
+
+    input_dict = {
+        "r0": {
+            "static_routes": [
+                {
+                    "network": NETWORK["ipv6"][0],
+                    "no_of_ip": 2,
+                    "next_hop": "Null0",
+                }
+            ]
+        }
+    }
+    result = create_static_routes(tgen, input_dict)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Verify that both routes are learned in R1 and R2")
+    dut = "r1"
+    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)
+
+    dut = "r2"
+    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)
+
+    step("Change route map with seq number 20 to deny.")
+    # Create route map
+    input_dict_3 = {
+        "r0": {
+            "route_maps": {
+                "rmap_ipv6": [
+                    {
+                        "action": "deny",
+                        "seq_id": "20",
+                        "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv4"}},
+                    }
+                ]
+            }
+        }
+    }
+    result = create_route_maps(tgen, input_dict_3)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step(
+        "Verify the route 10.0.20.2/32 is withdrawn and not present "
+        "in the routing table of R0 and R1."
+    )
+
+    input_dict = {
+        "r0": {"static_routes": [{"network": NETWORK["ipv6"][1], "next_hop": "Null0"}]}
+    }
+
+    dut = "r1"
+    protocol = "ospf"
+    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
+    )
+
+    dut = "r2"
+    protocol = "ospf"
+    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_tc24_p0(request):
+    """
+    OSPF Route map - Multiple set clauses.
+
+    Verify OSPF route map support functionality when we
+    add/remove route-maps with multiple match clauses and without
+    any set statement.(Match 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": 1,
+                    "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)
+
+    # Create ip prefix list
+    pfx_list = {
+        "r0": {
+            "prefix_lists": {
+                "ipv6": {
+                    "pf_list_1_ipv6": [
+                        {"seqid": 10, "network": "any", "action": "permit"}
+                    ]
+                }
+            }
+        }
+    }
+    result = create_prefix_lists(tgen, pfx_list)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("verify that prefix-list is created in R0.")
+    result = verify_prefix_lists(tgen, pfx_list)
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Prefix list not " "present. 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("Verify that metric falls back to original metric for ospf routes.")
+    dut = "r1"
+    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)
+
+    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"][1],
+                    "no_of_ip": 1,
+                    "next_hop": "Null0",
+                    "tag": 1000,
+                }
+            ]
+        }
+    }
+    result = create_static_routes(tgen, input_dict)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    # Create ip prefix list
+    pfx_list = {
+        "r0": {
+            "prefix_lists": {
+                "ipv6": {
+                    "pf_list_1_ipv6": [
+                        {"seqid": 10, "network": "any", "action": "permit"}
+                    ]
+                }
+            }
+        }
+    }
+    result = create_prefix_lists(tgen, pfx_list)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("verify that prefix-list is created in R0.")
+    result = verify_prefix_lists(tgen, pfx_list)
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+        tc_name, result
+    )
+
+    # Create route map
+    routemaps = {
+        "r0": {
+            "route_maps": {
+                "rmap_ipv6": [{"action": "permit", "match": {"ipv6": {"tag": "1000"}}}]
+            }
+        }
+    }
+    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)
+    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)
+
+    step("Delete the match clause with tag in route map")
+    # Create route map
+    routemaps = {
+        "r0": {
+            "route_maps": {
+                "rmap_ipv6": [
+                    {
+                        "action": "permit",
+                        "match": {"ipv6": {"tag": "1000", "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)
+    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)
+
+    step("Delete the match clause with metric in route map.")
+
+    # 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)
+
+    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)
+
+    write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index 4aa71bfb16c9cdc83a75de6411b71eebb53a9bbc..e01c6d604775e08da3dac89a8ba6e8513f6ef053 100644 (file)
@@ -281,6 +281,233 @@ 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
+    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.")
+    reset_config_on_routers(tgen)
+
+    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
+    )
+
+    step("verify intra area route is calculated for r0-r3 interface ip in R1")
+    ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+    ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+
+    llip = get_llip("r0", "r1")
+    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"}]}
+    }
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Delete the ip address on newly configured loopback of R0")
+    topo1 = {
+        "r0": {
+            "links": {
+                "r3": {
+                    "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+                    "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+                    "delete": True,
+                }
+            }
+        }
+    }
+
+    result = create_interfaces_cfg(tgen, topo1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(
+        tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+    )
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+    step("Add back the deleted ip address on newly configured interface of R0")
+    topo1 = {
+        "r0": {
+            "links": {
+                "r3": {
+                    "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+                    "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+                }
+            }
+        }
+    }
+
+    result = create_interfaces_cfg(tgen, topo1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Shut no shut interface on R0")
+    dut = "r0"
+    intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
+    shutdown_bringup_interface(tgen, dut, intf, False)
+
+    step("un shut the OSPF interface on R0")
+    dut = "r0"
+    shutdown_bringup_interface(tgen, dut, intf, True)
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    write_test_footer(tc_name)
+
+
+def test_ospfv3_redistribution_tc6_p0(request):
+    """Test OSPF inter area route calculations."""
+    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.")
+    reset_config_on_routers(tgen)
+
+    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
+    )
+
+    step("verify intra area route is calculated for r0-r3 interface ip in R1")
+    ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+    ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+    llip = get_llip("r0", "r1")
+    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"}]}
+    }
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Delete the ip address on newly configured loopback of R0")
+    topo1 = {
+        "r0": {
+            "links": {
+                "r3": {
+                    "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+                    "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+                    "delete": True,
+                }
+            }
+        }
+    }
+
+    result = create_interfaces_cfg(tgen, topo1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(
+        tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+    )
+    assert (
+        result is not True
+    ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+    step("Add back the deleted ip address on newly configured interface of R0")
+    topo1 = {
+        "r0": {
+            "links": {
+                "r3": {
+                    "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+                    "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+                }
+            }
+        }
+    }
+
+    result = create_interfaces_cfg(tgen, topo1)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    step("Shut no shut interface on R0")
+    dut = "r0"
+    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)
+
+    dut = "r1"
+    result = verify_ospf6_rib(tgen, dut, input_dict)
+    assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+    protocol = "ospf"
+    result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+    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
@@ -368,7 +595,6 @@ def test_ospfv3_cost_tc52_p0(request):
     write_test_footer(tc_name)
 
 
-
 if __name__ == "__main__":
     args = ["-s"] + sys.argv[1:]
     sys.exit(pytest.main(args))
index a84f1a1eb6e9aed8f392d871518b3c93775fbb8b..faae4b3e1764685e6aa74039be30df4a3f6c371e 100644 (file)
@@ -54,7 +54,7 @@ from lib.common_config import (
     create_route_maps,
     shutdown_bringup_interface,
     create_interfaces_cfg,
-    topo_daemons,
+    topo_daemons
 )
 from lib.topolog import logger
 from lib.topojson import build_topo_from_json, build_config_from_json
diff --git a/tests/topotests/pim_acl/h1/zebra.conf b/tests/topotests/pim_acl/h1/zebra.conf
new file mode 100644 (file)
index 0000000..3d6540d
--- /dev/null
@@ -0,0 +1,10 @@
+!
+hostname h1
+log file zebra.log
+!
+interface h1-eth0
+ description connection to r1 via sw1
+ ip address 192.168.100.10/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_acl/h2/zebra.conf b/tests/topotests/pim_acl/h2/zebra.conf
new file mode 100644 (file)
index 0000000..95342f9
--- /dev/null
@@ -0,0 +1,8 @@
+hostname h2
+!
+interface h2-eth0
+ description connection to r1 via sw2
+ ip address 192.168.101.2/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r1/acl_1_pim_join.json b/tests/topotests/pim_acl/r1/acl_1_pim_join.json
new file mode 100644 (file)
index 0000000..1b44b2b
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "r1-eth0":{
+    "name":"r1-eth0",
+    "state":"up",
+    "address":"192.168.100.1",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.1":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.1",
+        "upTime":"--:--:--",
+        "expire":"--:--",
+        "prune":"--:--",
+        "channelJoinName":"NOINFO",
+        "protocolIgmp":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_2_pim_join.json b/tests/topotests/pim_acl/r1/acl_2_pim_join.json
new file mode 100644 (file)
index 0000000..c020a48
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "r1-eth0":{
+    "name":"r1-eth0",
+    "state":"up",
+    "address":"192.168.100.1",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.17":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.17",
+        "upTime":"--:--:--",
+        "expire":"--:--",
+        "prune":"--:--",
+        "channelJoinName":"NOINFO",
+        "protocolIgmp":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_3_pim_join.json b/tests/topotests/pim_acl/r1/acl_3_pim_join.json
new file mode 100644 (file)
index 0000000..6122f73
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "r1-eth0":{
+    "name":"r1-eth0",
+    "state":"up",
+    "address":"192.168.100.1",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.32":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.32",
+        "upTime":"--:--:--",
+        "expire":"--:--",
+        "prune":"--:--",
+        "channelJoinName":"NOINFO",
+        "protocolIgmp":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_4_pim_join.json b/tests/topotests/pim_acl/r1/acl_4_pim_join.json
new file mode 100644 (file)
index 0000000..5f72256
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "r1-eth0":{
+    "name":"r1-eth0",
+    "state":"up",
+    "address":"192.168.100.1",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.255":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.255",
+        "upTime":"--:--:--",
+        "expire":"--:--",
+        "prune":"--:--",
+        "channelJoinName":"NOINFO",
+        "protocolIgmp":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_5_pim_join.json b/tests/topotests/pim_acl/r1/acl_5_pim_join.json
new file mode 100644 (file)
index 0000000..70021bd
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "r1-eth0":{
+    "name":"r1-eth0",
+    "state":"up",
+    "address":"192.168.100.1",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.97":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.97",
+        "upTime":"--:--:--",
+        "expire":"--:--",
+        "prune":"--:--",
+        "channelJoinName":"NOINFO",
+        "protocolIgmp":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_6_pim_join.json b/tests/topotests/pim_acl/r1/acl_6_pim_join.json
new file mode 100644 (file)
index 0000000..2baac6c
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "r1-eth0":{
+    "name":"r1-eth0",
+    "state":"up",
+    "address":"192.168.100.1",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.70":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.70",
+        "upTime":"--:--:--",
+        "expire":"--:--",
+        "prune":"--:--",
+        "channelJoinName":"NOINFO",
+        "protocolIgmp":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r1/ospf_neighbor.json b/tests/topotests/pim_acl/r1/ospf_neighbor.json
new file mode 100644 (file)
index 0000000..a8fc093
--- /dev/null
@@ -0,0 +1,59 @@
+{
+  "neighbors":{
+    "192.168.0.11":[
+      {
+        "priority":10,
+        "state":"Full\/Backup",
+        "address":"192.168.101.11",
+        "ifaceName":"r1-eth1:192.168.101.1",
+        "retransmitCounter":0,
+        "requestCounter":0,
+        "dbSummaryCounter":0
+      }
+    ],
+    "192.168.0.12":[
+      {
+        "priority":0,
+        "state":"Full\/DROther",
+        "address":"192.168.101.12",
+        "ifaceName":"r1-eth1:192.168.101.1",
+        "retransmitCounter":0,
+        "requestCounter":0,
+        "dbSummaryCounter":0
+      }
+    ],
+    "192.168.0.13":[
+      {
+        "priority":0,
+        "state":"Full\/DROther",
+        "address":"192.168.101.13",
+        "ifaceName":"r1-eth1:192.168.101.1",
+        "retransmitCounter":0,
+        "requestCounter":0,
+        "dbSummaryCounter":0
+      }
+    ],
+    "192.168.0.14":[
+      {
+        "priority":0,
+        "state":"Full\/DROther",
+        "address":"192.168.101.14",
+        "ifaceName":"r1-eth1:192.168.101.1",
+        "retransmitCounter":0,
+        "requestCounter":0,
+        "dbSummaryCounter":0
+      }
+    ],
+    "192.168.0.15":[
+      {
+        "priority":0,
+        "state":"Full\/DROther",
+        "address":"192.168.101.15",
+        "ifaceName":"r1-eth1:192.168.101.1",
+        "retransmitCounter":0,
+        "requestCounter":0,
+        "dbSummaryCounter":0
+      }
+    ]
+  }
+}
diff --git a/tests/topotests/pim_acl/r1/ospfd.conf b/tests/topotests/pim_acl/r1/ospfd.conf
new file mode 100644 (file)
index 0000000..e1f47fb
--- /dev/null
@@ -0,0 +1,16 @@
+hostname r1
+!
+debug ospf event
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+router ospf
+ ospf router-id 192.168.0.1
+ passive-interface r1-eth0
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+
diff --git a/tests/topotests/pim_acl/r1/pim_neighbor.json b/tests/topotests/pim_acl/r1/pim_neighbor.json
new file mode 100644 (file)
index 0000000..ae95e8d
--- /dev/null
@@ -0,0 +1,31 @@
+{
+  "r1-eth0":{
+  },
+  "r1-eth1":{
+    "192.168.101.12":{
+      "interface":"r1-eth1",
+      "neighbor":"192.168.101.12",
+      "drPriority":1
+    },
+    "192.168.101.15":{
+      "interface":"r1-eth1",
+      "neighbor":"192.168.101.15",
+      "drPriority":1
+    },
+    "192.168.101.14":{
+      "interface":"r1-eth1",
+      "neighbor":"192.168.101.14",
+      "drPriority":1
+    },
+    "192.168.101.11":{
+      "interface":"r1-eth1",
+      "neighbor":"192.168.101.11",
+      "drPriority":1
+    },
+    "192.168.101.13":{
+      "interface":"r1-eth1",
+      "neighbor":"192.168.101.13",
+      "drPriority":1
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r1/pimd.conf b/tests/topotests/pim_acl/r1/pimd.conf
new file mode 100644 (file)
index 0000000..a148c73
--- /dev/null
@@ -0,0 +1,31 @@
+hostname r1
+!
+debug igmp events
+debug igmp packets
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.11 prefix-list rp-pl-1
+ip pim rp 192.168.0.12 prefix-list rp-pl-2
+ip pim rp 192.168.0.13 prefix-list rp-pl-3
+ip pim rp 192.168.0.14 prefix-list rp-pl-4
+ip pim rp 192.168.0.15 prefix-list rp-pl-5
+ip pim join-prune-interval 5
+!
+interface r1-eth0
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+ip prefix-list rp-pl-1 seq 10 permit 239.100.0.0/28
+ip prefix-list rp-pl-2 seq 10 permit 239.100.0.17/32
+ip prefix-list rp-pl-3 seq 10 permit 239.100.0.32/27
+ip prefix-list rp-pl-4 seq 10 permit 239.100.0.128/25
+ip prefix-list rp-pl-4 seq 20 permit 239.100.0.96/28
+ip prefix-list rp-pl-5 seq 10 permit 239.100.0.64/28
diff --git a/tests/topotests/pim_acl/r1/zebra.conf b/tests/topotests/pim_acl/r1/zebra.conf
new file mode 100644 (file)
index 0000000..74feb8f
--- /dev/null
@@ -0,0 +1,18 @@
+!
+hostname r1
+log file zebra.log
+!
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 192.168.0.1/32
+!
+interface r1-eth0
+ description connection to h1 via sw1
+ ip address 192.168.100.1/24
+!
+interface r1-eth1
+ description connection to r11/12/13/14/15 via sw2
+ ip address 192.168.101.1/24
+!
diff --git a/tests/topotests/pim_acl/r11/acl_1_pim_join.json b/tests/topotests/pim_acl/r11/acl_1_pim_join.json
new file mode 100644 (file)
index 0000000..289bf51
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "r11-eth0":{
+    "name":"r11-eth0",
+    "state":"up",
+    "address":"192.168.101.11",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.1":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.1",
+        "prune":"--:--",
+        "channelJoinName":"JOIN",
+        "protocolPim":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r11/ospfd.conf b/tests/topotests/pim_acl/r11/ospfd.conf
new file mode 100644 (file)
index 0000000..e107220
--- /dev/null
@@ -0,0 +1,14 @@
+hostname r11
+!
+debug ospf event
+!
+interface r11-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.11
+ network 192.168.0.11/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r11/pimd.conf b/tests/topotests/pim_acl/r11/pimd.conf
new file mode 100644 (file)
index 0000000..b1d4520
--- /dev/null
@@ -0,0 +1,17 @@
+hostname r11
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.11 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r11-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r11/zebra.conf b/tests/topotests/pim_acl/r11/zebra.conf
new file mode 100644 (file)
index 0000000..137706d
--- /dev/null
@@ -0,0 +1,13 @@
+!
+hostname r11
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.11/32
+!
+interface r11-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.11/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r12/acl_2_pim_join.json b/tests/topotests/pim_acl/r12/acl_2_pim_join.json
new file mode 100644 (file)
index 0000000..76ab7ee
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "r12-eth0":{
+    "name":"r12-eth0",
+    "state":"up",
+    "address":"192.168.101.12",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.17":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.17",
+        "prune":"--:--",
+        "channelJoinName":"JOIN",
+        "protocolPim":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r12/ospfd.conf b/tests/topotests/pim_acl/r12/ospfd.conf
new file mode 100644 (file)
index 0000000..f9203c7
--- /dev/null
@@ -0,0 +1,14 @@
+hostname r12
+!
+debug ospf event
+!
+interface r12-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.12
+ network 192.168.0.12/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r12/pimd.conf b/tests/topotests/pim_acl/r12/pimd.conf
new file mode 100644 (file)
index 0000000..ba9e7d9
--- /dev/null
@@ -0,0 +1,17 @@
+hostname r12
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.12 239.100.0.17/32
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r12-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r12/zebra.conf b/tests/topotests/pim_acl/r12/zebra.conf
new file mode 100644 (file)
index 0000000..bede104
--- /dev/null
@@ -0,0 +1,13 @@
+!
+hostname r12
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.12/32
+!
+interface r12-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.12/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r13/acl_3_pim_join.json b/tests/topotests/pim_acl/r13/acl_3_pim_join.json
new file mode 100644 (file)
index 0000000..48ad72c
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "r13-eth0":{
+    "name":"r13-eth0",
+    "state":"up",
+    "address":"192.168.101.13",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.32":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.32",
+        "prune":"--:--",
+        "channelJoinName":"JOIN",
+        "protocolPim":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r13/ospfd.conf b/tests/topotests/pim_acl/r13/ospfd.conf
new file mode 100644 (file)
index 0000000..830c5a1
--- /dev/null
@@ -0,0 +1,14 @@
+hostname r13
+!
+debug ospf event
+!
+interface r13-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.13
+ network 192.168.0.13/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r13/pimd.conf b/tests/topotests/pim_acl/r13/pimd.conf
new file mode 100644 (file)
index 0000000..2ff1743
--- /dev/null
@@ -0,0 +1,17 @@
+hostname r13
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.13 239.100.0.32/27
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r13-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r13/zebra.conf b/tests/topotests/pim_acl/r13/zebra.conf
new file mode 100644 (file)
index 0000000..f9ff27a
--- /dev/null
@@ -0,0 +1,13 @@
+!
+hostname r13
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.13/32
+!
+interface r13-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.13/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r14/acl_4_pim_join.json b/tests/topotests/pim_acl/r14/acl_4_pim_join.json
new file mode 100644 (file)
index 0000000..46d86dd
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "r14-eth0":{
+    "name":"r14-eth0",
+    "state":"up",
+    "address":"192.168.101.14",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.255":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.255",
+        "prune":"--:--",
+        "channelJoinName":"JOIN",
+        "protocolPim":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r14/acl_5_pim_join.json b/tests/topotests/pim_acl/r14/acl_5_pim_join.json
new file mode 100644 (file)
index 0000000..2b291a8
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "r14-eth0":{
+    "name":"r14-eth0",
+    "state":"up",
+    "address":"192.168.101.14",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.97":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.97",
+        "prune":"--:--",
+        "channelJoinName":"JOIN",
+        "protocolPim":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r14/ospfd.conf b/tests/topotests/pim_acl/r14/ospfd.conf
new file mode 100644 (file)
index 0000000..422e4c0
--- /dev/null
@@ -0,0 +1,14 @@
+hostname r14
+!
+debug ospf event
+!
+interface r14-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.14
+ network 192.168.0.14/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r14/pimd.conf b/tests/topotests/pim_acl/r14/pimd.conf
new file mode 100644 (file)
index 0000000..1324a9e
--- /dev/null
@@ -0,0 +1,18 @@
+hostname r14
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.14 239.100.0.96/28
+ip pim rp 192.168.0.14 239.100.0.128/25
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r14-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r14/zebra.conf b/tests/topotests/pim_acl/r14/zebra.conf
new file mode 100644 (file)
index 0000000..8761b46
--- /dev/null
@@ -0,0 +1,13 @@
+!
+hostname r14
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.14/32
+!
+interface r14-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.14/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r15/acl_6_pim_join.json b/tests/topotests/pim_acl/r15/acl_6_pim_join.json
new file mode 100644 (file)
index 0000000..05fed4e
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "r15-eth0":{
+    "name":"r15-eth0",
+    "state":"up",
+    "address":"192.168.101.15",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.70":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.70",
+        "prune":"--:--",
+        "channelJoinName":"JOIN",
+        "protocolPim":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_acl/r15/ospfd.conf b/tests/topotests/pim_acl/r15/ospfd.conf
new file mode 100644 (file)
index 0000000..cd4d7b3
--- /dev/null
@@ -0,0 +1,14 @@
+hostname r15
+!
+debug ospf event
+!
+interface r15-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.15
+ network 192.168.0.15/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r15/pimd.conf b/tests/topotests/pim_acl/r15/pimd.conf
new file mode 100644 (file)
index 0000000..f47e78c
--- /dev/null
@@ -0,0 +1,17 @@
+hostname r15
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.15 239.100.0.64/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r15-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r15/zebra.conf b/tests/topotests/pim_acl/r15/zebra.conf
new file mode 100644 (file)
index 0000000..f6909dd
--- /dev/null
@@ -0,0 +1,13 @@
+!
+hostname r15
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.15/32
+!
+interface r15-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.15/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/test_pim_acl.py b/tests/topotests/pim_acl/test_pim_acl.py
new file mode 100755 (executable)
index 0000000..848f7fa
--- /dev/null
@@ -0,0 +1,418 @@
+#!/usr/bin/env python
+
+#
+# test_pim_acl.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_pim_acl.py: Test PIM with RP selection using ACLs
+"""
+
+# Test PIM RP selection with ACLs
+#
+# Testing RP selection with ACLs. R1 uses multiple ACLs
+# to select desired RPs (R11 to R15)
+#
+# Test steps:
+# - setup_module()
+#     Create topology. Hosts are only using zebra/staticd,
+#     no PIM, no OSPF (using IGMPv2 for multicast)
+# - test_ospf_convergence()
+#     Wait for OSPF convergence in each VRF. OSPF is run on
+#     R1 and R11 - R15.
+# - test_pim_convergence()
+#     Wait for PIM convergence on all routers. PIM is run on
+#     R1 and R11 - R15. 
+# - test_mcast_acl_1():
+#     Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1 which
+#     should use R11 as RP
+#     Stop multicast after verification
+# - test_mcast_acl_2():
+#     Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17 which
+#     should use R12 as RP
+#     Stop multicast after verification
+# - test_mcast_acl_3():
+#     Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32 which
+#     should use R13 as RP
+#     Stop multicast after verification
+# - test_mcast_acl_4():
+#     Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255 which
+#     should use R14 as RP
+#     Stop multicast after verification
+# - test_mcast_acl_5():
+#     Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97 which
+#     should use R14 as RP
+#     Stop multicast after verification
+# - test_mcast_acl_6():
+#     Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70 which
+#     should use R15 as RP
+#     Stop multicast after verification
+# - teardown_module()
+#     shutdown topology
+#
+
+
+TOPOLOGY = """
+                                             +----------+
+                                             |  Host H2 |
+                                             |  Source  |
+                                             +----------+
+                                                .2 |
+                             +-----------+         |        +----------+
+                             |           | .1      |    .11 | Host R11 |
++---------+                  |    R1     |---------+--------| PIM RP   |
+| Host H1 | 192.168.100.0/24 |           | 192.168.101.0/24 +----------+
+| receive |------------------| uses ACLs |         |        +----------+
+|IGMP JOIN| .10           .1 |  to pick  |         |    .12 | Host R12 |
++---------+                  |    RP     |         +--------| PIM RP   |
+                             |           |         |        +----------+
+                             +-----------+         |        +----------+
+                                                   |    .13 | Host R13 |
+                                                   +--------| PIM RP   |
+                                                   |        +----------+
+                                                   |        +----------+
+                                                   |    .14 | Host R14 |
+                                                   +--------| PIM RP   |
+                                                   |        +----------+
+                                                   |        +----------+
+                                                   |    .15 | Host R15 |
+                                                   +--------| PIM RP   |
+                                                            +----------+
+"""
+
+import json
+import functools
+import os
+import sys
+import pytest
+import re
+import time
+from time import sleep
+import socket
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.pimd]
+
+
+#
+# Test global variables:
+# They are used to handle communicating with external application.
+#
+APP_SOCK_PATH = '/tmp/topotests/apps.sock'
+HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py")
+app_listener = None
+app_clients = {}
+
+def listen_to_applications():
+    "Start listening socket to connect with applications."
+    # Remove old socket.
+    try:
+        os.unlink(APP_SOCK_PATH)
+    except OSError:
+        pass
+
+    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+    sock.bind(APP_SOCK_PATH)
+    sock.listen(10)
+    global app_listener
+    app_listener = sock
+
+def accept_host(host):
+    "Accept connection from application running in hosts."
+    global app_listener, app_clients
+    conn = app_listener.accept()
+    app_clients[host] = {
+        'fd': conn[0],
+        'address': conn[1]
+    }
+
+def close_applications():
+    "Signal applications to stop and close all sockets."
+    global app_listener, app_clients
+
+    if app_listener:
+        # Close listening socket.
+        app_listener.close()
+
+        # Remove old socket.
+        try:
+            os.unlink(APP_SOCK_PATH)
+        except OSError:
+            pass
+
+    # Close all host connections.
+    for host in ["h1", "h2"]:
+        if app_clients.get(host) is None:
+            continue
+        app_clients[host]["fd"].close()
+
+    # Reset listener and clients data struct
+    app_listener = None
+    app_clients = {}
+
+
+class PIMACLTopo(Topo):
+    "PIM ACL Test Topology"
+
+    def build(self):
+        tgen = get_topogen(self)
+
+        # Create the hosts
+        for hostNum in range(1,3):
+            tgen.add_router("h{}".format(hostNum))
+
+        # Create the main router
+        tgen.add_router("r1")
+
+        # Create the PIM RP routers
+        for rtrNum in range(11, 16):
+            tgen.add_router("r{}".format(rtrNum))
+
+        # Setup Switches and connections
+        for swNum in range(1, 3):
+            tgen.add_switch("sw{}".format(swNum))
+
+        # Add connections H1 to R1 switch sw1
+        tgen.gears["h1"].add_link(tgen.gears["sw1"])
+        tgen.gears["r1"].add_link(tgen.gears["sw1"])
+
+        # Add connections R1 to R1x switch sw2
+        tgen.gears["r1"].add_link(tgen.gears["sw2"])
+        tgen.gears["h2"].add_link(tgen.gears["sw2"])
+        tgen.gears["r11"].add_link(tgen.gears["sw2"])
+        tgen.gears["r12"].add_link(tgen.gears["sw2"])
+        tgen.gears["r13"].add_link(tgen.gears["sw2"])
+        tgen.gears["r14"].add_link(tgen.gears["sw2"])
+        tgen.gears["r15"].add_link(tgen.gears["sw2"])
+
+
+#####################################################
+#
+#   Tests starting
+#
+#####################################################
+
+def setup_module(module):
+    logger.info("PIM RP ACL Topology: \n {}".format(TOPOLOGY))
+
+    tgen = Topogen(PIMACLTopo, module.__name__)
+    tgen.start_topology()
+
+    # Starting Routers
+    router_list = tgen.routers()
+
+    for rname, router in router_list.items():
+        logger.info("Loading router %s" % rname)
+        router.load_config(
+            TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+        )
+        if rname[0] != 'h':
+            # Only load ospf on routers, not on end hosts
+            router.load_config(
+                TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+            )
+            router.load_config(
+                TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
+            )
+    tgen.start_router()
+
+
+def teardown_module(module):
+    tgen = get_topogen()
+    tgen.stop_topology()
+    close_applications()
+
+
+def test_ospf_convergence():
+    "Test for OSPFv2 convergence"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Checking OSPFv2 convergence on router r1")
+
+    router = tgen.gears["r1"]
+    reffile = os.path.join(CWD, "r1/ospf_neighbor.json")
+    expected = json.loads(open(reffile).read())
+
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip ospf neighbor json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "OSPF router R1 did not converge"
+    assert res is None, assertmsg
+
+
+def test_pim_convergence():
+    "Test for PIM convergence"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Checking PIM convergence on router r1")
+
+    router = tgen.gears["r1"]
+    reffile = os.path.join(CWD, "r1/pim_neighbor.json")
+    expected = json.loads(open(reffile).read())
+
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip pim neighbor json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "PIM router R1 did not converge"
+    assert res is None, assertmsg
+
+
+
+def check_mcast_entry(entry, mcastaddr, pimrp):
+    "Helper function to check RP"
+    tgen = get_topogen()
+
+    logger.info("Testing PIM RP selection for ACL {} entry using {}".format(entry, mcastaddr));
+
+    # Start applications socket.
+    listen_to_applications()
+
+    tgen.gears["h2"].run("{} --send='0.7' '{}' '{}' '{}' &".format(
+        HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, 'h2-eth0'))
+    accept_host("h2")
+
+    tgen.gears["h1"].run("{} '{}' '{}' '{}' &".format(
+        HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, 'h1-eth0'))
+    accept_host("h1")
+
+    logger.info("mcast join and source for {} started".format(mcastaddr))
+
+    # tgen.mininet_cli()
+
+    router = tgen.gears["r1"]
+    reffile = os.path.join(CWD, "r1/acl_{}_pim_join.json".format(entry))
+    expected = json.loads(open(reffile).read())
+
+    logger.info("verifying pim join on r1 for {}".format(mcastaddr))
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip pim join json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "PIM router r1 did not show join status"
+    assert res is None, assertmsg
+
+    logger.info("verifying pim join on PIM RP {} for {}".format(pimrp, mcastaddr))
+    router = tgen.gears[pimrp]
+    reffile = os.path.join(CWD, "{}/acl_{}_pim_join.json".format(pimrp, entry))
+    expected = json.loads(open(reffile).read())
+
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip pim join json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "PIM router {} did not get selected as the PIM RP".format(pimrp)
+    assert res is None, assertmsg
+
+    close_applications()
+    return
+
+
+def test_mcast_acl_1():
+    "Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    check_mcast_entry(1, '239.100.0.1', 'r11')
+
+
+def test_mcast_acl_2():
+    "Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    check_mcast_entry(2, '239.100.0.17', 'r12')
+
+
+def test_mcast_acl_3():
+    "Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    check_mcast_entry(3, '239.100.0.32', 'r13')
+
+
+def test_mcast_acl_4():
+    "Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    check_mcast_entry(4, '239.100.0.255', 'r14')
+
+
+def test_mcast_acl_5():
+    "Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    check_mcast_entry(5, '239.100.0.97', 'r14')
+
+
+def test_mcast_acl_6():
+    "Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    check_mcast_entry(6, '239.100.0.70', 'r15')
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index f64a46deb38b24b160d9b91d53577eb0f5d6dce1..737019fa51470b367451f567ee9a77dc8c60b707 100644 (file)
@@ -15,3 +15,4 @@ interface lo
   ip pim
 !
 ip pim rp 10.254.0.3
+ip pim join-prune-interval 5
index 6e35c97971cd2a145cd0018716b32fa915d11c2c..fd26bc4d712d62263e987a7cc15947e924786349 100644 (file)
@@ -6,6 +6,7 @@ interface rp-eth0
 interface lo
   ip pim
 !
+ip pim join-prune-interval 5
 ip pim rp 10.254.0.3
 ip pim register-accept-list ACCEPT
 
index 0b32ded19aa1a9b02021e005bd73d96d5f7ab459..9f389deb11aeb3b785ac259ea4c944824ddf7f99 100644 (file)
@@ -10,3 +10,4 @@ interface r2-eth2
  ip pim
  ip pim bfd
 !
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_igmp_vrf/h1/zebra.conf b/tests/topotests/pim_igmp_vrf/h1/zebra.conf
new file mode 100644 (file)
index 0000000..3d6540d
--- /dev/null
@@ -0,0 +1,10 @@
+!
+hostname h1
+log file zebra.log
+!
+interface h1-eth0
+ description connection to r1 via sw1
+ ip address 192.168.100.10/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h2/zebra.conf b/tests/topotests/pim_igmp_vrf/h2/zebra.conf
new file mode 100644 (file)
index 0000000..95342f9
--- /dev/null
@@ -0,0 +1,8 @@
+hostname h2
+!
+interface h2-eth0
+ description connection to r1 via sw2
+ ip address 192.168.101.2/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h3/zebra.conf b/tests/topotests/pim_igmp_vrf/h3/zebra.conf
new file mode 100644 (file)
index 0000000..ef99b1c
--- /dev/null
@@ -0,0 +1,10 @@
+!
+hostname h3
+log file zebra.log
+!
+interface h3-eth0
+ description connection to r1 via sw3
+ ip address 192.168.100.20/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h4/zebra.conf b/tests/topotests/pim_igmp_vrf/h4/zebra.conf
new file mode 100644 (file)
index 0000000..6a2e466
--- /dev/null
@@ -0,0 +1,8 @@
+hostname h4
+!
+interface h4-eth0
+ description connection to r1 via sw4
+ ip address 192.168.101.4/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json
new file mode 100644 (file)
index 0000000..604d25f
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "blue":{
+    "vrfName":"blue",
+    "neighbors":{
+      "192.168.0.11":[
+        {
+          "priority":10,
+          "state":"Full\/Backup",
+          "address":"192.168.101.11",
+          "ifaceName":"r1-eth1:192.168.101.1"
+        }
+      ]
+    }
+  }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json
new file mode 100644 (file)
index 0000000..456bb87
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "red":{
+    "vrfName":"red",
+    "neighbors":{
+      "192.168.0.12":[
+        {
+          "priority":10,
+          "state":"Full\/Backup",
+          "address":"192.168.101.12",
+          "ifaceName":"r1-eth3:192.168.101.1"
+        }
+      ]
+    }
+  }
+}
+
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospfd.conf b/tests/topotests/pim_igmp_vrf/r1/ospfd.conf
new file mode 100644 (file)
index 0000000..263b586
--- /dev/null
@@ -0,0 +1,26 @@
+hostname r1
+!
+debug ospf event
+!
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+interface r1-eth3
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+router ospf vrf blue
+ ospf router-id 192.168.0.1
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+router ospf vrf red
+ ospf router-id 192.168.0.1
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json
new file mode 100644 (file)
index 0000000..8568bae
--- /dev/null
@@ -0,0 +1,22 @@
+{
+  "r1-eth0":{
+    "name":"r1-eth0",
+    "state":"up",
+    "address":"192.168.100.1",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.1":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.1",
+        "upTime":"--:--:--",
+        "expire":"--:--",
+        "prune":"--:--",
+        "channelJoinName":"NOINFO",
+        "protocolIgmp":1
+      }
+    }
+  }
+}
+
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json
new file mode 100644 (file)
index 0000000..ea7d4ac
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "blue":{
+  },
+  "r1-eth0":{
+  },
+  "r1-eth1":{
+    "192.168.101.11":{
+      "interface":"r1-eth1",
+      "neighbor":"192.168.101.11",
+      "drPriority":1
+    }
+  }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json
new file mode 100644 (file)
index 0000000..d3642f8
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "pimreg11":{
+    "name":"pimreg11",
+    "state":"up",
+    "address":"0.0.0.0",
+    "flagAllMulticast":true,
+    "lanDelayEnabled":true,
+    "drAddress":"*",
+    "drPriority":1,
+    "drUptime":"--:--:--",
+    "drElections":0,
+    "drChanges":0
+  }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json
new file mode 100644 (file)
index 0000000..d0037ca
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "r1-eth2":{
+    "name":"r1-eth2",
+    "state":"up",
+    "address":"192.168.100.1",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.1":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.1",
+        "upTime":"--:--:--",
+        "expire":"--:--",
+        "prune":"--:--",
+        "channelJoinName":"NOINFO",
+        "protocolIgmp":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json
new file mode 100644 (file)
index 0000000..e17b408
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "r1-eth2":{
+  },
+  "r1-eth3":{
+    "192.168.101.12":{
+      "interface":"r1-eth3",
+      "neighbor":"192.168.101.12",
+      "drPriority":1
+    }
+  },
+  "red":{
+  }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json
new file mode 100644 (file)
index 0000000..45b6cd9
--- /dev/null
@@ -0,0 +1,14 @@
+{
+  "pimreg12":{
+    "name":"pimreg12",
+    "state":"up",
+    "address":"0.0.0.0",
+    "flagAllMulticast":true,
+    "lanDelayEnabled":true,
+    "drAddress":"*",
+    "drPriority":1,
+    "drUptime":"--:--:--",
+    "drElections":0,
+    "drChanges":0
+  }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pimd.conf b/tests/topotests/pim_igmp_vrf/r1/pimd.conf
new file mode 100644 (file)
index 0000000..f04c255
--- /dev/null
@@ -0,0 +1,27 @@
+hostname r1
+!
+debug igmp events
+debug igmp packets
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+interface r1-eth0
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+interface r1-eth2
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth3
+ ip pim
+!
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_igmp_vrf/r1/zebra.conf b/tests/topotests/pim_igmp_vrf/r1/zebra.conf
new file mode 100644 (file)
index 0000000..9da9280
--- /dev/null
@@ -0,0 +1,30 @@
+!
+hostname r1
+log file zebra.log
+!
+ip forwarding
+ipv6 forwarding
+!
+interface blue vrf blue
+ ip address 192.168.0.1/32
+!
+interface red vrf red
+ ip address 192.168.0.1/32
+!
+interface r1-eth0 vrf blue
+ description connection to h1 via sw1
+ ip address 192.168.100.1/24
+!
+interface r1-eth1 vrf blue
+ description connection to r11 via sw2
+ ip address 192.168.101.1/24
+!
+interface r1-eth2 vrf red
+ description connection to h1 via sw3
+ ip address 192.168.100.1/24
+!
+interface r1-eth3 vrf red
+ description connection to r12 via sw4
+ ip address 192.168.101.1/24
+!
+
diff --git a/tests/topotests/pim_igmp_vrf/r11/ospfd.conf b/tests/topotests/pim_igmp_vrf/r11/ospfd.conf
new file mode 100644 (file)
index 0000000..e107220
--- /dev/null
@@ -0,0 +1,14 @@
+hostname r11
+!
+debug ospf event
+!
+interface r11-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.11
+ network 192.168.0.11/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json b/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json
new file mode 100644 (file)
index 0000000..289bf51
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "r11-eth0":{
+    "name":"r11-eth0",
+    "state":"up",
+    "address":"192.168.101.11",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.1":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.1",
+        "prune":"--:--",
+        "channelJoinName":"JOIN",
+        "protocolPim":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r11/pimd.conf b/tests/topotests/pim_igmp_vrf/r11/pimd.conf
new file mode 100644 (file)
index 0000000..b1d4520
--- /dev/null
@@ -0,0 +1,17 @@
+hostname r11
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.11 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r11-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_igmp_vrf/r11/zebra.conf b/tests/topotests/pim_igmp_vrf/r11/zebra.conf
new file mode 100644 (file)
index 0000000..137706d
--- /dev/null
@@ -0,0 +1,13 @@
+!
+hostname r11
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.11/32
+!
+interface r11-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.11/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/ospfd.conf b/tests/topotests/pim_igmp_vrf/r12/ospfd.conf
new file mode 100644 (file)
index 0000000..03acc82
--- /dev/null
@@ -0,0 +1,14 @@
+hostname r12
+!
+debug ospf event
+!
+interface r12-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.12
+ network 192.168.0.12/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json b/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json
new file mode 100644 (file)
index 0000000..6926246
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "r12-eth0":{
+    "name":"r12-eth0",
+    "state":"up",
+    "address":"192.168.101.12",
+    "flagMulticast":true,
+    "flagBroadcast":true,
+    "lanDelayEnabled":true,
+    "239.100.0.1":{
+      "*":{
+        "source":"*",
+        "group":"239.100.0.1",
+        "prune":"--:--",
+        "channelJoinName":"JOIN",
+        "protocolPim":1
+      }
+    }
+  }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r12/pimd.conf b/tests/topotests/pim_igmp_vrf/r12/pimd.conf
new file mode 100644 (file)
index 0000000..5cb76ef
--- /dev/null
@@ -0,0 +1,17 @@
+hostname r12
+!
+debug pim events
+debug pim packets
+debug pim trace
+debug pim zebra
+debug pim bsm
+!
+ip pim rp 192.168.0.12 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r12-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/zebra.conf b/tests/topotests/pim_igmp_vrf/r12/zebra.conf
new file mode 100644 (file)
index 0000000..bede104
--- /dev/null
@@ -0,0 +1,13 @@
+!
+hostname r12
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.12/32
+!
+interface r12-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.12/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/test_pim_vrf.py b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py
new file mode 100755 (executable)
index 0000000..298adef
--- /dev/null
@@ -0,0 +1,462 @@
+#!/usr/bin/env python
+
+#
+# test_pim_vrf.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_pim_vrf.py: Test PIM with VRFs.
+"""
+
+# Tests PIM with VRF
+#
+# R1 is split into 2 VRF: Blue and Red, the others are normal
+# routers and Hosts
+# There are 2 similar topologies with overlapping IPs in each
+# section. 
+#
+# Test steps:
+# - setup_module()
+#     Create topology. Hosts are only using zebra/staticd,
+#     no PIM, no OSPF (using IGMPv2 for multicast)
+# - test_ospf_convergence()
+#     Wait for OSPF convergence in each VRF. OSPF is run on
+#     R1, R11 and R12.
+# - test_pim_convergence()
+#     Wait for PIM convergence in each VRF. PIM is run on
+#     R1, R11 and R12. R11 is the RP for vrf blue, R12 is RP
+#     for vrf red.
+# - test_vrf_pimreg_interfaces()
+#     Adding PIM RP in VRF information and verify pimreg 
+#     interfaces in VRF blue and red
+# - test_mcast_vrf_blue()
+#     Start multicast stream for group 239.100.0.1 from Host 
+#     H2 and join from Host H1 on vrf blue
+#     Verify PIM JOIN status on R1 and R11
+#     Stop multicast after verification
+# - test_mcast_vrf_red()
+#     Start multicast stream for group 239.100.0.1 from Host 
+#     H4 and join from Host H3 on vrf blue
+#     Verify PIM JOIN status on R1 and R12
+#     Stop multicast after verification
+# - teardown_module(module)
+#     shutdown topology
+#
+
+TOPOLOGY = """
+                                               +----------+
+                                               |  Host H2 |
+                                               |  Source  |
+                                               +----------+
+                                                 .2 |
++---------+                  +------------+         |        +---------+
+| Host H1 | 192.168.100.0/24 |            | .1      |    .11 | Host H2 |
+| receive |------------------|  VRF Blue  |---------+--------| PIM RP  |
+|IGMP JOIN| .10           .1 |            | 192.168.101.0/24 |         |  
++---------+                  |            |                  +---------+
+                            =| = = R1 = = |=
++---------+                  |            |                  +---------+
+| Host H3 | 192.168.100.0/24 |            | 192.168.101.0/24 | Host H4 |
+| receive |------------------|  VRF Red   |---------+--------| PIM RP  |
+|IGMP JOIN| .20           .1 |            | .1      |    .12 |         |
++---------+                  +------------+         |        +---------+
+                                                 .4 | 
+                                               +----------+
+                                               |  Host H4 |
+                                               |  Source  |
+                                               +----------+
+"""
+
+import json
+import functools
+import os
+import sys
+import pytest
+import re
+import time
+from time import sleep
+import socket
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.topotest import iproute2_is_vrf_capable
+from lib.common_config import (
+    required_linux_kernel_version)
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+pytestmark = [pytest.mark.pimd]
+
+
+#
+# Test global variables:
+# They are used to handle communicating with external application.
+#
+APP_SOCK_PATH = '/tmp/topotests/apps.sock'
+HELPER_APP_PATH = os.path.join(CWD, "../lib/mcast-tester.py")
+app_listener = None
+app_clients = {}
+
+def listen_to_applications():
+    "Start listening socket to connect with applications."
+    # Remove old socket.
+    try:
+        os.unlink(APP_SOCK_PATH)
+    except OSError:
+        pass
+
+    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+    sock.bind(APP_SOCK_PATH)
+    sock.listen(10)
+    global app_listener
+    app_listener = sock
+
+def accept_host(host):
+    "Accept connection from application running in hosts."
+    global app_listener, app_clients
+    conn = app_listener.accept()
+    app_clients[host] = {
+        'fd': conn[0],
+        'address': conn[1]
+    }
+
+def close_applications():
+    "Signal applications to stop and close all sockets."
+    global app_listener, app_clients
+
+    if app_listener:
+        # Close listening socket.
+        app_listener.close()
+
+        # Remove old socket.
+        try:
+            os.unlink(APP_SOCK_PATH)
+        except OSError:
+            pass
+
+    # Close all host connections.
+    for host in ["h1", "h2"]:
+        if app_clients.get(host) is None:
+            continue
+        app_clients[host]["fd"].close()
+
+    # Reset listener and clients data struct
+    app_listener = None
+    app_clients = {}
+
+
+class PIMVRFTopo(Topo):
+    "PIM VRF Test Topology"
+
+    def build(self):
+        tgen = get_topogen(self)
+
+        # Create the hosts
+        for hostNum in range(1,5):
+            tgen.add_router("h{}".format(hostNum))
+
+        # Create the main router
+        tgen.add_router("r1")
+
+        # Create the PIM RP routers
+        for rtrNum in range(11, 13):
+            tgen.add_router("r{}".format(rtrNum))
+
+        # Setup Switches and connections
+        for swNum in range(1, 5):
+            tgen.add_switch("sw{}".format(swNum))
+        
+        ################
+        # 1st set of connections to routers for VRF red
+        ################
+
+        # Add connections H1 to R1 switch sw1
+        tgen.gears["h1"].add_link(tgen.gears["sw1"])
+        tgen.gears["r1"].add_link(tgen.gears["sw1"])
+
+        # Add connections R1 to R1x switch sw2
+        tgen.gears["r1"].add_link(tgen.gears["sw2"])
+        tgen.gears["h2"].add_link(tgen.gears["sw2"])
+        tgen.gears["r11"].add_link(tgen.gears["sw2"])
+
+        ################
+        # 2nd set of connections to routers for vrf blue
+        ################
+
+        # Add connections H1 to R1 switch sw1
+        tgen.gears["h3"].add_link(tgen.gears["sw3"])
+        tgen.gears["r1"].add_link(tgen.gears["sw3"])
+
+        # Add connections R1 to R1x switch sw2
+        tgen.gears["r1"].add_link(tgen.gears["sw4"])
+        tgen.gears["h4"].add_link(tgen.gears["sw4"])
+        tgen.gears["r12"].add_link(tgen.gears["sw4"])
+
+#####################################################
+#
+#   Tests starting
+#
+#####################################################
+
+def setup_module(module):
+    logger.info("PIM IGMP VRF Topology: \n {}".format(TOPOLOGY))
+
+    tgen = Topogen(PIMVRFTopo, module.__name__)
+    tgen.start_topology()
+
+    vrf_setup_cmds = [
+        "ip link add name blue type vrf table 11",
+        "ip link add name red type vrf table 12",
+        "ip link set dev blue up",
+        "ip link set dev red up",
+        "ip link set dev r1-eth0 vrf blue up",
+        "ip link set dev r1-eth1 vrf blue up",
+        "ip link set dev r1-eth2 vrf red up",
+        "ip link set dev r1-eth3 vrf red up",
+    ]
+
+    # Starting Routers
+    router_list = tgen.routers()
+
+    # Create VRF on r2 first and add it's interfaces
+    for cmd in vrf_setup_cmds:
+        tgen.net["r1"].cmd(cmd)
+
+    for rname, router in router_list.items():
+        logger.info("Loading router %s" % rname)
+        router.load_config(
+            TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+        )
+        if rname[0] != 'h':
+            # Only load ospf on routers, not on end hosts
+            router.load_config(
+                TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+            )
+            router.load_config(
+                TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
+            )
+    tgen.start_router()
+
+
+def teardown_module(module):
+    tgen = get_topogen()
+    tgen.stop_topology()
+    close_applications()
+
+
+def test_ospf_convergence():
+    "Test for OSPFv2 convergence"
+    tgen = get_topogen()
+
+    # Required linux kernel version for this suite to run.
+    result = required_linux_kernel_version("4.15")
+    if result is not True:
+        pytest.skip("Kernel requirements are not met")
+
+    # iproute2 needs to support VRFs for this suite to run.
+    if not iproute2_is_vrf_capable():
+        pytest.skip("Installed iproute2 version does not support VRFs")
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Checking OSPFv2 convergence on router r1 for VRF blue")
+
+    router = tgen.gears["r1"]
+    reffile = os.path.join(CWD, "r1/ospf_blue_neighbor.json")
+    expected = json.loads(open(reffile).read())
+
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip ospf vrf blue neighbor json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "OSPF router R1 did not converge on VRF blue"
+    assert res is None, assertmsg
+
+    logger.info("Checking OSPFv2 convergence on router r1 for VRF red")
+
+    router = tgen.gears["r1"]
+    reffile = os.path.join(CWD, "r1/ospf_red_neighbor.json")
+    expected = json.loads(open(reffile).read())
+
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip ospf vrf red neighbor json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+    assertmsg = "OSPF router R1 did not converge on VRF red"
+    assert res is None, assertmsg
+
+
+def test_pim_convergence():
+    "Test for PIM convergence"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    logger.info("Checking PIM convergence on router r1 for VRF red")
+
+    router = tgen.gears["r1"]
+    reffile = os.path.join(CWD, "r1/pim_red_neighbor.json")
+    expected = json.loads(open(reffile).read())
+
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip pim vrf red neighbor json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+    assertmsg = "PIM router R1 did not converge for VRF red"
+    assert res is None, assertmsg
+
+    logger.info("Checking PIM convergence on router r1 for VRF blue")
+
+    router = tgen.gears["r1"]
+    reffile = os.path.join(CWD, "r1/pim_blue_neighbor.json")
+    expected = json.loads(open(reffile).read())
+
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip pim vrf blue neighbor json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+    assertmsg = "PIM router R1 did not converge for VRF blue"
+    assert res is None, assertmsg
+
+
+def test_vrf_pimreg_interfaces():
+    "Adding PIM RP in VRF information and verify pimreg interfaces"
+    tgen = get_topogen()
+
+    r1 = tgen.gears["r1"]
+    r1.vtysh_cmd("conf\ninterface blue\nip pim")
+    r1.vtysh_cmd("conf\nvrf blue\nip pim rp 192.168.0.11 239.100.0.1/32\nexit-vrf")
+
+    # Check pimreg11 interface on R1, VRF blue
+    reffile = os.path.join(CWD, "r1/pim_blue_pimreg11.json")
+    expected = json.loads(open(reffile).read())
+    test_func = functools.partial(
+        topotest.router_json_cmp, r1, "show ip pim vrf blue inter pimreg11 json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=5, wait=2)
+    assertmsg = "PIM router R1, VRF blue (table 11) pimreg11 interface missing or incorrect status"
+    assert res is None, assertmsg
+
+    r1.vtysh_cmd("conf\ninterface red\nip pim")
+    r1.vtysh_cmd("conf\nvrf red\nip pim rp 192.168.0.12 239.100.0.1/32\nexit-vrf")
+
+    # Check pimreg12 interface on R1, VRF red
+    reffile = os.path.join(CWD, "r1/pim_red_pimreg12.json")
+    expected = json.loads(open(reffile).read())
+    test_func = functools.partial(
+        topotest.router_json_cmp, r1, "show ip pim vrf red inter pimreg12 json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=5, wait=2)
+    assertmsg = "PIM router R1, VRF red (table 12) pimreg12 interface missing or incorrect status"
+    assert res is None, assertmsg
+
+
+##################################
+###  Test PIM / IGMP with VRF
+##################################
+
+def check_mcast_entry(mcastaddr, pimrp, receiver, sender, vrf):
+    "Helper function to check RP"
+    tgen = get_topogen()
+
+    logger.info("Testing PIM for VRF {} entry using {}".format(vrf, mcastaddr));
+
+    # Start applications socket.
+    listen_to_applications()
+
+    tgen.gears[sender].run("{} --send='0.7' '{}' '{}' '{}' &".format(
+        HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, '{}-eth0'.format(sender)))
+    accept_host(sender)
+
+    tgen.gears[receiver].run("{} '{}' '{}' '{}' &".format(
+        HELPER_APP_PATH, APP_SOCK_PATH, mcastaddr, '{}-eth0'.format(receiver)))
+    accept_host(receiver)
+
+    logger.info("mcast join and source for {} started".format(mcastaddr))
+
+    # tgen.mininet_cli()
+
+    router = tgen.gears["r1"]
+    reffile = os.path.join(CWD, "r1/pim_{}_join.json".format(vrf))
+    expected = json.loads(open(reffile).read())
+
+    logger.info("verifying pim join on r1 for {} on VRF {}".format(mcastaddr, vrf))
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip pim vrf {} join json".format(vrf),
+        expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
+    assertmsg = "PIM router r1 did not show join status on VRF".format(vrf)
+    assert res is None, assertmsg
+
+    logger.info("verifying pim join on PIM RP {} for {}".format(pimrp, mcastaddr))
+    router = tgen.gears[pimrp]
+    reffile = os.path.join(CWD, "{}/pim_{}_join.json".format(pimrp, vrf))
+    expected = json.loads(open(reffile).read())
+
+    test_func = functools.partial(
+        topotest.router_json_cmp, router, "show ip pim join json", expected
+    )
+    _, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
+    assertmsg = "PIM router {} did not get selected as the PIM RP for VRF {}".format(pimrp, vrf)
+    assert res is None, assertmsg
+
+    close_applications()
+    return
+
+
+def test_mcast_vrf_blue():
+    "Test vrf blue with 239.100.0.1"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    check_mcast_entry('239.100.0.1', 'r11', 'h1', 'h2', 'blue')
+
+
+def test_mcast_vrf_red():
+    "Test vrf red with 239.100.0.1"
+    tgen = get_topogen()
+
+    # Skip if previous fatal error condition is raised
+    if tgen.routers_have_failure():
+        pytest.skip(tgen.errors)
+
+    check_mcast_entry('239.100.0.1', 'r12', 'h3', 'h4', 'red')
+
+
+if __name__ == "__main__":
+    args = ["-s"] + sys.argv[1:]
+    sys.exit(pytest.main(args))
index dd3f44867406e8a2f3a1608cd3b99a03d30f28ba..5cee0aaa3f4444c36aea295c9d6f9845abbb1bfb 100644 (file)
@@ -478,14 +478,6 @@ static int vtysh_execute_func(const char *line, int pager)
        if (vline == NULL)
                return CMD_SUCCESS;
 
-       if (user_mode) {
-               if (strncmp("en", vector_slot(vline, 0), 2) == 0) {
-                       cmd_free_strvec(vline);
-                       vty_out(vty, "%% Command not allowed: enable\n");
-                       return CMD_WARNING;
-               }
-       }
-
        if (vtysh_add_timestamp && strncmp(line, "exit", 4)) {
                char ts[48];
 
@@ -2803,6 +2795,18 @@ DEFUNSH(VTYSH_INTERFACE, vtysh_quit_interface, vtysh_quit_interface_cmd, "quit",
        return vtysh_exit_interface(self, vty, argc, argv);
 }
 
+DEFUNSH(VTYSH_ZEBRA, vtysh_exit_pseudowire, vtysh_exit_pseudowire_cmd, "exit",
+       "Exit current mode and down to previous mode\n")
+{
+       return vtysh_exit(vty);
+}
+
+DEFUNSH(VTYSH_ZEBRA, vtysh_quit_pseudowire, vtysh_quit_pseudowire_cmd, "quit",
+       "Exit current mode and down to previous mode\n")
+{
+       return vtysh_exit_pseudowire(self, vty, argc, argv);
+}
+
 static char *do_prepend(struct vty *vty, struct cmd_token **argv, int argc)
 {
        const char *argstr[argc + 1];
@@ -2907,6 +2911,20 @@ DEFUNSH(VTYSH_ZEBRA, exit_link_params, exit_link_params_cmd, "exit-link-params",
        return CMD_SUCCESS;
 }
 
+DEFUNSH(VTYSH_ZEBRA, vtysh_exit_link_params, vtysh_exit_link_params_cmd, "exit",
+       "Exit current mode and down to previous mode\n")
+{
+       if (vty->node == LINK_PARAMS_NODE)
+               vty->node = INTERFACE_NODE;
+       return CMD_SUCCESS;
+}
+
+DEFUNSH(VTYSH_ZEBRA, vtysh_quit_link_params, vtysh_quit_link_params_cmd, "quit",
+       "Exit current mode and down to previous mode\n")
+{
+       return vtysh_exit_link_params(self, vty, argc, argv);
+}
+
 DEFUNSH_HIDDEN (0x00,
                 vtysh_debug_all,
                 vtysh_debug_all_cmd,
@@ -4445,13 +4463,14 @@ void vtysh_init_vty(void)
        install_element(INTERFACE_NODE, &vtysh_link_params_cmd);
        install_element(LINK_PARAMS_NODE, &exit_link_params_cmd);
        install_element(LINK_PARAMS_NODE, &vtysh_end_all_cmd);
-       install_element(LINK_PARAMS_NODE, &vtysh_exit_interface_cmd);
+       install_element(LINK_PARAMS_NODE, &vtysh_exit_link_params_cmd);
+       install_element(LINK_PARAMS_NODE, &vtysh_quit_link_params_cmd);
 
        install_node(&pw_node);
        install_element(CONFIG_NODE, &vtysh_pseudowire_cmd);
        install_element(PW_NODE, &vtysh_end_all_cmd);
-       install_element(PW_NODE, &vtysh_exit_interface_cmd);
-       install_element(PW_NODE, &vtysh_quit_interface_cmd);
+       install_element(PW_NODE, &vtysh_exit_pseudowire_cmd);
+       install_element(PW_NODE, &vtysh_quit_pseudowire_cmd);
 
        install_node(&vrf_node);
        install_element(CONFIG_NODE, &vtysh_vrf_cmd);
@@ -4485,7 +4504,8 @@ void vtysh_init_vty(void)
 
        /* vtysh */
 
-       install_element(VIEW_NODE, &vtysh_enable_cmd);
+       if (!user_mode)
+               install_element(VIEW_NODE, &vtysh_enable_cmd);
        install_element(ENABLE_NODE, &vtysh_config_terminal_cmd);
        install_element(ENABLE_NODE, &vtysh_disable_cmd);
 
index 1c990b5ed9e01845e9c8c4f884e8e24e70dc3880..e11883a803470e3a1d05a58ed0da45dc31fecb6f 100644 (file)
@@ -36,6 +36,12 @@ module frr-bgp-route-map {
       "Initial revision";
   }
 
+  identity match-alias {
+    base frr-route-map:rmap-match-type;
+    description
+      "Match BGP community alias name";
+  }
+
   identity match-local-preference {
     base frr-route-map:rmap-match-type;
     description
@@ -352,7 +358,14 @@ module frr-bgp-route-map {
       }
     }
 
-    case script { 
+    case alias {
+      when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'frr-bgp-route-map:match-alias')";
+      leaf alias {
+        type string;
+      }
+    }
+
+    case script {
       when "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:match-condition/frr-route-map:condition, 'frr-bgp-route-map:match-script')";
       leaf script {
         type string;
index 6329e45588990849249e144d74eafba4b7c1a156..e846ffa1f8a6c6fbff771cdcf592145b0b693ad9 100644 (file)
@@ -351,8 +351,8 @@ module frr-pim {
     }
 
     leaf hello-holdtime {
-      type uint8 {
-        range "1..180";
+      type uint16 {
+        range "1..630";
       }
       must ". > ./../hello-interval" {
       error-message "HoldTime must be greater than Hello";
@@ -529,7 +529,7 @@ module frr-pim {
     }
     leaf join-prune-interval {
       type uint16 {
-        range "60..600";
+        range "5..600";
       }
       default "60";
       description
index c2b4dcc52f8fa2c0aa8b12644d2ae17b1fb5996b..a51e0b82cb28bbb1062b4f9b08e996ccc1349c0d 100644 (file)
@@ -706,6 +706,9 @@ static int netlink_bridge_vxlan_update(struct interface *ifp,
        struct bridge_vlan_info *vinfo;
        vlanid_t access_vlan;
 
+       if (!af_spec)
+               return 0;
+
        /* There is a 1-to-1 mapping of VLAN to VxLAN - hence
         * only 1 access VLAN is accepted.
         */
@@ -742,23 +745,26 @@ static void netlink_bridge_vlan_update(struct interface *ifp,
        /* create a new bitmap space for re-eval */
        bf_init(zif->vlan_bitmap, IF_VLAN_BITMAP_MAX);
 
-       for (i = RTA_DATA(af_spec), rem = RTA_PAYLOAD(af_spec);
-                       RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
+       if (af_spec) {
+               for (i = RTA_DATA(af_spec), rem = RTA_PAYLOAD(af_spec);
+                    RTA_OK(i, rem); i = RTA_NEXT(i, rem)) {
 
-               if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
-                       continue;
+                       if (i->rta_type != IFLA_BRIDGE_VLAN_INFO)
+                               continue;
 
-               vinfo = RTA_DATA(i);
+                       vinfo = RTA_DATA(i);
 
-               if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
-                       vid_range_start = vinfo->vid;
-                       continue;
-               }
+                       if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
+                               vid_range_start = vinfo->vid;
+                               continue;
+                       }
 
-               if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
-                       vid_range_start = vinfo->vid;
+                       if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END))
+                               vid_range_start = vinfo->vid;
 
-               zebra_vlan_bitmap_compute(ifp, vid_range_start, vinfo->vid);
+                       zebra_vlan_bitmap_compute(ifp, vid_range_start,
+                                                 vinfo->vid);
+               }
        }
 
        zebra_vlan_mbr_re_eval(ifp, old_vlan_bitmap);
@@ -794,8 +800,6 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id,
 
        /* We are only interested in the access VLAN i.e., AF_SPEC */
        af_spec = tb[IFLA_AF_SPEC];
-       if (!af_spec)
-               return 0;
 
        if (IS_ZEBRA_IF_VXLAN(ifp))
                return netlink_bridge_vxlan_update(ifp, af_spec);
index 8b631a37266864f8088d2b1575c86682454ecdd1..011883649da6ce7d52990d15b7b93773abf70dc2 100644 (file)
@@ -550,6 +550,12 @@ bool nl_attr_put(struct nlmsghdr *n, unsigned int maxlen, int type,
        return true;
 }
 
+bool nl_attr_put8(struct nlmsghdr *n, unsigned int maxlen, int type,
+                 uint8_t data)
+{
+       return nl_attr_put(n, maxlen, type, &data, sizeof(uint8_t));
+}
+
 bool nl_attr_put16(struct nlmsghdr *n, unsigned int maxlen, int type,
                   uint16_t data)
 {
index a7b152b31bda7e20af98ebe26c4327f9327f2c09..d8e5671b72ddc69f60864ac3b21b6c7cf4c5486d 100644 (file)
@@ -38,6 +38,8 @@ extern "C" {
  */
 extern bool nl_attr_put(struct nlmsghdr *n, unsigned int maxlen, int type,
                        const void *data, unsigned int alen);
+extern bool nl_attr_put8(struct nlmsghdr *n, unsigned int maxlen, int type,
+                        uint8_t data);
 extern bool nl_attr_put16(struct nlmsghdr *n, unsigned int maxlen, int type,
                          uint16_t data);
 extern bool nl_attr_put32(struct nlmsghdr *n, unsigned int maxlen, int type,
index 89f46f9c97a030d61ea0bd43c8b253cc19775594..26f6d404e95f4a2bd6de375ab5e3ab334709aa97 100644 (file)
@@ -153,10 +153,16 @@ static bool zebra_redistribute_check(const struct route_entry *re,
                                     struct zserv *client,
                                     const struct prefix *p, int afi)
 {
+       struct zebra_vrf *zvrf;
+
        /* Process only if there is valid re */
        if (!re)
                return false;
 
+       zvrf = vrf_info_lookup(re->vrf_id);
+       if (re->vrf_id == VRF_DEFAULT && zvrf->table_id != re->table)
+               return false;
+
        /* If default route and redistributed */
        if (is_default_prefix(p)
            && vrf_bitmap_check(client->redist_default[afi], re->vrf_id))
index 08a675ef3a108331ab6d4d962dfd4bac07e0df91..b651edd8f99f3e5a6fd09c6e5dbfc554195ccf82 100644 (file)
  * Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer
  * or the number of bytes written to buf.
  */
-static ssize_t
-netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
-                       uint32_t filter_bm, uint32_t priority, uint32_t table,
-                       const struct prefix *src_ip,
-                       const struct prefix *dst_ip, uint32_t fwmark,
-                       uint8_t dsfield, void *buf, size_t buflen)
+static ssize_t netlink_rule_msg_encode(
+       int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm,
+       uint32_t priority, uint32_t table, const struct prefix *src_ip,
+       const struct prefix *dst_ip, uint32_t fwmark, uint8_t dsfield,
+       uint8_t ip_protocol, void *buf, size_t buflen)
 {
        uint8_t protocol = RTPROT_ZEBRA;
        int family;
@@ -136,6 +135,10 @@ netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx,
        if (filter_bm & PBR_FILTER_DSFIELD)
                req->frh.tos = dsfield;
 
+       /* protocol to match on */
+       if (filter_bm & PBR_FILTER_IP_PROTOCOL)
+               nl_attr_put8(&req->n, buflen, FRA_IP_PROTO, ip_protocol);
+
        /* Route table to use to forward, if filter criteria matches. */
        if (table < 256)
                req->frh.table = table;
@@ -168,7 +171,8 @@ static ssize_t netlink_rule_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf,
                dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx),
                dplane_ctx_rule_get_dst_ip(ctx),
                dplane_ctx_rule_get_fwmark(ctx),
-               dplane_ctx_rule_get_dsfield(ctx), buf, buflen);
+               dplane_ctx_rule_get_dsfield(ctx),
+               dplane_ctx_rule_get_ipproto(ctx), buf, buflen);
 }
 
 static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx,
@@ -181,7 +185,8 @@ static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx,
                dplane_ctx_rule_get_old_src_ip(ctx),
                dplane_ctx_rule_get_old_dst_ip(ctx),
                dplane_ctx_rule_get_old_fwmark(ctx),
-               dplane_ctx_rule_get_old_dsfield(ctx), buf, buflen);
+               dplane_ctx_rule_get_old_dsfield(ctx),
+               dplane_ctx_rule_get_old_ipproto(ctx), buf, buflen);
 }
 
 /* Public functions */
@@ -236,6 +241,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
        char *ifname;
        struct zebra_pbr_rule rule = {};
        uint8_t proto = 0;
+       uint8_t ip_proto = 0;
 
        /* Basic validation followed by extracting attributes. */
        if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE)
@@ -312,6 +318,9 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
        if (tb[FRA_PROTOCOL])
                proto = *(uint8_t *)RTA_DATA(tb[FRA_PROTOCOL]);
 
+       if (tb[FRA_IP_PROTO])
+               ip_proto = *(uint8_t *)RTA_DATA(tb[FRA_IP_PROTO]);
+
        ifname = (char *)RTA_DATA(tb[FRA_IFNAME]);
        strlcpy(rule.ifname, ifname, sizeof(rule.ifname));
 
@@ -326,7 +335,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
                        ret = dplane_pbr_rule_delete(&rule);
 
                        zlog_debug(
-                               "%s: %s leftover rule: family %s IF %s Pref %u Src %pFX Dst %pFX Table %u",
+                               "%s: %s leftover rule: family %s IF %s Pref %u Src %pFX Dst %pFX Table %u ip-proto: %u",
                                __func__,
                                ((ret == ZEBRA_DPLANE_REQUEST_FAILURE)
                                         ? "Failed to remove"
@@ -334,7 +343,7 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
                                nl_family_to_str(frh->family), rule.ifname,
                                rule.rule.priority, &rule.rule.filter.src_ip,
                                &rule.rule.filter.dst_ip,
-                               rule.rule.action.table);
+                               rule.rule.action.table, ip_proto);
                }
 
                /* TBD */
@@ -349,11 +358,12 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
 
        if (IS_ZEBRA_DEBUG_KERNEL)
                zlog_debug(
-                       "Rx %s family %s IF %s Pref %u Src %pFX Dst %pFX Table %u",
+                       "Rx %s family %s IF %s Pref %u Src %pFX Dst %pFX Table %u ip-proto: %u",
                        nl_msg_type_to_str(h->nlmsg_type),
                        nl_family_to_str(frh->family), rule.ifname,
                        rule.rule.priority, &rule.rule.filter.src_ip,
-                       &rule.rule.filter.dst_ip, rule.rule.action.table);
+                       &rule.rule.filter.dst_ip, rule.rule.action.table,
+                       ip_proto);
 
        return kernel_pbr_rule_del(&rule);
 }
index 70d8c4005d94b0f79ac9532253d556b160aeefa7..731f0c9ad1d315384fcb210237f956c583bc3b0e 100644 (file)
@@ -196,14 +196,14 @@ zebra_zebra_irdp_la_SOURCES = \
        zebra/irdp_main.c \
        zebra/irdp_packet.c \
        # end
-zebra_zebra_irdp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_zebra_irdp_la_LDFLAGS = $(MODULE_LDFLAGS)
 
 zebra_zebra_snmp_la_SOURCES = zebra/zebra_snmp.c
 zebra_zebra_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
-zebra_zebra_snmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_zebra_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
 zebra_zebra_snmp_la_LIBADD = lib/libfrrsnmp.la
 
-zebra_zebra_fpm_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_zebra_fpm_la_LDFLAGS = $(MODULE_LDFLAGS)
 zebra_zebra_fpm_la_LIBADD =
 zebra_zebra_fpm_la_SOURCES = zebra/zebra_fpm.c
 zebra_zebra_fpm_la_SOURCES += zebra/zebra_fpm_netlink.c
@@ -220,7 +220,7 @@ endif
 # Sample dataplane plugin
 if DEV_BUILD
 zebra_dplane_sample_plugin_la_SOURCES = zebra/sample_plugin.c
-zebra_dplane_sample_plugin_la_LDFLAGS = -module -shared -avoid-version -export-dynamic
+zebra_dplane_sample_plugin_la_LDFLAGS = $(MODULE_LDFLAGS)
 endif
 
 nodist_zebra_zebra_SOURCES = \
@@ -229,13 +229,13 @@ nodist_zebra_zebra_SOURCES = \
        # end
 
 zebra_zebra_cumulus_mlag_la_SOURCES = zebra/zebra_mlag_private.c
-zebra_zebra_cumulus_mlag_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_zebra_cumulus_mlag_la_LDFLAGS = $(MODULE_LDFLAGS)
 
 if LINUX
 module_LTLIBRARIES += zebra/dplane_fpm_nl.la
 
 zebra_dplane_fpm_nl_la_SOURCES = zebra/dplane_fpm_nl.c
-zebra_dplane_fpm_nl_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
+zebra_dplane_fpm_nl_la_LDFLAGS = $(MODULE_LDFLAGS)
 zebra_dplane_fpm_nl_la_LIBADD  =
 
 vtysh_scan += zebra/dplane_fpm_nl.c
index a53e38806227bd3673d26ce031ebbe94fd18c9ec..27fb5d7c228f27292c34fc39b4d4b6149d47558e 100644 (file)
@@ -3107,6 +3107,8 @@ static void zread_vrf_label(ZAPI_HANDLER_ARGS)
        }
 
        zvrf->label[afi] = nlabel;
+       zvrf->label_proto[afi] = client->proto;
+
 stream_failure:
        return;
 }
@@ -3129,6 +3131,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
                STREAM_GETL(s, zpr.rule.seq);
                STREAM_GETL(s, zpr.rule.priority);
                STREAM_GETL(s, zpr.rule.unique);
+               STREAM_GETC(s, zpr.rule.filter.ip_proto);
                STREAM_GETC(s, zpr.rule.filter.src_ip.family);
                STREAM_GETC(s, zpr.rule.filter.src_ip.prefixlen);
                STREAM_GET(&zpr.rule.filter.src_ip.u.prefix, s,
@@ -3162,6 +3165,9 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS)
                if (zpr.rule.filter.dsfield)
                        zpr.rule.filter.filter_bm |= PBR_FILTER_DSFIELD;
 
+               if (zpr.rule.filter.ip_proto)
+                       zpr.rule.filter.filter_bm |= PBR_FILTER_IP_PROTOCOL;
+
                if (zpr.rule.filter.fwmark)
                        zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK;
 
index 1217ed915a16ea4aee5afe6159a22a6a231b167f..2a30fc6eef771421860a1cbd5f671599766c291a 100644 (file)
@@ -259,6 +259,7 @@ struct dplane_ctx_rule {
        uint8_t dsfield;
        struct prefix src_ip;
        struct prefix dst_ip;
+       uint8_t ip_proto;
        char ifname[INTERFACE_NAMSIZ + 1];
 };
 
@@ -1929,6 +1930,20 @@ uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx)
        return ctx->u.rule.old.fwmark;
 }
 
+uint8_t dplane_ctx_rule_get_ipproto(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+
+       return ctx->u.rule.new.ip_proto;
+}
+
+uint8_t dplane_ctx_rule_get_old_ipproto(const struct zebra_dplane_ctx *ctx)
+{
+       DPLANE_CTX_VALID(ctx);
+
+       return ctx->u.rule.old.ip_proto;
+}
+
 uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx)
 {
        DPLANE_CTX_VALID(ctx);
@@ -2636,6 +2651,7 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule,
        dplane_rule->filter_bm = rule->rule.filter.filter_bm;
        dplane_rule->fwmark = rule->rule.filter.fwmark;
        dplane_rule->dsfield = rule->rule.filter.dsfield;
+       dplane_rule->ip_proto = rule->rule.filter.ip_proto;
        prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip);
        prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip);
        strlcpy(dplane_rule->ifname, rule->ifname, INTERFACE_NAMSIZ);
index e091655a48ebe1335c01f229d77a6f77bfb84383..5ec1bd5807cf5158bea2e1207a3b1d0ca4921de5 100644 (file)
@@ -493,6 +493,8 @@ uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx);
 uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx);
 uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx);
 uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_ipproto(const struct zebra_dplane_ctx *ctx);
+uint8_t dplane_ctx_rule_get_old_ipproto(const struct zebra_dplane_ctx *ctx);
 const struct prefix *
 dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx);
 const struct prefix *
index a2d1513ce4874530b71676f10bec90bcd5c001d6..66d2d6b4babbe9f90f03f0c49533918ee87ac5f0 100644 (file)
@@ -3931,6 +3931,40 @@ void zebra_mpls_cleanup_tables(struct zebra_vrf *zvrf)
        }
 }
 
+/*
+ * When a vrf label is assigned and the client goes away
+ * we should cleanup the vrf labels associated with
+ * that zclient.
+ */
+void zebra_mpls_client_cleanup_vrf_label(uint8_t proto)
+{
+       struct vrf *vrf;
+       struct zebra_vrf *def_zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+
+       if (def_zvrf == NULL)
+               return;
+
+       RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+               struct zebra_vrf *zvrf = vrf->info;
+               afi_t afi;
+
+               if (!zvrf)
+                       continue;
+
+               for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+                       if (zvrf->label_proto[afi] == proto
+                           && zvrf->label[afi] != MPLS_LABEL_NONE)
+                               lsp_uninstall(def_zvrf, zvrf->label[afi]);
+
+                       /*
+                        * Cleanup data structures by fiat
+                        */
+                       zvrf->label_proto[afi] = 0;
+                       zvrf->label[afi] = MPLS_LABEL_NONE;
+               }
+       }
+}
+
 /*
  * Called upon process exiting, need to delete LSP forwarding
  * entries from the kernel.
index 7059d393edf5af7cbbe907912cc78f4e5156198c..5195b2f14fed8ed76432ea5e8d05263fd8db431d 100644 (file)
@@ -416,6 +416,12 @@ void zebra_mpls_init(void);
  */
 void zebra_mpls_vty_init(void);
 
+/*
+ * When cleaning up a client connection ensure that there are no
+ * vrf labels that need cleaning up too
+ */
+void zebra_mpls_client_cleanup_vrf_label(uint8_t proto);
+
 /* Inline functions. */
 
 /*
index 7bcd097371778c416bddb11c943d260761ce5f6a..3607110aa26f35dd1e54e88aca8267ab3b6568f2 100644 (file)
@@ -166,10 +166,8 @@ uint32_t zebra_pbr_rules_hash_key(const void *arg)
                           rule->rule.action.table,
                           prefix_hash_key(&rule->rule.filter.src_ip));
 
-       if (rule->rule.filter.fwmark)
-               key = jhash_2words(rule->rule.filter.fwmark, rule->vrf_id, key);
-       else
-               key = jhash_1word(rule->vrf_id, key);
+       key = jhash_3words(rule->rule.filter.fwmark, rule->vrf_id,
+                          rule->rule.filter.ip_proto, key);
 
        key = jhash(rule->ifname, strlen(rule->ifname), key);
 
@@ -207,6 +205,9 @@ bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2)
        if (r1->rule.filter.fwmark != r2->rule.filter.fwmark)
                return false;
 
+       if (r1->rule.filter.ip_proto != r2->rule.filter.ip_proto)
+               return false;
+
        if (!prefix_same(&r1->rule.filter.src_ip, &r2->rule.filter.src_ip))
                return false;
 
index 57dd0c20ad7d37e5365f467ba8f68177f886f947..f32f09850b6a129e8379489d0a2b0abf7d9d5320 100644 (file)
@@ -105,6 +105,7 @@ struct zebra_vrf {
 
        /* MPLS Label to handle L3VPN <-> vrf popping */
        mpls_label_t label[AFI_MAX];
+       uint8_t label_proto[AFI_MAX];
 
        /* MPLS static LSP config table */
        struct hash *slsp_table;
index 2b46929acbbd40f6902363195ead62fac516a541..166079222146170ee75d5f75d305d488be271c32 100644 (file)
@@ -366,8 +366,6 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty,
                                                 buf1, sizeof(buf1)));
                json_object_int_add(json, "refCount",
                                    rb_host_count(&zrmac->host_rb));
-               json_object_int_add(json, "localSequence", zrmac->loc_seq);
-               json_object_int_add(json, "remoteSequence", zrmac->rem_seq);
                RB_FOREACH (hle, host_rb_tree_entry, &zrmac->host_rb)
                        json_object_array_add(
                                json_hosts,
index 1d94fcae6bbd6e06312facb1c54eff355748a9c3..e4a48093f7cbb53cdfb89f1de7a88f83d5953793 100644 (file)
@@ -595,6 +595,8 @@ static void zserv_client_free(struct zserv *client)
                close(client->sock);
 
                if (DYNAMIC_CLIENT_GR_DISABLED(client)) {
+                       zebra_mpls_client_cleanup_vrf_label(client->proto);
+
                        nroutes = rib_score_proto(client->proto,
                                                  client->instance);
                        zlog_notice(