Commit ed317862 authored by Ondřej Zajíček's avatar Ondřej Zajíček

OSPF NSSA support, inter-area LSA translation.

parent aca0e79f
......@@ -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,28 +278,22 @@ 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:
/* empty */
......
......@@ -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\tNSSA translation:\t%s%s", oa->translate ? "Yes" : "No",
oa->translate == TRANS_WAIT ? " (run down)" : "");
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\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, "");
}
......
......@@ -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? */
......
......@@ -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);
......
......@@ -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):
......
......@@ -884,14 +884,14 @@ flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
#ifdef OSPFv2
static inline void *
originate_ext_lsa_body(struct proto_ospf *po, u16 *length, net *n,
originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn,
u32 metric, ip_addr fwaddr, u32 tag)
{
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);
ext->metric = metric;
ext->netmask = ipa_mkmask(n->n.pxlen);
ext->netmask = ipa_mkmask(fn->pxlen);
ext->fwaddr = fwaddr;
ext->tag = tag;
......@@ -928,7 +928,7 @@ check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_add
#else /* OSPFv3 */
static inline void *
originate_ext_lsa_body(struct proto_ospf *po, u16 *length, net *n,
originate_ext_lsa_body(struct proto_ospf *po, u16 *length, struct fib_node *fn,
u32 metric, ip_addr fwaddr, u32 tag)
{
int size = sizeof(struct ospf_lsa_ext)
......@@ -942,7 +942,7 @@ originate_ext_lsa_body(struct proto_ospf *po, u16 *length, net *n,
ext->metric = metric;
u32 *buf = ext->rest;
buf = put_ipv6_prefix(buf, n->n.prefix, n->n.pxlen, 0, 0);
buf = put_ipv6_prefix(buf, fn->prefix, fn->pxlen, 0, 0);
if (ipa_nonzero(fwaddr))
{
......@@ -996,30 +996,32 @@ check_ext_lsa(struct top_hash_entry *en, struct fib_node *fn, u32 metric, ip_add
/**
* originate_ext_lsa - new route received from nest and filters
* @oa: ospf_area for which LSA is originated
* @n: network prefix and mask
* @e: rte
* @attrs: list of extended attributes
* @fn: network prefix and mask
* @type: the reason for origination of the LSA (EXT_EXPORT/EXT_NSSA)
* @metric: the metric of a route
* @fwaddr: the forwarding address
* @tag: the route tag
*
* 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 stub area.
* @oa should not be a stub area.
*
* 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, net *n, rte *e, struct ea_list *attrs)
originate_ext_lsa(struct ospf_area *oa, struct fib_node *fn, int type,
u32 metric, ip_addr fwaddr, u32 tag)
{
struct proto_ospf *po = oa->po;
struct proto *p = &po->proto;
struct fib_node *fn = &n->n;
struct ospf_lsa_header lsa;
struct top_hash_entry *en = NULL;
void *body;
int nssa = oa_is_nssa(oa);
u32 dom = nssa ? oa->areaid : 0;
// FIXME NSSA - handle P bit
// 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);
......@@ -1032,19 +1034,6 @@ originate_ext_lsa(struct ospf_area *oa, net *n, rte *e, struct ea_list *attrs)
lsa.id = fibnode_to_lsaid(po, fn);
lsa.rt = po->router_id;
/* Compute LSA content */
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 ((e->attrs->dest == RTD_ROUTER) &&
ipa_nonzero(e->attrs->gw) &&
!ipa_has_link_scope(e->attrs->gw) &&
(ospf_iface_find((struct proto_ospf *) p, e->attrs->iface) != NULL))
gw = e->attrs->gw;
if (nssa)
{
// FIXME NSSA Add check for gw, update option
......@@ -1052,7 +1041,7 @@ originate_ext_lsa(struct ospf_area *oa, net *n, rte *e, struct ea_list *attrs)
if ((en = ospf_hash_find_header(po->gr, dom, &lsa)) != NULL)
{
int rv = check_ext_lsa(en, fn, metric, gw, tag);
int rv = check_ext_lsa(en, fn, metric, fwaddr, tag);
if (rv < 0)
{
log(L_ERR, "%s: LSAID collision for %I/%d",
......@@ -1065,9 +1054,10 @@ originate_ext_lsa(struct ospf_area *oa, net *n, rte *e, struct ea_list *attrs)
}
lsa.sn = get_seqnum(en);
body = originate_ext_lsa_body(po, &lsa.length, n, metric, gw, tag);
body = originate_ext_lsa_body(po, &lsa.length, fn, metric, fwaddr, tag);
lsasum_calculate(&lsa, body);
fn->x1 = type;
en = lsa_install_new(po, &lsa, dom, body);
ospf_lsupd_flood(po, NULL, NULL, &lsa, dom, 1);