]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Fixes for OSPF NSSA handling.
authorOndrej Zajicek <santiago@crfreenet.org>
Sat, 3 Sep 2011 19:31:26 +0000 (21:31 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Sat, 3 Sep 2011 19:31:26 +0000 (21:31 +0200)
doc/bird.sgml
proto/ospf/config.Y
proto/ospf/lsupd.c
proto/ospf/ospf.c
proto/ospf/ospf.h
proto/ospf/rt.c
proto/ospf/rt.h
proto/ospf/topology.c
proto/ospf/topology.h

index 2435d1cb71f5121123c707ce314bc2fb53f2e848..a00062e878c5de7c85112f6bf8315b9ad17a5d34 100644 (file)
@@ -1655,10 +1655,12 @@ protocol ospf &lt;name&gt; {
        tick &lt;num&gt;;
        ecmp &lt;switch&gt; [limit &lt;num&gt;];
        area &lt;id&gt; {
-               stub &lt;switch&gt;;
-               nssa &lt;switch&gt;;
+               stub;
+               nssa;
                summary &lt;switch&gt;;
-               stub cost &lt;num&gt;;
+               default nssa &lt;switch&gt;;
+               default cost &lt;num&gt;;
+               default cost2 &lt;num&gt;;
                translator &lt;switch&gt;;
                translator stability &lt;num&gt;;
 
@@ -1747,20 +1749,20 @@ protocol ospf &lt;name&gt; {
         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
index e48f46029fbc257585240eb5f41f650f7b34a524..ec7da8e23e4e28b54cec2945baffbd1a58b63481 100644 (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 '}'
index 948f45812cc6064aba462757843b1dd16b80e463..325a8d00180feee61ebb69271e1cce7781a8bef8 100644 (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;
 
index ed49a599468147d7d49ab38b738569a522363fa4..ce7ad37cb2a2bc871ba7b482e1c79428c4647824 100644 (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);
 
index 2a7d3c1f9f5315ce2c66a416b421d2c286c10a78..2e99b0ff93f8e080ef014cae5f54ef8116ab73b4 100644 (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;
index 22638527f903b60a6cc7a265e1ea1e69fa44917a..42c54dfdc4a998283ccadfdc134560bc59f92eb0 100644 (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;
+      if (rt_propagate)
+       nfa.options |= ORTA_PROP;
+    }
 
     nfa.tag = rt_tag;
     nfa.rid = en->lsa.rt;
index cb4b652dff3b5646b31aa3c4ed515422cd1ded70..1ba76e3c03556a82265cebb03df202c5c495a60c 100644 (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
 
index 8df71b03f2b862ecfe373b765103291a9ad932fb..0b41d1eec4431034e4e4154f6d722e0e74b15de0 100644 (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)
 {
index a1c0cbfe2b63471577d0861ba0736acc8aba2a8a..b9bc9cf61e10a702f906601c02b32351e1342604 100644 (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);