diff options
| author | Renato Westphal <renato@opensourcerouting.org> | 2021-10-05 21:25:55 -0300 | 
|---|---|---|
| committer | Renato Westphal <renato@opensourcerouting.org> | 2021-10-05 21:25:55 -0300 | 
| commit | 3c77bc809fac8e3a564a6c2ecb380e3917085dcc (patch) | |
| tree | 53140c664cdb9cc74ac8c88eb990609d5867d8f6 | |
| parent | 2ad3c6dbbe562096cf87b6c51ea093ed608b44de (diff) | |
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>
| -rw-r--r-- | doc/user/ospf6d.rst | 15 | ||||
| -rw-r--r-- | ospf6d/ospf6_abr.c | 47 | ||||
| -rw-r--r-- | ospf6d/ospf6_area.c | 19 | ||||
| -rw-r--r-- | ospf6d/ospf6_area.h | 1 | ||||
| -rw-r--r-- | ospf6d/ospf6_nssa.c | 173 | ||||
| -rw-r--r-- | ospf6d/ospf6_route.h | 19 | ||||
| -rw-r--r-- | ospf6d/subdir.am | 1 | 
7 files changed, 250 insertions, 25 deletions
diff --git a/doc/user/ospf6d.rst b/doc/user/ospf6d.rst index d823c5d5b5..4e23223596 100644 --- a/doc/user/ospf6d.rst +++ b/doc/user/ospf6d.rst @@ -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 diff --git a/ospf6d/ospf6_abr.c b/ospf6d/ospf6_abr.c index 690a0dd254..ddc71b3bbf 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -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) { diff --git a/ospf6d/ospf6_area.c b/ospf6d/ospf6_area.c index 2e04e0ee09..996892c49c 100644 --- a/ospf6d/ospf6_area.c +++ b/ospf6d/ospf6_area.c @@ -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)); diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index 77cbad8b9e..905fbac949 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -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; diff --git a/ospf6d/ospf6_nssa.c b/ospf6d/ospf6_nssa.c index 3f555577e5..43bf1b06b3 100644 --- a/ospf6d/ospf6_nssa.c +++ b/ospf6d/ospf6_nssa.c @@ -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); diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index 397860515b..fd8b6a9c1d 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -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 { diff --git a/ospf6d/subdir.am b/ospf6d/subdir.am index be626646a0..34aabc205b 100644 --- a/ospf6d/subdir.am +++ b/ospf6d/subdir.am @@ -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  | 
