From 2918e61046388821c3d4411c602dc5b28ad59329 Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Sat, 3 Sep 2011 21:31:26 +0200 Subject: [PATCH] Fixes for OSPF NSSA handling. --- doc/bird.sgml | 32 ++++++++++----- proto/ospf/config.Y | 29 +++++++++++--- proto/ospf/lsupd.c | 2 +- proto/ospf/ospf.c | 24 +++++++++-- proto/ospf/ospf.h | 3 +- proto/ospf/rt.c | 30 ++++++++++---- proto/ospf/rt.h | 1 - proto/ospf/topology.c | 92 +++++++++++++++++++++++++++++++++++-------- proto/ospf/topology.h | 2 +- 9 files changed, 170 insertions(+), 45 deletions(-) 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);