summaryrefslogtreecommitdiffstats
path: root/net/bridge
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2015-02-16 12:57:53 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2015-03-02 20:10:51 -0500
commit72500bc11e4da570bd66c0965e2dc74f52c8aba2 (patch)
treeebedd0a97d7b72022078435b9bae4215ce2f5023 /net/bridge
parentee586bbc28fb7128133457cf711880d13a3b7ce4 (diff)
netfilter: bridge: rework reject handling
bridge reject handling is not straightforward, there are many subtle differences depending on configuration. skb->dev is either the bridge port (PRE_ROUTING) or the bridge itself (INPUT), so we need to use indev instead. Also, checksum validation will only work reliably if we trim skb according to the l3 header size. While at it, add csum validation for ipv6 and skip existing tests if skb was already checked e.g. by GRO. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net/bridge')
-rw-r--r--net/bridge/netfilter/nft_reject_bridge.c84
1 files changed, 66 insertions, 18 deletions
diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c
index 3244aead0926..5c6c96585acd 100644
--- a/net/bridge/netfilter/nft_reject_bridge.c
+++ b/net/bridge/netfilter/nft_reject_bridge.c
@@ -21,6 +21,7 @@
21#include <net/ip.h> 21#include <net/ip.h>
22#include <net/ip6_checksum.h> 22#include <net/ip6_checksum.h>
23#include <linux/netfilter_bridge.h> 23#include <linux/netfilter_bridge.h>
24#include <linux/netfilter_ipv6.h>
24#include "../br_private.h" 25#include "../br_private.h"
25 26
26static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb, 27static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb,
@@ -36,7 +37,12 @@ static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb,
36 skb_pull(nskb, ETH_HLEN); 37 skb_pull(nskb, ETH_HLEN);
37} 38}
38 39
39static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb, int hook) 40/* We cannot use oldskb->dev, it can be either bridge device (NF_BRIDGE INPUT)
41 * or the bridge port (NF_BRIDGE PREROUTING).
42 */
43static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb,
44 const struct net_device *dev,
45 int hook)
40{ 46{
41 struct sk_buff *nskb; 47 struct sk_buff *nskb;
42 struct iphdr *niph; 48 struct iphdr *niph;
@@ -65,11 +71,12 @@ static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb, int hook)
65 71
66 nft_reject_br_push_etherhdr(oldskb, nskb); 72 nft_reject_br_push_etherhdr(oldskb, nskb);
67 73
68 br_deliver(br_port_get_rcu(oldskb->dev), nskb); 74 br_deliver(br_port_get_rcu(dev), nskb);
69} 75}
70 76
71static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, int hook, 77static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb,
72 u8 code) 78 const struct net_device *dev,
79 int hook, u8 code)
73{ 80{
74 struct sk_buff *nskb; 81 struct sk_buff *nskb;
75 struct iphdr *niph; 82 struct iphdr *niph;
@@ -77,8 +84,9 @@ static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, int hook,
77 unsigned int len; 84 unsigned int len;
78 void *payload; 85 void *payload;
79 __wsum csum; 86 __wsum csum;
87 u8 proto;
80 88
81 if (!nft_bridge_iphdr_validate(oldskb)) 89 if (oldskb->csum_bad || !nft_bridge_iphdr_validate(oldskb))
82 return; 90 return;
83 91
84 /* IP header checks: fragment. */ 92 /* IP header checks: fragment. */
@@ -91,7 +99,17 @@ static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, int hook,
91 if (!pskb_may_pull(oldskb, len)) 99 if (!pskb_may_pull(oldskb, len))
92 return; 100 return;
93 101
94 if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), 0)) 102 if (pskb_trim_rcsum(oldskb, htons(ip_hdr(oldskb)->tot_len)))
103 return;
104
105 if (ip_hdr(oldskb)->protocol == IPPROTO_TCP ||
106 ip_hdr(oldskb)->protocol == IPPROTO_UDP)
107 proto = ip_hdr(oldskb)->protocol;
108 else
109 proto = 0;
110
111 if (!skb_csum_unnecessary(oldskb) &&
112 nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), proto))
95 return; 113 return;
96 114
97 nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmphdr) + 115 nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmphdr) +
@@ -120,11 +138,13 @@ static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, int hook,
120 138
121 nft_reject_br_push_etherhdr(oldskb, nskb); 139 nft_reject_br_push_etherhdr(oldskb, nskb);
122 140
123 br_deliver(br_port_get_rcu(oldskb->dev), nskb); 141 br_deliver(br_port_get_rcu(dev), nskb);
124} 142}
125 143
126static void nft_reject_br_send_v6_tcp_reset(struct net *net, 144static void nft_reject_br_send_v6_tcp_reset(struct net *net,
127 struct sk_buff *oldskb, int hook) 145 struct sk_buff *oldskb,
146 const struct net_device *dev,
147 int hook)
128{ 148{
129 struct sk_buff *nskb; 149 struct sk_buff *nskb;
130 const struct tcphdr *oth; 150 const struct tcphdr *oth;
@@ -152,12 +172,37 @@ static void nft_reject_br_send_v6_tcp_reset(struct net *net,
152 172
153 nft_reject_br_push_etherhdr(oldskb, nskb); 173 nft_reject_br_push_etherhdr(oldskb, nskb);
154 174
155 br_deliver(br_port_get_rcu(oldskb->dev), nskb); 175 br_deliver(br_port_get_rcu(dev), nskb);
176}
177
178static bool reject6_br_csum_ok(struct sk_buff *skb, int hook)
179{
180 const struct ipv6hdr *ip6h = ipv6_hdr(skb);
181 int thoff;
182 __be16 fo;
183 u8 proto = ip6h->nexthdr;
184
185 if (skb->csum_bad)
186 return false;
187
188 if (skb_csum_unnecessary(skb))
189 return true;
190
191 if (ip6h->payload_len &&
192 pskb_trim_rcsum(skb, ntohs(ip6h->payload_len) + sizeof(*ip6h)))
193 return false;
194
195 thoff = ipv6_skip_exthdr(skb, ((u8*)(ip6h+1) - skb->data), &proto, &fo);
196 if (thoff < 0 || thoff >= skb->len || (fo & htons(~0x7)) != 0)
197 return false;
198
199 return nf_ip6_checksum(skb, hook, thoff, proto) == 0;
156} 200}
157 201
158static void nft_reject_br_send_v6_unreach(struct net *net, 202static void nft_reject_br_send_v6_unreach(struct net *net,
159 struct sk_buff *oldskb, int hook, 203 struct sk_buff *oldskb,
160 u8 code) 204 const struct net_device *dev,
205 int hook, u8 code)
161{ 206{
162 struct sk_buff *nskb; 207 struct sk_buff *nskb;
163 struct ipv6hdr *nip6h; 208 struct ipv6hdr *nip6h;
@@ -176,6 +221,9 @@ static void nft_reject_br_send_v6_unreach(struct net *net,
176 if (!pskb_may_pull(oldskb, len)) 221 if (!pskb_may_pull(oldskb, len))
177 return; 222 return;
178 223
224 if (!reject6_br_csum_ok(oldskb, hook))
225 return;
226
179 nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmp6hdr) + 227 nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmp6hdr) +
180 LL_MAX_HEADER + len, GFP_ATOMIC); 228 LL_MAX_HEADER + len, GFP_ATOMIC);
181 if (!nskb) 229 if (!nskb)
@@ -205,7 +253,7 @@ static void nft_reject_br_send_v6_unreach(struct net *net,
205 253
206 nft_reject_br_push_etherhdr(oldskb, nskb); 254 nft_reject_br_push_etherhdr(oldskb, nskb);
207 255
208 br_deliver(br_port_get_rcu(oldskb->dev), nskb); 256 br_deliver(br_port_get_rcu(dev), nskb);
209} 257}
210 258
211static void nft_reject_bridge_eval(const struct nft_expr *expr, 259static void nft_reject_bridge_eval(const struct nft_expr *expr,
@@ -224,16 +272,16 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr,
224 case htons(ETH_P_IP): 272 case htons(ETH_P_IP):
225 switch (priv->type) { 273 switch (priv->type) {
226 case NFT_REJECT_ICMP_UNREACH: 274 case NFT_REJECT_ICMP_UNREACH:
227 nft_reject_br_send_v4_unreach(pkt->skb, 275 nft_reject_br_send_v4_unreach(pkt->skb, pkt->in,
228 pkt->ops->hooknum, 276 pkt->ops->hooknum,
229 priv->icmp_code); 277 priv->icmp_code);
230 break; 278 break;
231 case NFT_REJECT_TCP_RST: 279 case NFT_REJECT_TCP_RST:
232 nft_reject_br_send_v4_tcp_reset(pkt->skb, 280 nft_reject_br_send_v4_tcp_reset(pkt->skb, pkt->in,
233 pkt->ops->hooknum); 281 pkt->ops->hooknum);
234 break; 282 break;
235 case NFT_REJECT_ICMPX_UNREACH: 283 case NFT_REJECT_ICMPX_UNREACH:
236 nft_reject_br_send_v4_unreach(pkt->skb, 284 nft_reject_br_send_v4_unreach(pkt->skb, pkt->in,
237 pkt->ops->hooknum, 285 pkt->ops->hooknum,
238 nft_reject_icmp_code(priv->icmp_code)); 286 nft_reject_icmp_code(priv->icmp_code));
239 break; 287 break;
@@ -242,16 +290,16 @@ static void nft_reject_bridge_eval(const struct nft_expr *expr,
242 case htons(ETH_P_IPV6): 290 case htons(ETH_P_IPV6):
243 switch (priv->type) { 291 switch (priv->type) {
244 case NFT_REJECT_ICMP_UNREACH: 292 case NFT_REJECT_ICMP_UNREACH:
245 nft_reject_br_send_v6_unreach(net, pkt->skb, 293 nft_reject_br_send_v6_unreach(net, pkt->skb, pkt->in,
246 pkt->ops->hooknum, 294 pkt->ops->hooknum,
247 priv->icmp_code); 295 priv->icmp_code);
248 break; 296 break;
249 case NFT_REJECT_TCP_RST: 297 case NFT_REJECT_TCP_RST:
250 nft_reject_br_send_v6_tcp_reset(net, pkt->skb, 298 nft_reject_br_send_v6_tcp_reset(net, pkt->skb, pkt->in,
251 pkt->ops->hooknum); 299 pkt->ops->hooknum);
252 break; 300 break;
253 case NFT_REJECT_ICMPX_UNREACH: 301 case NFT_REJECT_ICMPX_UNREACH:
254 nft_reject_br_send_v6_unreach(net, pkt->skb, 302 nft_reject_br_send_v6_unreach(net, pkt->skb, pkt->in,
255 pkt->ops->hooknum, 303 pkt->ops->hooknum,
256 nft_reject_icmpv6_code(priv->icmp_code)); 304 nft_reject_icmpv6_code(priv->icmp_code));
257 break; 305 break;