diff options
166 files changed, 5416 insertions, 672 deletions
diff --git a/Makefile.am b/Makefile.am index 9bc5dd7d22..ce0f70a1a2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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@ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index d5bb53ad8d..66ff16d53a 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -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; diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 529abcbea0..61f57d0475 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -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); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index 1254591b87..9216426968 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -39,6 +39,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { } }, { + .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 = { .modify = lib_route_map_entry_match_condition_rmap_match_condition_script_modify, diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index f0e492eb61..069345b1a4 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -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); diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index 88e3f6438f..45f5c8f4bc 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -160,6 +160,62 @@ lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destro } /* + * 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 */ int diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 816ed88eec..a8bccecacf 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -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; } diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 2c7c087855..24652ee93a 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -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 { diff --git a/bgpd/subdir.am b/bgpd/subdir.am index 53225192f2..cb9af08c95 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -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 \ diff --git a/configure.ac b/configure.ac index 1ad87d9435..c86f47d073 100644 --- a/configure.ac +++ b/configure.ac @@ -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], diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 6b5a008761..88f0483901 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -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 diff --git a/doc/user/filter.rst b/doc/user/filter.rst index 1fb9beccdc..cbbcd47dc3 100644 --- a/doc/user/filter.rst +++ b/doc/user/filter.rst @@ -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 ------------------------------- diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index c4a1bc381e..5344f4cb05 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -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. - - diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 77134a7704..e59ed10896 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -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 diff --git a/doc/user/pim.rst b/doc/user/pim.rst index 83d19d6188..6f9aa289b4 100644 --- a/doc/user/pim.rst +++ b/doc/user/pim.rst @@ -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. diff --git a/fpm/subdir.am b/fpm/subdir.am index a645ca2b03..b5988137e0 100644 --- a/fpm/subdir.am +++ b/fpm/subdir.am @@ -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 \ diff --git a/grpc/subdir.am b/grpc/subdir.am index d9ec365ba8..cbebd72323 100644 --- a/grpc/subdir.am +++ b/grpc/subdir.am @@ -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 diff --git a/isisd/subdir.am b/isisd/subdir.am index 4243bd60cf..3e5816c16b 100644 --- a/isisd/subdir.am +++ b/isisd/subdir.am @@ -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 diff --git a/ldpd/subdir.am b/ldpd/subdir.am index b7e2ab72d6..083effb703 100644 --- a/ldpd/subdir.am +++ b/ldpd/subdir.am @@ -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 diff --git a/lib/command.c b/lib/command.c index 9dac60599c..fe17c68a8b 100644 --- a/lib/command.c +++ b/lib/command.c @@ -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; } @@ -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; }; /* diff --git a/lib/plist.c b/lib/plist.c index 0ee02f8a0b..2b42c43764 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -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); } diff --git a/lib/plist.h b/lib/plist.h index 57eb763a68..c9507df57c 100644 --- a/lib/plist.h +++ b/lib/plist.h @@ -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 *, diff --git a/lib/routemap.h b/lib/routemap.h index 4a40ec08b9..8af3b2c3c0 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -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")) diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index bf982cfa2b..ec9033b3aa 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -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( diff --git a/lib/subdir.am b/lib/subdir.am index 90301d800a..714af43238 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -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 \ diff --git a/mlag/subdir.am b/mlag/subdir.am index 49d1761505..376eea8bc9 100644 --- a/mlag/subdir.am +++ b/mlag/subdir.am @@ -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 diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index cc99d7c387..9dd232dae5 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -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; diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 3e911a743a..84111e4b7d 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -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)) { diff --git a/ospf6d/ospf6_flood.c b/ospf6d/ospf6_flood.c index 738c2218fa..0a384a98e6 100644 --- a/ospf6d/ospf6_flood.c +++ b/ospf6d/ospf6_flood.c @@ -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); diff --git a/ospf6d/ospf6_interface.c b/ospf6d/ospf6_interface.c index b52d6af90e..468a4b8e81 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -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; diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index c9cd74b691..b5efca743e 100644 --- a/ospf6d/ospf6_interface.h +++ b/ospf6d/ospf6_interface.h @@ -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); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index c971c6180e..06f64bbc40 100644 --- a/ospf6d/ospf6_intra.c +++ b/ospf6d/ospf6_intra.c @@ -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; diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c index 9f8cdf8fb7..bd6fb308dd 100644 --- a/ospf6d/ospf6_nssa.c +++ b/ospf6d/ospf6_nssa.c @@ -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; diff --git a/ospf6d/ospf6_route.c b/ospf6d/ospf6_route.c index 0a026785f4..80f0e7d26b 100644 --- a/ospf6d/ospf6_route.c +++ b/ospf6d/ospf6_route.c @@ -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) { diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index a791a82cd4..ecfb45d1ea 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -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; diff --git a/ospf6d/ospf6_top.c b/ospf6d/ospf6_top.c index 6f40989efd..92f1e50c65 100644 --- a/ospf6d/ospf6_top.c +++ b/ospf6d/ospf6_top.c @@ -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; diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 3eb423f681..b546ee87ae 100644 --- a/ospf6d/ospf6_top.h +++ b/ospf6d/ospf6_top.h @@ -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; diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index 72bc3a2f3a..5403e643dc 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -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); diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index 2b7bce5392..3f9ff76f3b 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -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 += \ diff --git a/ospfclient/subdir.am b/ospfclient/subdir.am index 756ad88f15..1f9547ab87 100644 --- a/ospfclient/subdir.am +++ b/ospfclient/subdir.am @@ -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 \ diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 00fbdc21a1..be06afe532 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -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 diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index bf2a8564f0..3819478cfc 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -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; } diff --git a/ospfd/subdir.am b/ospfd/subdir.am index 2c4cc262c1..4f9cbc7b1e 100644 --- a/ospfd/subdir.am +++ b/ospfd/subdir.am @@ -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 += \ diff --git a/pathd/subdir.am b/pathd/subdir.am index 693afabb39..f339c79225 100644 --- a/pathd/subdir.am +++ b/pathd/subdir.am @@ -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) diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index caeadb0644..694b915f48 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -85,6 +85,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 */ struct prefix *src; diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 3d56fc3daa..730f965cd0 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -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); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index fc5303c9d8..28def509d5 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -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); diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c index cc11a3cc17..37d206cc11 100644 --- a/pimd/pim_cmd.c +++ b/pimd/pim_cmd.c @@ -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 diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index 48b019c8c8..0b28a3e84c 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -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; diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c index 477cf991b4..71b2d9187a 100644 --- a/pimd/pim_igmp.c +++ b/pimd/pim_igmp.c @@ -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 */ diff --git a/pimd/pim_igmp.h b/pimd/pim_igmp.h index 88324b7937..abb8af836b 100644 --- a/pimd/pim_igmp.h +++ b/pimd/pim_igmp.h @@ -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); diff --git a/pimd/pim_nb_config.c b/pimd/pim_nb_config.c index dfdbd6dee2..bd5e215027 100644 --- a/pimd/pim_nb_config.c +++ b/pimd/pim_nb_config.c @@ -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; } diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c index 48b1a30f2d..571173c62a 100644 --- a/pimd/pim_neighbor.c +++ b/pimd/pim_neighbor.c @@ -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); diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index b6521132f7..56e1927528 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -204,6 +204,26 @@ static struct rp_info *pim_rp_find_exact(struct pim_instance *pim, } /* + * 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 */ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim, @@ -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; } } } diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index 504519c8a4..05b0f92a4b 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -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 diff --git a/qpb/subdir.am b/qpb/subdir.am index 704efc5930..e897822ecc 100644 --- a/qpb/subdir.am +++ b/qpb/subdir.am @@ -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 \ diff --git a/ripd/subdir.am b/ripd/subdir.am index 8de0fc4b5a..b43e369ab2 100644 --- a/ripd/subdir.am +++ b/ripd/subdir.am @@ -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 diff --git a/tests/.gitignore b/tests/.gitignore index 3fad1b0813..498d7dd0b7 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -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 diff --git a/tests/lib/cli/common_cli.c b/tests/lib/cli/common_cli.c index 49bc0f4fb2..8be81cc4cb 100644 --- a/tests/lib/cli/common_cli.c +++ b/tests/lib/cli/common_cli.c @@ -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); diff --git a/tests/lib/cli/common_cli.h b/tests/lib/cli/common_cli.h index 15abe3b855..3042ff5b12 100644 --- a/tests/lib/cli/common_cli.h +++ b/tests/lib/cli/common_cli.h @@ -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 index 0000000000..ee7a9ebf30 --- /dev/null +++ b/tests/lib/test_plist.c @@ -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". + */ +} diff --git a/tests/subdir.am b/tests/subdir.am index c2153140f5..86c1aa4284 100644 --- a/tests/subdir.am +++ b/tests/subdir.am @@ -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) diff --git a/tests/topotests/bgp_community_alias/r1/bgpd.conf b/tests/topotests/bgp_community_alias/r1/bgpd.conf index 06113bdd2a..a6366204e8 100644 --- a/tests/topotests/bgp_community_alias/r1/bgpd.conf +++ b/tests/topotests/bgp_community_alias/r1/bgpd.conf @@ -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 ! diff --git a/tests/topotests/bgp_community_alias/r2/bgpd.conf b/tests/topotests/bgp_community_alias/r2/bgpd.conf index fc67ff2ad2..9276fe592d 100644 --- a/tests/topotests/bgp_community_alias/r2/bgpd.conf +++ b/tests/topotests/bgp_community_alias/r2/bgpd.conf @@ -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 ! diff --git a/tests/topotests/bgp_community_alias/r2/zebra.conf b/tests/topotests/bgp_community_alias/r2/zebra.conf index a806628a8e..b8cb9baf3c 100644 --- a/tests/topotests/bgp_community_alias/r2/zebra.conf +++ b/tests/topotests/bgp_community_alias/r2/zebra.conf @@ -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 diff --git a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py index 90eeaaa731..c41ba810f1 100644 --- a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py +++ b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py @@ -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__": diff --git a/tests/topotests/evpn_pim_1/leaf1/pimd.conf b/tests/topotests/evpn_pim_1/leaf1/pimd.conf index 293e252086..d85f33d1fc 100644 --- a/tests/topotests/evpn_pim_1/leaf1/pimd.conf +++ b/tests/topotests/evpn_pim_1/leaf1/pimd.conf @@ -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 diff --git a/tests/topotests/evpn_pim_1/leaf2/pimd.conf b/tests/topotests/evpn_pim_1/leaf2/pimd.conf index 08d5a19a2a..d775b800b3 100644 --- a/tests/topotests/evpn_pim_1/leaf2/pimd.conf +++ b/tests/topotests/evpn_pim_1/leaf2/pimd.conf @@ -1,4 +1,5 @@ ip pim rp 192.168.100.1 +ip pim join-prune-interval 5 ! int lo ip pim diff --git a/tests/topotests/evpn_pim_1/spine/pimd.conf b/tests/topotests/evpn_pim_1/spine/pimd.conf index 56adda5cc4..12c6d6f85c 100644 --- a/tests/topotests/evpn_pim_1/spine/pimd.conf +++ b/tests/topotests/evpn_pim_1/spine/pimd.conf @@ -1,4 +1,5 @@ ip pim rp 192.168.100.1 +ip pim join-prune-interval 5 ! int lo ip pim diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 6a02e50127..07bb5153ab 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -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( diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py index dc9fe0fcca..6aa7a2c0a9 100644 --- a/tests/topotests/lib/ospf.py +++ b/tests/topotests/lib/ospf.py @@ -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 diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py index 23dcced2bf..b516a67d5c 100644 --- a/tests/topotests/lib/topotest.py +++ b/tests/topotests/lib/topotest.py @@ -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 diff --git a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf index 30cecee9e1..c2ffed4762 100644 --- a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf +++ b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf @@ -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 diff --git a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf index a51c6d58c7..1719a17007 100644 --- a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf +++ b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf @@ -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 diff --git a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf index 663f78620e..2748a55d83 100644 --- a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf +++ b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf @@ -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 diff --git a/tests/topotests/msdp_topo1/r1/pimd.conf b/tests/topotests/msdp_topo1/r1/pimd.conf index fc289031f4..4274315271 100644 --- a/tests/topotests/msdp_topo1/r1/pimd.conf +++ b/tests/topotests/msdp_topo1/r1/pimd.conf @@ -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 diff --git a/tests/topotests/msdp_topo1/r2/pimd.conf b/tests/topotests/msdp_topo1/r2/pimd.conf index ffa80b12d3..a4a69bf05c 100644 --- a/tests/topotests/msdp_topo1/r2/pimd.conf +++ b/tests/topotests/msdp_topo1/r2/pimd.conf @@ -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 diff --git a/tests/topotests/msdp_topo1/r3/pimd.conf b/tests/topotests/msdp_topo1/r3/pimd.conf index ab12f0573a..db94447c76 100644 --- a/tests/topotests/msdp_topo1/r3/pimd.conf +++ b/tests/topotests/msdp_topo1/r3/pimd.conf @@ -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 diff --git a/tests/topotests/msdp_topo1/r4/pimd.conf b/tests/topotests/msdp_topo1/r4/pimd.conf index b2e05cb3cb..e9bb59054c 100644 --- a/tests/topotests/msdp_topo1/r4/pimd.conf +++ b/tests/topotests/msdp_topo1/r4/pimd.conf @@ -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 index 0000000000..c928093925 --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json @@ -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 index 0000000000..226f84f320 --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json @@ -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 index 0000000000..a439375be8 --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py @@ -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 index 0000000000..9ca460e487 --- /dev/null +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py @@ -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)) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py index 4aa71bfb16..e01c6d6047 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py @@ -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)) diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py index a84f1a1eb6..faae4b3e17 100644 --- a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py +++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py @@ -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 index 0000000000..3d6540d40c --- /dev/null +++ b/tests/topotests/pim_acl/h1/zebra.conf @@ -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 index 0000000000..95342f9e8a --- /dev/null +++ b/tests/topotests/pim_acl/h2/zebra.conf @@ -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 index 0000000000..1b44b2b5cf --- /dev/null +++ b/tests/topotests/pim_acl/r1/acl_1_pim_join.json @@ -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 index 0000000000..c020a489a9 --- /dev/null +++ b/tests/topotests/pim_acl/r1/acl_2_pim_join.json @@ -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 index 0000000000..6122f73992 --- /dev/null +++ b/tests/topotests/pim_acl/r1/acl_3_pim_join.json @@ -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 index 0000000000..5f72256ba7 --- /dev/null +++ b/tests/topotests/pim_acl/r1/acl_4_pim_join.json @@ -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 index 0000000000..70021bdbec --- /dev/null +++ b/tests/topotests/pim_acl/r1/acl_5_pim_join.json @@ -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 index 0000000000..2baac6cb22 --- /dev/null +++ b/tests/topotests/pim_acl/r1/acl_6_pim_join.json @@ -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 index 0000000000..a8fc093e90 --- /dev/null +++ b/tests/topotests/pim_acl/r1/ospf_neighbor.json @@ -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 index 0000000000..e1f47fb3b1 --- /dev/null +++ b/tests/topotests/pim_acl/r1/ospfd.conf @@ -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 index 0000000000..ae95e8db14 --- /dev/null +++ b/tests/topotests/pim_acl/r1/pim_neighbor.json @@ -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 index 0000000000..a148c73146 --- /dev/null +++ b/tests/topotests/pim_acl/r1/pimd.conf @@ -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 index 0000000000..74feb8f6a7 --- /dev/null +++ b/tests/topotests/pim_acl/r1/zebra.conf @@ -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 index 0000000000..289bf51e76 --- /dev/null +++ b/tests/topotests/pim_acl/r11/acl_1_pim_join.json @@ -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 index 0000000000..e107220a4e --- /dev/null +++ b/tests/topotests/pim_acl/r11/ospfd.conf @@ -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 index 0000000000..b1d45205da --- /dev/null +++ b/tests/topotests/pim_acl/r11/pimd.conf @@ -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 index 0000000000..137706d245 --- /dev/null +++ b/tests/topotests/pim_acl/r11/zebra.conf @@ -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 index 0000000000..76ab7ee701 --- /dev/null +++ b/tests/topotests/pim_acl/r12/acl_2_pim_join.json @@ -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 index 0000000000..f9203c78e4 --- /dev/null +++ b/tests/topotests/pim_acl/r12/ospfd.conf @@ -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 index 0000000000..ba9e7d902f --- /dev/null +++ b/tests/topotests/pim_acl/r12/pimd.conf @@ -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 index 0000000000..bede104906 --- /dev/null +++ b/tests/topotests/pim_acl/r12/zebra.conf @@ -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 index 0000000000..48ad72cbe1 --- /dev/null +++ b/tests/topotests/pim_acl/r13/acl_3_pim_join.json @@ -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 index 0000000000..830c5a14b6 --- /dev/null +++ b/tests/topotests/pim_acl/r13/ospfd.conf @@ -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 index 0000000000..2ff1743574 --- /dev/null +++ b/tests/topotests/pim_acl/r13/pimd.conf @@ -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 index 0000000000..f9ff27abac --- /dev/null +++ b/tests/topotests/pim_acl/r13/zebra.conf @@ -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 index 0000000000..46d86dd40d --- /dev/null +++ b/tests/topotests/pim_acl/r14/acl_4_pim_join.json @@ -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 index 0000000000..2b291a8a0c --- /dev/null +++ b/tests/topotests/pim_acl/r14/acl_5_pim_join.json @@ -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 index 0000000000..422e4c08b0 --- /dev/null +++ b/tests/topotests/pim_acl/r14/ospfd.conf @@ -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 index 0000000000..1324a9e40b --- /dev/null +++ b/tests/topotests/pim_acl/r14/pimd.conf @@ -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 index 0000000000..8761b46206 --- /dev/null +++ b/tests/topotests/pim_acl/r14/zebra.conf @@ -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 index 0000000000..05fed4ecc5 --- /dev/null +++ b/tests/topotests/pim_acl/r15/acl_6_pim_join.json @@ -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 index 0000000000..cd4d7b3875 --- /dev/null +++ b/tests/topotests/pim_acl/r15/ospfd.conf @@ -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 index 0000000000..f47e78c221 --- /dev/null +++ b/tests/topotests/pim_acl/r15/pimd.conf @@ -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 index 0000000000..f6909dd020 --- /dev/null +++ b/tests/topotests/pim_acl/r15/zebra.conf @@ -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 index 0000000000..848f7fa8ed --- /dev/null +++ b/tests/topotests/pim_acl/test_pim_acl.py @@ -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)) diff --git a/tests/topotests/pim_basic/r1/pimd.conf b/tests/topotests/pim_basic/r1/pimd.conf index f64a46deb3..737019fa51 100644 --- a/tests/topotests/pim_basic/r1/pimd.conf +++ b/tests/topotests/pim_basic/r1/pimd.conf @@ -15,3 +15,4 @@ interface lo ip pim ! ip pim rp 10.254.0.3 +ip pim join-prune-interval 5 diff --git a/tests/topotests/pim_basic/rp/pimd.conf b/tests/topotests/pim_basic/rp/pimd.conf index 6e35c97971..fd26bc4d71 100644 --- a/tests/topotests/pim_basic/rp/pimd.conf +++ b/tests/topotests/pim_basic/rp/pimd.conf @@ -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 diff --git a/tests/topotests/pim_basic_topo2/r2/pimd.conf b/tests/topotests/pim_basic_topo2/r2/pimd.conf index 0b32ded19a..9f389deb11 100644 --- a/tests/topotests/pim_basic_topo2/r2/pimd.conf +++ b/tests/topotests/pim_basic_topo2/r2/pimd.conf @@ -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 index 0000000000..3d6540d40c --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/h1/zebra.conf @@ -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 index 0000000000..95342f9e8a --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/h2/zebra.conf @@ -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 index 0000000000..ef99b1cd8f --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/h3/zebra.conf @@ -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 index 0000000000..6a2e466000 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/h4/zebra.conf @@ -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 index 0000000000..604d25fac1 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json @@ -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 index 0000000000..456bb87520 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json @@ -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 index 0000000000..263b5867cc --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/ospfd.conf @@ -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 index 0000000000..8568bae2bc --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json @@ -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 index 0000000000..ea7d4aca6f --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json @@ -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 index 0000000000..d3642f854a --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json @@ -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 index 0000000000..d0037ca4b0 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json @@ -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 index 0000000000..e17b40854a --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json @@ -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 index 0000000000..45b6cd9645 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json @@ -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 index 0000000000..f04c255de9 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/pimd.conf @@ -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 index 0000000000..9da9280945 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r1/zebra.conf @@ -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 index 0000000000..e107220a4e --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r11/ospfd.conf @@ -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 index 0000000000..289bf51e76 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json @@ -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 index 0000000000..b1d45205da --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r11/pimd.conf @@ -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 index 0000000000..137706d245 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r11/zebra.conf @@ -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 index 0000000000..03acc82c1d --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r12/ospfd.conf @@ -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 index 0000000000..6926246568 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json @@ -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 index 0000000000..5cb76efa22 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r12/pimd.conf @@ -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 index 0000000000..bede104906 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/r12/zebra.conf @@ -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 index 0000000000..298adef9c6 --- /dev/null +++ b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py @@ -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)) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index dd3f448674..5cee0aaa3f 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -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); diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index 1c990b5ed9..e11883a803 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -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; diff --git a/yang/frr-pim.yang b/yang/frr-pim.yang index 6329e45588..e846ffa1f8 100644 --- a/yang/frr-pim.yang +++ b/yang/frr-pim.yang @@ -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 diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index c2b4dcc52f..a51e0b82cb 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -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); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 8b631a3726..011883649d 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -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) { diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index a7b152b31b..d8e5671b72 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -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, diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 89f46f9c97..26f6d404e9 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -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)) diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index 08a675ef3a..b651edd8f9 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -58,12 +58,11 @@ * 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); } diff --git a/zebra/subdir.am b/zebra/subdir.am index 70d8c4005d..731f0c9ad1 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -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 diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index a53e388062..27fb5d7c22 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -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; diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 1217ed915a..2a30fc6eef 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -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); diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index e091655a48..5ec1bd5807 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -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 * diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index a2d1513ce4..66d2d6b4ba 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -3932,6 +3932,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. * NOTE: Currently supported only for default VRF. diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 7059d393ed..5195b2f14f 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -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. */ /* diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index 7bcd097371..3607110aa2 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -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; diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 57dd0c20ad..f32f09850b 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -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; diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 2b46929acb..1660792221 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -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, diff --git a/zebra/zserv.c b/zebra/zserv.c index 1d94fcae6b..e4a48093f7 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -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( |
