net: Add IP packet checksum calculation utilities

Added function that is used when calculating various IP
protocol checksums. The function works with fragmented
net_buf data.

Change-Id: Icaef707ba15ac2729608929e52e235e6e8a154dc
Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2016-05-19 12:31:36 +03:00
commit 1a9421886e
2 changed files with 108 additions and 1 deletions

View file

@ -31,6 +31,7 @@ extern char *net_sprint_ll_addr_buf(uint8_t *ll, uint8_t ll_len,
char *buf, int buflen); char *buf, int buflen);
extern char *net_sprint_ip_addr_buf(uint8_t *ip, int ip_len, extern char *net_sprint_ip_addr_buf(uint8_t *ip, int ip_len,
char *buf, int buflen); char *buf, int buflen);
extern uint16_t net_calc_chksum(struct net_buf *buf, uint8_t proto);
#if NET_DEBUG > 0 #if NET_DEBUG > 0
static inline char *net_sprint_ll_addr(uint8_t *ll, uint8_t ll_len) static inline char *net_sprint_ll_addr(uint8_t *ll, uint8_t ll_len)

View file

@ -22,7 +22,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <misc/byteorder.h> #include <net/net_ip.h>
#include <net/nbuf.h>
#include <net/net_core.h>
char *net_byte_to_hex(uint8_t *ptr, uint8_t byte, char base, bool pad) char *net_byte_to_hex(uint8_t *ptr, uint8_t byte, char base, bool pad)
{ {
@ -228,3 +230,107 @@ char *net_sprint_ip_addr_buf(uint8_t *ip, int ip_len, char *buf, int buflen)
return buf; return buf;
} }
static uint16_t calc_chksum(uint16_t sum, const uint8_t *ptr, uint16_t len)
{
uint16_t tmp;
const uint8_t *end;
end = ptr + len - 1;
while (ptr < end) {
tmp = (ptr[0] << 8) + ptr[1];
sum += tmp;
if (sum < tmp) {
sum++;
}
ptr += 2;
}
if (ptr == end) {
tmp = ptr[0] << 8;
sum += tmp;
if (sum < tmp) {
sum++;
}
}
return sum;
}
static inline uint16_t calc_chksum_buf(uint16_t sum, struct net_buf *buf,
uint16_t upper_layer_len)
{
struct net_buf *frag = buf->frags;
uint16_t proto_len = net_nbuf_ip_hdr_len(buf) + net_nbuf_ext_len(buf) +
net_nbuf_ll_reserve(buf);
int16_t len = frag->len - proto_len;
uint8_t *ptr = frag->data + proto_len;
if (len < 0) {
NET_DBG("1st fragment len %u < IP header len %u",
frag->len, proto_len);
return 0;
}
while (frag) {
sum = calc_chksum(sum, ptr, len);
frag = frag->frags;
if (!frag) {
break;
}
ptr = frag->data;
/* Do we need to take first byte from next fragment */
if (len % 2) {
uint16_t tmp = *ptr;
sum += tmp;
if (sum < tmp) {
sum++;
}
len = frag->len - 1;
ptr++;
} else {
len = frag->len;
}
}
return sum;
}
uint16_t net_calc_chksum(struct net_buf *buf, uint8_t proto)
{
uint16_t upper_layer_len;
uint16_t sum;
switch (net_nbuf_family(buf)) {
#if defined(CONFIG_NET_IPV4)
case AF_INET:
upper_layer_len = (NET_IPV4_BUF(buf)->len[0] << 8) +
NET_IPV4_BUF(buf)->len[1] - net_nbuf_ext_len(buf);
sum = calc_chksum(upper_layer_len + proto,
(uint8_t *)&NET_IPV4_BUF(buf)->src,
2 * sizeof(struct in_addr));
break;
#endif
#if defined(CONFIG_NET_IPV6)
case AF_INET6:
upper_layer_len = (NET_IPV6_BUF(buf)->len[0] << 8) +
NET_IPV6_BUF(buf)->len[1] - net_nbuf_ext_len(buf);
sum = calc_chksum(upper_layer_len + proto,
(uint8_t *)&NET_IPV6_BUF(buf)->src,
2 * sizeof(struct in6_addr));
break;
#endif
default:
NET_DBG("Unknown protocol family %d", net_nbuf_family(buf));
return 0;
}
sum = calc_chksum_buf(sum, buf, upper_layer_len);
sum = (sum == 0) ? 0xffff : htons(sum);
return sum;
}