From 835a2d8a27f43c01521101458fde8c2ea3d88b3a Mon Sep 17 00:00:00 2001 From: Ken Bannister Date: Wed, 25 Nov 2015 23:33:28 -0500 Subject: [PATCH] Add inet_csum_slice() to fix checksum for a sliced layer 4 payload Padding for an odd number of bytes was not calculated properly. --- sys/include/net/inet_csum.h | 39 +++++++++++++++++++++--- sys/net/crosslayer/inet_csum/inet_csum.c | 17 ++++++++--- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/sys/include/net/inet_csum.h b/sys/include/net/inet_csum.h index 53134053f6..6356e96998 100644 --- a/sys/include/net/inet_csum.h +++ b/sys/include/net/inet_csum.h @@ -21,13 +21,15 @@ #define INET_CSUM_H_ #include +#include #ifdef __cplusplus extern "C" { #endif /** - * @brief Calculates the unnormalized Internet Checksum of @p buf. + * @brief Calculates the unnormalized Internet Checksum of @p buf, where the + * buffer provides a slice of the full checksum domain, calculated in order. * * @see * RFC 1071 @@ -35,14 +37,41 @@ extern "C" { * * @details The Internet Checksum is not normalized (i. e. its 1's complement * was not taken of the result) to use it for further calculation. + * This function handles padding an odd number of bytes across the full domain. * - * @param[in] sum An initial value for the checksum. - * @param[in] buf A buffer. - * @param[in] len Length of @p buf in byte. + * @param[in] sum An initial value for the checksum. + * @param[in] buf A buffer. + * @param[in] len Length of @p buf in byte. + * @param[in] accum_len Accumulated length of checksum domain that has already + * been checksummed. * * @return The unnormalized Internet Checksum of @p buf. */ -uint16_t inet_csum(uint16_t sum, const uint8_t *buf, uint16_t len); +uint16_t inet_csum_slice(uint16_t sum, const uint8_t *buf, uint16_t len, size_t accum_len); + +/** + * @brief Calculates the unnormalized Internet Checksum of @p buf, where the + * buffer provides a standalone domain for the checksum. + * + * @see + * RFC 1071 + * + * + * @details The Internet Checksum is not normalized (i. e. its 1's complement + * was not taken of the result) to use it for further calculation. + * This function, rather than inet_csum_slice(), has been used historically + * when we are not concerned with padding for an odd number of bytes. + * + * + * @param[in] sum An initial value for the checksum. + * @param[in] buf A buffer. + * @param[in] len Length of @p buf in byte. + * + * @return The unnormalized Internet Checksum of @p buf. + */ +static inline uint16_t inet_csum(uint16_t sum, const uint8_t *buf, uint16_t len) { + return inet_csum_slice(sum, buf, len, 0); +} #ifdef __cplusplus } diff --git a/sys/net/crosslayer/inet_csum/inet_csum.c b/sys/net/crosslayer/inet_csum/inet_csum.c index c53c025d60..4a125fab7f 100644 --- a/sys/net/crosslayer/inet_csum/inet_csum.c +++ b/sys/net/crosslayer/inet_csum/inet_csum.c @@ -20,7 +20,7 @@ #define ENABLE_DEBUG (0) #include "debug.h" -uint16_t inet_csum(uint16_t sum, const uint8_t *buf, uint16_t len) +uint16_t inet_csum_slice(uint16_t sum, const uint8_t *buf, uint16_t len, size_t accum_len) { uint32_t csum = sum; @@ -34,14 +34,23 @@ uint16_t inet_csum(uint16_t sum, const uint8_t *buf, uint16_t len) #endif #endif + if (len == 0) + return csum; + + if (accum_len & 1) { /* if accumulated length is odd */ + csum += *buf; /* add first byte as bottom half of 16-byte word */ + buf++; + len--; + accum_len++; + } + for (int i = 0; i < (len >> 1); buf += 2, i++) { csum += (*buf << 8) + *(buf + 1); /* group bytes by 16-byte words * and add them*/ } - if (len & 1) { /* if len is odd */ - csum += (*buf << 8); /* add last byte as top half of 16-byte word */ - } + if ((accum_len + len) & 1) /* if accumulated length is odd */ + csum += (*buf << 8); /* add last byte as top half of 16-byte word */ while (csum >> 16) { uint16_t carry = csum >> 16;