From 77edab640925e8d1131f0d00b41de055129ff99f Mon Sep 17 00:00:00 2001 From: Ondrej Zajicek Date: Fri, 1 May 2015 14:40:56 +0200 Subject: [PATCH] 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. --- lib/fletcher16.h | 196 ++++++++++++++++++++++++++++++++++++++++++ proto/ospf/lsalib.c | 149 +++++--------------------------- proto/ospf/lsalib.h | 4 +- proto/ospf/lsupd.c | 4 +- proto/ospf/topology.c | 6 +- 5 files changed, 226 insertions(+), 133 deletions(-) create mode 100644 lib/fletcher16.h diff --git a/lib/fletcher16.h b/lib/fletcher16.h new file mode 100644 index 00000000..0b245a71 --- /dev/null +++ b/lib/fletcher16.h @@ -0,0 +1,196 @@ +/* + * BIRD Library -- Fletcher-16 checksum + * + * (c) 2015 Ondrej Zajicek + * (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 diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index ce6fb178..66a3a23d 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -2,14 +2,15 @@ * BIRD -- OSPF * * (c) 1999--2004 Ondrej Filip - * (c) 2009--2014 Ondrej Zajicek - * (c) 2009--2014 CZ.NIC z.s.p.o. + * (c) 2009--2015 Ondrej Zajicek + * (c) 2009--2015 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ #include "ospf.h" +#include "lib/fletcher16.h" #ifndef CPU_BIG_ENDIAN void @@ -150,145 +151,41 @@ lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain) } - -/* 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]; - 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); -} -*/ - -#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)); + struct fletcher16_context ctx; + struct ospf_lsa_header hdr; + u16 len = lsa->length; /* - 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); - */ + * lsa and body are in the host order, we need to compute Fletcher-16 checksum + * for data in the network order. We also skip the initial age field. + */ - (void) lsasum_check(h, body, 1); + lsa_hton_hdr(lsa, &hdr); + hdr.checksum = 0; - // log(L_WARN "Checksum result %4x", h->checksum); - - lsa_ntoh_hdr(h, h); - lsa_ntoh_body1(body, length - sizeof(struct ospf_lsa_header)); + fletcher16_init(&ctx); + fletcher16_update(&ctx, (u8 *) &hdr + 2, sizeof(struct ospf_lsa_header) - 2); + fletcher16_update_n32(&ctx, body, len - 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 -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; - int c0 = 0, c1 = 0; - int x, y; - u16 length; + struct fletcher16_context ctx; - b = body; - sp = (char *) h; - sp += 2; /* Skip Age field */ - length = ntohs(h->length) - 2; - if (update) h->checksum = 0; + /* The whole LSA is at lsa_n in net order, we just skip initial age field */ - 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 - (u8 *) (h + 1))); - } + fletcher16_init(&ctx); + fletcher16_update(&ctx, (u8 *) lsa_n + 2, lsa_len - 2); - c1 += c0; - } - 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; + return fletcher16_compute(&ctx) == 0; } + int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2) /* Return codes from point of view of l1 */ diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h index 4ad770e8..ae6af044 100644 --- a/proto/ospf/lsalib.h +++ b/proto/ospf/lsalib.h @@ -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); +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_SAME 0 #define CMP_OLDER -1 diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 6e7dce26..c6a734ca 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -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", lsa_type, lsa.id, lsa.rt, lsa.sn, lsa.age, lsa.checksum); - /* RFC 2328 13. (1) - validate LSA checksum */ - if ((lsa_n->checksum == 0) || (lsasum_check(lsa_n, NULL, 0) != 0)) + /* RFC 2328 13. (1) - verify LSA checksum */ + if ((lsa_n->checksum == 0) || !lsa_verify_checksum(lsa_n, lsa_len)) SKIP("invalid checksum"); /* RFC 2328 13. (2) */ diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 0613d34d..8119cfa6 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -129,7 +129,7 @@ ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_ls en->lsa.age = 0; en->init_age = 0; 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", 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->init_age = 0; 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", 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->init_age = 0; 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); }