bird/proto/ospf/lsalib.c

563 lines
12 KiB
C
Raw Normal View History

/*
* 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"
2000-05-03 06:19:41 +08:00
void
flush_lsa(struct top_hash_entry *en, struct proto_ospf *po)
2000-05-03 06:19:41 +08:00
{
struct proto *p = &po->proto;
2004-06-06 17:37:54 +08:00
OSPF_TRACE(D_EVENTS,
2009-12-15 00:29:33 +08:00
"Going to remove LSA Type: %04x, Id: %R, Rt: %R, Age: %u, Seqno: 0x%x",
2009-07-23 22:51:28 +08:00
en->lsa.type, en->lsa.id, en->lsa.rt, en->lsa.age, en->lsa.sn);
2000-05-10 03:36:32 +08:00
s_rem_node(SNODE en);
2004-06-06 17:37:54 +08:00
if (en->lsa_body != NULL)
mb_free(en->lsa_body);
en->lsa_body = NULL;
ospf_hash_delete(po->gr, en);
2000-05-03 06:19:41 +08:00
}
void
ospf_flush_area(struct proto_ospf *po, u32 areaid)
{
struct top_hash_entry *en, *nxt;
WALK_SLIST_DELSAFE(en, nxt, po->lsal)
{
if ((LSA_SCOPE(&en->lsa) == LSA_SCOPE_AREA) && (en->domain == areaid))
flush_lsa(en, po);
}
}
/**
* 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.
*/
2000-05-03 06:19:41 +08:00
void
ospf_age(struct proto_ospf *po)
2000-05-03 06:19:41 +08:00
{
struct proto *p = &po->proto;
2004-06-06 17:37:54 +08:00
struct top_hash_entry *en, *nxt;
int flush = can_flush_lsa(po);
2000-05-31 22:21:56 +08:00
WALK_SLIST_DELSAFE(en, nxt, po->lsal)
2000-05-03 06:19:41 +08:00
{
2004-06-06 17:37:54 +08:00
if (en->lsa.age == LSA_MAXAGE)
2000-05-31 22:21:56 +08:00
{
2004-06-06 17:37:54 +08:00
if (flush)
flush_lsa(en, po);
2000-06-02 20:34:28 +08:00
continue;
2000-05-31 22:21:56 +08:00
}
if ((en->lsa.rt == po->router_id) && (en->lsa.age >= LSREFRESHTIME))
2000-05-31 22:21:56 +08:00
{
2009-07-23 22:51:28 +08:00
OSPF_TRACE(D_EVENTS, "Refreshing my LSA: Type: %u, Id: %R, Rt: %R",
en->lsa.type, en->lsa.id, en->lsa.rt);
2004-06-06 17:37:54 +08:00
en->lsa.sn++;
en->lsa.age = 0;
en->inst_t = now;
en->ini_age = 0;
2004-06-07 02:45:08 +08:00
lsasum_calculate(&en->lsa, en->lsa_body);
2009-08-21 15:27:52 +08:00
ospf_lsupd_flood(po, NULL, NULL, &en->lsa, en->domain, 1);
2004-06-06 17:37:54 +08:00
continue;
2000-05-31 22:21:56 +08:00
}
2004-06-06 17:37:54 +08:00
if ((en->lsa.age = (en->ini_age + (now - en->inst_t))) >= LSA_MAXAGE)
2000-05-31 22:21:56 +08:00
{
2004-06-06 17:37:54 +08:00
if (flush)
{
flush_lsa(en, po);
schedule_rtcalc(po);
}
2004-06-06 17:37:54 +08:00
else
en->lsa.age = LSA_MAXAGE;
2000-05-31 22:21:56 +08:00
}
2000-05-03 06:19:41 +08:00
}
}
#ifndef CPU_BIG_ENDIAN
void
htonlsah(struct ospf_lsa_header *h, struct ospf_lsa_header *n)
{
2004-06-06 17:37:54 +08:00
n->age = htons(h->age);
2009-08-21 15:27:52 +08:00
#ifdef OSPFv2
2004-06-06 17:37:54 +08:00
n->options = h->options;
2009-08-21 15:27:52 +08:00
#endif
2009-08-28 00:25:46 +08:00
n->type = htont(h->type);
2004-06-06 17:37:54 +08:00
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);
2009-09-08 23:06:47 +08:00
}
void
ntohlsah(struct ospf_lsa_header *n, struct ospf_lsa_header *h)
{
2004-06-06 17:37:54 +08:00
h->age = ntohs(n->age);
2009-08-21 15:27:52 +08:00
#ifdef OSPFv2
2004-06-06 17:37:54 +08:00
h->options = n->options;
2009-08-21 15:27:52 +08:00
#endif
2009-08-28 00:25:46 +08:00
h->type = ntoht(n->type);
2004-06-06 17:37:54 +08:00
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);
2009-09-08 23:06:47 +08:00
}
void
htonlsab(void *h, void *n, u16 len)
{
2009-09-08 23:06:47 +08:00
u32 *hid = h;
u32 *nid = n;
unsigned i;
2009-09-08 23:06:47 +08:00
for (i = 0; i < (len / sizeof(u32)); i++)
nid[i] = htonl(hid[i]);
}
void
ntohlsab(void *n, void *h, u16 len)
{
2009-09-08 23:06:47 +08:00
u32 *nid = n;
u32 *hid = h;
unsigned i;
2009-09-08 23:06:47 +08:00
for (i = 0; i < (len / sizeof(u32)); i++)
hid[i] = ntohl(nid[i]);
}
#endif /* little endian */
2009-12-15 00:29:33 +08:00
/*
2009-08-28 00:25:46 +08:00
void
buf_dump(const char *hdr, const byte *buf, int blen)
{
char b2[1024];
char *bp;
int first = 1;
int i;
const char *lhdr = hdr;
bp = b2;
for(i = 0; i < blen; i++)
{
if ((i > 0) && ((i % 16) == 0))
{
*bp = 0;
log(L_WARN "%s\t%s", lhdr, b2);
lhdr = "";
bp = b2;
}
bp += snprintf(bp, 1022, "%02x ", buf[i]);
}
*bp = 0;
log(L_WARN "%s\t%s", lhdr, b2);
}
2009-12-15 00:29:33 +08:00
*/
2009-08-28 00:25:46 +08:00
#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
2004-06-07 02:45:08 +08:00
lsasum_calculate(struct ospf_lsa_header *h, void *body)
{
2009-08-28 00:25:46 +08:00
u16 length = h->length;
2004-06-06 17:37:54 +08:00
// log(L_WARN "Checksum %R %R %d start (len %d)", h->id, h->rt, h->type, length);
2004-06-06 17:37:54 +08:00
htonlsah(h, h);
htonlsab1(body, length - sizeof(struct ospf_lsa_header));
2009-08-28 00:25:46 +08:00
/*
2009-08-28 00:25:46 +08:00
char buf[1024];
memcpy(buf, h, sizeof(struct ospf_lsa_header));
memcpy(buf + sizeof(struct ospf_lsa_header), body, length - sizeof(struct ospf_lsa_header));
buf_dump("CALC", buf, length);
*/
2004-06-07 02:45:08 +08:00
(void) lsasum_check(h, body);
2004-06-06 17:37:54 +08:00
// log(L_WARN "Checksum result %4x", h->checksum);
2009-08-28 00:25:46 +08:00
2004-06-06 17:37:54 +08:00
ntohlsah(h, h);
ntohlsab1(body, 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
2004-06-07 02:45:08 +08:00
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;
2009-08-28 00:25:46 +08:00
sp = (char *) h;
2009-08-21 15:27:52 +08:00
sp += 2; /* Skip Age field */
2004-06-06 17:37:54 +08:00
length = ntohs(h->length) - 2;
h->checksum = 0;
for (ep = sp + length; sp < ep; sp = q)
2004-06-06 17:37:54 +08:00
{ /* Actually MODX is very large, do we need the for-cyclus? */
q = sp + MODX;
2004-06-06 17:37:54 +08:00
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
*/
2004-06-06 17:37:54 +08:00
if ((b == NULL) || (p < (u8 *) (h + 1)))
{
2004-06-06 17:37:54 +08:00
c0 += *p;
}
else
{
c0 += *(b + (p - (u8 *) (h + 1)));
}
c1 += c0;
}
c0 %= 255;
c1 %= 255;
}
x = (int)((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255;
2004-06-06 17:37:54 +08:00
if (x <= 0)
x += 255;
y = 510 - c0 - x;
2004-06-06 17:37:54 +08:00
if (y > 255)
y -= 255;
2004-06-06 17:37:54 +08:00
((u8 *) & h->checksum)[0] = x;
((u8 *) & h->checksum)[1] = y;
return h->checksum;
}
2000-04-03 03:04:23 +08:00
int
lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
/* Return codes from point of view of l1 */
2000-04-03 03:04:23 +08:00
{
2004-06-06 17:37:54 +08:00
u32 sn1, sn2;
2004-06-06 17:37:54 +08:00
sn1 = l1->sn - LSA_INITSEQNO + 1;
sn2 = l2->sn - LSA_INITSEQNO + 1;
2004-06-06 17:37:54 +08:00
if (sn1 > sn2)
return CMP_NEWER;
if (sn1 < sn2)
return CMP_OLDER;
2000-05-30 23:01:51 +08:00
2004-06-06 17:37:54 +08:00
if (l1->checksum != l2->checksum)
return l1->checksum < l2->checksum ? CMP_OLDER : CMP_NEWER;
2000-05-30 23:01:51 +08:00
2004-06-06 17:37:54 +08:00
if ((l1->age == LSA_MAXAGE) && (l2->age != LSA_MAXAGE))
return CMP_NEWER;
if ((l2->age == LSA_MAXAGE) && (l1->age != LSA_MAXAGE))
return CMP_OLDER;
2000-05-30 23:01:51 +08:00
2004-06-06 17:37:54 +08:00
if (ABS(l1->age - l2->age) > LSA_MAXAGEDIFF)
return l1->age < l2->age ? CMP_NEWER : CMP_OLDER;
2000-05-30 23:01:51 +08:00
return CMP_SAME;
2000-04-03 03:04:23 +08:00
}
#define HDRLEN sizeof(struct ospf_lsa_header)
static int
lsa_validate_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body)
{
unsigned int i, max;
if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_rt)))
return 0;
struct ospf_lsa_rt_link *rtl = (struct ospf_lsa_rt_link *) (body + 1);
max = lsa_rt_count(lsa);
#ifdef OSPFv2
if (body->links != max)
return 0;
#endif
for (i = 0; i < max; i++)
{
u8 type = rtl[i].type;
if (!((type == LSART_PTP) ||
(type == LSART_NET) ||
#ifdef OSPFv2
(type == LSART_STUB) ||
#endif
(type == LSART_VLNK)))
return 0;
}
return 1;
}
static int
lsa_validate_net(struct ospf_lsa_header *lsa, struct ospf_lsa_net *body UNUSED)
{
if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_net)))
return 0;
return 1;
}
#ifdef OSPFv2
static int
lsa_validate_sum(struct ospf_lsa_header *lsa, struct ospf_lsa_sum *body)
{
if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum)))
return 0;
/* First field should have TOS = 0, we ignore other TOS fields */
if ((body->metric & LSA_SUM_TOS) != 0)
return 0;
return 1;
}
#define lsa_validate_sum_net(A,B) lsa_validate_sum(A,B)
#define lsa_validate_sum_rt(A,B) lsa_validate_sum(A,B)
static int
lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body)
{
if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext)))
return 0;
/* First field should have TOS = 0, we ignore other TOS fields */
if ((body->metric & LSA_EXT_TOS) != 0)
return 0;
return 1;
}
#else /* OSPFv3 */
static inline int
pxlen(u32 *buf)
{
return *buf >> 24;
}
static int
lsa_validate_sum_net(struct ospf_lsa_header *lsa, struct ospf_lsa_sum_net *body)
{
if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_sum_net) + 4))
return 0;
u8 pxl = pxlen(body->prefix);
if (pxl > MAX_PREFIX_LENGTH)
return 0;
if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum_net) +
IPV6_PREFIX_SPACE(pxl)))
return 0;
return 1;
}
static int
lsa_validate_sum_rt(struct ospf_lsa_header *lsa, struct ospf_lsa_sum_rt *body)
{
if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_sum_rt)))
return 0;
return 1;
}
static int
lsa_validate_ext(struct ospf_lsa_header *lsa, struct ospf_lsa_ext *body)
{
if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext) + 4))
return 0;
u8 pxl = pxlen(body->rest);
if (pxl > MAX_PREFIX_LENGTH)
return 0;
int len = IPV6_PREFIX_SPACE(pxl);
if (body->metric & LSA_EXT_FBIT) // forwardinf address
len += 16;
if (body->metric & LSA_EXT_TBIT) // route tag
len += 4;
if (*body->rest & 0xFFFF) // referenced LS type field
len += 4;
if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_ext) + len))
return 0;
return 1;
}
static int
lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, unsigned int offset, u8 *pbuf)
{
unsigned int bound = lsa->length - HDRLEN - 4;
u32 i;
for (i = 0; i < pxcount; i++)
{
if (offset > bound)
return 0;
u8 pxl = pxlen((u32 *) (pbuf + offset));
if (pxl > MAX_PREFIX_LENGTH)
return 0;
offset += IPV6_PREFIX_SPACE(pxl);
}
if (lsa->length != (HDRLEN + offset))
return 0;
return 1;
}
static int
lsa_validate_link(struct ospf_lsa_header *lsa, struct ospf_lsa_link *body)
{
if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_link)))
return 0;
return lsa_validate_pxlist(lsa, body->pxcount, sizeof(struct ospf_lsa_link), (u8 *) body);
}
static int
lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body)
{
if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_prefix)))
return 0;
return lsa_validate_pxlist(lsa, body->pxcount, sizeof(struct ospf_lsa_prefix), (u8 *) body);
}
#endif
/**
* lsa_validate - check whether given LSA is valid
* @lsa: LSA header
* @body: pointer to LSA body
*
* Checks internal structure of given LSA body (minimal length,
* consistency). Returns true if valid.
*/
int
lsa_validate(struct ospf_lsa_header *lsa, void *body)
{
switch (lsa->type)
{
case LSA_T_RT:
return lsa_validate_rt(lsa, body);
case LSA_T_NET:
return lsa_validate_net(lsa, body);
case LSA_T_SUM_NET:
return lsa_validate_sum_net(lsa, body);
case LSA_T_SUM_RT:
return lsa_validate_sum_rt(lsa, body);
case LSA_T_EXT:
2011-07-21 05:40:20 +08:00
case LSA_T_NSSA:
return lsa_validate_ext(lsa, body);
#ifdef OSPFv3
case LSA_T_LINK:
return lsa_validate_link(lsa, body);
case LSA_T_PREFIX:
return lsa_validate_prefix(lsa, body);
#endif
default:
/* In OSPFv3, unknown LSAs are OK,
In OSPFv2, unknown LSAs are already rejected
*/
return 1;
}
}
/**
* lsa_install_new - install new LSA into database
2009-08-21 15:27:52 +08:00
* @po: OSPF protocol
* @lsa: LSA header
2009-08-21 15:27:52 +08:00
* @domain: domain of LSA
2000-06-08 20:42:34 +08:00
* @body: pointer to LSA body
2009-08-21 15:27:52 +08:00
*
* This function ensures installing new LSA into LSA database. Old instance is
2002-04-03 23:41:05 +08:00
* 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 *
2009-08-21 15:27:52 +08:00
lsa_install_new(struct proto_ospf *po, struct ospf_lsa_header *lsa, u32 domain, void *body)
{
/* LSA can be temporarrily, but body must be mb_allocated. */
2004-06-06 17:37:54 +08:00
int change = 0;
struct top_hash_entry *en;
2009-08-25 22:42:14 +08:00
if ((en = ospf_hash_find_header(po->gr, domain, lsa)) == NULL)
{
2009-08-25 22:42:14 +08:00
en = ospf_hash_get_header(po->gr, domain, lsa);
2004-06-06 17:37:54 +08:00
change = 1;
}
else
{
2009-08-21 15:27:52 +08:00
if ((en->lsa.length != lsa->length)
#ifdef OSPFv2
|| (en->lsa.options != lsa->options)
#endif
|| (en->lsa.age == LSA_MAXAGE)
|| (lsa->age == LSA_MAXAGE)
|| memcmp(en->lsa_body, body, lsa->length - sizeof(struct ospf_lsa_header)))
2004-06-06 17:37:54 +08:00
change = 1;
2009-08-21 15:27:52 +08:00
s_rem_node(SNODE en);
}
2009-07-23 22:51:28 +08:00
DBG("Inst lsa: Id: %R, Rt: %R, 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);
2004-06-06 17:37:54 +08:00
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);
2004-06-06 17:37:54 +08:00
return en;
}