summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/user/ospf6d.rst8
-rw-r--r--ospf6d/ospf6_area.c79
-rw-r--r--ospf6d/ospf6_area.h7
-rw-r--r--ospf6d/ospf6_asbr.c3
-rw-r--r--ospf6d/ospf6_nssa.c96
-rw-r--r--ospf6d/ospf6_nssa.h3
-rw-r--r--ospf6d/ospf6_spf.c4
-rw-r--r--ospf6d/ospf6_top.h12
-rw-r--r--ospf6d/ospf6_zebra.c57
-rw-r--r--ospf6d/ospf6_zebra.h1
-rw-r--r--tests/topotests/ospf6_topo2/test_ospf6_topo2.py44
11 files changed, 301 insertions, 13 deletions
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst
index f0b0638ee..6e430ce01 100644
--- a/doc/user/ospf6d.rst
+++ b/doc/user/ospf6d.rst
@@ -178,7 +178,7 @@ OSPF6 area
.. clicmd:: area A.B.C.D nssa [no-summary]
-.. clicmd:: area (0-4294967295) nssa [no-summary]
+.. clicmd:: area (0-4294967295) nssa [no-summary] [default-information-originate [metric-type (1-2)] [metric (0-16777214)]]
Configure the area to be a NSSA (Not-So-Stubby Area).
@@ -198,6 +198,12 @@ OSPF6 area
advertisement of summaries into the area. In that case, a single Type-3 LSA
containing a default route is originated into the NSSA.
+ NSSA ABRs and ASBRs can be configured with `default-information-originate`
+ option to originate a Type-7 default route into the NSSA area. In the case
+ of NSSA ASBRs, the origination of the default route is conditioned to the
+ existence of a default route in the RIB that wasn't learned via the OSPF
+ protocol.
+
.. clicmd:: area A.B.C.D export-list NAME
.. clicmd:: area (0-4294967295) export-list NAME
diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c
index 2f5328c66..999266b8d 100644
--- a/ospf6d/ospf6_area.c
+++ b/ospf6d/ospf6_area.c
@@ -43,6 +43,7 @@
#include "ospf6_intra.h"
#include "ospf6_abr.h"
#include "ospf6_asbr.h"
+#include "ospf6_zebra.h"
#include "ospf6d.h"
#include "lib/json.h"
#include "ospf6_nssa.h"
@@ -234,6 +235,36 @@ static void ospf6_area_no_summary_unset(struct ospf6 *ospf6,
}
}
+static void ospf6_nssa_default_originate_set(struct ospf6 *ospf6,
+ struct ospf6_area *area,
+ int metric, int metric_type)
+{
+ if (!area->nssa_default_originate.enabled) {
+ area->nssa_default_originate.enabled = true;
+ if (++ospf6->nssa_default_import_check.refcnt == 1) {
+ ospf6->nssa_default_import_check.status = false;
+ ospf6_zebra_import_default_route(ospf6, false);
+ }
+ }
+
+ area->nssa_default_originate.metric_value = metric;
+ area->nssa_default_originate.metric_type = metric_type;
+}
+
+static void ospf6_nssa_default_originate_unset(struct ospf6 *ospf6,
+ struct ospf6_area *area)
+{
+ if (area->nssa_default_originate.enabled) {
+ area->nssa_default_originate.enabled = false;
+ if (--ospf6->nssa_default_import_check.refcnt == 0) {
+ ospf6->nssa_default_import_check.status = false;
+ ospf6_zebra_import_default_route(ospf6, true);
+ }
+ area->nssa_default_originate.metric_value = -1;
+ area->nssa_default_originate.metric_type = -1;
+ }
+}
+
/**
* Make new area structure.
*
@@ -648,6 +679,17 @@ void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6)
}
if (IS_AREA_NSSA(oa)) {
vty_out(vty, " area %s nssa", oa->name);
+ if (oa->nssa_default_originate.enabled) {
+ vty_out(vty, " default-information-originate");
+ if (oa->nssa_default_originate.metric_value
+ != -1)
+ vty_out(vty, " metric %d",
+ oa->nssa_default_originate
+ .metric_value);
+ if (oa->nssa_default_originate.metric_type
+ != DEFAULT_METRIC_TYPE)
+ vty_out(vty, " metric-type 1");
+ }
if (oa->no_summary)
vty_out(vty, " no-summary");
vty_out(vty, "\n");
@@ -1278,11 +1320,20 @@ DEFUN (no_ospf6_area_stub_no_summary,
}
DEFPY(ospf6_area_nssa, ospf6_area_nssa_cmd,
- "area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]",
+ "area <A.B.C.D|(0-4294967295)>$area_str nssa\
+ [{\
+ default-information-originate$dflt_originate [{metric (0-16777214)$mval|metric-type (1-2)$mtype}]\
+ |no-summary$no_summary\
+ }]",
"OSPF6 area parameters\n"
"OSPF6 area ID in IP address format\n"
"OSPF6 area ID as a decimal value\n"
"Configure OSPF6 area as nssa\n"
+ "Originate Type 7 default into NSSA area\n"
+ "OSPFv3 default metric\n"
+ "OSPFv3 metric\n"
+ "OSPFv3 metric type for default routes\n"
+ "Set OSPFv3 External Type 1/2 metrics\n"
"Do not inject inter-area routes into area\n")
{
struct ospf6_area *area;
@@ -1296,23 +1347,44 @@ DEFPY(ospf6_area_nssa, ospf6_area_nssa_cmd,
return CMD_WARNING_CONFIG_FAILED;
}
+ if (dflt_originate) {
+ if (mval_str == NULL)
+ mval = -1;
+ if (mtype_str == NULL)
+ mtype = DEFAULT_METRIC_TYPE;
+ ospf6_nssa_default_originate_set(ospf6, area, mval, mtype);
+ } else
+ ospf6_nssa_default_originate_unset(ospf6, area);
+
if (no_summary)
ospf6_area_no_summary_set(ospf6, area);
else
ospf6_area_no_summary_unset(ospf6, area);
- if (ospf6_check_and_set_router_abr(ospf6))
+
+ if (ospf6_check_and_set_router_abr(ospf6)) {
ospf6_abr_defaults_to_stub(ospf6);
+ ospf6_abr_nssa_type_7_defaults(ospf6);
+ }
return CMD_SUCCESS;
}
DEFPY(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd,
- "no area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]",
+ "no area <A.B.C.D|(0-4294967295)>$area_str nssa\
+ [{\
+ default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\
+ |no-summary\
+ }]",
NO_STR
"OSPF6 area parameters\n"
"OSPF6 area ID in IP address format\n"
"OSPF6 area ID as a decimal value\n"
"Configure OSPF6 area as nssa\n"
+ "Originate Type 7 default into NSSA area\n"
+ "OSPFv3 default metric\n"
+ "OSPFv3 metric\n"
+ "OSPFv3 metric type for default routes\n"
+ "Set OSPFv3 External Type 1/2 metrics\n"
"Do not inject inter-area routes into area\n")
{
struct ospf6_area *area;
@@ -1322,6 +1394,7 @@ DEFPY(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd,
ospf6_area_nssa_unset(ospf6, area);
ospf6_area_no_summary_unset(ospf6, area);
+ ospf6_nssa_default_originate_unset(ospf6, area);
return CMD_SUCCESS;
}
diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h
index 43ac60b26..77cbad8b9 100644
--- a/ospf6d/ospf6_area.h
+++ b/ospf6d/ospf6_area.h
@@ -52,6 +52,13 @@ struct ospf6_area {
/* Area type */
int no_summary;
+ /* NSSA default-information-originate */
+ struct {
+ bool enabled;
+ int metric_type;
+ int metric_value;
+ } nssa_default_originate;
+
/* Brouter traversal protection */
bool intra_brouter_calc;
diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c
index 85abd9d01..df40c608a 100644
--- a/ospf6d/ospf6_asbr.c
+++ b/ospf6d/ospf6_asbr.c
@@ -49,6 +49,7 @@
#include "ospf6_abr.h"
#include "ospf6_intra.h"
#include "ospf6_flood.h"
+#include "ospf6_nssa.h"
#include "ospf6d.h"
#include "ospf6_spf.h"
#include "ospf6_nssa.h"
@@ -85,7 +86,7 @@ static struct ospf6_lsa *ospf6_originate_type5_type7_lsas(
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, lnode, oa)) {
if (IS_AREA_NSSA(oa))
- ospf6_nssa_lsa_originate(route, oa);
+ ospf6_nssa_lsa_originate(route, oa, true);
}
return lsa;
diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c
index abaa16f41..2339d339f 100644
--- a/ospf6d/ospf6_nssa.c
+++ b/ospf6d/ospf6_nssa.c
@@ -368,6 +368,11 @@ static void ospf6_abr_task(struct ospf6 *ospf6)
if (IS_OSPF6_DEBUG_ABR)
zlog_debug("%s : announce stub defaults", __func__);
ospf6_abr_defaults_to_stub(ospf6);
+
+ if (IS_OSPF6_DEBUG_ABR)
+ zlog_debug("%s : announce NSSA Type-7 defaults",
+ __func__);
+ ospf6_abr_nssa_type_7_defaults(ospf6);
}
if (IS_OSPF6_DEBUG_ABR)
@@ -872,6 +877,83 @@ static void ospf6_abr_remove_unapproved_translates(struct ospf6 *ospf6)
zlog_debug("ospf_abr_remove_unapproved_translates(): Stop");
}
+static void ospf6_abr_nssa_type_7_default_create(struct ospf6 *ospf6,
+ struct ospf6_area *oa)
+{
+ struct ospf6_route *def;
+ int metric;
+ int metric_type;
+
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug("Announcing Type-7 default route into NSSA area %s",
+ oa->name);
+
+ def = ospf6_route_create(ospf6);
+ def->type = OSPF6_DEST_TYPE_NETWORK;
+ def->prefix.family = AF_INET6;
+ def->prefix.prefixlen = 0;
+ memset(&def->prefix.u.prefix6, 0, sizeof(struct in6_addr));
+ def->type = OSPF6_DEST_TYPE_NETWORK;
+ def->path.subtype = OSPF6_PATH_SUBTYPE_DEFAULT_RT;
+ if (CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR))
+ def->path.area_id = ospf6->backbone->area_id;
+ else
+ def->path.area_id = oa->area_id;
+
+ /* Compute default route type and metric. */
+ if (oa->nssa_default_originate.metric_value != -1)
+ metric = oa->nssa_default_originate.metric_value;
+ else
+ metric = DEFAULT_DEFAULT_ALWAYS_METRIC;
+ if (oa->nssa_default_originate.metric_type != -1)
+ metric_type = oa->nssa_default_originate.metric_type;
+ else
+ metric_type = DEFAULT_METRIC_TYPE;
+ def->path.metric_type = metric_type;
+ def->path.cost = metric;
+ if (metric_type == 1)
+ def->path.type = OSPF6_PATH_TYPE_EXTERNAL1;
+ else
+ def->path.type = OSPF6_PATH_TYPE_EXTERNAL2;
+
+ ospf6_nssa_lsa_originate(def, oa, false);
+ ospf6_route_delete(def);
+}
+
+static void ospf6_abr_nssa_type_7_default_delete(struct ospf6 *ospf6,
+ struct ospf6_area *oa)
+{
+ struct ospf6_lsa *lsa;
+
+ lsa = ospf6_lsdb_lookup(htons(OSPF6_LSTYPE_TYPE_7), 0,
+ oa->ospf6->router_id, oa->lsdb);
+ if (lsa && !OSPF6_LSA_IS_MAXAGE(lsa)) {
+ if (IS_OSPF6_DEBUG_NSSA)
+ zlog_debug(
+ "Withdrawing Type-7 default route from area %s",
+ oa->name);
+
+ ospf6_lsa_purge(lsa);
+ }
+}
+
+/* NSSA Type-7 default route. */
+void ospf6_abr_nssa_type_7_defaults(struct ospf6 *ospf6)
+{
+ struct listnode *node;
+ struct ospf6_area *oa;
+
+ for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, oa)) {
+ if (IS_AREA_NSSA(oa) && oa->nssa_default_originate.enabled
+ && (IS_OSPF6_ABR(ospf6)
+ || (IS_OSPF6_ASBR(ospf6)
+ && ospf6->nssa_default_import_check.status)))
+ ospf6_abr_nssa_type_7_default_create(ospf6, oa);
+ else
+ ospf6_abr_nssa_type_7_default_delete(ospf6, oa);
+ }
+}
+
static void ospf6_abr_nssa_task(struct ospf6 *ospf6)
{
/* called only if any_nssa */
@@ -1173,7 +1255,7 @@ static void ospf6_check_and_originate_type7_lsa(struct ospf6_area *area)
/* This means the Type-5 LSA was originated for this route */
if (route->path.origin.id != 0 && info->type != DEFAULT_ROUTE)
- ospf6_nssa_lsa_originate(route, area);
+ ospf6_nssa_lsa_originate(route, area, true);
}
/* Loop through the aggregation table to originate type-7 LSAs
@@ -1193,7 +1275,7 @@ static void ospf6_check_and_originate_type7_lsa(struct ospf6_area *area)
"Originating Type-7 LSAs for area %s",
area->name);
- ospf6_nssa_lsa_originate(aggr->route, area);
+ ospf6_nssa_lsa_originate(aggr->route, area, true);
}
}
@@ -1287,7 +1369,7 @@ static struct in6_addr *ospf6_get_nssa_fwd_addr(struct ospf6_area *oa)
}
void ospf6_nssa_lsa_originate(struct ospf6_route *route,
- struct ospf6_area *area)
+ struct ospf6_area *area, bool p_bit)
{
char buffer[OSPF6_MAX_LSASIZE];
struct ospf6_lsa_header *lsa_header;
@@ -1318,7 +1400,7 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route,
UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_E);
/* external route tag */
- if (info->tag)
+ if (info && info->tag)
SET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
else
UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T);
@@ -1333,7 +1415,8 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route,
as_external_lsa->prefix.prefix_options = route->prefix_options;
/* Set the P bit */
- as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P;
+ if (p_bit)
+ as_external_lsa->prefix.prefix_options |= OSPF6_PREFIX_OPTION_P;
/* don't use refer LS-type */
as_external_lsa->prefix.prefix_refer_lstype = htons(0);
@@ -1354,7 +1437,8 @@ void ospf6_nssa_lsa_originate(struct ospf6_route *route,
UNSET_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F);
/* External Route Tag */
- if (CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) {
+ if (info
+ && CHECK_FLAG(as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) {
route_tag_t network_order = htonl(info->tag);
memcpy(p, &network_order, sizeof(network_order));
diff --git a/ospf6d/ospf6_nssa.h b/ospf6d/ospf6_nssa.h
index 631503edc..99cb04c00 100644
--- a/ospf6d/ospf6_nssa.h
+++ b/ospf6d/ospf6_nssa.h
@@ -64,8 +64,9 @@ extern void ospf6_schedule_abr_task(struct ospf6 *ospf6);
extern void ospf6_area_nssa_update(struct ospf6_area *area);
void ospf6_asbr_prefix_readvertise(struct ospf6 *ospf6);
extern void ospf6_nssa_lsa_originate(struct ospf6_route *route,
- struct ospf6_area *area);
+ struct ospf6_area *area, bool p_bit);
extern void install_element_ospf6_debug_nssa(void);
+extern void ospf6_abr_nssa_type_7_defaults(struct ospf6 *osof6);
int ospf6_redistribute_check(struct ospf6 *ospf6, struct ospf6_route *route,
int type);
extern int ospf6_abr_translate_nssa(struct ospf6_area *area,
diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c
index e4de6ccf9..37e133f2f 100644
--- a/ospf6d/ospf6_spf.c
+++ b/ospf6d/ospf6_spf.c
@@ -646,8 +646,10 @@ static int ospf6_spf_calculation_thread(struct thread *t)
/* External LSA calculation */
ospf6_ase_calculate_timer_add(ospf6);
- if (ospf6_check_and_set_router_abr(ospf6))
+ if (ospf6_check_and_set_router_abr(ospf6)) {
ospf6_abr_defaults_to_stub(ospf6);
+ ospf6_abr_nssa_type_7_defaults(ospf6);
+ }
monotime(&end);
timersub(&end, &start, &runtime);
diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h
index 3188b1f58..8a9f87ecf 100644
--- a/ospf6d/ospf6_top.h
+++ b/ospf6d/ospf6_top.h
@@ -143,6 +143,18 @@ struct ospf6 {
/* OSPF6 redistribute configuration */
struct list *redist[ZEBRA_ROUTE_MAX + 1];
+ /* NSSA default-information-originate */
+ struct {
+ /* # of NSSA areas requesting default information */
+ uint16_t refcnt;
+
+ /*
+ * Whether a default route known through non-OSPF protocol is
+ * present in the RIB.
+ */
+ bool status;
+ } nssa_default_import_check;
+
uint8_t flag;
#define OSPF6_FLAG_ABR 0x04
#define OSPF6_FLAG_ASBR 0x08
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index c2e91d09b..9f491f1e8 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -37,6 +37,7 @@
#include "ospf6_lsa.h"
#include "ospf6_lsdb.h"
#include "ospf6_asbr.h"
+#include "ospf6_nssa.h"
#include "ospf6_zebra.h"
#include "ospf6d.h"
#include "ospf6_area.h"
@@ -129,6 +130,61 @@ void ospf6_zebra_no_redistribute(int type, vrf_id_t vrf_id)
AFI_IP6, type, 0, vrf_id);
}
+void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg)
+{
+ struct prefix prefix = {};
+ int command;
+
+ if (zclient->sock < 0) {
+ if (IS_OSPF6_DEBUG_ZEBRA(SEND))
+ zlog_debug(" Not connected to Zebra");
+ return;
+ }
+
+ prefix.family = AF_INET6;
+ prefix.prefixlen = 0;
+
+ if (unreg)
+ command = ZEBRA_IMPORT_ROUTE_UNREGISTER;
+ else
+ command = ZEBRA_IMPORT_ROUTE_REGISTER;
+
+ if (IS_OSPF6_DEBUG_ZEBRA(SEND))
+ zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__,
+ zserv_command_string(command), &prefix,
+ ospf6->vrf_id);
+
+ if (zclient_send_rnh(zclient, command, &prefix, true, ospf6->vrf_id)
+ == ZCLIENT_SEND_FAILURE)
+ flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed",
+ __func__);
+}
+
+static int ospf6_zebra_import_check_update(ZAPI_CALLBACK_ARGS)
+{
+ struct ospf6 *ospf6;
+ struct zapi_route nhr;
+
+ ospf6 = ospf6_lookup_by_vrf_id(vrf_id);
+ if (ospf6 == NULL || !IS_OSPF6_ASBR(ospf6))
+ return 0;
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &nhr)) {
+ zlog_err("%s[%u]: Failure to decode route", __func__,
+ ospf6->vrf_id);
+ return -1;
+ }
+
+ if (nhr.prefix.family != AF_INET6 || nhr.prefix.prefixlen != 0
+ || nhr.type == ZEBRA_ROUTE_OSPF6)
+ return 0;
+
+ ospf6->nssa_default_import_check.status = !!nhr.nexthop_num;
+ ospf6_abr_nssa_type_7_defaults(ospf6);
+
+ return 0;
+}
+
static int ospf6_zebra_if_address_update_add(ZAPI_CALLBACK_ARGS)
{
struct connected *c;
@@ -664,6 +720,7 @@ void ospf6_zebra_init(struct thread_master *master)
ospf6_zebra_if_address_update_delete;
zclient->redistribute_route_add = ospf6_zebra_read_route;
zclient->redistribute_route_del = ospf6_zebra_read_route;
+ zclient->import_check_update = ospf6_zebra_import_check_update;
/* Install command element for zebra node. */
install_element(VIEW_NODE, &show_ospf6_zebra_cmd);
diff --git a/ospf6d/ospf6_zebra.h b/ospf6d/ospf6_zebra.h
index 77e48673c..572bed9f5 100644
--- a/ospf6d/ospf6_zebra.h
+++ b/ospf6d/ospf6_zebra.h
@@ -55,6 +55,7 @@ extern void ospf6_zebra_no_redistribute(int, vrf_id_t vrf_id);
#define ospf6_zebra_is_redistribute(type, vrf_id) \
vrf_bitmap_check(zclient->redist[AFI_IP6][type], vrf_id)
extern void ospf6_zebra_init(struct thread_master *tm);
+extern void ospf6_zebra_import_default_route(struct ospf6 *ospf6, bool unreg);
extern void ospf6_zebra_add_discard(struct ospf6_route *request,
struct ospf6 *ospf6);
extern void ospf6_zebra_delete_discard(struct ospf6_route *request,
diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
index 997058f92..303bcd014 100644
--- a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
+++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
@@ -431,6 +431,50 @@ def test_nssa_no_summary():
assert result is None, assertmsg
+def test_nssa_default_originate():
+ """
+ Test the following:
+ * A type-7 default route should be originated into the NSSA area
+ when the default-information-originate option is configured;
+ * Once the default-information-originate option is unconfigured, the
+ previously originated Type-7 default route should be removed.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Configure r2 to announce a Type-7 default route.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ no default-information originate
+ area 2 nssa default-information-originate
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting Type-7 default-route to be added")
+ routes = {"::/0": {}}
+ expect_ospfv3_routes("r4", routes, wait=30, type="external-2")
+
+ #
+ # Configure r2 to stop announcing a Type-7 default route.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ area 2 nssa
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting Type-7 default route to be removed")
+ test_func = partial(dont_expect_route, "r4", "::/0", type="external-2")
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = "r4's Type-7 default route still exists"
+ assert result is None, assertmsg
+
+
def test_area_filters():
"""
Test ABR import/export filters.