diff options
author | Alin Năstac <alin.nastac@gmail.com> | 2016-06-24 14:41:10 +0200 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2016-06-26 16:09:48 +0200 |
commit | 86a2702a00c119d8fd1470784772754bede6888a (patch) | |
tree | dbf7ee03a63910fc4a1e309eab2bb50599987217 | |
parent | e6b93303437cd63d5f30787d5140ced73400e587 (diff) | |
download | upstream-86a2702a00c119d8fd1470784772754bede6888a.tar.gz upstream-86a2702a00c119d8fd1470784772754bede6888a.tar.bz2 upstream-86a2702a00c119d8fd1470784772754bede6888a.zip |
libnetfilter_queue: fix checksum computation
There are 2 issues fixed by this patch:
- UDP checksum is computed incorrectly, the used pseudo IP header
contains transport protocol 6 iso 17
- on big endian arches the UDP/TCP checksum is incorrectly
computed when payload length is odd
Signed-off-by: Alin Nastac <alin.nastac@gmail.com>
Signed-off-by: Felix Fietkau <nbd@nbd.name> [refresh patch]
-rw-r--r-- | package/libs/libnetfilter-queue/patches/100-checksum_computation.patch | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/package/libs/libnetfilter-queue/patches/100-checksum_computation.patch b/package/libs/libnetfilter-queue/patches/100-checksum_computation.patch new file mode 100644 index 0000000000..cbbff827dd --- /dev/null +++ b/package/libs/libnetfilter-queue/patches/100-checksum_computation.patch @@ -0,0 +1,113 @@ +--- a/src/extra/checksum.c ++++ b/src/extra/checksum.c +@@ -11,6 +11,7 @@ + + #include <stdio.h> + #include <stdbool.h> ++#include <endian.h> + #include <arpa/inet.h> + #include <netinet/ip.h> + #include <netinet/ip6.h> +@@ -26,8 +27,13 @@ uint16_t checksum(uint32_t sum, uint16_t + sum += *buf++; + size -= sizeof(uint16_t); + } +- if (size) +- sum += *(uint8_t *)buf; ++ if (size) { ++#if __BYTE_ORDER == __BIG_ENDIAN ++ sum += (uint16_t)*(uint8_t *)buf << 8; ++#else ++ sum += (uint16_t)*(uint8_t *)buf; ++#endif ++ } + + sum = (sum >> 16) + (sum & 0xffff); + sum += (sum >>16); +@@ -35,7 +41,7 @@ uint16_t checksum(uint32_t sum, uint16_t + return (uint16_t)(~sum); + } + +-uint16_t checksum_tcpudp_ipv4(struct iphdr *iph) ++uint16_t checksum_tcpudp_ipv4(struct iphdr *iph, uint16_t protocol_id) + { + uint32_t sum = 0; + uint32_t iph_len = iph->ihl*4; +@@ -46,13 +52,13 @@ uint16_t checksum_tcpudp_ipv4(struct iph + sum += (iph->saddr) & 0xFFFF; + sum += (iph->daddr >> 16) & 0xFFFF; + sum += (iph->daddr) & 0xFFFF; +- sum += htons(IPPROTO_TCP); ++ sum += htons(protocol_id); + sum += htons(len); + + return checksum(sum, (uint16_t *)payload, len); + } + +-uint16_t checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr) ++uint16_t checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr, uint16_t protocol_id) + { + uint32_t sum = 0; + uint32_t hdr_len = (uint32_t *)transport_hdr - (uint32_t *)ip6h; +@@ -68,7 +74,7 @@ uint16_t checksum_tcpudp_ipv6(struct ip6 + sum += (ip6h->ip6_dst.s6_addr16[i] >> 16) & 0xFFFF; + sum += (ip6h->ip6_dst.s6_addr16[i]) & 0xFFFF; + } +- sum += htons(IPPROTO_TCP); ++ sum += htons(protocol_id); + sum += htons(ip6h->ip6_plen); + + return checksum(sum, (uint16_t *)payload, len); +--- a/src/extra/tcp.c ++++ b/src/extra/tcp.c +@@ -91,7 +91,7 @@ nfq_tcp_compute_checksum_ipv4(struct tcp + { + /* checksum field in header needs to be zero for calculation. */ + tcph->check = 0; +- tcph->check = checksum_tcpudp_ipv4(iph); ++ tcph->check = checksum_tcpudp_ipv4(iph, IPPROTO_TCP); + } + EXPORT_SYMBOL(nfq_tcp_compute_checksum_ipv4); + +@@ -105,7 +105,7 @@ nfq_tcp_compute_checksum_ipv6(struct tcp + { + /* checksum field in header needs to be zero for calculation. */ + tcph->check = 0; +- tcph->check = checksum_tcpudp_ipv6(ip6h, tcph); ++ tcph->check = checksum_tcpudp_ipv6(ip6h, tcph, IPPROTO_TCP); + } + EXPORT_SYMBOL(nfq_tcp_compute_checksum_ipv6); + +--- a/src/extra/udp.c ++++ b/src/extra/udp.c +@@ -91,7 +91,7 @@ nfq_udp_compute_checksum_ipv4(struct udp + { + /* checksum field in header needs to be zero for calculation. */ + udph->check = 0; +- udph->check = checksum_tcpudp_ipv4(iph); ++ udph->check = checksum_tcpudp_ipv4(iph, IPPROTO_UDP); + } + EXPORT_SYMBOL(nfq_udp_compute_checksum_ipv4); + +@@ -110,7 +110,7 @@ nfq_udp_compute_checksum_ipv6(struct udp + { + /* checksum field in header needs to be zero for calculation. */ + udph->check = 0; +- udph->check = checksum_tcpudp_ipv6(ip6h, udph); ++ udph->check = checksum_tcpudp_ipv6(ip6h, udph, IPPROTO_UDP); + } + EXPORT_SYMBOL(nfq_udp_compute_checksum_ipv6); + +--- a/src/internal.h ++++ b/src/internal.h +@@ -13,8 +13,8 @@ struct iphdr; + struct ip6_hdr; + + uint16_t checksum(uint32_t sum, uint16_t *buf, int size); +-uint16_t checksum_tcpudp_ipv4(struct iphdr *iph); +-uint16_t checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr); ++uint16_t checksum_tcpudp_ipv4(struct iphdr *iph, uint16_t protocol_id); ++uint16_t checksum_tcpudp_ipv6(struct ip6_hdr *ip6h, void *transport_hdr, uint16_t protocol_id); + + struct pkt_buff { + uint8_t *mac_header; |