mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-26 06:39:35 +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
|
The `not-advertise` option, when present, prevents the summary route from
|
||||||
being advertised, effectively filtering the summarized routes.
|
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).
|
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
|
4. Support for NSSA Translator functionality when there are multiple NSSA
|
||||||
ABR in an area.
|
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:
|
||||||
|
|
||||||
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);
|
def->path.cost = metric_value(o, type, 0);
|
||||||
|
|
||||||
for (ALL_LIST_ELEMENTS(o->area_list, node, nnode, oa)) {
|
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
|
/* withdraw defaults when an area switches from stub to
|
||||||
* non-stub */
|
* non-stub */
|
||||||
route = ospf6_route_lookup(&def->prefix,
|
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);
|
SET_FLAG(def->flag, OSPF6_ROUTE_REMOVE);
|
||||||
ospf6_abr_originate_summary_to_area(def, oa);
|
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);
|
ospf6_route_delete(def);
|
||||||
|
@ -46,6 +46,9 @@
|
|||||||
#include "ospf6d.h"
|
#include "ospf6d.h"
|
||||||
#include "lib/json.h"
|
#include "lib/json.h"
|
||||||
#include "ospf6_nssa.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_AREA, "OSPF6 area");
|
||||||
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_PLISTNAME, "Prefix list name");
|
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
|
else
|
||||||
vty_out(vty, " area %s stub\n", oa->name);
|
vty_out(vty, " area %s stub\n", oa->name);
|
||||||
}
|
}
|
||||||
if (IS_AREA_NSSA(oa))
|
if (IS_AREA_NSSA(oa)) {
|
||||||
vty_out(vty, " area %s nssa\n", oa->name);
|
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))
|
if (PREFIX_NAME_IN(oa))
|
||||||
vty_out(vty, " area %s filter-list prefix %s in\n",
|
vty_out(vty, " area %s filter-list prefix %s in\n",
|
||||||
oa->name, PREFIX_NAME_IN(oa));
|
oa->name, PREFIX_NAME_IN(oa));
|
||||||
@ -1250,18 +1257,18 @@ DEFUN (no_ospf6_area_stub_no_summary,
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd,
|
DEFPY(ospf6_area_nssa, ospf6_area_nssa_cmd,
|
||||||
"area <A.B.C.D|(0-4294967295)> nssa",
|
"area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]",
|
||||||
"OSPF6 area parameters\n"
|
"OSPF6 area parameters\n"
|
||||||
"OSPF6 area ID in IP address format\n"
|
"OSPF6 area ID in IP address format\n"
|
||||||
"OSPF6 area ID as a decimal value\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;
|
struct ospf6_area *area;
|
||||||
|
|
||||||
VTY_DECLVAR_CONTEXT(ospf6, ospf6);
|
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)) {
|
if (!ospf6_area_nssa_set(ospf6, area)) {
|
||||||
vty_out(vty,
|
vty_out(vty,
|
||||||
@ -1269,26 +1276,32 @@ DEFUN(ospf6_area_nssa, ospf6_area_nssa_cmd,
|
|||||||
return CMD_WARNING_CONFIG_FAILED;
|
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;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd,
|
DEFPY(no_ospf6_area_nssa, no_ospf6_area_nssa_cmd,
|
||||||
"no area <A.B.C.D|(0-4294967295)> nssa",
|
"no area <A.B.C.D|(0-4294967295)>$area_str nssa [no-summary$no_summary]",
|
||||||
NO_STR
|
NO_STR
|
||||||
"OSPF6 area parameters\n"
|
"OSPF6 area parameters\n"
|
||||||
"OSPF6 area ID in IP address format\n"
|
"OSPF6 area ID in IP address format\n"
|
||||||
"OSPF6 area ID as a decimal value\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;
|
struct ospf6_area *area;
|
||||||
|
|
||||||
VTY_DECLVAR_CONTEXT(ospf6, ospf6);
|
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_nssa_unset(ospf6, area);
|
||||||
|
ospf6_area_no_summary_unset(ospf6, area);
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
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 */
|
/* Check the forwarding address */
|
||||||
if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) {
|
if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_F)) {
|
||||||
offset = sizeof(*external)
|
offset = sizeof(*external)
|
||||||
|
@ -92,6 +92,7 @@ ospf6d_ospf6d_snmp_la_LIBADD = lib/libfrrsnmp.la
|
|||||||
|
|
||||||
clippy_scan += \
|
clippy_scan += \
|
||||||
ospf6d/ospf6_top.c \
|
ospf6d/ospf6_top.c \
|
||||||
|
ospf6d/ospf6_area.c \
|
||||||
ospf6d/ospf6_asbr.c \
|
ospf6d/ospf6_asbr.c \
|
||||||
ospf6d/ospf6_lsa.c \
|
ospf6d/ospf6_lsa.c \
|
||||||
ospf6d/ospf6_gr_helper.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
|
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."
|
"Run command `ipv6 ospf6 route` and expect route with type."
|
||||||
tgen = get_topogen()
|
tgen = get_topogen()
|
||||||
|
|
||||||
if detail == False:
|
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:
|
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))
|
logger.info("waiting OSPFv3 router '{}' route".format(router))
|
||||||
test_func = partial(
|
test_func = partial(
|
||||||
@ -91,6 +97,21 @@ def expect_ospfv3_routes(router, routes, wait=5, detail=False):
|
|||||||
assert result is None, assertmsg
|
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):
|
def build_topo(tgen):
|
||||||
"Build function"
|
"Build function"
|
||||||
|
|
||||||
@ -338,13 +359,6 @@ def test_nssa_lsa_type7():
|
|||||||
return lsa
|
return lsa
|
||||||
return None
|
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")
|
logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to go away")
|
||||||
|
|
||||||
# Test that LSA doesn't exist.
|
# Test that LSA doesn't exist.
|
||||||
@ -354,12 +368,69 @@ def test_nssa_lsa_type7():
|
|||||||
assert result is None, assertmsg
|
assert result is None, assertmsg
|
||||||
|
|
||||||
# Test that route doesn't exist.
|
# 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)
|
_, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
|
||||||
assertmsg = '"{}" route still exists'.format("r4")
|
assertmsg = '"{}" route still exists'.format("r4")
|
||||||
assert result is None, assertmsg
|
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):
|
def teardown_module(_mod):
|
||||||
"Teardown the pytest environment"
|
"Teardown the pytest environment"
|
||||||
tgen = get_topogen()
|
tgen = get_topogen()
|
||||||
|
Loading…
Reference in New Issue
Block a user