On Fri, 2 Feb 2024 15:11:34 +0100 Laurent Vivier <lvivier(a)redhat.com> wrote:The TCP and UDP checksums are computed using the data in the TCP/UDP payload but also some informations in the IP header (protocol, length, source and destination addresses). We add two functions, proto_ipv4_header_checksum() and proto_ipv6_header_checksum(), to compute the checksum of the IP header part. Signed-off-by: Laurent Vivier <lvivier(a)redhat.com> --- ip.h | 24 ++++++++++++++++++++++++ tcp.c | 40 +++++++++++++++------------------------- udp.c | 6 ++---- 3 files changed, 41 insertions(+), 29 deletions(-) diff --git a/ip.h b/ip.h index ff7902c45a95..87cb8dd21d2e 100644 --- a/ip.h +++ b/ip.h @@ -97,4 +97,28 @@ static inline uint16_t ipv4_hdr_checksum(struct iphdr *iph, int proto) return ~csum_fold(sum); } + +static inline uint32_t proto_ipv4_header_checksum(struct iphdr *iph, int proto)Function comments, proto can be uint8_t for consistency, and probably all these don't need to be inlines. Interesting readings in case you haven't come across them: https://lwn.net/Kernel/Index/#Inline_functions https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Doc…+{ + uint32_t sum = htons(proto); + + sum += (iph->saddr >> 16) & 0xffff; + sum += iph->saddr & 0xffff; + sum += (iph->daddr >> 16) & 0xffff; + sum += iph->daddr & 0xffff; + sum += htons(ntohs(iph->tot_len) - 20); + + return sum; +} + +static inline uint32_t proto_ipv6_header_checksum(struct ipv6hdr *ip6h, + int proto) +{ + uint32_t sum = htons(proto) + ip6h->payload_len; + + sum += sum_16b(&ip6h->saddr, sizeof(ip6h->saddr)); + sum += sum_16b(&ip6h->daddr, sizeof(ip6h->daddr)); + + return sum; +} #endif /* IP_H */ diff --git a/tcp.c b/tcp.c index 293ab12d8c21..2fd6bc2eda53 100644 --- a/tcp.c +++ b/tcp.c @@ -938,39 +938,25 @@ static void tcp_sock_set_bufsize(const struct ctx *c, int s) * tcp_update_check_tcp4() - Update TCP checksum from stored one * @buf: L2 packet buffer with final IPv4 header */ -static void tcp_update_check_tcp4(struct tcp4_l2_buf_t *buf) +static uint16_t tcp_update_check_tcp4(struct iphdr *iph) { - uint16_t tlen = ntohs(buf->iph.tot_len) - 20; - uint32_t sum = htons(IPPROTO_TCP); + struct tcphdr *th = (void *)(iph + 1); + uint16_t tlen = ntohs(iph->tot_len) - 20; + uint32_t sum = proto_ipv4_header_checksum(iph, IPPROTO_TCP); - sum += (buf->iph.saddr >> 16) & 0xffff; - sum += buf->iph.saddr & 0xffff; - sum += (buf->iph.daddr >> 16) & 0xffff; - sum += buf->iph.daddr & 0xffff; - sum += htons(ntohs(buf->iph.tot_len) - 20); - - buf->th.check = 0; - buf->th.check = csum(&buf->th, tlen, sum); + return csum(th, tlen, sum); } /** * tcp_update_check_tcp6() - Calculate TCP checksum for IPv6 * @buf: L2 packet buffer with final IPv6 header */ -static void tcp_update_check_tcp6(struct tcp6_l2_buf_t *buf) +static uint16_t tcp_update_check_tcp6(struct ipv6hdr *ip6h) { - int len = ntohs(buf->ip6h.payload_len) + sizeof(struct ipv6hdr); - - buf->ip6h.hop_limit = IPPROTO_TCP; - buf->ip6h.version = 0; - buf->ip6h.nexthdr = 0; + struct tcphdr *th = (void *)(ip6h + 1); + uint32_t sum = proto_ipv6_header_checksum(ip6h, IPPROTO_TCP); - buf->th.check = 0; - buf->th.check = csum(&buf->ip6h, len, 0); - - buf->ip6h.hop_limit = 255; - buf->ip6h.version = 6; - buf->ip6h.nexthdr = IPPROTO_TCP; + return csum(th, ntohs(ip6h->payload_len), sum); } /** @@ -1381,7 +1367,7 @@ do { \ SET_TCP_HEADER_COMMON_V4_V6(b, conn, seq); - tcp_update_check_tcp4(b); + b->th.check = tcp_update_check_tcp4(&b->iph); tlen = tap_iov_len(c, &b->taph, ip_len); } else { @@ -1400,7 +1386,11 @@ do { \ SET_TCP_HEADER_COMMON_V4_V6(b, conn, seq); - tcp_update_check_tcp6(b); + b->th.check = tcp_update_check_tcp6(&b->ip6h); + + b->ip6h.hop_limit = 255; + b->ip6h.version = 6; + b->ip6h.nexthdr = IPPROTO_TCP; b->ip6h.flow_lbl[0] = (conn->sock >> 16) & 0xf; b->ip6h.flow_lbl[1] = (conn->sock >> 8) & 0xff; diff --git a/udp.c b/udp.c index 6f867df81c05..96b4e6ca9a85 100644 --- a/udp.c +++ b/udp.c @@ -669,10 +669,8 @@ static size_t udp_update_hdr6(const struct ctx *c, int n, in_port_t dstport, b->uh.source = b->s_in6.sin6_port; b->uh.dest = htons(dstport); b->uh.len = b->ip6h.payload_len; - - b->ip6h.hop_limit = IPPROTO_UDP; - b->ip6h.version = b->ip6h.nexthdr = b->uh.check = 0; - b->uh.check = csum(&b->ip6h, ip_len, 0); + b->uh.check = csum(&b->uh, ntohs(b->ip6h.payload_len), + proto_ipv6_header_checksum(&b->ip6h, IPPROTO_UDP)); b->ip6h.version = 6; b->ip6h.nexthdr = IPPROTO_UDP; b->ip6h.hop_limit = 255;-- Stefano