Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

netfilter: conntrack: skip verification of zero UDP checksum

The checksum is optional for UDP packets. However nf_reject would
previously require a valid checksum to elicit a response such as
ICMP_DEST_UNREACH.

Add some logic to nf_reject_verify_csum to determine if a UDP packet has
a zero checksum and should therefore not be verified.

Signed-off-by: Kevin Mitchell <kevmitch@arista.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>

authored by

Kevin Mitchell and committed by
Pablo Neira Ayuso
4f9bd530 3412e164

+26 -9
+17 -4
include/net/netfilter/nf_reject.h
··· 5 5 #include <linux/types.h> 6 6 #include <uapi/linux/in.h> 7 7 8 - static inline bool nf_reject_verify_csum(__u8 proto) 8 + static inline bool nf_reject_verify_csum(struct sk_buff *skb, int dataoff, 9 + __u8 proto) 9 10 { 10 11 /* Skip protocols that don't use 16-bit one's complement checksum 11 12 * of the entire payload. 12 13 */ 13 14 switch (proto) { 15 + /* Protocols with optional checksums. */ 16 + case IPPROTO_UDP: { 17 + const struct udphdr *udp_hdr; 18 + struct udphdr _udp_hdr; 19 + 20 + udp_hdr = skb_header_pointer(skb, dataoff, 21 + sizeof(_udp_hdr), 22 + &_udp_hdr); 23 + if (!udp_hdr || udp_hdr->check) 24 + return true; 25 + 26 + return false; 27 + } 28 + case IPPROTO_GRE: 29 + 14 30 /* Protocols with other integrity checks. */ 15 31 case IPPROTO_AH: 16 32 case IPPROTO_ESP: ··· 35 19 /* Protocols with partial checksums. */ 36 20 case IPPROTO_UDPLITE: 37 21 case IPPROTO_DCCP: 38 - 39 - /* Protocols with optional checksums. */ 40 - case IPPROTO_GRE: 41 22 return false; 42 23 } 43 24 return true;
+7 -3
net/ipv4/netfilter/nf_reject_ipv4.c
··· 80 80 struct iphdr *niph; 81 81 struct icmphdr *icmph; 82 82 unsigned int len; 83 + int dataoff; 83 84 __wsum csum; 84 85 u8 proto; 85 86 ··· 100 99 if (pskb_trim_rcsum(oldskb, ntohs(ip_hdr(oldskb)->tot_len))) 101 100 return NULL; 102 101 102 + dataoff = ip_hdrlen(oldskb); 103 103 proto = ip_hdr(oldskb)->protocol; 104 104 105 105 if (!skb_csum_unnecessary(oldskb) && 106 - nf_reject_verify_csum(proto) && 106 + nf_reject_verify_csum(oldskb, dataoff, proto) && 107 107 nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), proto)) 108 108 return NULL; 109 109 ··· 313 311 void nf_send_unreach(struct sk_buff *skb_in, int code, int hook) 314 312 { 315 313 struct iphdr *iph = ip_hdr(skb_in); 314 + int dataoff = ip_hdrlen(skb_in); 316 315 u8 proto = iph->protocol; 317 316 318 317 if (iph->frag_off & htons(IP_OFFSET)) ··· 323 320 nf_reject_fill_skb_dst(skb_in) < 0) 324 321 return; 325 322 326 - if (skb_csum_unnecessary(skb_in) || !nf_reject_verify_csum(proto)) { 323 + if (skb_csum_unnecessary(skb_in) || 324 + !nf_reject_verify_csum(skb_in, dataoff, proto)) { 327 325 icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); 328 326 return; 329 327 } 330 328 331 - if (nf_ip_checksum(skb_in, hook, ip_hdrlen(skb_in), proto) == 0) 329 + if (nf_ip_checksum(skb_in, hook, dataoff, proto) == 0) 332 330 icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0); 333 331 } 334 332 EXPORT_SYMBOL_GPL(nf_send_unreach);
+2 -2
net/ipv6/netfilter/nf_reject_ipv6.c
··· 31 31 if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0) 32 32 return false; 33 33 34 - if (!nf_reject_verify_csum(proto)) 34 + if (!nf_reject_verify_csum(skb, thoff, proto)) 35 35 return true; 36 36 37 37 return nf_ip6_checksum(skb, hook, thoff, proto) == 0; ··· 388 388 if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0) 389 389 return false; 390 390 391 - if (!nf_reject_verify_csum(proto)) 391 + if (!nf_reject_verify_csum(skb, thoff, proto)) 392 392 return true; 393 393 394 394 return nf_ip6_checksum(skb, hook, thoff, proto) == 0;