ospf6d: add support for NSSA Type-7 address ranges

Implement NSSA address ranges as specified by RFC 3101:

   NSSA border routers may be configured with Type-7 address ranges.
   Each Type-7 address range is defined as an [address,mask] pair.  Many
   separate Type-7 networks may fall into a single Type-7 address range,
   just as a subnetted network is composed of many separate subnets.
   NSSA border routers may aggregate Type-7 routes by advertising a
   single Type-5 LSA for each Type-7 address range.  The Type-5 LSA
   resulting from a Type-7 address range match will be distributed to
   all Type-5 capable areas.

Syntax:
  area A.B.C.D nssa range X:X::X:X/M [<not-advertise|cost (0-16777215)>]

Example:
  router ospf6
   ospf6 router-id 1.1.1.1
   area 1 nssa
   area 1 nssa range 2001:db8:1000::/64
   area 1 nssa range 2001:db8:2000::/64
  !

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
This commit is contained in:
Renato Westphal 2021-10-05 21:25:55 -03:00
parent 2ad3c6dbbe
commit 3c77bc809f
7 changed files with 250 additions and 25 deletions

View File

@ -204,6 +204,21 @@ OSPF6 area
existence of a default route in the RIB that wasn't learned via the OSPF
protocol.
.. clicmd:: area A.B.C.D nssa range X:X::X:X/M [<not-advertise|cost (0-16777215)>]
.. clicmd:: area (0-4294967295) nssa range X:X::X:X/M [<not-advertise|cost (0-16777215)>]
Summarize a group of external subnets into a single Type-7 LSA, which is
then translated to a Type-5 LSA and avertised to the backbone.
This command can only be used at the area boundary (NSSA ABR router).
By default, the metric of the summary route is calculated as the highest
metric among the summarized routes. The `cost` option, however, can be used
to set an explicit metric.
The `not-advertise` option, when present, prevents the summary route from
being advertised, effectively filtering the summarized routes.
.. clicmd:: area A.B.C.D export-list NAME
.. clicmd:: area (0-4294967295) export-list NAME

View File

@ -593,10 +593,14 @@ void ospf6_abr_range_reset_cost(struct ospf6 *ospf6)
struct ospf6_area *oa;
struct ospf6_route *range;
for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa))
for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) {
for (range = ospf6_route_head(oa->range_table); range;
range = ospf6_route_next(range))
OSPF6_ABR_RANGE_CLEAR_COST(range);
for (range = ospf6_route_head(oa->nssa_range_table); range;
range = ospf6_route_next(range))
OSPF6_ABR_RANGE_CLEAR_COST(range);
}
}
static inline uint32_t ospf6_abr_range_compute_cost(struct ospf6_route *range,
@ -607,10 +611,19 @@ static inline uint32_t ospf6_abr_range_compute_cost(struct ospf6_route *range,
for (ro = ospf6_route_match_head(&range->prefix, o->route_table); ro;
ro = ospf6_route_match_next(&range->prefix, ro)) {
if (ro->path.area_id == range->path.area_id
&& (ro->path.type == OSPF6_PATH_TYPE_INTRA)
&& !CHECK_FLAG(ro->flag, OSPF6_ROUTE_REMOVE))
cost = MAX(cost, ro->path.cost);
if (CHECK_FLAG(ro->flag, OSPF6_ROUTE_REMOVE))
continue;
if (ro->path.area_id != range->path.area_id)
continue;
if (CHECK_FLAG(range->flag, OSPF6_ROUTE_NSSA_RANGE)
&& ro->path.type != OSPF6_PATH_TYPE_EXTERNAL1
&& ro->path.type != OSPF6_PATH_TYPE_EXTERNAL2)
continue;
if (!CHECK_FLAG(range->flag, OSPF6_ROUTE_NSSA_RANGE)
&& ro->path.type != OSPF6_PATH_TYPE_INTRA)
continue;
cost = MAX(cost, ro->path.cost);
}
return cost;
@ -659,6 +672,8 @@ void ospf6_abr_range_update(struct ospf6_route *range, struct ospf6 *ospf6)
int summary_orig = 0;
assert(range->type == OSPF6_DEST_TYPE_RANGE);
oa = ospf6_area_lookup(range->path.area_id, ospf6);
assert(oa);
/* update range's cost and active flag */
cost = ospf6_abr_range_compute_cost(range, ospf6);
@ -687,8 +702,26 @@ void ospf6_abr_range_update(struct ospf6_route *range, struct ospf6 *ospf6)
if (IS_OSPF6_DEBUG_ABR)
zlog_debug("%s: range %pFX update", __func__, &range->prefix);
for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa))
summary_orig += ospf6_abr_originate_summary_to_area(range, oa);
if (CHECK_FLAG(range->flag, OSPF6_ROUTE_NSSA_RANGE)) {
if (CHECK_FLAG(range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY)
&& !CHECK_FLAG(range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE)) {
ospf6_nssa_lsa_originate(range, oa, true);
summary_orig = 1;
} else {
struct ospf6_lsa *lsa;
lsa = ospf6_lsdb_lookup(range->path.origin.type,
range->path.origin.id,
ospf6->router_id, oa->lsdb);
if (lsa)
ospf6_lsa_premature_aging(lsa);
}
} else {
for (ALL_LIST_ELEMENTS(ospf6->area_list, node, nnode, oa)) {
summary_orig +=
ospf6_abr_originate_summary_to_area(range, oa);
}
}
if (CHECK_FLAG(range->flag, OSPF6_ROUTE_ACTIVE_SUMMARY)
&& summary_orig) {

View File

@ -306,6 +306,8 @@ struct ospf6_area *ospf6_area_create(uint32_t area_id, struct ospf6 *o, int df)
oa->range_table = OSPF6_ROUTE_TABLE_CREATE(AREA, PREFIX_RANGES);
oa->range_table->scope = oa;
oa->nssa_range_table = OSPF6_ROUTE_TABLE_CREATE(AREA, PREFIX_RANGES);
oa->nssa_range_table->scope = oa;
oa->summary_prefix = OSPF6_ROUTE_TABLE_CREATE(AREA, SUMMARY_PREFIXES);
oa->summary_prefix->scope = oa;
oa->summary_router = OSPF6_ROUTE_TABLE_CREATE(AREA, SUMMARY_ROUTERS);
@ -360,6 +362,7 @@ void ospf6_area_delete(struct ospf6_area *oa)
ospf6_route_table_delete(oa->route_table);
ospf6_route_table_delete(oa->range_table);
ospf6_route_table_delete(oa->nssa_range_table);
ospf6_route_table_delete(oa->summary_prefix);
ospf6_route_table_delete(oa->summary_router);
@ -691,6 +694,22 @@ void ospf6_area_config_write(struct vty *vty, struct ospf6 *ospf6)
vty_out(vty, " no-summary");
vty_out(vty, "\n");
}
for (range = ospf6_route_head(oa->nssa_range_table); range;
range = ospf6_route_next(range)) {
vty_out(vty, " area %s nssa range %pFX", oa->name,
&range->prefix);
if (CHECK_FLAG(range->flag,
OSPF6_ROUTE_DO_NOT_ADVERTISE)) {
vty_out(vty, " not-advertise");
} else {
if (range->path.u.cost_config
!= OSPF_AREA_RANGE_COST_UNSPEC)
vty_out(vty, " cost %u",
range->path.u.cost_config);
}
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));

View File

@ -46,6 +46,7 @@ struct ospf6_area {
/* Summary routes to be originated (includes Configured Address Ranges)
*/
struct ospf6_route_table *range_table;
struct ospf6_route_table *nssa_range_table;
struct ospf6_route_table *summary_prefix;
struct ospf6_route_table *summary_router;

View File

@ -49,6 +49,9 @@
#include "ospf6_asbr.h"
#include "ospf6d.h"
#include "ospf6_nssa.h"
#ifndef VTYSH_EXTRACT_PL
#include "ospf6d/ospf6_nssa_clippy.c"
#endif
DEFINE_MTYPE_STATIC(OSPF6D, OSPF6_LSA, "OSPF6 LSA");
unsigned char config_debug_ospf6_nssa = 0;
@ -412,6 +415,7 @@ static struct ospf6_lsa *ospf6_lsa_translated_nssa_new(struct ospf6_area *area,
struct ospf6 *ospf6 = area->ospf6;
ptrdiff_t tag_offset = 0;
route_tag_t network_order;
struct ospf6_route *range;
if (IS_OSPF6_DEBUG_NSSA)
zlog_debug("%s : Start", __func__);
@ -423,6 +427,25 @@ static struct ospf6_lsa *ospf6_lsa_translated_nssa_new(struct ospf6_area *area,
return NULL;
}
/* find the translated Type-5 for this Type-7 */
nssa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
type7->header);
prefix.family = AF_INET6;
prefix.prefixlen = nssa->prefix.prefix_length;
ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa, &nssa->prefix);
/* Check if the Type-7 LSA should be suppressed by aggregation. */
range = ospf6_route_lookup_bestmatch(&prefix, area->nssa_range_table);
if (range && !prefix_same(&prefix, &range->prefix)
&& !CHECK_FLAG(range->flag, OSPF6_ROUTE_REMOVE)) {
if (IS_OSPF6_DEBUG_NSSA)
zlog_debug(
"%s: LSA %s suppressed by range %pFX of area %s",
__func__, type7->name, &range->prefix,
area->name);
return NULL;
}
/* prepare buffer */
memset(buffer, 0, sizeof(buffer));
lsa_header = (struct ospf6_lsa_header *)buffer;
@ -439,14 +462,6 @@ static struct ospf6_lsa *ospf6_lsa_translated_nssa_new(struct ospf6_area *area,
memcpy(extnew, ext, sizeof(struct ospf6_as_external_lsa));
/* find the translated Type-5 for this Type-7 */
nssa = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
type7->header);
prefix.family = AF_INET6;
prefix.prefixlen = nssa->prefix.prefix_length;
ospf6_prefix_in6_addr(&prefix.u.prefix6, nssa, &nssa->prefix);
/* set Prefix */
memcpy(new_ptr, old_ptr, OSPF6_PREFIX_SPACE(ext->prefix.prefix_length));
ospf6_prefix_apply_mask(&extnew->prefix);
@ -600,7 +615,6 @@ static void ospf6_abr_translate_nssa(struct ospf6_area *area, struct ospf6_lsa *
/* Incoming Type-7 or later aggregated Type-7
*
* LSA is skipped if P-bit is off.
* LSA is aggregated if within range.
*
* The Type-7 is translated, Installed/Approved as a Type-5 into
* global LSDB, then Flooded through AS
@ -732,6 +746,32 @@ static void ospf6_abr_process_nssa_translates(struct ospf6 *ospf6)
zlog_debug("%s : Stop", __func__);
}
static void ospf6_abr_send_nssa_aggregates(struct ospf6 *ospf6)
{
struct listnode *node;
struct ospf6_area *area;
struct ospf6_route *range;
if (IS_OSPF6_DEBUG_NSSA)
zlog_debug("%s: Start", __func__);
for (ALL_LIST_ELEMENTS_RO(ospf6->area_list, node, area)) {
if (area->NSSATranslatorState == OSPF6_NSSA_TRANSLATE_DISABLED)
continue;
if (IS_OSPF6_DEBUG_NSSA)
zlog_debug("%s: looking at area %pI4", __func__,
&area->area_id);
for (range = ospf6_route_head(area->nssa_range_table); range;
range = ospf6_route_next(range))
ospf6_abr_range_update(range, ospf6);
}
if (IS_OSPF6_DEBUG_NSSA)
zlog_debug("%s: Stop", __func__);
}
static void ospf6_abr_remove_unapproved_translates(struct ospf6 *ospf6)
{
struct ospf6_lsa *lsa;
@ -862,6 +902,11 @@ static void ospf6_abr_nssa_task(struct ospf6 *ospf6)
ospf6_abr_unapprove_translates(ospf6);
/* Originate Type-7 aggregates */
if (IS_OSPF6_DEBUG_NSSA)
zlog_debug("ospf6_abr_nssa_task(): send NSSA aggregates");
ospf6_abr_send_nssa_aggregates(ospf6);
/* For all NSSAs, Type-7s, translate to 5's, INSTALL/FLOOD, or
* Aggregate as Type-7
* Install or Approve in Type-5 Global LSDB
@ -1097,6 +1142,13 @@ int ospf6_area_nssa_unset(struct ospf6 *ospf6, struct ospf6_area *area)
UNSET_FLAG(area->flag, OSPF6_AREA_NSSA);
if (IS_OSPF6_DEBUG_NSSA)
zlog_debug("area %s nssa reset", area->name);
/* Clear the table of NSSA ranges. */
ospf6_route_table_delete(area->nssa_range_table);
area->nssa_range_table =
OSPF6_ROUTE_TABLE_CREATE(AREA, PREFIX_RANGES);
area->nssa_range_table->scope = area;
ospf6_area_nssa_update(area);
}
@ -1240,6 +1292,106 @@ void ospf6_abr_check_translate_nssa(struct ospf6_area *area,
}
}
DEFPY (area_nssa_range,
area_nssa_range_cmd,
"area <A.B.C.D|(0-4294967295)>$area nssa range X:X::X:X/M$prefix [<not-advertise$not_adv|cost (0-16777215)$cost>]",
"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"
"Configured address range\n"
"Specify IPv6 prefix\n"
"Do not advertise\n"
"User specified metric for this range\n"
"Advertised metric for this range\n")
{
struct ospf6_area *oa;
struct ospf6_route *range;
VTY_DECLVAR_CONTEXT(ospf6, ospf6);
OSPF6_CMD_AREA_GET(area, oa, ospf6);
if (!IS_AREA_NSSA(oa)) {
vty_out(vty, "%% First configure %s as an NSSA area\n", area);
return CMD_WARNING;
}
range = ospf6_route_lookup((struct prefix *)prefix,
oa->nssa_range_table);
if (range == NULL) {
range = ospf6_route_create(ospf6);
range->type = OSPF6_DEST_TYPE_RANGE;
SET_FLAG(range->flag, OSPF6_ROUTE_NSSA_RANGE);
prefix_copy(&range->prefix, prefix);
range->path.area_id = oa->area_id;
range->path.metric_type = 2;
range->path.cost = OSPF_AREA_RANGE_COST_UNSPEC;
range->path.origin.type = htons(OSPF6_LSTYPE_TYPE_7);
range->path.origin.id = htonl(ospf6->external_id++);
range->path.origin.adv_router = ospf6->router_id;
ospf6_route_add(range, oa->nssa_range_table);
}
/* process "not-advertise" */
if (not_adv)
SET_FLAG(range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE);
else
UNSET_FLAG(range->flag, OSPF6_ROUTE_DO_NOT_ADVERTISE);
/* process "cost" */
if (!cost_str)
cost = OSPF_AREA_RANGE_COST_UNSPEC;
range->path.u.cost_config = cost;
/* Redo summaries if required */
if (ospf6_check_and_set_router_abr(ospf6))
ospf6_schedule_abr_task(ospf6);
return CMD_SUCCESS;
}
DEFPY (no_area_nssa_range,
no_area_nssa_range_cmd,
"no area <A.B.C.D|(0-4294967295)>$area nssa range X:X::X:X/M$prefix [<not-advertise|cost (0-16777215)>]",
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"
"Configured address range\n"
"Specify IPv6 prefix\n"
"Do not advertise\n"
"User specified metric for this range\n"
"Advertised metric for this range\n")
{
struct ospf6_area *oa;
struct ospf6_route *range;
VTY_DECLVAR_CONTEXT(ospf6, ospf6);
OSPF6_CMD_AREA_GET(area, oa, ospf6);
range = ospf6_route_lookup((struct prefix *)prefix,
oa->nssa_range_table);
if (range == NULL) {
vty_out(vty, "%% range %s does not exists.\n", prefix_str);
return CMD_SUCCESS;
}
if (ospf6_check_and_set_router_abr(oa->ospf6)) {
/* Blow away the aggregated LSA and route */
SET_FLAG(range->flag, OSPF6_ROUTE_REMOVE);
/* Redo summaries if required */
thread_execute(master, ospf6_abr_task_timer, ospf6, 0);
}
ospf6_route_remove(range, oa->nssa_range_table);
return CMD_SUCCESS;
}
DEFUN(debug_ospf6_nssa, debug_ospf6_nssa_cmd,
"debug ospf6 nssa",
DEBUG_STR
@ -1269,6 +1421,9 @@ void config_write_ospf6_debug_nssa(struct vty *vty)
void install_element_ospf6_debug_nssa(void)
{
install_element(OSPF6_NODE, &area_nssa_range_cmd);
install_element(OSPF6_NODE, &no_area_nssa_range_cmd);
install_element(ENABLE_NODE, &debug_ospf6_nssa_cmd);
install_element(ENABLE_NODE, &no_debug_ospf6_nssa_cmd);
install_element(CONFIG_NODE, &debug_ospf6_nssa_cmd);

View File

@ -186,7 +186,7 @@ struct ospf6_route {
struct timeval changed;
/* flag */
uint8_t flag;
uint16_t flag;
/* Prefix Options */
uint8_t prefix_options;
@ -221,14 +221,15 @@ struct ospf6_route {
#define OSPF6_DEST_TYPE_RANGE 5
#define OSPF6_DEST_TYPE_MAX 6
#define OSPF6_ROUTE_CHANGE 0x01
#define OSPF6_ROUTE_ADD 0x02
#define OSPF6_ROUTE_REMOVE 0x04
#define OSPF6_ROUTE_BEST 0x08
#define OSPF6_ROUTE_ACTIVE_SUMMARY 0x10
#define OSPF6_ROUTE_DO_NOT_ADVERTISE 0x20
#define OSPF6_ROUTE_WAS_REMOVED 0x40
#define OSPF6_ROUTE_BLACKHOLE_ADDED 0x80
#define OSPF6_ROUTE_CHANGE 0x0001
#define OSPF6_ROUTE_ADD 0x0002
#define OSPF6_ROUTE_REMOVE 0x0004
#define OSPF6_ROUTE_BEST 0x0008
#define OSPF6_ROUTE_ACTIVE_SUMMARY 0x0010
#define OSPF6_ROUTE_DO_NOT_ADVERTISE 0x0020
#define OSPF6_ROUTE_WAS_REMOVED 0x0040
#define OSPF6_ROUTE_BLACKHOLE_ADDED 0x0080
#define OSPF6_ROUTE_NSSA_RANGE 0x0100
struct ospf6;
struct ospf6_route_table {

View File

@ -99,6 +99,7 @@ clippy_scan += \
ospf6d/ospf6_lsa.c \
ospf6d/ospf6_gr_helper.c \
ospf6d/ospf6_gr.c \
ospf6d/ospf6_nssa.c \
ospf6d/ospf6_route.c \
# end