OSPF: Redesign LSA checksumming
New LSA checksumming code separates generic Fletcher-16 and OSPF-specific code and avoids back and forth endianity conversions, making it much more readable and also several times faster.
This commit is contained in:
parent
30d09eb96e
commit
77edab6409
5 changed files with 226 additions and 133 deletions
196
lib/fletcher16.h
Normal file
196
lib/fletcher16.h
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
/*
|
||||||
|
* BIRD Library -- Fletcher-16 checksum
|
||||||
|
*
|
||||||
|
* (c) 2015 Ondrej Zajicek <santiago@crfreenet.org>
|
||||||
|
* (c) 2015 CZ.NIC z.s.p.o.
|
||||||
|
*
|
||||||
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DOC: Fletcher-16 checksum
|
||||||
|
*
|
||||||
|
* Fletcher-16 checksum is a position-dependent checksum algorithm used for
|
||||||
|
* error-detection e.g. in OSPF LSAs.
|
||||||
|
*
|
||||||
|
* To generate Fletcher-16 checksum, zero the checksum field in data, initialize
|
||||||
|
* the context by fletcher16_init(), process the data by fletcher16_update(),
|
||||||
|
* compute the checksum value by fletcher16_final() and store it to the checksum
|
||||||
|
* field in data by put_u16() (or other means involving htons() conversion).
|
||||||
|
*
|
||||||
|
* To verify Fletcher-16 checksum, initialize the context by fletcher16_init(),
|
||||||
|
* process the data by fletcher16_update(), compute a passing checksum by
|
||||||
|
* fletcher16_compute() and check if it is zero.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _BIRD_FLETCHER16_H_
|
||||||
|
#define _BIRD_FLETCHER16_H_
|
||||||
|
|
||||||
|
#include "nest/bird.h"
|
||||||
|
|
||||||
|
|
||||||
|
struct fletcher16_context
|
||||||
|
{
|
||||||
|
int c0, c1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fletcher16_init - initialize Fletcher-16 context
|
||||||
|
* @ctx: the context
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
fletcher16_init(struct fletcher16_context *ctx)
|
||||||
|
{
|
||||||
|
ctx->c0 = ctx->c1 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fletcher16_update - process data to Fletcher-16 context
|
||||||
|
* @ctx: the context
|
||||||
|
* @buf: data buffer
|
||||||
|
* @len: data length
|
||||||
|
*
|
||||||
|
* fletcher16_update() reads data from the buffer @buf and updates passing sums
|
||||||
|
* in the context @ctx. It may be used multiple times for multiple blocks of
|
||||||
|
* checksummed data.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
fletcher16_update(struct fletcher16_context *ctx, const u8* buf, int len)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The Fletcher-16 sum is essentially a sequence of
|
||||||
|
* ctx->c1 += ctx->c0 += *buf++, modulo 255.
|
||||||
|
*
|
||||||
|
* In the inner loop, we eliminate modulo operation and we do some loop
|
||||||
|
* unrolling. MODX is the maximal number of steps that can be done without
|
||||||
|
* modulo before overflow, see RFC 1008 for details. We use a bit smaller
|
||||||
|
* value to cover for initial steps due to loop unrolling.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MODX 4096
|
||||||
|
|
||||||
|
int blen, i;
|
||||||
|
|
||||||
|
blen = len % 4;
|
||||||
|
len -= blen;
|
||||||
|
|
||||||
|
for (i = 0; i < blen; i++)
|
||||||
|
ctx->c1 += ctx->c0 += *buf++;
|
||||||
|
|
||||||
|
do {
|
||||||
|
blen = MIN(len, MODX);
|
||||||
|
len -= blen;
|
||||||
|
|
||||||
|
for (i = 0; i < blen; i += 4)
|
||||||
|
{
|
||||||
|
ctx->c1 += ctx->c0 += *buf++;
|
||||||
|
ctx->c1 += ctx->c0 += *buf++;
|
||||||
|
ctx->c1 += ctx->c0 += *buf++;
|
||||||
|
ctx->c1 += ctx->c0 += *buf++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->c0 %= 255;
|
||||||
|
ctx->c1 %= 255;
|
||||||
|
|
||||||
|
} while (len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fletcher16_update_n32 - process data to Fletcher-16 context, with endianity adjustment
|
||||||
|
* @ctx: the context
|
||||||
|
* @buf: data buffer
|
||||||
|
* @len: data length
|
||||||
|
*
|
||||||
|
* fletcher16_update_n32() works like fletcher16_update(), except it applies
|
||||||
|
* 32-bit host/network endianity swap to the data before they are processed.
|
||||||
|
* I.e., it assumes that the data is a sequence of u32 that must be converted by
|
||||||
|
* ntohl() or htonl() before processing. The @buf need not to be aligned, but
|
||||||
|
* its length (@len) must be multiple of 4. Note that on big endian systems the
|
||||||
|
* host endianity is the same as the network endianity, therefore there is no
|
||||||
|
* endianity swap.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
fletcher16_update_n32(struct fletcher16_context *ctx, const u8* buf, int len)
|
||||||
|
{
|
||||||
|
/* See fletcher16_update() for details */
|
||||||
|
|
||||||
|
int blen, i;
|
||||||
|
|
||||||
|
do {
|
||||||
|
blen = MIN(len, MODX);
|
||||||
|
len -= blen;
|
||||||
|
|
||||||
|
for (i = 0; i < blen; i += 4)
|
||||||
|
{
|
||||||
|
#ifdef CPU_BIG_ENDIAN
|
||||||
|
ctx->c1 += ctx->c0 += *buf++;
|
||||||
|
ctx->c1 += ctx->c0 += *buf++;
|
||||||
|
ctx->c1 += ctx->c0 += *buf++;
|
||||||
|
ctx->c1 += ctx->c0 += *buf++;
|
||||||
|
#else
|
||||||
|
ctx->c1 += ctx->c0 += buf[3];
|
||||||
|
ctx->c1 += ctx->c0 += buf[2];
|
||||||
|
ctx->c1 += ctx->c0 += buf[1];
|
||||||
|
ctx->c1 += ctx->c0 += buf[0];
|
||||||
|
buf += 4;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->c0 %= 255;
|
||||||
|
ctx->c1 %= 255;
|
||||||
|
|
||||||
|
} while (len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fletcher16_final - compute final Fletcher-16 checksum value
|
||||||
|
* @ctx: the context
|
||||||
|
* @len: total data length
|
||||||
|
* @pos: offset in data where the checksum will be stored
|
||||||
|
*
|
||||||
|
* fletcher16_final() computes the final checksum value and returns it.
|
||||||
|
* The caller is responsible for storing it in the appropriate position.
|
||||||
|
* The checksum value depends on @len and @pos, but only their difference
|
||||||
|
* (i.e. the offset from the end) is significant.
|
||||||
|
*
|
||||||
|
* The checksum value is represented as u16, although it is defined as two
|
||||||
|
* consecutive bytes. We treat them as one u16 in big endian / network order.
|
||||||
|
* I.e., the returned value is in the form that would be returned by get_u16()
|
||||||
|
* from the checksum field in the data buffer, therefore the caller should use
|
||||||
|
* put_u16() or an explicit host-to-network conversion when storing it to the
|
||||||
|
* checksum field in the data buffer.
|
||||||
|
*
|
||||||
|
* Note that the returned checksum value is always nonzero.
|
||||||
|
*/
|
||||||
|
static inline u16
|
||||||
|
fletcher16_final(struct fletcher16_context *ctx, int len, int pos)
|
||||||
|
{
|
||||||
|
int x = ((len - pos - 1) * ctx->c0 - ctx->c1) % 255;
|
||||||
|
if (x <= 0)
|
||||||
|
x += 255;
|
||||||
|
|
||||||
|
int y = 510 - ctx->c0 - x;
|
||||||
|
if (y > 255)
|
||||||
|
y -= 255;
|
||||||
|
|
||||||
|
return (x << 8) | y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fletcher16_compute - compute Fletcher-16 sum for verification
|
||||||
|
* @ctx: the context
|
||||||
|
*
|
||||||
|
* fletcher16_compute() returns a passing Fletcher-16 sum for processed data.
|
||||||
|
* If the data contains the proper Fletcher-16 checksum value, the returned
|
||||||
|
* value is zero.
|
||||||
|
*/
|
||||||
|
static inline u16
|
||||||
|
fletcher16_compute(struct fletcher16_context *ctx)
|
||||||
|
{
|
||||||
|
return (ctx->c0 << 8) | ctx->c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,14 +2,15 @@
|
||||||
* BIRD -- OSPF
|
* BIRD -- OSPF
|
||||||
*
|
*
|
||||||
* (c) 1999--2004 Ondrej Filip <feela@network.cz>
|
* (c) 1999--2004 Ondrej Filip <feela@network.cz>
|
||||||
* (c) 2009--2014 Ondrej Zajicek <santiago@crfreenet.org>
|
* (c) 2009--2015 Ondrej Zajicek <santiago@crfreenet.org>
|
||||||
* (c) 2009--2014 CZ.NIC z.s.p.o.
|
* (c) 2009--2015 CZ.NIC z.s.p.o.
|
||||||
*
|
*
|
||||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ospf.h"
|
#include "ospf.h"
|
||||||
|
|
||||||
|
#include "lib/fletcher16.h"
|
||||||
|
|
||||||
#ifndef CPU_BIG_ENDIAN
|
#ifndef CPU_BIG_ENDIAN
|
||||||
void
|
void
|
||||||
|
@ -150,145 +151,41 @@ lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
void
|
void
|
||||||
buf_dump(const char *hdr, const byte *buf, int blen)
|
lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body)
|
||||||
{
|
{
|
||||||
char b2[1024];
|
struct fletcher16_context ctx;
|
||||||
char *bp;
|
struct ospf_lsa_header hdr;
|
||||||
int first = 1;
|
u16 len = lsa->length;
|
||||||
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);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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 = h->length;
|
|
||||||
|
|
||||||
// log(L_WARN "Checksum %R %R %d start (len %d)", h->id, h->rt, h->type, length);
|
|
||||||
lsa_hton_hdr(h, h);
|
|
||||||
lsa_hton_body1(body, length - sizeof(struct ospf_lsa_header));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
char buf[1024];
|
* lsa and body are in the host order, we need to compute Fletcher-16 checksum
|
||||||
memcpy(buf, h, sizeof(struct ospf_lsa_header));
|
* for data in the network order. We also skip the initial age field.
|
||||||
memcpy(buf + sizeof(struct ospf_lsa_header), body, length - sizeof(struct ospf_lsa_header));
|
*/
|
||||||
buf_dump("CALC", buf, length);
|
|
||||||
*/
|
|
||||||
|
|
||||||
(void) lsasum_check(h, body, 1);
|
lsa_hton_hdr(lsa, &hdr);
|
||||||
|
hdr.checksum = 0;
|
||||||
|
|
||||||
// log(L_WARN "Checksum result %4x", h->checksum);
|
fletcher16_init(&ctx);
|
||||||
|
fletcher16_update(&ctx, (u8 *) &hdr + 2, sizeof(struct ospf_lsa_header) - 2);
|
||||||
lsa_ntoh_hdr(h, h);
|
fletcher16_update_n32(&ctx, body, len - sizeof(struct ospf_lsa_header));
|
||||||
lsa_ntoh_body1(body, length - sizeof(struct ospf_lsa_header));
|
lsa->checksum = fletcher16_final(&ctx, len, OFFSETOF(struct ospf_lsa_header, checksum));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculates the Fletcher checksum of an OSPF LSA.
|
|
||||||
*
|
|
||||||
* If 'update' is non-zero, the checkbytes (X and Y in RFC905) are calculated
|
|
||||||
* and the checksum field in the header is updated. The return value is the
|
|
||||||
* checksum as placed in the header (in network byte order).
|
|
||||||
*
|
|
||||||
* If 'update' is zero, only C0 and C1 are calculated and the header is kept
|
|
||||||
* intact. The return value is a combination of C0 and C1; if the return value
|
|
||||||
* is exactly zero the checksum is considered valid, any non-zero value is
|
|
||||||
* invalid.
|
|
||||||
*
|
|
||||||
* Note that this function expects the input LSA to be in network byte order.
|
|
||||||
*/
|
|
||||||
u16
|
u16
|
||||||
lsasum_check(struct ospf_lsa_header *h, void *body, int update)
|
lsa_verify_checksum(const void *lsa_n, int lsa_len)
|
||||||
{
|
{
|
||||||
u8 *sp, *ep, *p, *q, *b;
|
struct fletcher16_context ctx;
|
||||||
int c0 = 0, c1 = 0;
|
|
||||||
int x, y;
|
|
||||||
u16 length;
|
|
||||||
|
|
||||||
b = body;
|
/* The whole LSA is at lsa_n in net order, we just skip initial age field */
|
||||||
sp = (char *) h;
|
|
||||||
sp += 2; /* Skip Age field */
|
|
||||||
length = ntohs(h->length) - 2;
|
|
||||||
if (update) h->checksum = 0;
|
|
||||||
|
|
||||||
for (ep = sp + length; sp < ep; sp = q)
|
fletcher16_init(&ctx);
|
||||||
{ /* Actually MODX is very large, do we need the for-cyclus? */
|
fletcher16_update(&ctx, (u8 *) lsa_n + 2, lsa_len - 2);
|
||||||
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 - (u8 *) (h + 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
c1 += c0;
|
return fletcher16_compute(&ctx) == 0;
|
||||||
}
|
|
||||||
c0 %= 255;
|
|
||||||
c1 %= 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!update) {
|
|
||||||
/*
|
|
||||||
* When testing the checksum, we don't need to calculate x and y. The
|
|
||||||
* checksum passes if c0 and c1 are both 0.
|
|
||||||
*/
|
|
||||||
return (c0 << 8) | (c1 & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
x = (int)((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
|
int
|
||||||
lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
|
lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
|
||||||
/* Return codes from point of view of l1 */
|
/* Return codes from point of view of l1 */
|
||||||
|
|
|
@ -46,9 +46,9 @@ static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
|
||||||
|
|
||||||
|
|
||||||
int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
|
int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
|
||||||
|
void lsa_generate_checksum(struct ospf_lsa_header *lsa, const u8 *body);
|
||||||
|
u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
|
||||||
|
|
||||||
void lsasum_calculate(struct ospf_lsa_header *header, void *body);
|
|
||||||
u16 lsasum_check(struct ospf_lsa_header *h, void *body, int update);
|
|
||||||
#define CMP_NEWER 1
|
#define CMP_NEWER 1
|
||||||
#define CMP_SAME 0
|
#define CMP_SAME 0
|
||||||
#define CMP_OLDER -1
|
#define CMP_OLDER -1
|
||||||
|
|
|
@ -530,8 +530,8 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
|
||||||
DBG("Update Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n",
|
DBG("Update Type: %04x, Id: %R, Rt: %R, Sn: 0x%08x, Age: %u, Sum: %u\n",
|
||||||
lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum);
|
lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum);
|
||||||
|
|
||||||
/* RFC 2328 13. (1) - validate LSA checksum */
|
/* RFC 2328 13. (1) - verify LSA checksum */
|
||||||
if ((lsa_n->checksum == 0) || (lsasum_check(lsa_n, NULL, 0) != 0))
|
if ((lsa_n->checksum == 0) || !lsa_verify_checksum(lsa_n, lsa_len))
|
||||||
SKIP("invalid checksum");
|
SKIP("invalid checksum");
|
||||||
|
|
||||||
/* RFC 2328 13. (2) */
|
/* RFC 2328 13. (2) */
|
||||||
|
|
|
@ -129,7 +129,7 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls
|
||||||
en->lsa.age = 0;
|
en->lsa.age = 0;
|
||||||
en->init_age = 0;
|
en->init_age = 0;
|
||||||
en->inst_time = now;
|
en->inst_time = now;
|
||||||
lsasum_calculate(&en->lsa, en->lsa_body);
|
lsa_generate_checksum(&en->lsa, en->lsa_body);
|
||||||
|
|
||||||
OSPF_TRACE(D_EVENTS, "Advancing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
|
OSPF_TRACE(D_EVENTS, "Advancing LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
|
||||||
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
|
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
|
||||||
|
@ -238,7 +238,7 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
|
||||||
en->lsa.age = 0;
|
en->lsa.age = 0;
|
||||||
en->init_age = 0;
|
en->init_age = 0;
|
||||||
en->inst_time = now;
|
en->inst_time = now;
|
||||||
lsasum_calculate(&en->lsa, en->lsa_body);
|
lsa_generate_checksum(&en->lsa, en->lsa_body);
|
||||||
|
|
||||||
OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
|
OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
|
||||||
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
|
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn);
|
||||||
|
@ -382,7 +382,7 @@ ospf_refresh_lsa(struct ospf_proto *p, struct top_hash_entry *en)
|
||||||
en->lsa.age = 0;
|
en->lsa.age = 0;
|
||||||
en->init_age = 0;
|
en->init_age = 0;
|
||||||
en->inst_time = now;
|
en->inst_time = now;
|
||||||
lsasum_calculate(&en->lsa, en->lsa_body);
|
lsa_generate_checksum(&en->lsa, en->lsa_body);
|
||||||
ospf_flood_lsa(p, en, NULL);
|
ospf_flood_lsa(p, en, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue