Merge branch 'acpi-ec'
[linux-drm-fsl-dcu.git] / net / bridge / netfilter / nft_reject_bridge.c
1 /*
2  * Copyright (c) 2014 Pablo Neira Ayuso <pablo@netfilter.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8
9 #include <linux/kernel.h>
10 #include <linux/init.h>
11 #include <linux/module.h>
12 #include <linux/netlink.h>
13 #include <linux/netfilter.h>
14 #include <linux/netfilter/nf_tables.h>
15 #include <net/netfilter/nf_tables.h>
16 #include <net/netfilter/nft_reject.h>
17 #include <net/netfilter/nf_tables_bridge.h>
18 #include <net/netfilter/ipv4/nf_reject.h>
19 #include <net/netfilter/ipv6/nf_reject.h>
20 #include <linux/ip.h>
21 #include <net/ip.h>
22 #include <net/ip6_checksum.h>
23 #include <linux/netfilter_bridge.h>
24 #include "../br_private.h"
25
26 static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb,
27                                         struct sk_buff *nskb)
28 {
29         struct ethhdr *eth;
30
31         eth = (struct ethhdr *)skb_push(nskb, ETH_HLEN);
32         skb_reset_mac_header(nskb);
33         ether_addr_copy(eth->h_source, eth_hdr(oldskb)->h_dest);
34         ether_addr_copy(eth->h_dest, eth_hdr(oldskb)->h_source);
35         eth->h_proto = eth_hdr(oldskb)->h_proto;
36         skb_pull(nskb, ETH_HLEN);
37 }
38
39 static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb, int hook)
40 {
41         struct sk_buff *nskb;
42         struct iphdr *niph;
43         const struct tcphdr *oth;
44         struct tcphdr _oth;
45
46         if (!nft_bridge_iphdr_validate(oldskb))
47                 return;
48
49         oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook);
50         if (!oth)
51                 return;
52
53         nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) +
54                          LL_MAX_HEADER, GFP_ATOMIC);
55         if (!nskb)
56                 return;
57
58         skb_reserve(nskb, LL_MAX_HEADER);
59         niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP,
60                                    sysctl_ip_default_ttl);
61         nf_reject_ip_tcphdr_put(nskb, oldskb, oth);
62         niph->ttl       = sysctl_ip_default_ttl;
63         niph->tot_len   = htons(nskb->len);
64         ip_send_check(niph);
65
66         nft_reject_br_push_etherhdr(oldskb, nskb);
67
68         br_deliver(br_port_get_rcu(oldskb->dev), nskb);
69 }
70
71 static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, int hook,
72                                           u8 code)
73 {
74         struct sk_buff *nskb;
75         struct iphdr *niph;
76         struct icmphdr *icmph;
77         unsigned int len;
78         void *payload;
79         __wsum csum;
80
81         if (!nft_bridge_iphdr_validate(oldskb))
82                 return;
83
84         /* IP header checks: fragment. */
85         if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET))
86                 return;
87
88         /* RFC says return as much as we can without exceeding 576 bytes. */
89         len = min_t(unsigned int, 536, oldskb->len);
90
91         if (!pskb_may_pull(oldskb, len))
92                 return;
93
94         if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), 0))
95                 return;
96
97         nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmphdr) +
98                          LL_MAX_HEADER + len, GFP_ATOMIC);
99         if (!nskb)
100                 return;
101
102         skb_reserve(nskb, LL_MAX_HEADER);
103         niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_ICMP,
104                                    sysctl_ip_default_ttl);
105
106         skb_reset_transport_header(nskb);
107         icmph = (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));
108         memset(icmph, 0, sizeof(*icmph));
109         icmph->type     = ICMP_DEST_UNREACH;
110         icmph->code     = code;
111
112         payload = skb_put(nskb, len);
113         memcpy(payload, skb_network_header(oldskb), len);
114
115         csum = csum_partial((void *)icmph, len + sizeof(struct icmphdr), 0);
116         icmph->checksum = csum_fold(csum);
117
118         niph->tot_len   = htons(nskb->len);
119         ip_send_check(niph);
120
121         nft_reject_br_push_etherhdr(oldskb, nskb);
122
123         br_deliver(br_port_get_rcu(oldskb->dev), nskb);
124 }
125
126 static void nft_reject_br_send_v6_tcp_reset(struct net *net,
127                                             struct sk_buff *oldskb, int hook)
128 {
129         struct sk_buff *nskb;
130         const struct tcphdr *oth;
131         struct tcphdr _oth;
132         unsigned int otcplen;
133         struct ipv6hdr *nip6h;
134
135         if (!nft_bridge_ip6hdr_validate(oldskb))
136                 return;
137
138         oth = nf_reject_ip6_tcphdr_get(oldskb, &_oth, &otcplen, hook);
139         if (!oth)
140                 return;
141
142         nskb = alloc_skb(sizeof(struct ipv6hdr) + sizeof(struct tcphdr) +
143                          LL_MAX_HEADER, GFP_ATOMIC);
144         if (!nskb)
145                 return;
146
147         skb_reserve(nskb, LL_MAX_HEADER);
148         nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP,
149                                      net->ipv6.devconf_all->hop_limit);
150         nf_reject_ip6_tcphdr_put(nskb, oldskb, oth, otcplen);
151         nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr));
152
153         nft_reject_br_push_etherhdr(oldskb, nskb);
154
155         br_deliver(br_port_get_rcu(oldskb->dev), nskb);
156 }
157
158 static void nft_reject_br_send_v6_unreach(struct net *net,
159                                           struct sk_buff *oldskb, int hook,
160                                           u8 code)
161 {
162         struct sk_buff *nskb;
163         struct ipv6hdr *nip6h;
164         struct icmp6hdr *icmp6h;
165         unsigned int len;
166         void *payload;
167
168         if (!nft_bridge_ip6hdr_validate(oldskb))
169                 return;
170
171         /* Include "As much of invoking packet as possible without the ICMPv6
172          * packet exceeding the minimum IPv6 MTU" in the ICMP payload.
173          */
174         len = min_t(unsigned int, 1220, oldskb->len);
175
176         if (!pskb_may_pull(oldskb, len))
177                 return;
178
179         nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct icmp6hdr) +
180                          LL_MAX_HEADER + len, GFP_ATOMIC);
181         if (!nskb)
182                 return;
183
184         skb_reserve(nskb, LL_MAX_HEADER);
185         nip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_ICMPV6,
186                                      net->ipv6.devconf_all->hop_limit);
187
188         skb_reset_transport_header(nskb);
189         icmp6h = (struct icmp6hdr *)skb_put(nskb, sizeof(struct icmp6hdr));
190         memset(icmp6h, 0, sizeof(*icmp6h));
191         icmp6h->icmp6_type = ICMPV6_DEST_UNREACH;
192         icmp6h->icmp6_code = code;
193
194         payload = skb_put(nskb, len);
195         memcpy(payload, skb_network_header(oldskb), len);
196         nip6h->payload_len = htons(nskb->len - sizeof(struct ipv6hdr));
197
198         icmp6h->icmp6_cksum =
199                 csum_ipv6_magic(&nip6h->saddr, &nip6h->daddr,
200                                 nskb->len - sizeof(struct ipv6hdr),
201                                 IPPROTO_ICMPV6,
202                                 csum_partial(icmp6h,
203                                              nskb->len - sizeof(struct ipv6hdr),
204                                              0));
205
206         nft_reject_br_push_etherhdr(oldskb, nskb);
207
208         br_deliver(br_port_get_rcu(oldskb->dev), nskb);
209 }
210
211 static void nft_reject_bridge_eval(const struct nft_expr *expr,
212                                  struct nft_data data[NFT_REG_MAX + 1],
213                                  const struct nft_pktinfo *pkt)
214 {
215         struct nft_reject *priv = nft_expr_priv(expr);
216         struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
217         const unsigned char *dest = eth_hdr(pkt->skb)->h_dest;
218
219         if (is_broadcast_ether_addr(dest) ||
220             is_multicast_ether_addr(dest))
221                 goto out;
222
223         switch (eth_hdr(pkt->skb)->h_proto) {
224         case htons(ETH_P_IP):
225                 switch (priv->type) {
226                 case NFT_REJECT_ICMP_UNREACH:
227                         nft_reject_br_send_v4_unreach(pkt->skb,
228                                                       pkt->ops->hooknum,
229                                                       priv->icmp_code);
230                         break;
231                 case NFT_REJECT_TCP_RST:
232                         nft_reject_br_send_v4_tcp_reset(pkt->skb,
233                                                         pkt->ops->hooknum);
234                         break;
235                 case NFT_REJECT_ICMPX_UNREACH:
236                         nft_reject_br_send_v4_unreach(pkt->skb,
237                                                       pkt->ops->hooknum,
238                                                       nft_reject_icmp_code(priv->icmp_code));
239                         break;
240                 }
241                 break;
242         case htons(ETH_P_IPV6):
243                 switch (priv->type) {
244                 case NFT_REJECT_ICMP_UNREACH:
245                         nft_reject_br_send_v6_unreach(net, pkt->skb,
246                                                       pkt->ops->hooknum,
247                                                       priv->icmp_code);
248                         break;
249                 case NFT_REJECT_TCP_RST:
250                         nft_reject_br_send_v6_tcp_reset(net, pkt->skb,
251                                                         pkt->ops->hooknum);
252                         break;
253                 case NFT_REJECT_ICMPX_UNREACH:
254                         nft_reject_br_send_v6_unreach(net, pkt->skb,
255                                                       pkt->ops->hooknum,
256                                                       nft_reject_icmpv6_code(priv->icmp_code));
257                         break;
258                 }
259                 break;
260         default:
261                 /* No explicit way to reject this protocol, drop it. */
262                 break;
263         }
264 out:
265         data[NFT_REG_VERDICT].verdict = NF_DROP;
266 }
267
268 static int nft_reject_bridge_validate(const struct nft_ctx *ctx,
269                                       const struct nft_expr *expr,
270                                       const struct nft_data **data)
271 {
272         return nft_chain_validate_hooks(ctx->chain, (1 << NF_BR_PRE_ROUTING) |
273                                                     (1 << NF_BR_LOCAL_IN));
274 }
275
276 static int nft_reject_bridge_init(const struct nft_ctx *ctx,
277                                   const struct nft_expr *expr,
278                                   const struct nlattr * const tb[])
279 {
280         struct nft_reject *priv = nft_expr_priv(expr);
281         int icmp_code, err;
282
283         err = nft_reject_bridge_validate(ctx, expr, NULL);
284         if (err < 0)
285                 return err;
286
287         if (tb[NFTA_REJECT_TYPE] == NULL)
288                 return -EINVAL;
289
290         priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE]));
291         switch (priv->type) {
292         case NFT_REJECT_ICMP_UNREACH:
293         case NFT_REJECT_ICMPX_UNREACH:
294                 if (tb[NFTA_REJECT_ICMP_CODE] == NULL)
295                         return -EINVAL;
296
297                 icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]);
298                 if (priv->type == NFT_REJECT_ICMPX_UNREACH &&
299                     icmp_code > NFT_REJECT_ICMPX_MAX)
300                         return -EINVAL;
301
302                 priv->icmp_code = icmp_code;
303                 break;
304         case NFT_REJECT_TCP_RST:
305                 break;
306         default:
307                 return -EINVAL;
308         }
309         return 0;
310 }
311
312 static int nft_reject_bridge_dump(struct sk_buff *skb,
313                                   const struct nft_expr *expr)
314 {
315         const struct nft_reject *priv = nft_expr_priv(expr);
316
317         if (nla_put_be32(skb, NFTA_REJECT_TYPE, htonl(priv->type)))
318                 goto nla_put_failure;
319
320         switch (priv->type) {
321         case NFT_REJECT_ICMP_UNREACH:
322         case NFT_REJECT_ICMPX_UNREACH:
323                 if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code))
324                         goto nla_put_failure;
325                 break;
326         }
327
328         return 0;
329
330 nla_put_failure:
331         return -1;
332 }
333
334 static struct nft_expr_type nft_reject_bridge_type;
335 static const struct nft_expr_ops nft_reject_bridge_ops = {
336         .type           = &nft_reject_bridge_type,
337         .size           = NFT_EXPR_SIZE(sizeof(struct nft_reject)),
338         .eval           = nft_reject_bridge_eval,
339         .init           = nft_reject_bridge_init,
340         .dump           = nft_reject_bridge_dump,
341         .validate       = nft_reject_bridge_validate,
342 };
343
344 static struct nft_expr_type nft_reject_bridge_type __read_mostly = {
345         .family         = NFPROTO_BRIDGE,
346         .name           = "reject",
347         .ops            = &nft_reject_bridge_ops,
348         .policy         = nft_reject_policy,
349         .maxattr        = NFTA_REJECT_MAX,
350         .owner          = THIS_MODULE,
351 };
352
353 static int __init nft_reject_bridge_module_init(void)
354 {
355         return nft_register_expr(&nft_reject_bridge_type);
356 }
357
358 static void __exit nft_reject_bridge_module_exit(void)
359 {
360         nft_unregister_expr(&nft_reject_bridge_type);
361 }
362
363 module_init(nft_reject_bridge_module_init);
364 module_exit(nft_reject_bridge_module_exit);
365
366 MODULE_LICENSE("GPL");
367 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
368 MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "reject");