mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-25 13:27:03 +00:00
Merge pull request #9491 from opensourcerouting/totally-nssa
ospf6d: add support for NSSA totally stub areas
This commit is contained in:
commit
5e6004e53a
@ -176,9 +176,9 @@ OSPF6 area
|
||||
The `not-advertise` option, when present, prevents the summary route from
|
||||
being advertised, effectively filtering the summarized routes.
|
||||
|
||||
.. clicmd:: area A.B.C.D nssa
|
||||
.. clicmd:: area A.B.C.D nssa [no-summary]
|
||||
|
||||
.. clicmd:: area (0-4294967295) nssa
|
||||
.. clicmd:: area (0-4294967295) nssa [no-summary]
|
||||
|
||||
Configure the area to be a NSSA (Not-So-Stubby Area).
|
||||
|
||||
@ -194,6 +194,10 @@ OSPF6 area
|
||||
4. Support for NSSA Translator functionality when there are multiple NSSA
|
||||
ABR in an area.
|
||||
|
||||
An NSSA ABR can be configured with the `no-summary` option to prevent the
|
||||
advertisement of summaries into the area. In that case, a single Type-3 LSA
|
||||
containing a default route is originated into the NSSA.
|
||||
|
||||
.. _ospf6-interface:
|
||||
|
||||
OSPF6 interface
|
||||
|
@ -748,7 +748,15 @@ void ospf6_abr_defaults_to_stub(struct ospf6 *o)
|
||||
def->path.cost = metric_value(o, type, 0);
|
||||
|
||||
for (ALL_LIST_ELEMENTS(o->area_list, node, nnode, oa)) {
|
||||
if (!IS_AREA_STUB(oa)) {
|
||||
if (IS_AREA_STUB(oa) || (IS_AREA_NSSA(oa) && oa->no_summary)) {
|
||||
/* announce defaults to stubby areas */
|
||||
if (IS_OSPF6_DEBUG_ABR)
|
||||
zlog_debug(
|
||||
"Announcing default route into stubby area %s",
|
||||
oa->name);
|
||||
UNSET_FLAG(def->flag, OSPF6_ROUTE_REMOVE);
|
||||
ospf6_abr_originate_summary_to_area(def, oa);
|
||||
} else {
|
||||
/* withdraw defaults when an area switches from stub to
|
||||
* non-stub */
|
||||
route = ospf6_route_lookup(&def->prefix,
|
||||
@ -762,14 +770,6 @@ void ospf6_abr_defaults_to_stub(struct ospf6 *o)
|
||||
SET_FLAG(def->flag, OSPF6_ROUTE_REMOVE);
|
||||
ospf6_abr_originate_summary_to_area(def, oa);
|
||||
}
|
||||
} else {
|
||||
/* announce defaults to stubby areas */
|
||||
if (IS_OSPF6_DEBUG_ABR)
|
||||
zlog_debug(
|
||||
"Announcing default route into stubby area %s",
|
||||
oa->name);
|
||||
UNSET_FLAG(def->flag, OSPF6_ROUTE_REMOVE);
|
||||
ospf6_abr_originate_summary_to_area(def, oa);
|
||||
}
|
||||
}
|
||||
ospf6_route_delete(def);
|
||||
|
@ -46,6 +46,9 @@
|
||||
#include "ospf6d.h"
|
||||
#include "lib/json.h"
|
||||
#include "ospf6_nssa.h"
|
||||
#ifndef VTYSH_EXTRACT_PL
|
||||
#include "ospf6d/ospf6_area_clippy.c"
|
||||
#endif
|
||||
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_AREA, "OSPF6 area");
|
||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name");
|
||||
@ -643,8 +646,12 @@ void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6)
|
||||
else
|
||||
vty_out(vty, " area %s stub\n", oa->name);
|
||||
}
|
||||
if (IS_AREA_NSSA(oa))
|
||||
vty_out(vty, " area %s nssa\n", oa->name);
|
||||
if (IS_AREA_NSSA(oa)) {
|
||||
vty_out(vty, " area %s nssa", oa->name);
|
||||
if (oa->no_summary)
|
||||
vty_out(vty, " no-summary");
|
||||
vty_out(vty, "\n");
|
||||
}
|
||||
if (PREFIX_NAME_IN(oa))
|
||||
vty_out(vty, " area %s filter-list prefix %s in\n",
|
||||
oa->name, PREFIX_NAME_IN(oa));
|
||||
@ -1250,18 +1257,18 @@ DEFUN (no_ospf6_area_stub_no_summary,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd,
|
||||
"area <A.B.C.D|(0-4294967295)> nssa",
|
||||
DEFPY(ospf6_area_nssa, ospf6_area_nssa_cmd,
|
||||
"area <A.B.C.D|(0-4294967295)>$area_str nssa [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")
|
||||
"Configure OSPF6 area as nssa\n"
|
||||
"Do not inject inter-area routes into area\n")
|
||||
{
|
||||
int idx_ipv4_number = 1;
|
||||
struct ospf6_area *area;
|
||||
|
||||
VTY_DECLVAR_CONTEXT(ospf6, ospf6);
|
||||
OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6);
|
||||
OSPF6_CMD_AREA_GET(area_str, area, ospf6);
|
||||
|
||||
if (!ospf6_area_nssa_set(ospf6, area)) {
|
||||
vty_out(vty,
|
||||
@ -1269,26 +1276,32 @@ DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd,
|
||||
return CMD_WARNING_CONFIG_FAILED;
|
||||
}
|
||||
|
||||
ospf6_area_no_summary_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))
|
||||
ospf6_abr_defaults_to_stub(ospf6);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd,
|
||||
"no area <A.B.C.D|(0-4294967295)> nssa",
|
||||
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_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")
|
||||
"Configure OSPF6 area as nssa\n"
|
||||
"Do not inject inter-area routes into area\n")
|
||||
{
|
||||
int idx_ipv4_number = 2;
|
||||
struct ospf6_area *area;
|
||||
|
||||
VTY_DECLVAR_CONTEXT(ospf6, ospf6);
|
||||
OSPF6_CMD_AREA_GET(argv[idx_ipv4_number]->arg, area, ospf6);
|
||||
OSPF6_CMD_AREA_GET(area_str, area, ospf6);
|
||||
|
||||
ospf6_area_nssa_unset(ospf6, area);
|
||||
ospf6_area_no_summary_unset(ospf6, area);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
@ -588,6 +588,32 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 3101 - Section 2.5:
|
||||
* "If the destination is a Type-7 default route (destination ID =
|
||||
* DefaultDestination) and one of the following is true, then do
|
||||
* nothing with this LSA and consider the next in the list:
|
||||
*
|
||||
* o The calculating router is a border router and the LSA has
|
||||
* its P-bit clear. Appendix E describes a technique
|
||||
* whereby an NSSA border router installs a Type-7 default
|
||||
* LSA without propagating it.
|
||||
*
|
||||
* o The calculating router is a border router and is
|
||||
* suppressing the import of summary routes as Type-3
|
||||
* summary-LSAs".
|
||||
*/
|
||||
if (ntohs(lsa->header->type) == OSPF6_LSTYPE_TYPE_7
|
||||
&& external->prefix.prefix_length == 0
|
||||
&& CHECK_FLAG(ospf6->flag, OSPF6_FLAG_ABR)
|
||||
&& (CHECK_FLAG(external->prefix.prefix_options,
|
||||
OSPF6_PREFIX_OPTION_P)
|
||||
|| oa->no_summary)) {
|
||||
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL))
|
||||
zlog_debug("Skipping Type-7 default route");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check the forwarding address */
|
||||
if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) {
|
||||
offset = sizeof(*external)
|
||||
|
@ -92,6 +92,7 @@ ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la
|
||||
|
||||
clippy_scan += \
|
||||
ospf6d/ospf6_top.c \
|
||||
ospf6d/ospf6_area.c \
|
||||
ospf6d/ospf6_asbr.c \
|
||||
ospf6d/ospf6_lsa.c \
|
||||
ospf6d/ospf6_gr_helper.c \
|
||||
|
@ -72,14 +72,20 @@ def expect_lsas(router, area, lsas, wait=5, extra_params=""):
|
||||
assert result is None, assertmsg
|
||||
|
||||
|
||||
def expect_ospfv3_routes(router, routes, wait=5, detail=False):
|
||||
def expect_ospfv3_routes(router, routes, wait=5, type=None, detail=False):
|
||||
"Run command `ipv6 ospf6 route` and expect route with type."
|
||||
tgen = get_topogen()
|
||||
|
||||
if detail == False:
|
||||
cmd = "show ipv6 ospf6 route json"
|
||||
if type == None:
|
||||
cmd = "show ipv6 ospf6 route json"
|
||||
else:
|
||||
cmd = "show ipv6 ospf6 route {} json".format(type)
|
||||
else:
|
||||
cmd = "show ipv6 ospf6 route detail json"
|
||||
if type == None:
|
||||
cmd = "show ipv6 ospf6 route detail json"
|
||||
else:
|
||||
cmd = "show ipv6 ospf6 route {} detail json".format(type)
|
||||
|
||||
logger.info("waiting OSPFv3 router '{}' route".format(router))
|
||||
test_func = partial(
|
||||
@ -91,6 +97,21 @@ def expect_ospfv3_routes(router, routes, wait=5, detail=False):
|
||||
assert result is None, assertmsg
|
||||
|
||||
|
||||
def dont_expect_route(router, unexpected_route, type=None):
|
||||
"Specialized test function to expect route go missing"
|
||||
tgen = get_topogen()
|
||||
|
||||
if type == None:
|
||||
cmd = "show ipv6 ospf6 route json"
|
||||
else:
|
||||
cmd = "show ipv6 ospf6 route {} json".format(type)
|
||||
|
||||
output = tgen.gears[router].vtysh_cmd(cmd, isjson=True)
|
||||
if unexpected_route in output["routes"]:
|
||||
return output["routes"][unexpected_route]
|
||||
return None
|
||||
|
||||
|
||||
def build_topo(tgen):
|
||||
"Build function"
|
||||
|
||||
@ -338,13 +359,6 @@ def test_nssa_lsa_type7():
|
||||
return lsa
|
||||
return None
|
||||
|
||||
def dont_expect_route(unexpected_route):
|
||||
"Specialized test function to expect route go missing"
|
||||
output = tgen.gears["r4"].vtysh_cmd("show ipv6 ospf6 route json", isjson=True)
|
||||
if unexpected_route in output["routes"]:
|
||||
return output["routes"][unexpected_route]
|
||||
return None
|
||||
|
||||
logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to go away")
|
||||
|
||||
# Test that LSA doesn't exist.
|
||||
@ -354,12 +368,69 @@ def test_nssa_lsa_type7():
|
||||
assert result is None, assertmsg
|
||||
|
||||
# Test that route doesn't exist.
|
||||
test_func = partial(dont_expect_route, "2001:db8:100::/64")
|
||||
test_func = partial(dont_expect_route, "r4", "2001:db8:100::/64")
|
||||
_, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
|
||||
assertmsg = '"{}" route still exists'.format("r4")
|
||||
assert result is None, assertmsg
|
||||
|
||||
|
||||
def test_nssa_no_summary():
|
||||
"""
|
||||
Test the following:
|
||||
* Type-3 inter-area routes should be removed when the NSSA no-summary option
|
||||
is configured;
|
||||
* A type-3 inter-area default route should be originated into the NSSA area
|
||||
when the no-summary option is configured;
|
||||
* Once the no-summary option is unconfigured, all previously existing
|
||||
Type-3 inter-area routes should be re-added, and the inter-area default
|
||||
route removed.
|
||||
"""
|
||||
tgen = get_topogen()
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
#
|
||||
# Configure area 1 as a NSSA totally stub area.
|
||||
#
|
||||
config = """
|
||||
configure terminal
|
||||
router ospf6
|
||||
area 2 nssa no-summary
|
||||
"""
|
||||
tgen.gears["r2"].vtysh_cmd(config)
|
||||
|
||||
logger.info("Expecting inter-area routes to be removed")
|
||||
for route in ["2001:db8:1::/64", "2001:db8:2::/64"]:
|
||||
test_func = partial(dont_expect_route, "r4", route, type="inter-area")
|
||||
_, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
|
||||
assertmsg = "{}'s {} inter-area route still exists".format("r4", route)
|
||||
assert result is None, assertmsg
|
||||
|
||||
logger.info("Expecting inter-area default-route to be added")
|
||||
routes = {"::/0": {}}
|
||||
expect_ospfv3_routes("r4", routes, wait=30, type="inter-area")
|
||||
|
||||
#
|
||||
# Configure area 1 as a regular NSSA area.
|
||||
#
|
||||
config = """
|
||||
configure terminal
|
||||
router ospf6
|
||||
area 2 nssa
|
||||
"""
|
||||
tgen.gears["r2"].vtysh_cmd(config)
|
||||
|
||||
logger.info("Expecting inter-area routes to be re-added")
|
||||
routes = {"2001:db8:1::/64": {}, "2001:db8:2::/64": {}}
|
||||
expect_ospfv3_routes("r4", routes, wait=30, type="inter-area")
|
||||
|
||||
logger.info("Expecting inter-area default route to be removed")
|
||||
test_func = partial(dont_expect_route, "r4", "::/0", type="inter-area")
|
||||
_, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
|
||||
assertmsg = "{}'s inter-area default route still exists".format("r4")
|
||||
assert result is None, assertmsg
|
||||
|
||||
|
||||
def teardown_module(_mod):
|
||||
"Teardown the pytest environment"
|
||||
tgen = get_topogen()
|
||||
|
Loading…
Reference in New Issue
Block a user