Commit ed317862 authored by Ondrej Zajicek's avatar Ondrej Zajicek
Browse files

OSPF NSSA support, inter-area LSA translation.

parent aca0e79f
Loading
Loading
Loading
Loading
+16 −17
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ CF_DEFINES

static struct ospf_area_config *this_area;
static struct nbma_node *this_nbma;
static list *this_nets;
static struct area_net_config *this_pref;
static struct ospf_stubnet_config *this_stubnet; 

@@ -85,6 +86,7 @@ ospf_proto_finish(void)
    add_head(&cf->area_list, NODE ac);
    init_list(&ac->patt_list);
    init_list(&ac->net_list);
    init_list(&ac->enet_list);
    init_list(&ac->stubnet_list);
  }

@@ -100,7 +102,7 @@ CF_KEYWORDS(HELLO, TRANSMIT, PRIORITY, DEAD, TYPE, BROADCAST, BCAST)
CF_KEYWORDS(NONBROADCAST, NBMA, POINTOPOINT, PTP, POINTOMULTIPOINT, PTMP)
CF_KEYWORDS(NONE, SIMPLE, AUTHENTICATION, STRICT, CRYPTOGRAPHIC)
CF_KEYWORDS(ELIGIBLE, POLL, NETWORKS, HIDDEN, VIRTUAL, CHECK, LINK)
CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY)
CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL)
CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY)

%type <t> opttext
@@ -139,9 +141,11 @@ ospf_area_start: AREA idval {
  this_area->areaid = $2;
  this_area->stub_cost = DEFAULT_STUB_COST;
  this_area->type = OPT_E;
  this_area->transint = DEFAULT_TRANSINT;

  init_list(&this_area->patt_list);
  init_list(&this_area->net_list);
  init_list(&this_area->enet_list);
  init_list(&this_area->stubnet_list);
 }
 ;
@@ -160,8 +164,9 @@ ospf_area_item:
 | NSSA { this_area->type = OPT_N; }
 | SUMMARY bool { this_area->summary = $2; }
 | TRANSLATOR bool { this_area->translator = $2; }
 | TRANSLATOR STABILITY bool { this_area->transint = $3; }
 | NETWORKS '{' pref_list '}'
 | TRANSLATOR STABILITY expr { this_area->transint = $3; }
 | NETWORKS { this_nets = &this_area->net_list; } '{' pref_list '}'
 | EXTERNAL { this_nets = &this_area->enet_list; } '{' pref_list '}'
 | STUBNET ospf_stubnet
 | INTERFACE ospf_iface
 | ospf_vlink
@@ -273,27 +278,21 @@ pref_list:
 | pref_list pref_item
 ;

pref_item:
   pref_el
 | pref_hid;
pref_item: pref_base pref_opt ';' ;

pref_el: prefix ';'
pref_base: prefix
 {
   this_pref = cfg_allocz(sizeof(struct area_net_config));
   add_tail(&this_area->net_list, NODE this_pref);
   add_tail(this_nets, NODE this_pref);
   this_pref->px.addr = $1.addr;
   this_pref->px.len = $1.len;
 }
;

pref_hid: prefix HIDDEN ';'
 {
   this_pref = cfg_allocz(sizeof(struct area_net_config));
   add_tail(&this_area->net_list, NODE this_pref);
   this_pref->px.addr = $1.addr;
   this_pref->px.len = $1.len;
   this_pref->hidden = 1;
 }
pref_opt:
 /* empty */
 | HIDDEN { this_pref->hidden = 1; }
 | TAG expr { this_pref->tag = $2; }
 ;

ipa_list:
+60 −13
Original line number Diff line number Diff line
@@ -126,11 +126,19 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac)
    struct area_net *an;

    fib_init(&oa->net_fib, po->proto.pool, sizeof(struct area_net), 0, ospf_area_initfib);
    fib_init(&oa->enet_fib, po->proto.pool, sizeof(struct area_net), 0, ospf_area_initfib);

    WALK_LIST(anc, ac->net_list)
    {
      an = (struct area_net *) fib_get(&oa->net_fib, &anc->px.addr, anc->px.len);
      an->hidden = an->hidden;
      an->hidden = anc->hidden;
    }

    WALK_LIST(anc, ac->enet_list)
    {
      an = (struct area_net *) fib_get(&oa->enet_fib, &anc->px.addr, anc->px.len);
      an->hidden = anc->hidden;
      an->tag = anc->tag;
    }
}

@@ -177,6 +185,7 @@ ospf_area_remove(struct ospf_area *oa)
 
  fib_free(&oa->rtr);
  fib_free(&oa->net_fib);
  fib_free(&oa->enet_fib);

  if (oa->translator_timer)
    rfree(oa->translator_timer);
@@ -550,16 +559,35 @@ ospf_rt_notify(struct proto *p, rtable *tbl UNUSED, net * n, rte * new, rte * ol
{
  struct proto_ospf *po = (struct proto_ospf *) p;
  struct ospf_area *oa = ospf_main_area(po);
  ort *nf = (ort *) fib_get(&po->rtf, &n->n.prefix, n->n.pxlen);
  struct fib_node *fn = &nf->fn;

/* Temporarily down write anything
  OSPF_TRACE(D_EVENTS, "Got route %I/%d %s", p->name, n->n.prefix,
    n->n.pxlen, new ? "up" : "down");
*/
  if (!new)
  {
    if (fn->x1 != EXT_EXPORT)
      return;

  if (new)			/* Got some new route */
    originate_ext_lsa(oa, n, new, attrs);
  else
    flush_ext_lsa(oa, n);
    flush_ext_lsa(oa, fn);

    /* Old external route might blocked some NSSA translation */
    if (po->areano > 1)
      schedule_rtcalc(po);
  }

  /* Get route attributes */
  u32 m1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY);
  u32 m2 = ea_get_int(attrs, EA_OSPF_METRIC2, 10000);
  u32 metric = (m1 != LSINFINITY) ? m1 : (m2 | LSA_EXT_EBIT);
  u32 tag = ea_get_int(attrs, EA_OSPF_TAG, 0);
  ip_addr gw = IPA_NONE;
  // FIXME check for gw should be per ifa, not per iface
  if ((new->attrs->dest == RTD_ROUTER) &&
      ipa_nonzero(new->attrs->gw) &&
      !ipa_has_link_scope(new->attrs->gw) &&
      (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);
}

static void
@@ -652,6 +680,7 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac)

  /* Handle net_list */
  fib_free(&oa->net_fib);
  fib_free(&oa->enet_fib);
  add_area_nets(oa, nac);

  /* No need to handle stubnet_list */
@@ -804,11 +833,14 @@ ospf_sh(struct proto *p)
        }
      }
    }
    // FIXME NSSA:
    // cli_msg(-1014, "\t\tStub:\t%s", oa->stub ? "Yes" : "No");

    cli_msg(-1014, "\t\tStub:\t%s", oa_is_stub(oa) ? "Yes" : "No");
    cli_msg(-1014, "\t\tNSSA:\t%s", oa_is_nssa(oa) ? "Yes" : "No");
    cli_msg(-1014, "\t\tTransit:\t%s", oa->trcap ? "Yes" : "No");

    if (oa_is_nssa(oa))
      cli_msg(-1014, "\t\tNSSA translation:\t%s%s", oa->translate ? "Yes" : "No",
	      oa->translate == TRANS_WAIT ? " (run down)" : "");
    cli_msg(-1014, "\t\tTransit:\t%s", oa->trcap ? "Yes" : "No");
    cli_msg(-1014, "\t\tNumber of interfaces:\t%u", ifano);
    cli_msg(-1014, "\t\tNumber of neighbors:\t%u", nno);
    cli_msg(-1014, "\t\tNumber of adjacent neighbors:\t%u", adjno);
@@ -826,6 +858,21 @@ ospf_sh(struct proto *p)
		anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : "");
    }
    FIB_WALK_END;

    firstfib = 1;
    FIB_WALK(&oa->enet_fib, nftmp)
    {
      anet = (struct area_net *) nftmp;
      if(firstfib)
      {
        cli_msg(-1014, "\t\tArea external networks:");
        firstfib = 0;
      }
      cli_msg(-1014, "\t\t\t%1I/%u\t%s\t%s", anet->fn.prefix, anet->fn.pxlen,
		anet->hidden ? "Hidden" : "Advertise", anet->active ? "Active" : "");
    }
    FIB_WALK_END;

  }
  cli_msg(0, "");
}
+5 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ do { if ((p->debug & D_PACKETS) || OSPF_FORCE_DEBUG) \
#define DEFAULT_RFC1583 0	/* compatibility with rfc1583 */
#define DEFAULT_STUB_COST 1000
#define DEFAULT_ECMP_LIMIT 16
#define DEFAULT_TRANSINT 40


struct ospf_config
@@ -101,6 +102,7 @@ struct area_net_config
  node n;
  struct prefix px;
  int hidden;
  u32 tag;
};

struct area_net
@@ -109,6 +111,7 @@ struct area_net
  int hidden;
  int active;
  u32 metric;
  u32 tag;
};

struct ospf_stubnet_config
@@ -131,6 +134,7 @@ struct ospf_area_config
  u32 transint;			/* Translator stability interval */
  list patt_list;
  list net_list;	      	/* List of aggregate networks for that area */
  list enet_list;	      	/* List of aggregate external (NSSA) networks */
  list stubnet_list;		/* List of stub networks added to Router LSA */
};

@@ -734,6 +738,7 @@ struct ospf_area
  struct top_hash_entry *pxr_lsa; /* Originated prefix LSA */
  list cand;			/* List of candidates for RT calc. */
  struct fib net_fib;		/* Networks to advertise or not */
  struct fib enet_fib;		/* External networks for NSSAs */
  u32 options;			/* Optional features */
  byte origrt;			/* Rt lsa origination scheduled? */
  byte trcap;			/* Transit capability? */
+212 −30
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ ospf_rt_initort(struct fib_node *fn)
  ort *ri = (ort *) fn;
  reset_ri(ri);
  ri->old_rta = NULL;
  ri->fn.x0 = 0;
  ri->fn.x0 = ri->fn.x1 = 0;
}

static inline int
@@ -128,6 +128,23 @@ ri_better_asbr(struct proto_ospf *po, orta *new, orta *old)
  return 0;
}

static int
orta_prio(orta *nf)
{
  /* RFC 3103 2.5 (6e) priorities */
  u32 opts = nf->options & (ORTA_NSSA | ORTA_PROP);

  /* A Type-7 LSA with the P-bit set */
  if (opts == (ORTA_NSSA | ORTA_PROP))
    return 2;

  /* A Type-5 LSA */
  if (opts == 0)
    return 1;

  return 0;
}

/* 16.4. (6), return 1 if new is better */
static int
ri_better_ext(struct proto_ospf *po, orta *new, orta *old)
@@ -172,7 +189,19 @@ ri_better_ext(struct proto_ospf *po, orta *new, orta *old)
  if (new->metric1 > old->metric1)
    return 0;

  /* RFC 3103, 2.5. (6e) - missing, is this necessary? */
  /* RFC 3103, 2.5. (6e) */
  int new_prio = orta_prio(new);
  int old_prio = orta_prio(old);

  if (new_prio > old_prio)
    return 1;

  if (old_prio > new_prio)
    return 0;

  /* make it more deterministic */
  if (new->rid > old->rid)
    return 1;

  return 0;
}
@@ -949,6 +978,96 @@ check_sum_rt_lsa(struct proto_ospf *po, ort *nf)
  }
}

static inline int
decide_nssa_lsa(ort *nf, u32 *rt_metric, ip_addr *rt_fwaddr, u32 *rt_tag)
{
  struct ospf_area *oa = nf->n.oa;
  struct top_hash_entry *en = nf->n.en;
  int propagate;

  if (!rt_is_nssa(nf) || !oa->translate)
    return 0;

  /* Condensed area network found */ 
  if (fib_route(&oa->enet_fib, nf->fn.prefix, nf->fn.pxlen))
    return 0;

  if (!en || (en->lsa.type != LSA_T_NSSA))
    return 0;

  /* We do not store needed data in struct orta, we have to parse the LSA */
  struct ospf_lsa_ext *le = en->lsa_body;

#ifdef OSPFv2
  *rt_fwaddr = le->fwaddr;
  *rt_tag = le->tag;
  propagate = en->lsa.options & OPT_P;
#else /* OSPFv3 */
  u32 *buf = le->rest;
  u8 pxlen = (*buf >> 24);
  u8 pxopts = (*buf >> 16);
  buf += IPV6_PREFIX_WORDS(pxlen);  /* Skip the IP prefix */

  if (pxopts & OPT_PX_NU)
    return 0;

  if (le->metric & LSA_EXT_FBIT)
    buf = lsa_get_ipv6_addr(buf, rt_fwaddr);
  else
    *rt_fwaddr = IPA_NONE;

  if (le->metric & LSA_EXT_TBIT)
    *rt_tag = *buf++;
  else
    *rt_tag = 0;

  propagate = pxopts & OPT_PX_P;
#endif

  if (!propagate || ipa_zero(*rt_fwaddr))
    return 0;

  *rt_metric = le->metric & (METRIC_MASK | LSA_EXT_EBIT);
  return 1;
}

/* RFC 3103 3.2 - translating Type-7 LSAs into Type-5 LSAs */
static inline void
check_nssa_lsa(struct proto_ospf *po, ort *nf)
{
  struct fib_node *fn = &nf->fn;
  struct area_net *anet = NULL;
  struct ospf_area *oa = NULL;
  u32 rt_metric, rt_tag;
  ip_addr rt_fwaddr;

  /* Do not translate LSA if there is already the external LSA from route export */
  if (fn->x1 == EXT_EXPORT)
    return;

  /* RT entry marked as area network */
  if (fn->x0)
  {
    /* Find that area network */
    WALK_LIST(oa, po->area_list)
    {
      anet = (struct area_net *) fib_find(&oa->enet_fib, &fn->prefix, fn->pxlen);
      if (anet)
	break;
    }
  }

  /* 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);

  /* 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);

  else if (fn->x1 == EXT_NSSA)
    flush_ext_lsa(po->backbone, fn);
}

/* RFC 2328 16.7. p2 - find new/lost vlink endpoints */
static void
@@ -1000,26 +1119,13 @@ ospf_check_vlinks(struct proto_ospf *po)
  }
}

static void
translator_timer_hook(timer *timer)
{
  struct ospf_area *oa = timer->data;
  
  if (oa->translate != TRANS_WAIT)
    return;

  oa->translate = TRANS_OFF;
  schedule_rtcalc(oa->po);
}


/* Miscellaneous route processing that needs to be done by ABRs */
static void
ospf_rt_abr(struct proto_ospf *po)
ospf_rt_abr1(struct proto_ospf *po)
{
  struct top_hash_entry *en;
  struct area_net *anet;
  ort *nf, *nf2, *default_nf;
  ort *nf, *default_nf;

  FIB_WALK(&po->rtf, nftmp)
  {
@@ -1087,8 +1193,41 @@ ospf_rt_abr(struct proto_ospf *po)
  }


  /* Originate or flush ASBR summary LSAs */
  FIB_WALK(&po->backbone->rtr, nftmp)
  {
    check_sum_rt_lsa(po, (ort *) nftmp);
  }
  FIB_WALK_END;


  /* RFC 2328 16.7. p2 - find new/lost vlink endpoints */
  ospf_check_vlinks(po);
}


static void
translator_timer_hook(timer *timer)
{
  struct ospf_area *oa = timer->data;
  
  if (oa->translate != TRANS_WAIT)
    return;

  oa->translate = TRANS_OFF;
  schedule_rtcalc(oa->po);
}

static void
ospf_rt_abr2(struct proto_ospf *po)
{
  struct ospf_area *oa;
  struct top_hash_entry *en;
  ort *nf, *nf2;


  /* RFC 3103 3.1 - type-7 translator election */
  struct ospf_area *bb = oa->po->backbone;
  struct ospf_area *bb = po->backbone;
  WALK_LIST(oa, po->area_list)
    if (oa_is_nssa(oa))
    {
@@ -1143,18 +1282,48 @@ ospf_rt_abr(struct proto_ospf *po)
    }


  /* Originate or flush ASBR summary LSAs */
  FIB_WALK(&po->backbone->rtr, nftmp)
  /* Compute condensed external networks */
  FIB_WALK(&po->rtf, nftmp)
  {
    check_sum_rt_lsa(po, (ort *) nftmp);
    nf = (ort *) nftmp;
    if (rt_is_nssa(nf))
    {
      struct area_net *anet = (struct area_net *)
	fib_route(&nf->n.oa->enet_fib, nf->fn.prefix, nf->fn.pxlen);

      if (anet)
      {
	if (!anet->active)
	{
	  anet->active = 1;

	  /* Get a RT entry and mark it to know that it is an area network */
	  nf2 = (ort *) fib_get(&po->rtf, &anet->fn.prefix, anet->fn.pxlen);
	  nf2->fn.x0 = 1;
	}

	u32 metric = (nf->n.type == RTS_OSPF_EXT1) ?
	  nf->n.metric1 : ((nf->n.metric2 + 1) | LSA_EXT_EBIT);

	if (anet->metric < metric)
	  anet->metric = metric;
      }
    }
  }
  FIB_WALK_END;


  /* RFC 2328 16.7. p2 - find new/lost vlink endpoints */
  ospf_check_vlinks(po);
  FIB_WALK(&po->rtf, nftmp)
  {
    nf = (ort *) nftmp;

    check_sum_net_lsa(po, nf);
    check_nssa_lsa(po, nf);
  }
  FIB_WALK_END;
}


/* Like fib_route(), but ignores dummy rt entries */
static void *
ospf_fib_route(struct fib *f, ip_addr a, int len)
@@ -1254,7 +1423,7 @@ ospf_ext_spf(struct proto_ospf *po)

    /* 16.4. (3) */
    /* If there are more areas, we already precomputed preferred ASBR
       entries in ospf_rt_abr() and stored them in the backbone
       entries in ospf_rt_abr1() and stored them in the backbone
       table. For NSSA, we examine the area to which the LSA is assigned */
    if (en->lsa.type == LSA_T_EXT)
      atmp = ospf_main_area(po);
@@ -1333,12 +1502,17 @@ 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;
    nfa.oa = nf1->n.oa; /* undefined in RFC 2328 */
    nfa.oa = atmp; /* undefined in RFC 2328 */
    nfa.voa = NULL;
    nfa.nhs = nhs;
    nfa.en = en; /* store LSA for later (NSSA processing) */

    ri_install_ext(po, ip, pxlen, &nfa);
  }
@@ -1391,6 +1565,14 @@ ospf_rt_reset(struct proto_ospf *po)
	anet->metric = 0;
      }
      FIB_WALK_END;

      FIB_WALK(&oa->enet_fib, nftmp)
      {
	anet = (struct area_net *) nftmp;
	anet->active = 0;
	anet->metric = 0;
      }
      FIB_WALK_END;
    }
  }
}
@@ -1430,11 +1612,14 @@ ospf_rt_spf(struct proto_ospf *po)
      ospf_rt_sum_tr(oa);

  if (po->areano > 1)
    ospf_rt_abr(po);
    ospf_rt_abr1(po);

  /* 16. (5) */
  ospf_ext_spf(po);

  if (po->areano > 1)
    ospf_rt_abr2(po);

  rt_sync(po);
  lp_flush(po->nhpool);
  
@@ -1781,9 +1966,6 @@ again1:
	}
    }

    if (po->areano > 1)
      check_sum_net_lsa(po, nf);

    /* Remove configured stubnets */
    if (!nf->n.nhs)
      reset_ri(nf);
@@ -1846,7 +2028,7 @@ again1:
    }

    /* Remove unused rt entry. Entries with fn.x0 == 1 are persistent. */
    if (!nf->n.type && !nf->fn.x0)
    if (!nf->n.type && !nf->fn.x0 && !nf->fn.x1)
    {
      FIB_ITERATE_PUT(&fit, nftmp);
      fib_delete(fib, nftmp);
+15 −4
Original line number Diff line number Diff line
@@ -35,6 +35,9 @@ typedef struct orta
   * intra-area (type == RTS_OSPF) and its area is not a backbone.
   */
#define ORTA_PREF 0x80000000
#define ORTA_NSSA 0x40000000
#define ORTA_PROP 0x20000000

  u32 metric1;
  u32 metric2;
  u32 tag;
@@ -43,13 +46,10 @@ typedef struct orta
  struct ospf_area *voa;	/* Used when route is replaced in ospf_rt_sum_tr(),
				   NULL otherwise */
  struct mpnh *nhs;		/* Next hops computed during SPF */
  struct top_hash_entry *en;	/* LSA responsible for this orta */
}
orta;

//  struct ospf_iface *ifa;	/* Outgoing interface */
//  ip_addr nh;			/* Next hop */


typedef struct ort
{
  /*
@@ -57,6 +57,10 @@ typedef struct ort
   * LSAs that don't have 'proper' rt entry (area networks + default to stubs)
   * to keep uid stable (used for LSA ID in OSPFv3 - see fibnode_to_lsaid()).
   *
   * We use fn.x1 to note whether the external route was originated
   * from the route export (in ospf_rt_notify()) or from the NSSA
   * route translation (in check_nssa_lsa()).
   *
   * old_* values are here to represent the last route update. old_rta
   * is cached (we keep reference), mainly for multipath nexthops.
   * old_rta == NULL means route wasn not in the last update, in that
@@ -69,6 +73,13 @@ typedef struct ort
}
ort;

static inline int rt_is_nssa(ort *nf)
{ return nf->n.options & ORTA_NSSA; }


#define EXT_EXPORT 1
#define EXT_NSSA 2

/*
 * Invariants for structs top_hash_entry (nodes of LSA db)
 * enforced by SPF calculation for final nodes (color == INSPF):
Loading