Fixes for OSPF NSSA handling.

This commit is contained in:
Ondrej Zajicek 2011-09-03 21:31:26 +02:00
parent b1b1943360
commit 2918e61046
9 changed files with 170 additions and 45 deletions

View file

@ -1655,10 +1655,12 @@ protocol ospf <name> {
tick <num>;
ecmp <switch> [limit <num>];
area <id> {
stub <switch>;
nssa <switch>;
stub;
nssa;
summary <switch>;
stub cost <num>;
default nssa <switch>;
default cost <num>;
default cost2 <num>;
translator <switch>;
translator stability <num>;
@ -1747,20 +1749,20 @@ protocol ospf <name> {
address, similarly to a router ID). The most important area is
the backbone (ID 0) to which every other area must be connected.
<tag>stub <M>switch</M></tag>
<tag>stub</tag>
This option configures the area to be a stub area. External
routes are not flooded into stub areas. Also summary LSAs can be
limited in stub areas (see option <cf/summary/). Default
value is no. (Area is not stub.)
limited in stub areas (see option <cf/summary/).
By default, the area is not a stub area.
<tag>nssa <M>switch</M></tag>
<tag>nssa</tag>
This option configures the area to be a NSSA (Not-So-Stubby
Area). NSSA is a variant of a stub area which allows a
limited way of external route propagation. Global external
routes are not propagated into a NSSA, but an external route
can be imported into NSSA as a (area-wide) NSSA-LSA (and
possibly translated and/or aggregated on area boundary).
Default value is no. (Area is not NSSA.)
By default, the area is not NSSA.
<tag>summary <M>switch</M></tag>
This option controls propagation of summary LSAs into stub or
@ -1771,10 +1773,22 @@ protocol ospf &lt;name&gt; {
summary LSAs could lead to more efficient routing at the cost
of larger link state database. Default value is no.
<tag>stub cost <M>num</M></tag>
<tag>default nssa <M>switch</M></tag>
When <cf/summary/ option is enabled, default summary route is
no longer propagated to the NSSA. In that case, this option
allows to originate default route as NSSA-LSA to the NSSA.
Default value is no.
<tag>default cost <M>num</M></tag>
This option controls the cost of a default route propagated to
stub and NSSA areas. Default value is 1000.
<tag>default cost2 <M>num</M></tag>
When a default route is originated as NSSA-LSA, its cost
can use either type 1 or type 2 metric. This option allows
to specify the cost of a default route in type 2 metric.
By default, type 1 metric (option <cf/default cost/) is used.
<tag>translator <M>switch</M></tag>
This option controls translation of NSSA-LSAs into external
LSAs. By default, one translator per NSSA is automatically

View file

@ -58,7 +58,16 @@ static void
ospf_area_finish(void)
{
if ((this_area->areaid == 0) && (this_area->type != OPT_E))
cf_error( "Backbone area cannot be stub/NSSA");
cf_error("Backbone area cannot be stub/NSSA");
if (this_area->summary && (this_area->type == OPT_E))
cf_error("Only Stub/NSSA areas can use summary propagation");
if (this_area->default_nssa && ((this_area->type != OPT_N) || ! this_area->summary))
cf_error("Only NSSA areas with summary propagation can use NSSA default route");
if ((this_area->default_cost & LSA_EXT_EBIT) && ! this_area->default_nssa)
cf_error("Only NSSA default route can use type 2 metric");
}
static void
@ -94,10 +103,17 @@ ospf_proto_finish(void)
cf_error( "Vlinks cannot be used on single area router");
}
static inline void
check_defcost(int cost)
{
if ((cost <= 0) || (cost >= LSINFINITY))
cf_error("Default cost must be in range 1-%d", LSINFINITY);
}
CF_DECLS
CF_KEYWORDS(OSPF, AREA, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID)
CF_KEYWORDS(NEIGHBORS, RFC1583COMPAT, STUB, TICK, COST, RETRANSMIT)
CF_KEYWORDS(NEIGHBORS, RFC1583COMPAT, STUB, TICK, COST, COST2, RETRANSMIT)
CF_KEYWORDS(HELLO, TRANSMIT, PRIORITY, DEAD, TYPE, BROADCAST, BCAST)
CF_KEYWORDS(NONBROADCAST, NBMA, POINTOPOINT, PTP, POINTOMULTIPOINT, PTMP)
CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC)
@ -139,7 +155,7 @@ ospf_area_start: AREA idval {
this_area = cfg_allocz(sizeof(struct ospf_area_config));
add_tail(&OSPF_CFG->area_list, NODE this_area);
this_area->areaid = $2;
this_area->stub_cost = DEFAULT_STUB_COST;
this_area->default_cost = DEFAULT_STUB_COST;
this_area->type = OPT_E;
this_area->transint = DEFAULT_TRANSINT;
@ -159,10 +175,13 @@ ospf_area_opts:
;
ospf_area_item:
STUB COST expr { this_area->stub_cost = $3 ; if($3<=0) cf_error("Stub cost must be greater than zero"); }
| STUB bool { this_area->type = $2 ? 0 : OPT_E; /* We should remove the option */ }
STUB bool { this_area->type = $2 ? 0 : OPT_E; /* We should remove the option */ }
| NSSA { this_area->type = OPT_N; }
| SUMMARY bool { this_area->summary = $2; }
| DEFAULT NSSA bool { this_area->default_nssa = $3; }
| DEFAULT COST expr { this_area->default_cost = $3; check_defcost($3); }
| DEFAULT COST2 expr { this_area->default_cost = $3 | LSA_EXT_EBIT; check_defcost($3); }
| STUB COST expr { this_area->default_cost = $3; check_defcost($3); }
| TRANSLATOR bool { this_area->translator = $2; }
| TRANSLATOR STABILITY expr { this_area->transint = $3; }
| NETWORKS { this_nets = &this_area->net_list; } '{' pref_list '}'

View file

@ -127,7 +127,7 @@ ospf_lsa_flooding_allowed(struct ospf_lsa_header *lsa, u32 domain, struct ospf_i
case LSA_SCOPE_AS:
if (ifa->type == OSPF_IT_VLINK)
return 0;
if (ifa->oa->stub)
if (!oa_is_ext(ifa->oa))
return 0;
return 1;

View file

@ -170,6 +170,14 @@ ospf_area_add(struct proto_ospf *po, struct ospf_area_config *ac, int reconf)
oa->options = OPT_R | ac->type | OPT_V6;
#endif
/*
* Set E-bit for NSSA ABR routers. No need to explicitly call
* schedule_rt_lsa() for other areas, will be done anyway.
* We use cf->abr because po->areano is not yet complete.
*/
if (oa_is_nssa(oa) && ((struct ospf_config *) (p->cf))->abr)
po->ebit = 1;
if (reconf)
ospf_ifaces_reconfigure(oa, ac);
}
@ -453,7 +461,7 @@ area_disp(struct ospf_area *oa)
}
/**
* ospf_disp - invokes routing table calctulation, aging and also area_disp()
* ospf_disp - invokes routing table calculation, aging and also area_disp()
* @timer: timer usually called every @proto_ospf->tick second, @timer->data
* point to @proto_ospf
*/
@ -572,6 +580,8 @@ ospf_rt_notify(struct proto *p, rtable *tbl UNUSED, net * n, rte * new, rte * ol
/* Old external route might blocked some NSSA translation */
if (po->areano > 1)
schedule_rtcalc(po);
return;
}
/* Get route attributes */
@ -587,7 +597,7 @@ ospf_rt_notify(struct proto *p, rtable *tbl UNUSED, net * n, rte * new, rte * ol
(ospf_iface_find((struct proto_ospf *) p, new->attrs->iface) != NULL))
gw = new->attrs->gw;
originate_ext_lsa(oa, fn, EXT_EXPORT, metric, gw, tag);
originate_ext_lsa(oa, fn, EXT_EXPORT, metric, gw, tag, 1);
}
static void
@ -674,7 +684,15 @@ static void
ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)
{
oa->ac = nac;
// FIXME NSSA check type
// FIXME better area type reconfiguration
#ifdef OSPFv2
oa->options = nac->type;
#else /* OSPFv3 */
oa->options = OPT_R | nac->type | OPT_V6;
#endif
if (oa_is_nssa(oa) && (oa->po->areano > 1))
oa->po->ebit = 1;
ospf_ifaces_reconfigure(oa, nac);

View file

@ -126,10 +126,11 @@ struct ospf_area_config
{
node n;
u32 areaid;
u32 stub_cost; /* Cost of default route for stub areas */
u32 default_cost; /* Cost of default route for stub areas */
u8 type; /* Area type (standard, stub, NSSA), represented
by option flags (OPT_E, OPT_N) */
u8 summary; /* Import summaries to this stub/NSSA area, valid for ABR */
u8 default_nssa; /* Generate default NSSA route for NSSA+summary area */
u8 translator; /* Translator role, for NSSA ABR */
u32 transint; /* Translator stability interval */
list patt_list;

View file

@ -1059,11 +1059,11 @@ check_nssa_lsa(struct proto_ospf *po, ort *nf)
/* RFC 3103 3.2 (3) - originate the aggregated address range */
if (anet && anet->active && !anet->hidden && oa->translate)
originate_ext_lsa(po->backbone, fn, EXT_NSSA, anet->metric, IPA_NONE, anet->tag);
originate_ext_lsa(po->backbone, fn, EXT_NSSA, anet->metric, IPA_NONE, anet->tag, 0);
/* RFC 3103 3.2 (2) - originate the same network */
else if (decide_nssa_lsa(nf, &rt_metric, &rt_fwaddr, &rt_tag))
originate_ext_lsa(po->backbone, fn, EXT_NSSA, rt_metric, rt_fwaddr, rt_tag);
originate_ext_lsa(po->backbone, fn, EXT_NSSA, rt_metric, rt_fwaddr, rt_tag, 0);
else if (fn->x1 == EXT_NSSA)
flush_ext_lsa(po->backbone, fn);
@ -1173,11 +1173,24 @@ ospf_rt_abr1(struct proto_ospf *po)
/* 12.4.3.1. - originate or flush default route for stub/NSSA areas */
if (oa_is_stub(oa) || (oa_is_nssa(oa) && !oa->ac->summary))
originate_sum_net_lsa(oa, &default_nf->fn, oa->ac->stub_cost);
originate_sum_net_lsa(oa, &default_nf->fn, oa->ac->default_cost);
else
flush_sum_lsa(oa, &default_nf->fn, ORT_NET);
// FIXME NSSA add support for type 7 default route ?
/*
* Originate type-7 default route for NSSA areas
*
* Because type-7 default LSAs are originated by ABRs, they do not
* collide with other type-7 LSAs (as ABRs generate type-5 LSAs
* for both external route export or external-NSSA translation),
* so we use 0 for the src arg.
*/
if (oa_is_nssa(oa) && oa->ac->default_nssa)
originate_ext_lsa(oa, &default_nf->fn, 0, oa->ac->default_cost, IPA_NONE, 0, 0);
else
flush_ext_lsa(oa, &default_nf->fn);
/* RFC 2328 16.4. (3) - precompute preferred ASBR entries */
if (oa_is_ext(oa))
@ -1256,7 +1269,6 @@ ospf_rt_abr2(struct proto_ospf *po)
{
translate = 0;
goto decided;
break;
}
}
FIB_WALK_END;
@ -1286,7 +1298,7 @@ ospf_rt_abr2(struct proto_ospf *po)
FIB_WALK(&po->rtf, nftmp)
{
nf = (ort *) nftmp;
if (rt_is_nssa(nf))
if (rt_is_nssa(nf) && (nf->n.options & ORTA_PROP))
{
struct area_net *anet = (struct area_net *)
fib_route(&nf->n.oa->enet_fib, nf->fn.prefix, nf->fn.pxlen);
@ -1503,9 +1515,11 @@ ospf_ext_spf(struct proto_ospf *po)
/* Whether the route is preferred in route selection according to 16.4.1 */
nfa.options = epath_preferred(&nf2->n) ? ORTA_PREF : 0;
if (en->lsa.type == LSA_T_NSSA)
{
nfa.options |= ORTA_NSSA;
if (rt_propagate)
nfa.options |= ORTA_PROP;
}
nfa.tag = rt_tag;
nfa.rid = en->lsa.rt;

View file

@ -76,7 +76,6 @@ ort;
static inline int rt_is_nssa(ort *nf)
{ return nf->n.options & ORTA_NSSA; }
#define EXT_EXPORT 1
#define EXT_NSSA 2

View file

@ -885,7 +885,7 @@ flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
static inline void *
originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn,
u32 metric, ip_addr fwaddr, u32 tag)
u32 metric, ip_addr fwaddr, u32 tag, int pbit UNUSED)
{
struct ospf_lsa_ext *ext = mb_alloc(po->proto.pool, sizeof(struct ospf_lsa_ext));
*length = sizeof(struct ospf_lsa_header) + sizeof(struct ospf_lsa_ext);
@ -929,10 +929,10 @@ check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_add
static inline void *
originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn,
u32 metric, ip_addr fwaddr, u32 tag)
u32 metric, ip_addr fwaddr, u32 tag, int pbit)
{
int size = sizeof(struct ospf_lsa_ext)
+ IPV6_PREFIX_SPACE(n->n.pxlen)
+ IPV6_PREFIX_SPACE(fn->pxlen)
+ (ipa_nonzero(fwaddr) ? 16 : 0)
+ (tag ? 4 : 0);
@ -942,7 +942,7 @@ originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn,
ext->metric = metric;
u32 *buf = ext->rest;
buf = put_ipv6_prefix(buf, fn->prefix, fn->pxlen, 0, 0);
buf = put_ipv6_prefix(buf, fn->prefix, fn->pxlen, pbit ? OPT_PX_P : 0, 0);
if (ipa_nonzero(fwaddr))
{
@ -993,25 +993,77 @@ check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_add
#endif
static inline ip_addr
find_surrogate_fwaddr(struct ospf_area *oa)
{
struct proto_ospf *po = oa->po;
struct ospf_iface *ifa;
struct ifa *a, *cur_addr = NULL;
int np, cur_np = 0;
WALK_LIST(ifa, po->iface_list)
{
if ((ifa->oa != oa) ||
(ifa->state == OSPF_IS_DOWN) ||
(ifa->type == OSPF_IT_VLINK))
continue;
#ifdef OSPFv2
a = ifa->addr;
if (a->flags & IA_PEER)
continue;
np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1;
if (np > cur_np)
{
cur_addr = a;
cur_np = np;
}
#else /* OSPFv3 */
WALK_LIST(a, ifa->iface->addrs)
{
if ((a->flags & IA_SECONDARY) ||
(a->flags & IA_PEER) ||
(a->scope <= SCOPE_LINK))
continue;
np = ((a->flags & IA_HOST) || ifa->stub) ? 2 : 1;
if (np > cur_np)
{
cur_addr = a;
cur_np = np;
}
}
#endif
}
return cur_addr ? cur_addr->ip : IPA_NONE;
}
/**
* originate_ext_lsa - new route received from nest and filters
* @oa: ospf_area for which LSA is originated
* @fn: network prefix and mask
* @type: the reason for origination of the LSA (EXT_EXPORT/EXT_NSSA)
* @src: the source of origination of the LSA (EXT_EXPORT/EXT_NSSA)
* @metric: the metric of a route
* @fwaddr: the forwarding address
* @tag: the route tag
* @pbit: P-bit for NSSA LSAs, ignored for external LSAs
*
* If I receive a message that new route is installed, I try to originate an
* external LSA. If @oa is an NSSA area, NSSA-LSA is originated instead.
* @oa should not be a stub area.
* @oa should not be a stub area. @src does not specify whether the LSA
* is external or NSSA, but it specifies the source of origination -
* the export from ospf_rt_notify(), or the NSSA-EXT translation.
*
* The function also sets flag ebit. If it's the first time, the new router lsa
* origination is necessary.
*/
void
originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int type,
u32 metric, ip_addr fwaddr, u32 tag)
originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src,
u32 metric, ip_addr fwaddr, u32 tag, int pbit)
{
struct proto_ospf *po = oa->po;
struct proto *p = &po->proto;
@ -1021,22 +1073,28 @@ originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int type,
int nssa = oa_is_nssa(oa);
u32 dom = nssa ? oa->areaid : 0;
// FIXME NSSA - handle P bit, currently always set (from oa->options)
OSPF_TRACE(D_EVENTS, "Originating %s-LSA for %I/%d",
nssa ? "NSSA" : "AS-external", fn->prefix, fn->pxlen);
lsa.age = 0;
#ifdef OSPFv2
lsa.options = oa->options;
lsa.options = nssa ? (pbit ? OPT_P : 0) : OPT_E;
#endif
lsa.type = nssa ? LSA_T_NSSA : LSA_T_EXT;
lsa.id = fibnode_to_lsaid(po, fn);
lsa.rt = po->router_id;
if (nssa)
if (nssa && pbit && ipa_zero(fwaddr))
{
// FIXME NSSA Add check for gw, update option
/* NSSA-LSA with P-bit set must have non-zero forwarding address */
fwaddr = find_surrogate_fwaddr(oa);
if (ipa_zero(fwaddr))
{
log(L_ERR, "%s: Cannot find forwarding address for NSSA-LSA %I/%d",
p->name, fn->prefix, fn->pxlen);
return;
}
}
if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL)
@ -1054,10 +1112,12 @@ originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int type,
}
lsa.sn = get_seqnum(en);
body = originate_ext_lsa_body(po, &lsa.length, fn, metric, fwaddr, tag);
body = originate_ext_lsa_body(po, &lsa.length, fn, metric, fwaddr, tag, pbit);
lsasum_calculate(&lsa, body);
fn->x1 = type;
if (src)
fn->x1 = src;
en = lsa_install_new(po, &lsa, dom, body);
ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);
@ -1325,7 +1385,7 @@ prefix_advance(u32 *buf)
return buf + IPV6_PREFIX_WORDS(pxl);
}
/* FIXME eliminate items wit LA bit set? see 4.4.3.9 */
/* FIXME eliminate items with LA bit set? see 4.4.3.9 */
static void
add_prefix(struct proto_ospf *po, u32 *px, int offset, int *pxc)
{

View file

@ -71,7 +71,7 @@ int can_flush_lsa(struct proto_ospf *po);
void originate_sum_net_lsa(struct ospf_area *oa, struct fib_node *fn, int metric);
void originate_sum_rt_lsa(struct ospf_area *oa, struct fib_node *fn, int metric, u32 options UNUSED);
void flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type);
void originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int type, u32 metric, ip_addr fwaddr, u32 tag);
void originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int src, u32 metric, ip_addr fwaddr, u32 tag, int pbit);
void flush_ext_lsa(struct ospf_area *oa, struct fib_node *fn);