ospfd: implement Type-7 default routes for NSSA areas

Add the "default-information-originate" option to the "area X nssa"
command. That option allows the origination of Type-7 default routes
on NSSA ABRs and ASBRs.

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
This commit is contained in:
Renato Westphal 2023-03-07 21:13:53 -03:00
parent e85194f572
commit 017714e3ad
10 changed files with 370 additions and 18 deletions

View File

@ -431,6 +431,16 @@ Areas
configured not to advertise forwarding addresses into the backbone to direct
forwarded traffic to the NSSA ABR translator.
.. clicmd:: area A.B.C.D nssa default-information-originate [metric-type (1-2)] [metric (0-16777214)]
.. clicmd:: area (0-4294967295) nssa default-information-originate [metric-type (1-2)] [metric (0-16777214)]
NSSA ABRs and ASBRs can be configured with the `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 default-cost (0-16777215)

View File

@ -1807,6 +1807,82 @@ static void ospf_abr_announce_non_dna_routers(struct event *thread)
OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT, "%s(): Stop", __func__);
}
static void ospf_abr_nssa_type7_default_create(struct ospf *ospf,
struct ospf_area *area,
struct ospf_lsa *lsa)
{
struct external_info ei;
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
"Announcing Type-7 default route into NSSA area %pI4",
&area->area_id);
/* Prepare the extrenal_info for aggregator */
memset(&ei, 0, sizeof(struct external_info));
ei.p.family = AF_INET;
ei.p.prefixlen = 0;
ei.tag = 0;
ei.type = 0;
ei.instance = ospf->instance;
/* Compute default route type and metric. */
if (area->nssa_default_originate.metric_value != -1)
ei.route_map_set.metric =
area->nssa_default_originate.metric_value;
else
ei.route_map_set.metric = DEFAULT_DEFAULT_ALWAYS_METRIC;
if (area->nssa_default_originate.metric_type != -1)
ei.route_map_set.metric_type =
area->nssa_default_originate.metric_type;
else
ei.route_map_set.metric_type = DEFAULT_METRIC_TYPE;
if (!lsa)
ospf_nssa_lsa_originate(area, &ei);
else
ospf_nssa_lsa_refresh(area, lsa, &ei);
}
static void ospf_abr_nssa_type7_default_delete(struct ospf *ospf,
struct ospf_area *area,
struct ospf_lsa *lsa)
{
if (lsa && !CHECK_FLAG(lsa->flags, OSPF_LSA_IN_MAXAGE)) {
if (IS_DEBUG_OSPF_NSSA)
zlog_debug(
"Withdrawing Type-7 default route from area %pI4",
&area->area_id);
ospf_ls_retransmit_delete_nbr_area(area, lsa);
ospf_refresher_unregister_lsa(ospf, lsa);
ospf_lsa_flush_area(lsa, area);
}
}
/* NSSA Type-7 default route. */
void ospf_abr_nssa_type7_defaults(struct ospf *ospf)
{
struct ospf_area *area;
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) {
struct in_addr id = {};
struct ospf_lsa *lsa;
lsa = ospf_lsdb_lookup_by_id(area->lsdb, OSPF_AS_NSSA_LSA, id,
area->ospf->router_id);
if (area->external_routing == OSPF_AREA_NSSA
&& area->nssa_default_originate.enabled
&& (IS_OSPF_ABR(ospf)
|| (IS_OSPF_ASBR(ospf)
&& ospf->nssa_default_import_check.status)))
ospf_abr_nssa_type7_default_create(ospf, area, lsa);
else
ospf_abr_nssa_type7_default_delete(ospf, area, lsa);
}
}
static int ospf_abr_remove_unapproved_translates_apply(struct ospf *ospf,
struct ospf_lsa *lsa)
{
@ -2031,6 +2107,11 @@ void ospf_abr_task(struct ospf *ospf)
zlog_debug("%s: announce stub defaults", __func__);
ospf_abr_announce_stub_defaults(ospf);
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("%s: announce NSSA Type-7 defaults",
__func__);
ospf_abr_nssa_type7_defaults(ospf);
if (ospf->fr_configured) {
OSPF_LOG_DEBUG(IS_DEBUG_OSPF_EVENT,
"%s(): announce non-DNArouters",

View File

@ -73,6 +73,7 @@ extern void ospf_schedule_abr_task(struct ospf *);
extern void ospf_abr_announce_network_to_area(struct prefix_ipv4 *, uint32_t,
struct ospf_area *);
extern void ospf_abr_nssa_type7_defaults(struct ospf *ospf);
extern void ospf_abr_nssa_check_status(struct ospf *ospf);
extern void ospf_abr_generate_indication_lsa(struct ospf *ospf,
const struct ospf_area *area);

View File

@ -1868,8 +1868,7 @@ static struct ospf_lsa *ospf_external_lsa_new(struct ospf *ospf,
}
/* As Type-7 */
static void ospf_install_flood_nssa(struct ospf *ospf, struct ospf_lsa *lsa,
struct external_info *ei)
static void ospf_install_flood_nssa(struct ospf *ospf, struct ospf_lsa *lsa)
{
struct ospf_lsa *new;
struct as_external_lsa *extlsa;
@ -2253,7 +2252,7 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf,
/* stay away from translated LSAs! */
!(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT)))
ospf_install_flood_nssa(
ospf, new, ei); /* Install/Flood Type-7 to all NSSAs */
ospf, new); /* Install/Flood Type-7 to all NSSAs */
/* Debug logging. */
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
@ -2266,6 +2265,100 @@ struct ospf_lsa *ospf_external_lsa_originate(struct ospf *ospf,
return new;
}
/* Originate an NSSA-LSA, install and flood. */
struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area,
struct external_info *ei)
{
struct ospf *ospf = area->ospf;
struct ospf_lsa *new;
if (ospf->gr_info.restart_in_progress) {
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE))
zlog_debug(
"LSA[Type7]: Graceful Restart in progress, don't originate");
return NULL;
}
if (ospf->router_id.s_addr == INADDR_ANY) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"LSA[Type7:%pI4]: deferring NSSA-LSA origination, router ID is zero",
&ei->p.prefix);
return NULL;
}
/* Create new NSSA-LSA instance. */
if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"LSA[Type7:%pI4]: Could not originate NSSA-LSA",
&ei->p.prefix);
return NULL;
}
new->data->type = OSPF_AS_NSSA_LSA;
new->area = area;
/* Install newly created LSA into Type-7 LSDB. */
ospf_lsa_install(ospf, NULL, new);
/* Update LSA origination count. */
ospf->lsa_originate_count++;
/* Flooding new LSA */
ospf_flood_through_area(area, NULL, new);
/* Debug logging. */
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
zlog_debug("LSA[Type%d:%pI4]: Originate NSSA-LSA %p",
new->data->type, &new->data->id, (void *)new);
ospf_lsa_header_dump(new->data);
}
return new;
}
/* Refresh NSSA-LSA. */
struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area,
struct ospf_lsa *lsa,
struct external_info *ei)
{
struct ospf *ospf = area->ospf;
struct ospf_lsa *new;
/* Delete LSA from neighbor retransmit-list. */
ospf_ls_retransmit_delete_nbr_as(ospf, lsa);
/* Unregister AS-external-LSA from refresh-list. */
ospf_refresher_unregister_lsa(ospf, lsa);
/* Create new NSSA-LSA instance. */
if ((new = ospf_external_lsa_new(ospf, ei, NULL)) == NULL) {
if (IS_DEBUG_OSPF_EVENT)
zlog_debug(
"LSA[Type7:%pI4]: Could not originate NSSA-LSA",
&ei->p.prefix);
return NULL;
}
new->data->type = OSPF_AS_NSSA_LSA;
new->data->ls_seqnum = lsa_seqnum_increment(lsa);
new->area = area;
/* Install newly created LSA into Type-7 LSDB. */
ospf_lsa_install(ospf, NULL, new);
/* Flooding new LSA */
ospf_flood_through_area(area, NULL, new);
/* Debug logging. */
if (IS_DEBUG_OSPF(lsa, LSA_GENERATE)) {
zlog_debug("LSA[Type%d:%pI4]: NSSA-LSA refresh",
new->data->type, &new->data->id);
ospf_lsa_header_dump(new->data);
}
return new;
}
static struct external_info *ospf_default_external_info(struct ospf *ospf)
{
int type;
@ -2611,8 +2704,8 @@ struct ospf_lsa *ospf_external_lsa_refresh(struct ospf *ospf,
/* If any attached NSSA, install as Type-7, flood to all NSSA Areas */
if (ospf->anyNSSA && !(CHECK_FLAG(new->flags, OSPF_LSA_LOCAL_XLT)))
ospf_install_flood_nssa(ospf, new,
ei); /* Install/Flood per new rules */
ospf_install_flood_nssa(ospf,
new); /* Install/Flood per new rules */
/* Register self-originated LSA to refresh queue.
* Translated LSAs should not be registered, but refreshed upon

View File

@ -278,6 +278,11 @@ extern struct in_addr ospf_get_ip_from_ifp(struct ospf_interface *);
extern struct ospf_lsa *ospf_external_lsa_originate(struct ospf *,
struct external_info *);
extern struct ospf_lsa *ospf_nssa_lsa_originate(struct ospf_area *area,
struct external_info *ei);
extern struct ospf_lsa *ospf_nssa_lsa_refresh(struct ospf_area *area,
struct ospf_lsa *lsa,
struct external_info *ei);
extern void ospf_external_lsa_rid_change(struct ospf *ospf);
extern struct ospf_lsa *ospf_lsa_lookup(struct ospf *ospf, struct ospf_area *,
uint32_t, struct in_addr,

View File

@ -1438,6 +1438,7 @@ DEFPY (ospf_area_nssa,
"area <A.B.C.D|(0-4294967295)>$area_str nssa\
[{\
<translate-candidate|translate-never|translate-always>$translator_role\
|default-information-originate$dflt_originate [{metric (0-16777214)$mval|metric-type (1-2)$mtype}]\
|no-summary$no_summary\
|suppress-fa$suppress_fa\
}]",
@ -1448,6 +1449,11 @@ DEFPY (ospf_area_nssa,
"Configure NSSA-ABR for translate election (default)\n"
"Configure NSSA-ABR to never translate\n"
"Configure NSSA-ABR to always translate\n"
"Originate Type 7 default into NSSA area\n"
"OSPF default metric\n"
"OSPF metric\n"
"OSPF metric type for default routes\n"
"Set OSPF External Type 1/2 metrics\n"
"Do not inject inter-area routes into nssa\n"
"Suppress forwarding address\n")
{
@ -1481,6 +1487,18 @@ DEFPY (ospf_area_nssa,
OSPF_NSSA_ROLE_CANDIDATE);
}
if (dflt_originate) {
int metric_type = DEFAULT_METRIC_TYPE;
if (mval_str == NULL)
mval = -1;
if (mtype_str)
(void)str2metric_type(mtype_str, &metric_type);
ospf_area_nssa_default_originate_set(ospf, area_id, mval,
metric_type);
} else
ospf_area_nssa_default_originate_unset(ospf, area_id);
if (no_summary)
ospf_area_nssa_no_summary_set(ospf, area_id);
else
@ -1504,6 +1522,7 @@ DEFPY (no_ospf_area_nssa,
"no area <A.B.C.D|(0-4294967295)>$area_str nssa\
[{\
<translate-candidate|translate-never|translate-always>\
|default-information-originate [{metric (0-16777214)|metric-type (1-2)}]\
|no-summary\
|suppress-fa\
}]",
@ -1515,6 +1534,11 @@ DEFPY (no_ospf_area_nssa,
"Configure NSSA-ABR for translate election (default)\n"
"Configure NSSA-ABR to never translate\n"
"Configure NSSA-ABR to always translate\n"
"Originate Type 7 default into NSSA area\n"
"OSPF default metric\n"
"OSPF metric\n"
"OSPF metric type for default routes\n"
"Set OSPF External Type 1/2 metrics\n"
"Do not inject inter-area routes into nssa\n"
"Suppress forwarding address\n")
{
@ -1527,6 +1551,7 @@ DEFPY (no_ospf_area_nssa,
/* Flush the NSSA LSA for the specified area */
ospf_flush_lsa_from_area(ospf, area_id, OSPF_AS_NSSA_LSA);
ospf_area_no_summary_unset(ospf, area_id);
ospf_area_nssa_default_originate_unset(ospf, area_id);
ospf_area_nssa_suppress_fa_unset(ospf, area_id);
ospf_area_nssa_unset(ospf, area_id);
@ -11872,29 +11897,39 @@ static int config_write_ospf_area(struct vty *vty, struct ospf *ospf)
vty_out(vty, " no-summary\n");
vty_out(vty, "\n");
} else if (area->external_routing == OSPF_AREA_NSSA) {
vty_out(vty, " area %s nssa", buf);
switch (area->NSSATranslatorRole) {
case OSPF_NSSA_ROLE_NEVER:
vty_out(vty,
" area %s nssa translate-never\n",
buf);
vty_out(vty, " translate-never");
break;
case OSPF_NSSA_ROLE_ALWAYS:
vty_out(vty,
" area %s nssa translate-always\n",
buf);
vty_out(vty, " translate-always");
break;
case OSPF_NSSA_ROLE_CANDIDATE:
vty_out(vty, " area %s nssa \n", buf);
break;
}
if (area->nssa_default_originate.enabled) {
vty_out(vty,
" default-information-originate");
if (area->nssa_default_originate
.metric_value
!= -1)
vty_out(vty, " metric %d",
area->nssa_default_originate
.metric_value);
if (area->nssa_default_originate
.metric_type
!= DEFAULT_METRIC_TYPE)
vty_out(vty, " metric-type 1");
}
if (area->no_summary)
vty_out(vty,
" area %s nssa no-summary\n",
buf);
vty_out(vty, " no-summary");
if (area->suppress_fa)
vty_out(vty,
" area %s nssa suppress-fa\n",
buf);
vty_out(vty, " suppress-fa");
vty_out(vty, "\n");
}
if (area->default_cost != 1)

View File

@ -20,6 +20,7 @@
#include "log.h"
#include "route_opaque.h"
#include "lib/bfd.h"
#include "lib/lib_errors.h"
#include "nexthop.h"
#include "ospfd/ospfd.h"
@ -1470,6 +1471,61 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
return 0;
}
void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg)
{
struct prefix prefix = {};
int command;
if (zclient->sock < 0) {
if (IS_DEBUG_OSPF(zebra, ZEBRA))
zlog_debug(" Not connected to Zebra");
return;
}
prefix.family = AF_INET;
prefix.prefixlen = 0;
if (unreg)
command = ZEBRA_NEXTHOP_UNREGISTER;
else
command = ZEBRA_NEXTHOP_REGISTER;
if (IS_DEBUG_OSPF(zebra, ZEBRA))
zlog_debug("%s: sending cmd %s for %pFX (vrf %u)", __func__,
zserv_command_string(command), &prefix,
ospf->vrf_id);
if (zclient_send_rnh(zclient, command, &prefix, SAFI_UNICAST, false,
true, ospf->vrf_id) == ZCLIENT_SEND_FAILURE)
flog_err(EC_LIB_ZAPI_SOCKET, "%s: zclient_send_rnh() failed",
__func__);
}
static int ospf_zebra_import_check_update(ZAPI_CALLBACK_ARGS)
{
struct ospf *ospf;
struct zapi_route nhr;
struct prefix matched;
ospf = ospf_lookup_by_vrf_id(vrf_id);
if (ospf == NULL || !IS_OSPF_ASBR(ospf))
return 0;
if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) {
zlog_err("%s[%u]: Failure to decode route", __func__,
ospf->vrf_id);
return -1;
}
if (matched.family != AF_INET || matched.prefixlen != 0 ||
nhr.type == ZEBRA_ROUTE_OSPF)
return 0;
ospf->nssa_default_import_check.status = !!nhr.nexthop_num;
ospf_abr_nssa_type7_defaults(ospf);
return 0;
}
int ospf_distribute_list_out_set(struct ospf *ospf, int type, const char *name)
{
@ -2132,6 +2188,7 @@ static zclient_handler *const ospf_handlers[] = {
[ZEBRA_REDISTRIBUTE_ROUTE_ADD] = ospf_zebra_read_route,
[ZEBRA_REDISTRIBUTE_ROUTE_DEL] = ospf_zebra_read_route,
[ZEBRA_NEXTHOP_UPDATE] = ospf_zebra_import_check_update,
[ZEBRA_OPAQUE_MESSAGE] = ospf_opaque_msg_handler,

View File

@ -69,6 +69,7 @@ extern int ospf_redistribute_set(struct ospf *, struct ospf_redist *, int,
unsigned short, int, int);
extern int ospf_redistribute_unset(struct ospf *, int, unsigned short);
extern int ospf_redistribute_default_set(struct ospf *, int, int, int);
extern void ospf_zebra_import_default_route(struct ospf *ospf, bool unreg);
extern int ospf_distribute_list_out_set(struct ospf *, int, const char *);
extern int ospf_distribute_list_out_unset(struct ospf *, int, const char *);
extern void ospf_routemap_set(struct ospf_redist *, const char *);

View File

@ -1774,6 +1774,51 @@ int ospf_area_nssa_translator_role_set(struct ospf *ospf,
return 1;
}
void ospf_area_nssa_default_originate_set(struct ospf *ospf,
struct in_addr area_id, int metric,
int metric_type)
{
struct ospf_area *area;
area = ospf_area_lookup_by_area_id(ospf, area_id);
if (area == NULL)
return;
if (!area->nssa_default_originate.enabled) {
area->nssa_default_originate.enabled = true;
if (++ospf->nssa_default_import_check.refcnt == 1) {
ospf->nssa_default_import_check.status = false;
ospf_zebra_import_default_route(ospf, false);
}
}
area->nssa_default_originate.metric_value = metric;
area->nssa_default_originate.metric_type = metric_type;
}
void ospf_area_nssa_default_originate_unset(struct ospf *ospf,
struct in_addr area_id)
{
struct ospf_area *area;
area = ospf_area_lookup_by_area_id(ospf, area_id);
if (area == NULL)
return;
if (area->nssa_default_originate.enabled) {
area->nssa_default_originate.enabled = false;
if (--ospf->nssa_default_import_check.refcnt == 0) {
ospf->nssa_default_import_check.status = false;
ospf_zebra_import_default_route(ospf, true);
}
area->nssa_default_originate.metric_value = -1;
area->nssa_default_originate.metric_type = -1;
if (!IS_OSPF_ABR(ospf))
ospf_abr_nssa_type7_defaults(ospf);
}
}
int ospf_area_export_list_set(struct ospf *ospf, struct ospf_area *area,
const char *list_name)
{

View File

@ -302,6 +302,18 @@ struct ospf {
int default_metric; /* Default metric for redistribute. */
/* 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;
#define OSPF_LSA_REFRESHER_GRANULARITY 10
#define OSPF_LSA_REFRESHER_SLOTS \
((OSPF_LS_REFRESH_TIME + OSPF_LS_REFRESH_SHIFT) \
@ -561,6 +573,13 @@ struct ospf_area {
#define PREFIX_LIST_OUT(A) (A)->plist_out.list
#define PREFIX_NAME_OUT(A) (A)->plist_out.name
/* NSSA default-information-originate */
struct {
bool enabled;
int metric_type;
int metric_value;
} nssa_default_originate;
/* Shortest Path Tree. */
struct vertex *spf;
struct list *spf_vertex_list;
@ -704,6 +723,11 @@ extern int ospf_area_nssa_suppress_fa_unset(struct ospf *ospf,
struct in_addr area_id);
extern int ospf_area_nssa_translator_role_set(struct ospf *ospf,
struct in_addr area_id, int role);
extern void ospf_area_nssa_default_originate_set(struct ospf *ospf,
struct in_addr area_id,
int metric, int metric_type);
extern void ospf_area_nssa_default_originate_unset(struct ospf *ospf,
struct in_addr area_id);
extern int ospf_area_export_list_set(struct ospf *ospf,
struct ospf_area *area_id,
const char *list_name);