diff --git a/doc/bird.sgml b/doc/bird.sgml
index 2435d1cb..a00062e8 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -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.
- stub switch
+ stub
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 nssa switch
+ nssa
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.
summary switch
This option controls propagation of summary LSAs into stub or
@@ -1771,10 +1773,22 @@ protocol ospf <name> {
summary LSAs could lead to more efficient routing at the cost
of larger link state database. Default value is no.
- stub cost num
+ default nssa switch
+ When default cost num
This option controls the cost of a default route propagated to
stub and NSSA areas. Default value is 1000.
+ default cost2 num
+ 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 translator switch
This option controls translation of NSSA-LSAs into external
LSAs. By default, one translator per NSSA is automatically
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index e48f4602..ec7da8e2 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -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 '}'
diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c
index 948f4581..325a8d00 100644
--- a/proto/ospf/lsupd.c
+++ b/proto/ospf/lsupd.c
@@ -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;
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index ed49a599..ce7ad37c 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -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);
diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
index 2a7d3c1f..2e99b0ff 100644
--- a/proto/ospf/ospf.h
+++ b/proto/ospf/ospf.h
@@ -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;
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index 22638527..42c54dfd 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -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;
+ if (rt_propagate)
+ nfa.options |= ORTA_PROP;
+ }
nfa.tag = rt_tag;
nfa.rid = en->lsa.rt;
diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h
index cb4b652d..1ba76e3c 100644
--- a/proto/ospf/rt.h
+++ b/proto/ospf/rt.h
@@ -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
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index 8df71b03..0b41d1ee 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -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)
{
diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h
index a1c0cbfe..b9bc9cf6 100644
--- a/proto/ospf/topology.h
+++ b/proto/ospf/topology.h
@@ -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);