-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@
const char *com2alias =
bgp_community2alias(
communities[i]);
- if (strncmp(alias, com2alias,
- strlen(com2alias))
+ if (strcmp(alias, com2alias)
== 0) {
found = true;
break;
const char *com2alias =
bgp_community2alias(
communities[i]);
- if (strncmp(alias, com2alias,
- strlen(com2alias))
+ if (strcmp(alias, com2alias)
== 0) {
found = true;
break;
#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"
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. */
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,
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);
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);
.destroy = lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy,
}
},
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:alias",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_alias_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy,
+ }
+ },
{
.xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script",
.cbs = {
/* 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);
return NB_OK;
}
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:alias
+ */
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *alias;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ alias = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "alias";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "alias", alias,
+ RMAP_EVENT_MATCH_ADDED, args->errmsg,
+ args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_VALIDATION;
+ }
+
+ break;
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
/*
* XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script
*/
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;
#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,
vty_out(vty, "preference %hhu\n", cache->preference);
}
- vty_out(vty, " exit\n");
+ vty_out(vty, " exit\n");
return 1;
}
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 {
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 \
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
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
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],
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
.. 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
-------------------------------
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.
-
-
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
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)
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.
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 \
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
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
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
|| node == BGP_FLOWSPECV6_NODE))
return false;
+ if (target_node == INTERFACE_NODE && node == LINK_PARAMS_NODE)
+ return false;
+
return true;
}
#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 */
/* Filter with fwmark */
uint32_t fwmark;
+
+ /* Filter with the ip protocol */
+ uint8_t ip_proto;
};
/*
}
static int prefix_list_entry_match(struct prefix_list_entry *pentry,
- const struct prefix *p)
+ const struct prefix *p, bool address_mode)
{
int ret;
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)
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;
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;
}
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;
if (which) {
if (pbest)
- *which = &pbest->prefix;
+ *which = pbest;
else
*which = NULL;
}
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)
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);
}
};
struct prefix_list;
+struct prefix_list_entry;
struct orf_prefix {
uint32_t seq;
*
* 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 *,
/* 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"))
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(
# 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 = \
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 \
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 \
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 \
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
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
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
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 \
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
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;
/* 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,
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;
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;
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);
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)) {
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);
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)
/* 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,
}
/* 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);
int idx_ipv4 = 3;
uint32_t area_id;
int format;
- int ipv6_count = 0;
assert(ifp);
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;
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);
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;
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);
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;
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) {
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),
}
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));
/* 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)) {
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;
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;
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));
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;
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;
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;
{
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;
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) {
/* Optional Capabilities */
uint8_t options[3];
- /* Prefix Options */
- uint8_t prefix_options;
-
/* Associated Area */
in_addr_t area_id;
/* flag */
uint8_t flag;
+ /* Prefix Options */
+ uint8_t prefix_options;
+
/* route option */
void *route_option;
struct ospf6_interface *oi;
struct interface *ifp;
vrf_id_t vrf_id = VRF_DEFAULT;
- int ipv6_count = 0;
uint32_t area_id;
int format;
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;
#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;
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);
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 += \
#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 \
flog_err(EC_LIB_SOCKET,
"ospf_read_sock_init: socket: %s",
safe_strerror(errno));
- exit(1);
+ return -1;
}
#ifdef IP_HDRINCL
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;
}
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;
}
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;
}
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 += \
#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)
*/
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
*/
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
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)
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);
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);
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);
struct interface *ifp;
time_t now;
char buf[PREFIX_STRLEN];
+ char quer_buf[PREFIX_STRLEN];
json_object *json = NULL;
json_object *json_row = NULL;
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;
"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);
}
} 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);
}
}
}
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;
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,
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",
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 {
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");
}
}
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"
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"
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
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
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;
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
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);
}
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);
zlog_warn(
"Recv IGMP packet with invalid ttl=%u, discarding the packet",
ip_hdr->ip_ttl);
- return -1;
+ return false;
}
}
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;
}
}
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;
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;
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];
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 */
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;
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);
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;
}
__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) {
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);
return NULL;
}
+/*
+ * XXX: long-term issue: we don't actually have a good "ip address-list"
+ * implementation. ("access-list XYZ" is the closest but honestly it's
+ * kinda garbage.)
+ *
+ * So it's using a prefix-list to match an address here, which causes very
+ * unexpected results for the user since prefix-lists by default only match
+ * when the prefix length is an exact match too. i.e. you'd have to add the
+ * "le 32" and do "ip prefix-list foo permit 10.0.0.0/24 le 32"
+ *
+ * To avoid this pitfall, this code uses "address_mode = true" for the prefix
+ * list match (this is the only user for that.)
+ *
+ * In the long run, we need to add a "ip address-list", but that's a wholly
+ * separate bag of worms, and existing configs using ip prefix-list would
+ * drop into the UX pitfall.
+ */
+
+#include "lib/plist_int.h"
+
/*
* Given a group, return the rp_info for that group
*/
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;
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;
}
}
}
}
#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
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 \
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
/lib/test_nexthop
/lib/test_nexthop_iter
/lib/test_ntop
+/lib/test_plist
/lib/test_prefix2str
/lib/test_printfrr
/lib/test_privs
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);
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);
#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);
--- /dev/null
+/*
+ * 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".
+ */
+}
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 \
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)
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
!
!
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
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
!
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
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__":
debug pim nht
debug pim zebra
ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
!
int lo
ip pim
ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
!
int lo
ip pim
ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
!
int lo
ip pim
)
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(
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,
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"]
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"]
config_data.append(cmd)
-
# router id
router_id = ospf_data.setdefault("router_id", None)
del_router_id = ospf_data.setdefault("del_router_id", False)
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)
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", {})
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"
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:
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", {})
True or False
"""
logger.debug("Enter lib config_ospf_interface")
+ result = False
if not input_dict:
input_dict = deepcopy(topo)
else:
# 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)
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()")
################################
# 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
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
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))
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"]
_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
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:
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(
"%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
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))
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):
# 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))
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"
# 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.
"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)
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
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
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
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
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
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
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
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
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
--- /dev/null
+{
+ "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
--- /dev/null
+{
+ "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": {}}
+ }
+ }
+ }
+}
--- /dev/null
+#!/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))
--- /dev/null
+#!/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))
# ##################################
# 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
write_test_footer(tc_name)
-
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
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
--- /dev/null
+!
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "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
+ }
+ ]
+ }
+}
--- /dev/null
+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
+
--- /dev/null
+{
+ "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
+ }
+ }
+}
--- /dev/null
+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
--- /dev/null
+!
+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
+!
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+!
+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
+!
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+!
+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
+!
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+!
+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
+!
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+!
+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
+!
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+!
+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
+!
--- /dev/null
+#!/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))
ip pim
!
ip pim rp 10.254.0.3
+ip pim join-prune-interval 5
interface lo
ip pim
!
+ip pim join-prune-interval 5
ip pim rp 10.254.0.3
ip pim register-accept-list ACCEPT
ip pim
ip pim bfd
!
+ip pim join-prune-interval 5
--- /dev/null
+!
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+!
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+ }
+}
--- /dev/null
+{
+ "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"
+ }
+ ]
+ }
+ }
+}
+
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
+
--- /dev/null
+{
+ "blue":{
+ },
+ "r1-eth0":{
+ },
+ "r1-eth1":{
+ "192.168.101.11":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.11",
+ "drPriority":1
+ }
+ }
+}
--- /dev/null
+{
+ "pimreg11":{
+ "name":"pimreg11",
+ "state":"up",
+ "address":"0.0.0.0",
+ "flagAllMulticast":true,
+ "lanDelayEnabled":true,
+ "drAddress":"*",
+ "drPriority":1,
+ "drUptime":"--:--:--",
+ "drElections":0,
+ "drChanges":0
+ }
+}
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+{
+ "r1-eth2":{
+ },
+ "r1-eth3":{
+ "192.168.101.12":{
+ "interface":"r1-eth3",
+ "neighbor":"192.168.101.12",
+ "drPriority":1
+ }
+ },
+ "red":{
+ }
+}
--- /dev/null
+{
+ "pimreg12":{
+ "name":"pimreg12",
+ "state":"up",
+ "address":"0.0.0.0",
+ "flagAllMulticast":true,
+ "lanDelayEnabled":true,
+ "drAddress":"*",
+ "drPriority":1,
+ "drUptime":"--:--:--",
+ "drElections":0,
+ "drChanges":0
+ }
+}
--- /dev/null
+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
--- /dev/null
+!
+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
+!
+
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+!
+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
+!
--- /dev/null
+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
+!
--- /dev/null
+{
+ "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
+ }
+ }
+ }
+}
--- /dev/null
+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
+!
--- /dev/null
+!
+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
+!
--- /dev/null
+#!/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))
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];
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];
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,
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);
/* 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);
"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
}
}
- 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;
}
leaf hello-holdtime {
- type uint8 {
- range "1..180";
+ type uint16 {
+ range "1..630";
}
must ". > ./../hello-interval" {
error-message "HoldTime must be greater than Hello";
}
leaf join-prune-interval {
type uint16 {
- range "60..600";
+ range "5..600";
}
default "60";
description
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.
*/
/* 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);
/* 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);
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)
{
*/
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,
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))
* 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;
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;
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,
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 */
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)
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));
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"
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 */
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);
}
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
# 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 = \
# 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
}
zvrf->label[afi] = nlabel;
+ zvrf->label_proto[afi] = client->proto;
+
stream_failure:
return;
}
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,
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;
uint8_t dsfield;
struct prefix src_ip;
struct prefix dst_ip;
+ uint8_t ip_proto;
char ifname[INTERFACE_NAMSIZ + 1];
};
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);
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);
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 *
}
}
+/*
+ * 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.
*/
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. */
/*
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);
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;
/* 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;
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,
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(