mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-17 16:48:43 +00:00
a97122a3ca
The code generating LSAs for PTP OSPF links is buggy. The old behavior is that it generates PTP link if there is a full/ptp neighbor and stub link if there isn't. According to RFC 2328, the correct behavior is to generate stub link in both cases (in the first case together with PTP link). And because of buggy detection of unnumbered networks, for numbered networks the code creates stub links with 0.0.0.0/32.
962 lines
24 KiB
C
962 lines
24 KiB
C
/*
|
|
* BIRD -- OSPF Topological Database
|
|
*
|
|
* (c) 1999 Martin Mares <mj@ucw.cz>
|
|
* (c) 1999--2004 Ondrej Filip <feela@network.cz>
|
|
*
|
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
|
*/
|
|
|
|
#include "nest/bird.h"
|
|
#include "lib/string.h"
|
|
|
|
#include "ospf.h"
|
|
|
|
#define HASH_DEF_ORDER 6
|
|
#define HASH_HI_MARK *4
|
|
#define HASH_HI_STEP 2
|
|
#define HASH_HI_MAX 16
|
|
#define HASH_LO_MARK /5
|
|
#define HASH_LO_STEP 2
|
|
#define HASH_LO_MIN 8
|
|
|
|
int ptp_unnumbered_stub_lsa = 0;
|
|
|
|
static void *
|
|
originate_rt_lsa_body(struct ospf_area *oa, u16 * length)
|
|
{
|
|
struct proto_ospf *po = oa->po;
|
|
struct ospf_iface *ifa;
|
|
int j = 0, k = 0;
|
|
u16 i = 0;
|
|
struct ospf_lsa_rt *rt;
|
|
struct ospf_lsa_rt_link *ln, *ln_after;
|
|
struct ospf_neighbor *neigh;
|
|
|
|
DBG("%s: Originating RT_lsa body for area \"%I\".\n", po->proto.name,
|
|
oa->areaid);
|
|
|
|
WALK_LIST(ifa, po->iface_list)
|
|
{
|
|
if ((ifa->oa == oa) && (ifa->state != OSPF_IS_DOWN))
|
|
{
|
|
i++;
|
|
if ((ifa->type == OSPF_IT_PTP) && (ifa->state == OSPF_IS_PTP) &&
|
|
(ptp_unnumbered_stub_lsa || !(ifa->iface->addr->flags & IA_UNNUMBERED)))
|
|
i++;
|
|
}
|
|
}
|
|
rt = mb_allocz(po->proto.pool, sizeof(struct ospf_lsa_rt) +
|
|
i * sizeof(struct ospf_lsa_rt_link));
|
|
if (po->areano > 1)
|
|
rt->veb.bit.b = 1;
|
|
if ((po->ebit) && (!oa->stub))
|
|
rt->veb.bit.e = 1;
|
|
ln = (struct ospf_lsa_rt_link *) (rt + 1);
|
|
ln_after = ln + i;
|
|
|
|
WALK_LIST(ifa, po->iface_list)
|
|
{
|
|
if ((ifa->type == OSPF_IT_VLINK) && (ifa->voa == oa) && (!EMPTY_LIST(ifa->neigh_list)))
|
|
{
|
|
neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
|
|
if ((neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
|
|
rt->veb.bit.v = 1;
|
|
}
|
|
|
|
if ((ifa->oa != oa) || (ifa->state == OSPF_IS_DOWN))
|
|
continue;
|
|
|
|
if (ln == ln_after)
|
|
die("LSA space overflow");
|
|
|
|
if (ifa->state == OSPF_IS_LOOP)
|
|
{
|
|
ln->type = 3;
|
|
ln->id = ipa_to_u32(ifa->iface->addr->ip);
|
|
ln->data = 0xffffffff;
|
|
ln->metric = 0;
|
|
ln->notos = 0;
|
|
}
|
|
else
|
|
{
|
|
switch (ifa->type)
|
|
{
|
|
case OSPF_IT_PTP: /* rfc2328 - pg126 */
|
|
neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
|
|
if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL))
|
|
{
|
|
ln->type = LSART_PTP;
|
|
ln->id = neigh->rid;
|
|
ln->metric = ifa->cost;
|
|
ln->notos = 0;
|
|
if (ifa->iface->addr->flags & IA_UNNUMBERED)
|
|
{
|
|
ln->data = ifa->iface->index;
|
|
}
|
|
else
|
|
{
|
|
ln->data = ipa_to_u32(ifa->iface->addr->ip);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ln--;
|
|
i--; /* No link added */
|
|
}
|
|
|
|
if ((ifa->state == OSPF_IS_PTP) &&
|
|
(ptp_unnumbered_stub_lsa || !(ifa->iface->addr->flags & IA_UNNUMBERED)))
|
|
{
|
|
ln++;
|
|
if (ln == ln_after)
|
|
die("LSA space overflow");
|
|
|
|
ln->type = LSART_STUB;
|
|
ln->metric = ifa->cost;
|
|
ln->notos = 0;
|
|
if (ifa->iface->addr->flags & IA_UNNUMBERED)
|
|
{
|
|
ln->id = ipa_to_u32(ifa->iface->addr->opposite);
|
|
ln->data = 0xffffffff;
|
|
}
|
|
else
|
|
{
|
|
ln->data = ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen));
|
|
ln->id = ipa_to_u32(ifa->iface->addr->prefix) & ln->data;
|
|
}
|
|
}
|
|
break;
|
|
case OSPF_IT_BCAST:
|
|
case OSPF_IT_NBMA:
|
|
if (ifa->state == OSPF_IS_WAITING)
|
|
{
|
|
ln->type = LSART_STUB;
|
|
ln->data = ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen));
|
|
ln->id = ipa_to_u32(ifa->iface->addr->prefix) & ln->data;
|
|
ln->metric = ifa->cost;
|
|
ln->notos = 0;
|
|
}
|
|
else
|
|
{
|
|
j = 0, k = 0;
|
|
WALK_LIST(neigh, ifa->neigh_list)
|
|
{
|
|
if ((neigh->rid == ifa->drid) && (neigh->state == NEIGHBOR_FULL))
|
|
k = 1;
|
|
if (neigh->state == NEIGHBOR_FULL)
|
|
j = 1;
|
|
}
|
|
if (((ifa->state == OSPF_IS_DR) && (j == 1)) || (k == 1))
|
|
{
|
|
ln->type = LSART_NET;
|
|
ln->id = ipa_to_u32(ifa->drip);
|
|
ln->data = ipa_to_u32(ifa->iface->addr->ip);
|
|
ln->metric = ifa->cost;
|
|
ln->notos = 0;
|
|
}
|
|
else
|
|
{
|
|
ln->type = LSART_STUB;
|
|
ln->data = ipa_to_u32(ipa_mkmask(ifa->iface->addr->pxlen));
|
|
ln->id = ipa_to_u32(ifa->iface->addr->prefix) & ln->data;
|
|
ln->metric = ifa->cost;
|
|
ln->notos = 0;
|
|
}
|
|
}
|
|
break;
|
|
case OSPF_IT_VLINK:
|
|
neigh = (struct ospf_neighbor *) HEAD(ifa->neigh_list);
|
|
if ((!EMPTY_LIST(ifa->neigh_list)) && (neigh->state == NEIGHBOR_FULL) && (ifa->cost <= 0xffff))
|
|
{
|
|
ln->type = LSART_VLNK;
|
|
ln->id = neigh->rid;
|
|
ln->metric = ifa->cost;
|
|
ln->notos = 0;
|
|
}
|
|
else
|
|
{
|
|
ln--;
|
|
i--; /* No link added */
|
|
}
|
|
break;
|
|
default:
|
|
ln--;
|
|
i--; /* No link added */
|
|
log("Unknown interface type %s", ifa->iface->name);
|
|
break;
|
|
}
|
|
}
|
|
ln++;
|
|
}
|
|
rt->links = i;
|
|
*length = i * sizeof(struct ospf_lsa_rt_link) + sizeof(struct ospf_lsa_rt) +
|
|
sizeof(struct ospf_lsa_header);
|
|
return rt;
|
|
}
|
|
|
|
/**
|
|
* originate_rt_lsa - build new instance of router LSA
|
|
* @oa: ospf_area which is LSA built to
|
|
*
|
|
* It builds router LSA walking through all OSPF interfaces in
|
|
* specified OSPF area. This function is mostly called from
|
|
* area_disp(). Builds new LSA, increases sequence number (if old
|
|
* instance exists) and sets age of LSA to zero.
|
|
*/
|
|
void
|
|
originate_rt_lsa(struct ospf_area *oa)
|
|
{
|
|
struct ospf_lsa_header lsa;
|
|
struct proto_ospf *po = oa->po;
|
|
struct proto *p = &po->proto;
|
|
u32 rtid = po->proto.cf->global->router_id;
|
|
struct top_hash_entry *en;
|
|
void *body;
|
|
|
|
if ((oa->rt) && ((oa->rt->inst_t + MINLSINTERVAL)) > now)
|
|
return;
|
|
/*
|
|
* Tick is probably set to very low value. We cannot
|
|
* originate new LSA before MINLSINTERVAL. We will
|
|
* try to do it next tick.
|
|
*/
|
|
|
|
OSPF_TRACE(D_EVENTS, "Originating RT_lsa for area \"%I\".", oa->areaid);
|
|
|
|
lsa.age = 0;
|
|
lsa.id = rtid;
|
|
lsa.type = LSA_T_RT;
|
|
lsa.rt = rtid;
|
|
lsa.options = oa->opt.byte;
|
|
if (oa->rt == NULL)
|
|
{
|
|
lsa.sn = LSA_INITSEQNO;
|
|
}
|
|
else
|
|
{
|
|
lsa.sn = oa->rt->lsa.sn + 1;
|
|
}
|
|
body = originate_rt_lsa_body(oa, &lsa.length);
|
|
lsasum_calculate(&lsa, body);
|
|
en = lsa_install_new(&lsa, body, oa);
|
|
oa->rt = en;
|
|
ospf_lsupd_flood(NULL, NULL, &oa->rt->lsa, NULL, oa, 1);
|
|
schedule_rtcalc(po);
|
|
oa->origrt = 0;
|
|
}
|
|
|
|
static void *
|
|
originate_net_lsa_body(struct ospf_iface *ifa, u16 * length,
|
|
struct proto_ospf *po)
|
|
{
|
|
u16 i = 1;
|
|
struct ospf_neighbor *n;
|
|
u32 *body;
|
|
struct ospf_lsa_net *net;
|
|
|
|
net = mb_alloc(po->proto.pool, sizeof(u32) * (ifa->fadj + 1) +
|
|
sizeof(struct ospf_lsa_net));
|
|
net->netmask = ipa_mkmask(ifa->iface->addr->pxlen);
|
|
|
|
body = (u32 *) (net + 1);
|
|
i = 1;
|
|
*body = po->proto.cf->global->router_id;
|
|
WALK_LIST(n, ifa->neigh_list)
|
|
{
|
|
if (n->state == NEIGHBOR_FULL)
|
|
{
|
|
*(body + i) = n->rid;
|
|
i++;
|
|
}
|
|
}
|
|
*length = i * sizeof(u32) + sizeof(struct ospf_lsa_header) +
|
|
sizeof(struct ospf_lsa_net);
|
|
return net;
|
|
}
|
|
|
|
/**
|
|
* originate_net_lsa - originates of deletes network LSA
|
|
* @ifa: interface which is LSA originated for
|
|
*
|
|
* Interface counts number of adjacent neighbors. If this number is
|
|
* lower than one or interface is not in state %OSPF_IS_DR it deletes
|
|
* and premature ages instance of network LSA for specified interface.
|
|
* In other case, new instance of network LSA is originated.
|
|
*/
|
|
void
|
|
originate_net_lsa(struct ospf_iface *ifa)
|
|
{
|
|
struct proto_ospf *po = ifa->oa->po;
|
|
struct ospf_lsa_header lsa;
|
|
u32 rtid = po->proto.cf->global->router_id;
|
|
struct proto *p = &po->proto;
|
|
void *body;
|
|
|
|
if (ifa->nlsa && ((ifa->nlsa->inst_t + MINLSINTERVAL) > now))
|
|
return;
|
|
/*
|
|
* It's too early to originate new network LSA. We will
|
|
* try to do it next tick
|
|
*/
|
|
|
|
if ((ifa->state != OSPF_IS_DR) || (ifa->fadj == 0))
|
|
{
|
|
if (ifa->nlsa == NULL)
|
|
return;
|
|
|
|
OSPF_TRACE(D_EVENTS, "Deleting Net lsa for iface \"%s\".",
|
|
ifa->iface->name);
|
|
ifa->nlsa->lsa.sn += 1;
|
|
ifa->nlsa->lsa.age = LSA_MAXAGE;
|
|
ospf_lsupd_flood(NULL, NULL, &ifa->nlsa->lsa, NULL, ifa->oa, 0);
|
|
s_rem_node(SNODE ifa->nlsa);
|
|
if (ifa->nlsa->lsa_body != NULL)
|
|
mb_free(ifa->nlsa->lsa_body);
|
|
ifa->nlsa->lsa_body = NULL;
|
|
ospf_hash_delete(po->gr, ifa->nlsa);
|
|
schedule_rtcalc(po);
|
|
ifa->nlsa = NULL;
|
|
return;
|
|
}
|
|
|
|
OSPF_TRACE(D_EVENTS, "Originating Net lsa for iface \"%s\".",
|
|
ifa->iface->name);
|
|
|
|
lsa.age = 0;
|
|
lsa.id = ipa_to_u32(ifa->iface->addr->ip);
|
|
lsa.type = LSA_T_NET;
|
|
lsa.rt = rtid;
|
|
lsa.options = ifa->oa->opt.byte;
|
|
if (ifa->nlsa == NULL)
|
|
{
|
|
lsa.sn = LSA_INITSEQNO;
|
|
}
|
|
else
|
|
{
|
|
lsa.sn = ifa->nlsa->lsa.sn + 1;
|
|
}
|
|
|
|
body = originate_net_lsa_body(ifa, &lsa.length, po);
|
|
lsasum_calculate(&lsa, body);
|
|
ifa->nlsa = lsa_install_new(&lsa, body, ifa->oa);
|
|
ospf_lsupd_flood(NULL, NULL, &ifa->nlsa->lsa, NULL, ifa->oa, 1);
|
|
ifa->orignet = 0;
|
|
}
|
|
|
|
|
|
static void *
|
|
originate_ext_lsa_body(net * n, rte * e, struct proto_ospf *po,
|
|
struct ea_list *attrs)
|
|
{
|
|
struct proto *p = &po->proto;
|
|
struct ospf_lsa_ext *ext;
|
|
struct ospf_lsa_ext_tos *et;
|
|
u32 m1 = ea_get_int(attrs, EA_OSPF_METRIC1, LSINFINITY);
|
|
u32 m2 = ea_get_int(attrs, EA_OSPF_METRIC2, 10000);
|
|
u32 tag = ea_get_int(attrs, EA_OSPF_TAG, 0);
|
|
int inas = 0;
|
|
|
|
ext = mb_alloc(p->pool, sizeof(struct ospf_lsa_ext) +
|
|
sizeof(struct ospf_lsa_ext_tos));
|
|
ext->netmask = ipa_mkmask(n->n.pxlen);
|
|
|
|
et = (struct ospf_lsa_ext_tos *) (ext + 1);
|
|
|
|
if (m1 != LSINFINITY)
|
|
{
|
|
et->etm.metric = m1;
|
|
et->etm.etos.tos = 0;
|
|
et->etm.etos.ebit = 0;
|
|
}
|
|
else
|
|
{
|
|
et->etm.metric = m2;
|
|
et->etm.etos.tos = 0;
|
|
et->etm.etos.ebit = 1;
|
|
}
|
|
et->tag = tag;
|
|
if (!ipa_equal(e->attrs->gw, IPA_NONE))
|
|
{
|
|
if (ospf_iface_find((struct proto_ospf *) p, e->attrs->iface) != NULL)
|
|
inas = 1;
|
|
}
|
|
|
|
if (!inas)
|
|
et->fwaddr = IPA_NONE;
|
|
else
|
|
et->fwaddr = e->attrs->gw;
|
|
return ext;
|
|
}
|
|
|
|
/**
|
|
* max_ext_lsa - calculate the maximum amount of external networks
|
|
* possible for the given prefix length.
|
|
* @pxlen: network prefix length
|
|
*
|
|
* This is a fix for the previous static use of MAXNETS which did
|
|
* only work correct if MAXNETS < possible IPs for given prefix.
|
|
* This solution is kind of a hack as there can now only be one
|
|
* route for /32 type entries but this is better than the crashes
|
|
* I did experience whith close together /32 routes originating
|
|
* on different hosts.
|
|
*/
|
|
|
|
int
|
|
max_ext_lsa(unsigned pxlen)
|
|
{
|
|
int i;
|
|
for (i = 1; pxlen < BITS_PER_IP_ADDRESS; pxlen++, i <<= 1)
|
|
if (i >= MAXNETS)
|
|
return MAXNETS;
|
|
return i;
|
|
}
|
|
|
|
void
|
|
flush_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type)
|
|
{
|
|
struct proto_ospf *po = oa->po;
|
|
struct proto *p = &po->proto;
|
|
struct top_hash_entry *en;
|
|
u32 rtid = po->proto.cf->global->router_id;
|
|
struct ospf_lsa_header lsa;
|
|
int max, i;
|
|
struct ospf_lsa_sum *sum = NULL;
|
|
|
|
lsa.rt = rtid;
|
|
lsa.type = LSA_T_SUM_NET;
|
|
if (type == ORT_ROUTER)
|
|
lsa.type = LSA_T_SUM_RT;
|
|
|
|
max = max_ext_lsa(fn->pxlen);
|
|
|
|
for (i = 0; i < max; i++)
|
|
{
|
|
lsa.id = ipa_to_u32(fn->prefix) + i;
|
|
if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) != NULL)
|
|
{
|
|
sum = en->lsa_body;
|
|
if ((type == ORT_ROUTER) || (fn->pxlen == ipa_mklen(sum->netmask)))
|
|
{
|
|
en->lsa.age = LSA_MAXAGE;
|
|
en->lsa.sn = LSA_MAXSEQNO;
|
|
lsasum_calculate(&en->lsa, sum);
|
|
OSPF_TRACE(D_EVENTS, "Flushing summary lsa. (id=%I, type=%d)", en->lsa.id, en->lsa.type);
|
|
ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, oa, 1);
|
|
if (can_flush_lsa(po)) flush_lsa(en, po);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
originate_sum_lsa(struct ospf_area *oa, struct fib_node *fn, int type, int metric)
|
|
{
|
|
struct proto_ospf *po = oa->po;
|
|
struct proto *p = &po->proto;
|
|
struct top_hash_entry *en;
|
|
u32 rtid = po->proto.cf->global->router_id;
|
|
struct ospf_lsa_header lsa;
|
|
int i, max, mlen = fn->pxlen, free = 0;
|
|
u32 freeid = 0xFFFF;
|
|
struct ospf_lsa_sum *sum = NULL;
|
|
union ospf_lsa_sum_tm *tm;
|
|
lsa.type = LSA_T_SUM_NET;
|
|
|
|
if (type == ORT_ROUTER)
|
|
{
|
|
lsa.type = LSA_T_SUM_RT;
|
|
mlen = 0;
|
|
}
|
|
|
|
lsa.age = 0;
|
|
lsa.rt = rtid;
|
|
lsa.sn = LSA_INITSEQNO;
|
|
lsa.length = sizeof(struct ospf_lsa_sum) + sizeof(union ospf_lsa_sum_tm) +
|
|
sizeof(struct ospf_lsa_header);
|
|
lsa.options = oa->opt.byte;
|
|
|
|
max = max_ext_lsa(fn->pxlen);
|
|
for (i = 0; i < max; i++)
|
|
{
|
|
lsa.id = ipa_to_u32(fn->prefix) + i;
|
|
if ((en = ospf_hash_find_header(po->gr, oa->areaid, &lsa)) == NULL)
|
|
{
|
|
if (!free)
|
|
{
|
|
freeid = lsa.id;
|
|
free = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sum = en->lsa_body;
|
|
if (mlen == ipa_mklen(sum->netmask))
|
|
{
|
|
tm = (union ospf_lsa_sum_tm *) (sum + 1);
|
|
if (tm->metric == (unsigned) metric) return; /* No reason for origination */
|
|
lsa.sn = en->lsa.sn + 1;
|
|
freeid = en->lsa.id;
|
|
free = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!free)
|
|
{
|
|
log("%s: got more routes for one /%d network then %d, ignoring", p->name,
|
|
fn->pxlen, max);
|
|
return;
|
|
}
|
|
lsa.id = freeid;
|
|
|
|
OSPF_TRACE(D_EVENTS, "Originating summary (type %d) lsa for %I/%d (met %d).", lsa.type, fn->prefix,
|
|
fn->pxlen, metric);
|
|
|
|
sum = mb_alloc(p->pool, sizeof(struct ospf_lsa_sum) + sizeof(union ospf_lsa_sum_tm));
|
|
sum->netmask = ipa_mkmask(mlen);
|
|
tm = (union ospf_lsa_sum_tm *) (sum + 1);
|
|
tm->metric = metric;
|
|
tm->tos.tos = 0;
|
|
|
|
lsasum_calculate(&lsa, sum);
|
|
en = lsa_install_new(&lsa, sum, oa);
|
|
ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, oa, 1);
|
|
}
|
|
|
|
void
|
|
check_sum_lsa(struct proto_ospf *po, ort *nf, int dest)
|
|
{
|
|
struct ospf_area *oa;
|
|
int flush, mlen;
|
|
ip_addr ip;
|
|
|
|
if (po->areano < 2) return;
|
|
|
|
if ((nf->n.type > RTS_OSPF_IA) && (nf->o.type > RTS_OSPF_IA)) return;
|
|
|
|
#ifdef LOCAL_DEBUG
|
|
DBG("Checking...dest = %d, %I/%d", dest, nf->fn.prefix, nf->fn.pxlen);
|
|
if (nf->n.oa) DBG("New: met=%d, oa=%d", nf->n.metric1, nf->n.oa->areaid);
|
|
if (nf->o.oa) DBG("Old: met=%d, oa=%d", nf->o.metric1, nf->o.oa->areaid);
|
|
#endif
|
|
|
|
WALK_LIST(oa, po->area_list)
|
|
{
|
|
flush = 0;
|
|
if ((nf->n.metric1 >= LSINFINITY) || (nf->n.type > RTS_OSPF_IA))
|
|
flush = 1;
|
|
if ((dest == ORT_ROUTER) && (!(nf->n.capa & ORTA_ASBR)))
|
|
flush = 1;
|
|
if ((!nf->n.oa) || (nf->n.oa->areaid == oa->areaid))
|
|
flush = 1;
|
|
|
|
if (nf->n.ifa) {
|
|
if (nf->n.ifa->oa->areaid == oa->areaid)
|
|
flush = 1;
|
|
}
|
|
else flush = 1;
|
|
|
|
/* Don't send summary into stub areas
|
|
* We send just default route (and later) */
|
|
if (oa->stub)
|
|
flush = 1;
|
|
|
|
mlen = nf->fn.pxlen;
|
|
ip = ipa_and(nf->fn.prefix, ipa_mkmask(mlen));
|
|
|
|
if ((oa == po->backbone) && (nf->n.type == RTS_OSPF_IA)) flush = 1; /* Only intra-area can go to the backbone */
|
|
|
|
if ((!flush) && (dest == ORT_NET) && fib_route(&nf->n.oa->net_fib, ip, mlen)) /* The route fits into area networks */
|
|
{
|
|
flush = 1;
|
|
if ((nf->n.oa == po->backbone) && (oa->trcap)) flush = 0;
|
|
}
|
|
|
|
if(flush)
|
|
flush_sum_lsa(oa, &nf->fn, dest);
|
|
else
|
|
originate_sum_lsa(oa, &nf->fn, dest, nf->n.metric1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* originate_ext_lsa - new route received from nest and filters
|
|
* @n: network prefix and mask
|
|
* @e: rte
|
|
* @po: current instance of OSPF
|
|
* @attrs: list of extended attributes
|
|
*
|
|
* If I receive a message that new route is installed, I try to originate an
|
|
* external LSA. The LSA header of such LSA does not contain information about
|
|
* prefix length, so if I have to originate multiple LSAs for route with
|
|
* different prefixes I try to increment prefix id to find a "free" one.
|
|
*
|
|
* The function also sets flag ebit. If it's the first time, the new router lsa
|
|
* origination is necessary.
|
|
*/
|
|
void
|
|
originate_ext_lsa(net * n, rte * e, struct proto_ospf *po,
|
|
struct ea_list *attrs)
|
|
{
|
|
struct ospf_lsa_header lsa;
|
|
u32 rtid = po->proto.cf->global->router_id;
|
|
struct top_hash_entry *en = NULL;
|
|
void *body = NULL;
|
|
struct proto *p = &po->proto;
|
|
struct ospf_area *oa;
|
|
struct ospf_lsa_ext *ext1, *ext2;
|
|
int i, max;
|
|
|
|
OSPF_TRACE(D_EVENTS, "Originating Ext lsa for %I/%d.", n->n.prefix,
|
|
n->n.pxlen);
|
|
|
|
lsa.age = 0;
|
|
lsa.id = ipa_to_u32(n->n.prefix);
|
|
lsa.type = LSA_T_EXT;
|
|
lsa.rt = rtid;
|
|
lsa.sn = LSA_INITSEQNO;
|
|
lsa.options = 0;
|
|
|
|
body = originate_ext_lsa_body(n, e, po, attrs);
|
|
lsa.length = sizeof(struct ospf_lsa_ext) + sizeof(struct ospf_lsa_ext_tos) +
|
|
sizeof(struct ospf_lsa_header);
|
|
ext1 = body;
|
|
max = max_ext_lsa(n->n.pxlen);
|
|
|
|
for (i = 0; i < max; i++)
|
|
{
|
|
if ((en = ospf_hash_find_header(po->gr, 0 , &lsa)) != NULL)
|
|
{
|
|
ext2 = en->lsa_body;
|
|
if (ipa_compare(ext1->netmask, ext2->netmask) != 0)
|
|
lsa.id++;
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (i == max)
|
|
{
|
|
log("%s: got more routes for one /%d network then %d, ignoring", p->name,
|
|
n->n.pxlen, max);
|
|
mb_free(body);
|
|
return;
|
|
}
|
|
lsasum_calculate(&lsa, body);
|
|
WALK_LIST(oa, po->area_list)
|
|
{
|
|
if (!oa->stub)
|
|
{
|
|
en = lsa_install_new(&lsa, body, oa);
|
|
ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, oa, 1);
|
|
body = originate_ext_lsa_body(n, e, po, attrs);
|
|
}
|
|
}
|
|
mb_free(body);
|
|
|
|
if (po->ebit == 0)
|
|
{
|
|
po->ebit = 1;
|
|
WALK_LIST(oa, po->area_list)
|
|
{
|
|
schedule_rt_lsa(oa);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
ospf_top_ht_alloc(struct top_graph *f)
|
|
{
|
|
f->hash_size = 1 << f->hash_order;
|
|
f->hash_mask = f->hash_size - 1;
|
|
if (f->hash_order > HASH_HI_MAX - HASH_HI_STEP)
|
|
f->hash_entries_max = ~0;
|
|
else
|
|
f->hash_entries_max = f->hash_size HASH_HI_MARK;
|
|
if (f->hash_order < HASH_LO_MIN + HASH_LO_STEP)
|
|
f->hash_entries_min = 0;
|
|
else
|
|
f->hash_entries_min = f->hash_size HASH_LO_MARK;
|
|
DBG("Allocating OSPF hash of order %d: %d hash_entries, %d low, %d high\n",
|
|
f->hash_order, f->hash_size, f->hash_entries_min, f->hash_entries_max);
|
|
f->hash_table =
|
|
mb_alloc(f->pool, f->hash_size * sizeof(struct top_hash_entry *));
|
|
bzero(f->hash_table, f->hash_size * sizeof(struct top_hash_entry *));
|
|
}
|
|
|
|
static inline void
|
|
ospf_top_ht_free(struct top_hash_entry **h)
|
|
{
|
|
mb_free(h);
|
|
}
|
|
|
|
static inline u32
|
|
ospf_top_hash_u32(u32 a)
|
|
{
|
|
/* Shamelessly stolen from IP address hashing in ipv4.h */
|
|
a ^= a >> 16;
|
|
a ^= a << 10;
|
|
return a;
|
|
}
|
|
|
|
static inline unsigned
|
|
ospf_top_hash(struct top_graph *f, u32 areaid, u32 lsaid, u32 rtrid, u32 type)
|
|
{
|
|
#if 1 /* Dirty patch to make rt table calculation work. */
|
|
return (ospf_top_hash_u32(lsaid) +
|
|
ospf_top_hash_u32((type ==
|
|
LSA_T_NET) ? lsaid : rtrid) + type +
|
|
(type == LSA_T_EXT ? 0 : areaid)) & f->hash_mask;
|
|
#else
|
|
return (ospf_top_hash_u32(lsaid) + ospf_top_hash_u32(rtrid) +
|
|
type + areaid) & f->hash_mask;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* ospf_top_new - allocated new topology database
|
|
* @p: current instance of OSPF
|
|
*
|
|
* This dynamically hashed structure is often used for keeping LSAs. Mainly
|
|
* its used in @ospf_area structure.
|
|
*/
|
|
struct top_graph *
|
|
ospf_top_new(pool *pool)
|
|
{
|
|
struct top_graph *f;
|
|
|
|
f = mb_allocz(pool, sizeof(struct top_graph));
|
|
f->pool = pool;
|
|
f->hash_slab = sl_new(f->pool, sizeof(struct top_hash_entry));
|
|
f->hash_order = HASH_DEF_ORDER;
|
|
ospf_top_ht_alloc(f);
|
|
f->hash_entries = 0;
|
|
f->hash_entries_min = 0;
|
|
return f;
|
|
}
|
|
|
|
void
|
|
ospf_top_free(struct top_graph *f)
|
|
{
|
|
rfree(f->hash_slab);
|
|
ospf_top_ht_free(f->hash_table);
|
|
mb_free(f);
|
|
}
|
|
|
|
static void
|
|
ospf_top_rehash(struct top_graph *f, int step)
|
|
{
|
|
unsigned int oldn, oldh;
|
|
struct top_hash_entry **n, **oldt, **newt, *e, *x;
|
|
|
|
oldn = f->hash_size;
|
|
oldt = f->hash_table;
|
|
DBG("Re-hashing topology hash from order %d to %d\n", f->hash_order,
|
|
f->hash_order + step);
|
|
f->hash_order += step;
|
|
ospf_top_ht_alloc(f);
|
|
newt = f->hash_table;
|
|
|
|
for (oldh = 0; oldh < oldn; oldh++)
|
|
{
|
|
e = oldt[oldh];
|
|
while (e)
|
|
{
|
|
x = e->next;
|
|
n = newt + ospf_top_hash(f, e->oa->areaid, e->lsa.id, e->lsa.rt, e->lsa.type);
|
|
e->next = *n;
|
|
*n = e;
|
|
e = x;
|
|
}
|
|
}
|
|
ospf_top_ht_free(oldt);
|
|
}
|
|
|
|
struct top_hash_entry *
|
|
ospf_hash_find_header(struct top_graph *f, u32 areaid, struct ospf_lsa_header *h)
|
|
{
|
|
return ospf_hash_find(f, areaid, h->id, h->rt, h->type);
|
|
}
|
|
|
|
struct top_hash_entry *
|
|
ospf_hash_get_header(struct top_graph *f, struct ospf_area *oa, struct ospf_lsa_header *h)
|
|
{
|
|
return ospf_hash_get(f, oa, h->id, h->rt, h->type);
|
|
}
|
|
|
|
struct top_hash_entry *
|
|
ospf_hash_find(struct top_graph *f, u32 areaid, u32 lsa, u32 rtr, u32 type)
|
|
{
|
|
struct top_hash_entry *e;
|
|
|
|
e = f->hash_table[ospf_top_hash(f, areaid, lsa, rtr, type)];
|
|
|
|
/* Dirty patch to make rt table calculation work. */
|
|
if (type == LSA_T_NET)
|
|
{
|
|
while (e && (e->lsa.id != lsa || e->lsa.type != LSA_T_NET || e->oa->areaid != areaid))
|
|
e = e->next;
|
|
}
|
|
else if (type == LSA_T_EXT)
|
|
{
|
|
while (e && (e->lsa.id != lsa || e->lsa.type != type || e->lsa.rt != rtr))
|
|
e = e->next;
|
|
}
|
|
else
|
|
{
|
|
while (e && (e->lsa.id != lsa || e->lsa.type != type || e->lsa.rt != rtr || e->oa->areaid != areaid))
|
|
e = e->next;
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
struct top_hash_entry *
|
|
ospf_hash_get(struct top_graph *f, struct ospf_area *oa, u32 lsa, u32 rtr, u32 type)
|
|
{
|
|
struct top_hash_entry **ee;
|
|
struct top_hash_entry *e;
|
|
u32 nareaid = (type == LSA_T_EXT ? 0 : oa->areaid);
|
|
|
|
ee = f->hash_table + ospf_top_hash(f, nareaid, lsa, rtr, type);
|
|
e = *ee;
|
|
|
|
if (type == LSA_T_EXT)
|
|
{
|
|
while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type))
|
|
e = e->next;
|
|
}
|
|
else
|
|
{
|
|
while (e && (e->lsa.id != lsa || e->lsa.rt != rtr || e->lsa.type != type || e->oa->areaid != nareaid))
|
|
e = e->next;
|
|
}
|
|
|
|
if (e)
|
|
return e;
|
|
|
|
e = sl_alloc(f->hash_slab);
|
|
e->color = OUTSPF;
|
|
e->dist = LSINFINITY;
|
|
e->nhi = NULL;
|
|
e->nh = IPA_NONE;
|
|
e->lb = IPA_NONE;
|
|
e->lsa.id = lsa;
|
|
e->lsa.rt = rtr;
|
|
e->lsa.type = type;
|
|
e->lsa_body = NULL;
|
|
e->nhi = NULL;
|
|
e->oa = oa;
|
|
e->next = *ee;
|
|
*ee = e;
|
|
if (f->hash_entries++ > f->hash_entries_max)
|
|
ospf_top_rehash(f, HASH_HI_STEP);
|
|
return e;
|
|
}
|
|
|
|
void
|
|
ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e)
|
|
{
|
|
struct top_hash_entry **ee = f->hash_table +
|
|
ospf_top_hash(f, e->oa->areaid, e->lsa.id, e->lsa.rt, e->lsa.type);
|
|
|
|
while (*ee)
|
|
{
|
|
if (*ee == e)
|
|
{
|
|
*ee = e->next;
|
|
sl_free(f->hash_slab, e);
|
|
if (f->hash_entries-- < f->hash_entries_min)
|
|
ospf_top_rehash(f, -HASH_LO_STEP);
|
|
return;
|
|
}
|
|
ee = &((*ee)->next);
|
|
}
|
|
bug("ospf_hash_delete() called for invalid node");
|
|
}
|
|
|
|
static void
|
|
ospf_dump_lsa(struct top_hash_entry *he, struct proto *p)
|
|
{
|
|
struct ospf_lsa_rt *rt = NULL;
|
|
struct ospf_lsa_rt_link *rr = NULL;
|
|
struct ospf_lsa_net *ln = NULL;
|
|
u32 *rts = NULL;
|
|
u32 i, max;
|
|
|
|
OSPF_TRACE(D_EVENTS, "- %1x %-1I %-1I %4u 0x%08x 0x%04x %-1I",
|
|
he->lsa.type, he->lsa.id, he->lsa.rt, he->lsa.age,
|
|
he->lsa.sn, he->lsa.checksum, he->oa ? he->oa->areaid : 0 );
|
|
|
|
switch (he->lsa.type)
|
|
{
|
|
case LSA_T_RT:
|
|
rt = he->lsa_body;
|
|
rr = (struct ospf_lsa_rt_link *) (rt + 1);
|
|
|
|
for (i = 0; i < rt->links; i++)
|
|
OSPF_TRACE(D_EVENTS, " - %1x %-1I %-1I %5u", rr[i].type, rr[i].id, rr[i].data, rr[i].metric);
|
|
break;
|
|
|
|
case LSA_T_NET:
|
|
ln = he->lsa_body;
|
|
rts = (u32 *) (ln + 1);
|
|
max = (he->lsa.length - sizeof(struct ospf_lsa_header) -
|
|
sizeof(struct ospf_lsa_net)) / sizeof(u32);
|
|
|
|
for (i = 0; i < max; i++)
|
|
OSPF_TRACE(D_EVENTS, " - %-1I", rts[i]);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
ospf_top_dump(struct top_graph *f, struct proto *p)
|
|
{
|
|
unsigned int i;
|
|
OSPF_TRACE(D_EVENTS, "Hash entries: %d", f->hash_entries);
|
|
|
|
for (i = 0; i < f->hash_size; i++)
|
|
{
|
|
struct top_hash_entry *e;
|
|
for (e = f->hash_table[i]; e != NULL; e = e->next)
|
|
ospf_dump_lsa(e, p);
|
|
}
|
|
}
|
|
|
|
/* This is very inefficient, please don't call it often */
|
|
|
|
/* I should also test for every LSA if it's in some link state
|
|
* retransmission list for every neighbor. I will not test it.
|
|
* It could happen that I'll receive some strange ls ack's.
|
|
*/
|
|
|
|
int
|
|
can_flush_lsa(struct proto_ospf *po)
|
|
{
|
|
struct ospf_iface *ifa;
|
|
struct ospf_neighbor *n;
|
|
|
|
WALK_LIST(ifa, po->iface_list)
|
|
{
|
|
WALK_LIST(n, ifa->neigh_list)
|
|
{
|
|
if ((n->state == NEIGHBOR_EXCHANGE) || (n->state == NEIGHBOR_LOADING))
|
|
return 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|