bird/proto/ospf/lsalib.c
Ondrej Filip 86c84d76b7 Huge OSPF database redesign. Since now, all LSAs of all areas
are in single database. This avoids duplication of external LSAs and
fixes bug in external LSA distribution.
2004-07-15 16:37:52 +00:00

491 lines
11 KiB
C

/*
* BIRD -- OSPF
*
* (c) 1999--2004 Ondrej Filip <feela@network.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "ospf.h"
void
flush_lsa(struct top_hash_entry *en, struct proto_ospf *po)
{
struct proto *p = &po->proto;
OSPF_TRACE(D_EVENTS,
"Going to remove node Type: %u, Id: %I, Rt: %I, Age: %u, SN: 0x%x",
en->lsa.type, en->lsa.id, en->lsa.rt, en->lsa.age, en->lsa.sn);
s_rem_node(SNODE en);
if (en->lsa_body != NULL)
mb_free(en->lsa_body);
en->lsa_body = NULL;
ospf_hash_delete(po->gr, en);
}
/**
* ospf_age
* @po: ospf protocol
*
* This function is periodicaly invoked from ospf_disp(). It computes the new
* age of all LSAs and old (@age is higher than %LSA_MAXAGE) LSAs are flushed
* whenever possible. If an LSA originated by the router itself is older
* than %LSREFRESHTIME a new instance is originated.
*
* The RFC says that a router should check the checksum of every LSA to detect
* hardware problems. BIRD does not do this to minimalize CPU utilization.
*
* If routing table calculation is scheduled, it also invalidates the old routing
* table calculation results.
*/
void
ospf_age(struct proto_ospf *po)
{
struct proto *p = &po->proto;
struct top_hash_entry *en, *nxt;
int flush = can_flush_lsa(po);
if (po->cleanup) OSPF_TRACE(D_EVENTS, "Running ospf_age cleanup");
WALK_SLIST_DELSAFE(en, nxt, po->lsal)
{
if (po->cleanup)
{
en->color = OUTSPF;
en->dist = LSINFINITY;
en->nhi = NULL;
en->nh = IPA_NONE;
en->lb = IPA_NONE;
DBG("Infinitying Type: %u, Id: %I, Rt: %I\n", en->lsa.type, en->lsa.id,
en->lsa.rt);
}
if (en->lsa.age == LSA_MAXAGE)
{
if (flush)
flush_lsa(en, po);
continue;
}
if ((en->lsa.rt == p->cf->global->router_id) &&(en->lsa.age >=
LSREFRESHTIME))
{
OSPF_TRACE(D_EVENTS, "Refreshing my LSA: Type: %u, Id: %I, Rt: %I",
en->lsa.type, en->lsa.id, en->lsa.rt);
en->lsa.sn++;
en->lsa.age = 0;
en->inst_t = now;
en->ini_age = 0;
lsasum_calculate(&en->lsa, en->lsa_body);
ospf_lsupd_flood(NULL, NULL, &en->lsa, NULL, en->oa, 1);
continue;
}
if ((en->lsa.age = (en->ini_age + (now - en->inst_t))) >= LSA_MAXAGE)
{
if (flush)
{
flush_lsa(en, po);
schedule_rtcalc(po);
}
else
en->lsa.age = LSA_MAXAGE;
}
}
po->cleanup = 0;
}
void
htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n)
{
n->age = htons(h->age);
n->options = h->options;
n->type = h->type;
n->id = htonl(h->id);
n->rt = htonl(h->rt);
n->sn = htonl(h->sn);
n->checksum = htons(h->checksum);
n->length = htons(h->length);
};
void
ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h)
{
h->age = ntohs(n->age);
h->options = n->options;
h->type = n->type;
h->id = ntohl(n->id);
h->rt = ntohl(n->rt);
h->sn = ntohl(n->sn);
h->checksum = ntohs(n->checksum);
h->length = ntohs(n->length);
};
void
htonlsab(void *h, void *n, u8 type, u16 len)
{
unsigned int i;
switch (type)
{
case LSA_T_RT:
{
struct ospf_lsa_rt *hrt, *nrt;
struct ospf_lsa_rt_link *hrtl, *nrtl;
u16 links;
nrt = n;
hrt = h;
links = hrt->links;
nrt->veb.byte = hrt->veb.byte;
nrt->padding = 0;
nrt->links = htons(hrt->links);
nrtl = (struct ospf_lsa_rt_link *) (nrt + 1);
hrtl = (struct ospf_lsa_rt_link *) (hrt + 1);
for (i = 0; i < links; i++)
{
(nrtl + i)->id = htonl((hrtl + i)->id);
(nrtl + i)->data = htonl((hrtl + i)->data);
(nrtl + i)->type = (hrtl + i)->type;
(nrtl + i)->notos = (hrtl + i)->notos;
(nrtl + i)->metric = htons((hrtl + i)->metric);
}
break;
}
case LSA_T_NET:
{
u32 *hid, *nid;
nid = n;
hid = h;
for (i = 0; i < (len / sizeof(u32)); i++)
{
*(nid + i) = htonl(*(hid + i));
}
break;
}
case LSA_T_SUM_NET:
case LSA_T_SUM_RT:
{
struct ospf_lsa_sum *hs, *ns;
union ospf_lsa_sum_tm *hn, *nn;
hs = h;
ns = n;
ns->netmask = hs->netmask;
ipa_hton(ns->netmask);
hn = (union ospf_lsa_sum_tm *) (hs + 1);
nn = (union ospf_lsa_sum_tm *) (ns + 1);
for (i = 0; i < ((len - sizeof(struct ospf_lsa_sum)) /
sizeof(union ospf_lsa_sum_tm)); i++)
{
(nn + i)->metric = htonl((hn + i)->metric);
//(nn + i)->tos = (hn + i)->tos;
//(nn + i)->metric = htons((hn + i)->metric);
//(nn + i)->padding = 0;
}
break;
}
case LSA_T_EXT:
{
struct ospf_lsa_ext *he, *ne;
struct ospf_lsa_ext_tos *ht, *nt;
he = h;
ne = n;
ne->netmask = he->netmask;
ipa_hton(ne->netmask);
ht = (struct ospf_lsa_ext_tos *) (he + 1);
nt = (struct ospf_lsa_ext_tos *) (ne + 1);
for (i = 0; i < ((len - sizeof(struct ospf_lsa_ext)) /
sizeof(struct ospf_lsa_ext_tos)); i++)
{
(nt + i)->etm.metric = htonl((ht + i)->etm.metric);
//(nt + i)->tos = (ht + i)->tos;
//(nt + i)->padding = 0;
//(nt + i)->metric = htons((ht + i)->metric);
(nt + i)->fwaddr = (ht + i)->fwaddr;
ipa_hton((nt + i)->fwaddr);
(nt + i)->tag = htonl((ht + i)->tag);
}
break;
}
default:
bug("(hton): Unknown LSA");
}
};
void
ntohlsab(void *n, void *h, u8 type, u16 len)
{
unsigned int i;
switch (type)
{
case LSA_T_RT:
{
struct ospf_lsa_rt *hrt, *nrt;
struct ospf_lsa_rt_link *hrtl, *nrtl;
u16 links;
nrt = n;
hrt = h;
hrt->veb.byte = nrt->veb.byte;
hrt->padding = 0;
links = hrt->links = ntohs(nrt->links);
nrtl = (struct ospf_lsa_rt_link *) (nrt + 1);
hrtl = (struct ospf_lsa_rt_link *) (hrt + 1);
for (i = 0; i < links; i++)
{
(hrtl + i)->id = ntohl((nrtl + i)->id);
(hrtl + i)->data = ntohl((nrtl + i)->data);
(hrtl + i)->type = (nrtl + i)->type;
(hrtl + i)->notos = (nrtl + i)->notos;
(hrtl + i)->metric = ntohs((nrtl + i)->metric);
}
break;
}
case LSA_T_NET:
{
u32 *hid, *nid;
hid = h;
nid = n;
for (i = 0; i < (len / sizeof(u32)); i++)
{
*(hid + i) = ntohl(*(nid + i));
}
break;
}
case LSA_T_SUM_NET:
case LSA_T_SUM_RT:
{
struct ospf_lsa_sum *hs, *ns;
union ospf_lsa_sum_tm *hn, *nn;
hs = h;
ns = n;
hs->netmask = ns->netmask;
ipa_ntoh(hs->netmask);
hn = (union ospf_lsa_sum_tm *) (hs + 1);
nn = (union ospf_lsa_sum_tm *) (ns + 1);
for (i = 0; i < ((len - sizeof(struct ospf_lsa_sum)) /
sizeof(union ospf_lsa_sum_tm)); i++)
{
(hn + i)->metric = ntohl((nn + i)->metric);
//(hn + i)->tos = (nn + i)->tos;
//(hn + i)->metric = ntohs((nn + i)->metric);
//(hn + i)->padding = 0;
}
break;
}
case LSA_T_EXT:
{
struct ospf_lsa_ext *he, *ne;
struct ospf_lsa_ext_tos *ht, *nt;
he = h;
ne = n;
he->netmask = ne->netmask;
ipa_ntoh(he->netmask);
ht = (struct ospf_lsa_ext_tos *) (he + 1);
nt = (struct ospf_lsa_ext_tos *) (ne + 1);
for (i = 0; i < ((len - sizeof(struct ospf_lsa_ext)) /
sizeof(struct ospf_lsa_ext_tos)); i++)
{
(ht + i)->etm.metric = ntohl((nt + i)->etm.metric);
//(ht + i)->etos = (nt + i)->etos;
//(ht + i)->padding = 0;
//(ht + i)->metric = ntohs((nt + i)->metric);
(ht + i)->fwaddr = (nt + i)->fwaddr;
ipa_ntoh((ht + i)->fwaddr);
(ht + i)->tag = ntohl((nt + i)->tag);
}
break;
}
default:
bug("(ntoh): Unknown LSA");
}
};
#define MODX 4102 /* larges signed value without overflow */
/* Fletcher Checksum -- Refer to RFC1008. */
#define MODX 4102
#define LSA_CHECKSUM_OFFSET 15
/* FIXME This is VERY uneficient, I have huge endianity problems */
void
lsasum_calculate(struct ospf_lsa_header *h, void *body)
{
u16 length;
length = h->length;
htonlsah(h, h);
htonlsab(body, body, h->type, length - sizeof(struct ospf_lsa_header));
(void) lsasum_check(h, body);
ntohlsah(h, h);
ntohlsab(body, body, h->type, length - sizeof(struct ospf_lsa_header));
}
/*
* Note, that this function expects that LSA is in big endianity
* It also returns value in big endian
*/
u16
lsasum_check(struct ospf_lsa_header *h, void *body)
{
u8 *sp, *ep, *p, *q, *b;
int c0 = 0, c1 = 0;
int x, y;
u16 length;
b = body;
sp = (char *) &h->options;
length = ntohs(h->length) - 2;
h->checksum = 0;
for (ep = sp + length; sp < ep; sp = q)
{ /* Actually MODX is very large, do we need the for-cyclus? */
q = sp + MODX;
if (q > ep)
q = ep;
for (p = sp; p < q; p++)
{
/*
* I count with bytes from header and than from body
* but if there is no body, it's appended to header
* (probably checksum in update receiving) and I go on
* after header
*/
if ((b == NULL) || (p < (u8 *) (h + 1)))
{
c0 += *p;
}
else
{
c0 += *(b + (p - sp) - sizeof(struct ospf_lsa_header) + 2);
}
c1 += c0;
}
c0 %= 255;
c1 %= 255;
}
x = ((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255;
if (x <= 0)
x += 255;
y = 510 - c0 - x;
if (y > 255)
y -= 255;
((u8 *) & h->checksum)[0] = x;
((u8 *) & h->checksum)[1] = y;
return h->checksum;
}
int
lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
/* Return codes from point of view of l1 */
{
u32 sn1, sn2;
sn1 = l1->sn - LSA_INITSEQNO + 1;
sn2 = l2->sn - LSA_INITSEQNO + 1;
if (sn1 > sn2)
return CMP_NEWER;
if (sn1 < sn2)
return CMP_OLDER;
if (l1->checksum != l2->checksum)
return l1->checksum < l2->checksum ? CMP_OLDER : CMP_NEWER;
if ((l1->age == LSA_MAXAGE) && (l2->age != LSA_MAXAGE))
return CMP_NEWER;
if ((l2->age == LSA_MAXAGE) && (l1->age != LSA_MAXAGE))
return CMP_OLDER;
if (ABS(l1->age - l2->age) > LSA_MAXAGEDIFF)
return l1->age < l2->age ? CMP_NEWER : CMP_OLDER;
return CMP_SAME;
}
/**
* lsa_install_new - install new LSA into database
* @lsa: LSA header
* @body: pointer to LSA body
* @oa: current ospf_area
*
* This function ensures installing new LSA into LSA database. Old instance is
* replaced. Several actions are taken to detect if new routing table
* calculation is necessary. This is described in 13.2 of RFC 2328.
*/
struct top_hash_entry *
lsa_install_new(struct ospf_lsa_header *lsa, void *body, struct ospf_area *oa)
{
/* LSA can be temporarrily, but body must be mb_allocated. */
int change = 0;
unsigned i;
struct top_hash_entry *en;
struct proto_ospf *po = oa->po;
if ((en = ospf_hash_find_header(po->gr, oa->areaid, lsa)) == NULL)
{
en = ospf_hash_get_header(po->gr, oa, lsa);
change = 1;
}
else
{
if ((en->lsa.length != lsa->length) || (en->lsa.options != lsa->options)
|| ((en->lsa.age == LSA_MAXAGE) || (lsa->age == LSA_MAXAGE)))
change = 1;
else
{
u8 *k = en->lsa_body, *l = body;
for (i = 0; i < (lsa->length - sizeof(struct ospf_lsa_header)); i++)
{
if (*(k + i) != *(l + i))
{
change = 1;
break;
}
}
}
s_rem_node(SNODE en);
}
DBG("Inst lsa: Id: %I, Rt: %I, Type: %u, Age: %u, Sum: %u, Sn: 0x%x\n",
lsa->id, lsa->rt, lsa->type, lsa->age, lsa->checksum, lsa->sn);
s_add_tail(&po->lsal, SNODE en);
en->inst_t = now;
if (en->lsa_body != NULL)
mb_free(en->lsa_body);
en->lsa_body = body;
memcpy(&en->lsa, lsa, sizeof(struct ospf_lsa_header));
en->ini_age = en->lsa.age;
if (change)
{
schedule_rtcalc(po);
}
return en;
}