forked from mirrors/linux
		
	netfilter: reject: skip csum verification for protocols that don't support it
Some protocols have other means to verify the payload integrity (AH, ESP, SCTP) while others are incompatible with nf_ip(6)_checksum implementation because checksum is either optional or might be partial (UDPLITE, DCCP, GRE). Because nf_ip(6)_checksum was used to validate the packets, ip(6)tables REJECT rules were not capable to generate ICMP(v6) errors for the protocols mentioned above. This commit also fixes the incorrect pseudo-header protocol used for IPv4 packets that carry other transport protocols than TCP or UDP (pseudo-header used protocol 0 iso the proper value). Signed-off-by: Alin Nastac <alin.nastac@gmail.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
This commit is contained in:
		
							parent
							
								
									13f5251fd1
								
							
						
					
					
						commit
						7fc3822536
					
				
					 6 changed files with 39 additions and 12 deletions
				
			
		| 
						 | 
					@ -5,6 +5,7 @@
 | 
				
			||||||
#include <linux/skbuff.h>
 | 
					#include <linux/skbuff.h>
 | 
				
			||||||
#include <net/ip.h>
 | 
					#include <net/ip.h>
 | 
				
			||||||
#include <net/icmp.h>
 | 
					#include <net/icmp.h>
 | 
				
			||||||
 | 
					#include <net/netfilter/nf_reject.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void nf_send_unreach(struct sk_buff *skb_in, int code, int hook);
 | 
					void nf_send_unreach(struct sk_buff *skb_in, int code, int hook);
 | 
				
			||||||
void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook);
 | 
					void nf_send_reset(struct net *net, struct sk_buff *oldskb, int hook);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
#define _IPV6_NF_REJECT_H
 | 
					#define _IPV6_NF_REJECT_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/icmpv6.h>
 | 
					#include <linux/icmpv6.h>
 | 
				
			||||||
 | 
					#include <net/netfilter/nf_reject.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code,
 | 
					void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code,
 | 
				
			||||||
		      unsigned int hooknum);
 | 
							      unsigned int hooknum);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										27
									
								
								include/net/netfilter/nf_reject.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								include/net/netfilter/nf_reject.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					#ifndef _NF_REJECT_H
 | 
				
			||||||
 | 
					#define _NF_REJECT_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool nf_reject_verify_csum(__u8 proto)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Skip protocols that don't use 16-bit one's complement checksum
 | 
				
			||||||
 | 
						 * of the entire payload.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						switch (proto) {
 | 
				
			||||||
 | 
							/* Protocols with other integrity checks. */
 | 
				
			||||||
 | 
							case IPPROTO_AH:
 | 
				
			||||||
 | 
							case IPPROTO_ESP:
 | 
				
			||||||
 | 
							case IPPROTO_SCTP:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Protocols with partial checksums. */
 | 
				
			||||||
 | 
							case IPPROTO_UDPLITE:
 | 
				
			||||||
 | 
							case IPPROTO_DCCP:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Protocols with optional checksums. */
 | 
				
			||||||
 | 
							case IPPROTO_GRE:
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _NF_REJECT_H */
 | 
				
			||||||
| 
						 | 
					@ -125,13 +125,10 @@ static void nft_reject_br_send_v4_unreach(struct net *net,
 | 
				
			||||||
	if (pskb_trim_rcsum(oldskb, ntohs(ip_hdr(oldskb)->tot_len)))
 | 
						if (pskb_trim_rcsum(oldskb, ntohs(ip_hdr(oldskb)->tot_len)))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ip_hdr(oldskb)->protocol == IPPROTO_TCP ||
 | 
					 | 
				
			||||||
	    ip_hdr(oldskb)->protocol == IPPROTO_UDP)
 | 
					 | 
				
			||||||
	proto = ip_hdr(oldskb)->protocol;
 | 
						proto = ip_hdr(oldskb)->protocol;
 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		proto = 0;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!skb_csum_unnecessary(oldskb) &&
 | 
						if (!skb_csum_unnecessary(oldskb) &&
 | 
				
			||||||
 | 
						    nf_reject_verify_csum(proto) &&
 | 
				
			||||||
	    nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), proto))
 | 
						    nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), proto))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -234,6 +231,9 @@ static bool reject6_br_csum_ok(struct sk_buff *skb, int hook)
 | 
				
			||||||
	if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
 | 
						if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!nf_reject_verify_csum(proto))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nf_ip6_checksum(skb, hook, thoff, proto) == 0;
 | 
						return nf_ip6_checksum(skb, hook, thoff, proto) == 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -173,21 +173,16 @@ EXPORT_SYMBOL_GPL(nf_send_reset);
 | 
				
			||||||
void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
 | 
					void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct iphdr *iph = ip_hdr(skb_in);
 | 
						struct iphdr *iph = ip_hdr(skb_in);
 | 
				
			||||||
	u8 proto;
 | 
						u8 proto = iph->protocol;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (iph->frag_off & htons(IP_OFFSET))
 | 
						if (iph->frag_off & htons(IP_OFFSET))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (skb_csum_unnecessary(skb_in)) {
 | 
						if (skb_csum_unnecessary(skb_in) || !nf_reject_verify_csum(proto)) {
 | 
				
			||||||
		icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
 | 
							icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP)
 | 
					 | 
				
			||||||
		proto = iph->protocol;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		proto = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (nf_ip_checksum(skb_in, hook, ip_hdrlen(skb_in), proto) == 0)
 | 
						if (nf_ip_checksum(skb_in, hook, ip_hdrlen(skb_in), proto) == 0)
 | 
				
			||||||
		icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
 | 
							icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -233,6 +233,9 @@ static bool reject6_csum_ok(struct sk_buff *skb, int hook)
 | 
				
			||||||
	if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
 | 
						if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!nf_reject_verify_csum(proto))
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nf_ip6_checksum(skb, hook, thoff, proto) == 0;
 | 
						return nf_ip6_checksum(skb, hook, thoff, proto) == 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue