diff options
Diffstat (limited to 'lib/checksum.c')
-rw-r--r-- | lib/checksum.c | 96 |
1 files changed, 43 insertions, 53 deletions
diff --git a/lib/checksum.c b/lib/checksum.c index c9a590b9..f6d74d31 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -14,20 +14,27 @@ in_cksum(void *parg, int nbytes) { u_short *ptr = parg; register long sum; /* assumes long == 32 bits */ + u_short oddbyte; register u_short answer; /* assumes u_short == 16 bits */ - register int count; + /* * Our algorithm is simple, using a 32-bit accumulator (sum), * we add sequential 16-bit words to it, and at the end, fold back * all the carry bits from the top 16 bits into the lower 16 bits. */ - sum = 0; - count = nbytes >> 1; /* div by 2 */ - for(ptr--; count; --count) - sum += *++ptr; - if (nbytes & 1) /* Odd */ - sum += *(u_char *)(++ptr); /* one byte only */ + sum = 0; + while (nbytes > 1) { + sum += *ptr++; + nbytes -= 2; + } + + /* mop up an odd byte, if necessary */ + if (nbytes == 1) { + oddbyte = 0; /* make sure top half is zero */ + *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */ + sum += oddbyte; + } /* * Add back carry outs from top 16 bits to low 16 bits. @@ -45,80 +52,63 @@ in_cksum(void *parg, int nbytes) /* To be consistent, offset is 0-based index, rather than the 1-based index required in the specification ISO 8473, Annex C.1 */ u_int16_t -fletcher_checksum(u_char * buffer, int len, u_int16_t offset) +fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset) { u_int8_t *p; - int x; - int y; -#ifdef UNSIGNED_FLETCHER - u_int32_t mul; - u_int32_t c0, c1; -#else - int32_t c0, c1; -#endif + int x, y, c0, c1; + u_int16_t checksum; u_int16_t *csum; - int init_len, partial_len; + size_t partial_len, i, left = len; + + checksum = 0; + + assert (offset < len); /* * Zero the csum in the packet. */ csum = (u_int16_t *) (buffer + offset); - *csum = 0; + *(csum) = 0; - p = buffer - 1; + p = buffer; c0 = 0; c1 = 0; - init_len = len; - while (len != 0) + while (left != 0) { - partial_len = MIN(len, MODX); - len -= partial_len; + partial_len = MIN(left, MODX); - do + for (i = 0; i < partial_len; i++) { - c0 = c0 + *(++p); + c0 = c0 + *(p++); c1 += c0; - } while (--partial_len); + } c0 = c0 % 255; c1 = c1 % 255; - } -#ifdef UNSIGNED_FLETCHER - /* This can do both unsigned and signed c0, c1 and MODX=5802 - * but costs more instructions. - */ - mul = (init_len - offset)*(c0); - x = mul - c0 - c1; - y = c1 - mul - 1; - - x %= 255; - y %= 255; - - if (y > 0) - y++; - if (x < 0) - x--; + left -= partial_len; + } + + /* The cast is important, to ensure the mod is taken as a signed value. */ + x = ((int)(len - offset - 1) * c0 - c1) % 255; - if (x == 0) - x = 255; - if (y == 0) - y = 1; -#else - x = ((init_len - offset - 1) * c0 - c1) % 255; if (x <= 0) x += 255; y = 510 - c0 - x; - if (y > 255) + if (y > 255) y -= 255; -#endif + /* * Now we write this to the packet. * We could skip this step too, since the checksum returned would * be stored into the checksum field by the caller. - * Checksum is always big endian. */ - *csum = htons((x << 8) | (y & 0xFF)); - return *csum; + buffer[offset] = x; + buffer[offset + 1] = y; + + /* Take care of the endian issue */ + checksum = htons((x << 8) | (y & 0xFF)); + + return checksum; } |