aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2015-02-16 12:54:04 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2015-03-02 20:10:35 -0500
commitee586bbc28fb7128133457cf711880d13a3b7ce4 (patch)
treed1e1991b5d622b588c11608b9164499a96869e51
parentb898441f4ece44933af90b116b467f7864dd1ae7 (diff)
netfilter: reject: don't send icmp error if csum is invalid
tcp resets are never emitted if the packet that triggers the reject/reset has an invalid checksum. For icmp error responses there was no such check. It allows to distinguish icmp response generated via iptables -I INPUT -p udp --dport 42 -j REJECT and those emitted by network stack (won't respond if csum is invalid, REJECT does). Arguably its possible to avoid this by using conntrack and only using REJECT with -m conntrack NEW/RELATED. However, this doesn't work when connection tracking is not in use or when using nf_conntrack_checksum=0. Furthermore, sending errors in response to invalid csums doesn't make much sense so just add similar test as in nf_send_reset. Validate csum if needed and only send the response if it is ok. Reference: http://bugzilla.redhat.com/show_bug.cgi?id=1169829 Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/net/netfilter/ipv4/nf_reject.h6
-rw-r--r--include/net/netfilter/ipv6/nf_reject.h11
-rw-r--r--net/ipv4/netfilter/ipt_REJECT.c17
-rw-r--r--net/ipv4/netfilter/nf_reject_ipv4.c23
-rw-r--r--net/ipv4/netfilter/nft_reject_ipv4.c3
-rw-r--r--net/ipv6/netfilter/nf_reject_ipv6.c35
-rw-r--r--net/netfilter/nft_reject_inet.c6
7 files changed, 76 insertions, 25 deletions
diff --git a/include/net/netfilter/ipv4/nf_reject.h b/include/net/netfilter/ipv4/nf_reject.h
index 03e928a55229..864127573c32 100644
--- a/include/net/netfilter/ipv4/nf_reject.h
+++ b/include/net/netfilter/ipv4/nf_reject.h
@@ -5,11 +5,7 @@
5#include <net/ip.h> 5#include <net/ip.h>
6#include <net/icmp.h> 6#include <net/icmp.h>
7 7
8static inline void nf_send_unreach(struct sk_buff *skb_in, int code) 8void nf_send_unreach(struct sk_buff *skb_in, int code, int hook);
9{
10 icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
11}
12
13void nf_send_reset(struct sk_buff *oldskb, int hook); 9void nf_send_reset(struct sk_buff *oldskb, int hook);
14 10
15const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb, 11const struct tcphdr *nf_reject_ip_tcphdr_get(struct sk_buff *oldskb,
diff --git a/include/net/netfilter/ipv6/nf_reject.h b/include/net/netfilter/ipv6/nf_reject.h
index 23216d48abf9..0ae445d3f217 100644
--- a/include/net/netfilter/ipv6/nf_reject.h
+++ b/include/net/netfilter/ipv6/nf_reject.h
@@ -3,15 +3,8 @@
3 3
4#include <linux/icmpv6.h> 4#include <linux/icmpv6.h>
5 5
6static inline void 6void nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code,
7nf_send_unreach6(struct net *net, struct sk_buff *skb_in, unsigned char code, 7 unsigned int hooknum);
8 unsigned int hooknum)
9{
10 if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
11 skb_in->dev = net->loopback_dev;
12
13 icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0);
14}
15 8
16void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook); 9void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook);
17 10
diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c
index 8f48f5517e33..87907d4bd259 100644
--- a/net/ipv4/netfilter/ipt_REJECT.c
+++ b/net/ipv4/netfilter/ipt_REJECT.c
@@ -34,31 +34,32 @@ static unsigned int
34reject_tg(struct sk_buff *skb, const struct xt_action_param *par) 34reject_tg(struct sk_buff *skb, const struct xt_action_param *par)
35{ 35{
36 const struct ipt_reject_info *reject = par->targinfo; 36 const struct ipt_reject_info *reject = par->targinfo;
37 int hook = par->hooknum;
37 38
38 switch (reject->with) { 39 switch (reject->with) {
39 case IPT_ICMP_NET_UNREACHABLE: 40 case IPT_ICMP_NET_UNREACHABLE:
40 nf_send_unreach(skb, ICMP_NET_UNREACH); 41 nf_send_unreach(skb, ICMP_NET_UNREACH, hook);
41 break; 42 break;
42 case IPT_ICMP_HOST_UNREACHABLE: 43 case IPT_ICMP_HOST_UNREACHABLE:
43 nf_send_unreach(skb, ICMP_HOST_UNREACH); 44 nf_send_unreach(skb, ICMP_HOST_UNREACH, hook);
44 break; 45 break;
45 case IPT_ICMP_PROT_UNREACHABLE: 46 case IPT_ICMP_PROT_UNREACHABLE:
46 nf_send_unreach(skb, ICMP_PROT_UNREACH); 47 nf_send_unreach(skb, ICMP_PROT_UNREACH, hook);
47 break; 48 break;
48 case IPT_ICMP_PORT_UNREACHABLE: 49 case IPT_ICMP_PORT_UNREACHABLE:
49 nf_send_unreach(skb, ICMP_PORT_UNREACH); 50 nf_send_unreach(skb, ICMP_PORT_UNREACH, hook);
50 break; 51 break;
51 case IPT_ICMP_NET_PROHIBITED: 52 case IPT_ICMP_NET_PROHIBITED:
52 nf_send_unreach(skb, ICMP_NET_ANO); 53 nf_send_unreach(skb, ICMP_NET_ANO, hook);
53 break; 54 break;
54 case IPT_ICMP_HOST_PROHIBITED: 55 case IPT_ICMP_HOST_PROHIBITED:
55 nf_send_unreach(skb, ICMP_HOST_ANO); 56 nf_send_unreach(skb, ICMP_HOST_ANO, hook);
56 break; 57 break;
57 case IPT_ICMP_ADMIN_PROHIBITED: 58 case IPT_ICMP_ADMIN_PROHIBITED:
58 nf_send_unreach(skb, ICMP_PKT_FILTERED); 59 nf_send_unreach(skb, ICMP_PKT_FILTERED, hook);
59 break; 60 break;
60 case IPT_TCP_RESET: 61 case IPT_TCP_RESET:
61 nf_send_reset(skb, par->hooknum); 62 nf_send_reset(skb, hook);
62 case IPT_ICMP_ECHOREPLY: 63 case IPT_ICMP_ECHOREPLY:
63 /* Doesn't happen. */ 64 /* Doesn't happen. */
64 break; 65 break;
diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c
index 536da7bc598a..b7405eb7f1ef 100644
--- a/net/ipv4/netfilter/nf_reject_ipv4.c
+++ b/net/ipv4/netfilter/nf_reject_ipv4.c
@@ -164,4 +164,27 @@ void nf_send_reset(struct sk_buff *oldskb, int hook)
164} 164}
165EXPORT_SYMBOL_GPL(nf_send_reset); 165EXPORT_SYMBOL_GPL(nf_send_reset);
166 166
167void nf_send_unreach(struct sk_buff *skb_in, int code, int hook)
168{
169 struct iphdr *iph = ip_hdr(skb_in);
170 u8 proto;
171
172 if (skb_in->csum_bad || iph->frag_off & htons(IP_OFFSET))
173 return;
174
175 if (skb_csum_unnecessary(skb_in)) {
176 icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
177 return;
178 }
179
180 if (iph->protocol == IPPROTO_TCP || iph->protocol == IPPROTO_UDP)
181 proto = iph->protocol;
182 else
183 proto = 0;
184
185 if (nf_ip_checksum(skb_in, hook, ip_hdrlen(skb_in), proto) == 0)
186 icmp_send(skb_in, ICMP_DEST_UNREACH, code, 0);
187}
188EXPORT_SYMBOL_GPL(nf_send_unreach);
189
167MODULE_LICENSE("GPL"); 190MODULE_LICENSE("GPL");
diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c
index d729542bd1b7..16a5d4d73d75 100644
--- a/net/ipv4/netfilter/nft_reject_ipv4.c
+++ b/net/ipv4/netfilter/nft_reject_ipv4.c
@@ -27,7 +27,8 @@ static void nft_reject_ipv4_eval(const struct nft_expr *expr,
27 27
28 switch (priv->type) { 28 switch (priv->type) {
29 case NFT_REJECT_ICMP_UNREACH: 29 case NFT_REJECT_ICMP_UNREACH:
30 nf_send_unreach(pkt->skb, priv->icmp_code); 30 nf_send_unreach(pkt->skb, priv->icmp_code,
31 pkt->ops->hooknum);
31 break; 32 break;
32 case NFT_REJECT_TCP_RST: 33 case NFT_REJECT_TCP_RST:
33 nf_send_reset(pkt->skb, pkt->ops->hooknum); 34 nf_send_reset(pkt->skb, pkt->ops->hooknum);
diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c
index d05b36440e8b..68e0bb4db1bf 100644
--- a/net/ipv6/netfilter/nf_reject_ipv6.c
+++ b/net/ipv6/netfilter/nf_reject_ipv6.c
@@ -208,4 +208,39 @@ void nf_send_reset6(struct net *net, struct sk_buff *oldskb, int hook)
208} 208}
209EXPORT_SYMBOL_GPL(nf_send_reset6); 209EXPORT_SYMBOL_GPL(nf_send_reset6);
210 210
211static bool reject6_csum_ok(struct sk_buff *skb, int hook)
212{
213 const struct ipv6hdr *ip6h = ipv6_hdr(skb);
214 int thoff;
215 __be16 fo;
216 u8 proto;
217
218 if (skb->csum_bad)
219 return false;
220
221 if (skb_csum_unnecessary(skb))
222 return true;
223
224 proto = ip6h->nexthdr;
225 thoff = ipv6_skip_exthdr(skb, ((u8*)(ip6h+1) - skb->data), &proto, &fo);
226
227 if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
228 return false;
229
230 return nf_ip6_checksum(skb, hook, thoff, proto) == 0;
231}
232
233void nf_send_unreach6(struct net *net, struct sk_buff *skb_in,
234 unsigned char code, unsigned int hooknum)
235{
236 if (!reject6_csum_ok(skb_in, hooknum))
237 return;
238
239 if (hooknum == NF_INET_LOCAL_OUT && skb_in->dev == NULL)
240 skb_in->dev = net->loopback_dev;
241
242 icmpv6_send(skb_in, ICMPV6_DEST_UNREACH, code, 0);
243}
244EXPORT_SYMBOL_GPL(nf_send_unreach6);
245
211MODULE_LICENSE("GPL"); 246MODULE_LICENSE("GPL");
diff --git a/net/netfilter/nft_reject_inet.c b/net/netfilter/nft_reject_inet.c
index 7b5f9d58680a..92877114aff4 100644
--- a/net/netfilter/nft_reject_inet.c
+++ b/net/netfilter/nft_reject_inet.c
@@ -28,14 +28,16 @@ static void nft_reject_inet_eval(const struct nft_expr *expr,
28 case NFPROTO_IPV4: 28 case NFPROTO_IPV4:
29 switch (priv->type) { 29 switch (priv->type) {
30 case NFT_REJECT_ICMP_UNREACH: 30 case NFT_REJECT_ICMP_UNREACH:
31 nf_send_unreach(pkt->skb, priv->icmp_code); 31 nf_send_unreach(pkt->skb, priv->icmp_code,
32 pkt->ops->hooknum);
32 break; 33 break;
33 case NFT_REJECT_TCP_RST: 34 case NFT_REJECT_TCP_RST:
34 nf_send_reset(pkt->skb, pkt->ops->hooknum); 35 nf_send_reset(pkt->skb, pkt->ops->hooknum);
35 break; 36 break;
36 case NFT_REJECT_ICMPX_UNREACH: 37 case NFT_REJECT_ICMPX_UNREACH:
37 nf_send_unreach(pkt->skb, 38 nf_send_unreach(pkt->skb,
38 nft_reject_icmp_code(priv->icmp_code)); 39 nft_reject_icmp_code(priv->icmp_code),
40 pkt->ops->hooknum);
39 break; 41 break;
40 } 42 }
41 break; 43 break;