Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[linux-2.6-microblaze.git] / net / netfilter / nft_chain_filter.c
1 #include <linux/init.h>
2 #include <linux/kernel.h>
3 #include <linux/netdevice.h>
4 #include <net/net_namespace.h>
5 #include <net/netns/generic.h>
6 #include <net/netfilter/nf_tables.h>
7 #include <linux/netfilter_ipv4.h>
8 #include <linux/netfilter_ipv6.h>
9 #include <linux/netfilter_bridge.h>
10 #include <linux/netfilter_arp.h>
11 #include <net/netfilter/nf_tables_ipv4.h>
12 #include <net/netfilter/nf_tables_ipv6.h>
13
14 extern unsigned int nf_tables_net_id;
15
16 #ifdef CONFIG_NF_TABLES_IPV4
17 static unsigned int nft_do_chain_ipv4(void *priv,
18                                       struct sk_buff *skb,
19                                       const struct nf_hook_state *state)
20 {
21         struct nft_pktinfo pkt;
22
23         nft_set_pktinfo(&pkt, skb, state);
24         nft_set_pktinfo_ipv4(&pkt, skb);
25
26         return nft_do_chain(&pkt, priv);
27 }
28
29 static const struct nft_chain_type nft_chain_filter_ipv4 = {
30         .name           = "filter",
31         .type           = NFT_CHAIN_T_DEFAULT,
32         .family         = NFPROTO_IPV4,
33         .hook_mask      = (1 << NF_INET_LOCAL_IN) |
34                           (1 << NF_INET_LOCAL_OUT) |
35                           (1 << NF_INET_FORWARD) |
36                           (1 << NF_INET_PRE_ROUTING) |
37                           (1 << NF_INET_POST_ROUTING),
38         .hooks          = {
39                 [NF_INET_LOCAL_IN]      = nft_do_chain_ipv4,
40                 [NF_INET_LOCAL_OUT]     = nft_do_chain_ipv4,
41                 [NF_INET_FORWARD]       = nft_do_chain_ipv4,
42                 [NF_INET_PRE_ROUTING]   = nft_do_chain_ipv4,
43                 [NF_INET_POST_ROUTING]  = nft_do_chain_ipv4,
44         },
45 };
46
47 static void nft_chain_filter_ipv4_init(void)
48 {
49         nft_register_chain_type(&nft_chain_filter_ipv4);
50 }
51 static void nft_chain_filter_ipv4_fini(void)
52 {
53         nft_unregister_chain_type(&nft_chain_filter_ipv4);
54 }
55
56 #else
57 static inline void nft_chain_filter_ipv4_init(void) {}
58 static inline void nft_chain_filter_ipv4_fini(void) {}
59 #endif /* CONFIG_NF_TABLES_IPV4 */
60
61 #ifdef CONFIG_NF_TABLES_ARP
62 static unsigned int nft_do_chain_arp(void *priv, struct sk_buff *skb,
63                                      const struct nf_hook_state *state)
64 {
65         struct nft_pktinfo pkt;
66
67         nft_set_pktinfo(&pkt, skb, state);
68         nft_set_pktinfo_unspec(&pkt, skb);
69
70         return nft_do_chain(&pkt, priv);
71 }
72
73 static const struct nft_chain_type nft_chain_filter_arp = {
74         .name           = "filter",
75         .type           = NFT_CHAIN_T_DEFAULT,
76         .family         = NFPROTO_ARP,
77         .owner          = THIS_MODULE,
78         .hook_mask      = (1 << NF_ARP_IN) |
79                           (1 << NF_ARP_OUT),
80         .hooks          = {
81                 [NF_ARP_IN]             = nft_do_chain_arp,
82                 [NF_ARP_OUT]            = nft_do_chain_arp,
83         },
84 };
85
86 static void nft_chain_filter_arp_init(void)
87 {
88         nft_register_chain_type(&nft_chain_filter_arp);
89 }
90
91 static void nft_chain_filter_arp_fini(void)
92 {
93         nft_unregister_chain_type(&nft_chain_filter_arp);
94 }
95 #else
96 static inline void nft_chain_filter_arp_init(void) {}
97 static inline void nft_chain_filter_arp_fini(void) {}
98 #endif /* CONFIG_NF_TABLES_ARP */
99
100 #ifdef CONFIG_NF_TABLES_IPV6
101 static unsigned int nft_do_chain_ipv6(void *priv,
102                                       struct sk_buff *skb,
103                                       const struct nf_hook_state *state)
104 {
105         struct nft_pktinfo pkt;
106
107         nft_set_pktinfo(&pkt, skb, state);
108         nft_set_pktinfo_ipv6(&pkt, skb);
109
110         return nft_do_chain(&pkt, priv);
111 }
112
113 static const struct nft_chain_type nft_chain_filter_ipv6 = {
114         .name           = "filter",
115         .type           = NFT_CHAIN_T_DEFAULT,
116         .family         = NFPROTO_IPV6,
117         .hook_mask      = (1 << NF_INET_LOCAL_IN) |
118                           (1 << NF_INET_LOCAL_OUT) |
119                           (1 << NF_INET_FORWARD) |
120                           (1 << NF_INET_PRE_ROUTING) |
121                           (1 << NF_INET_POST_ROUTING),
122         .hooks          = {
123                 [NF_INET_LOCAL_IN]      = nft_do_chain_ipv6,
124                 [NF_INET_LOCAL_OUT]     = nft_do_chain_ipv6,
125                 [NF_INET_FORWARD]       = nft_do_chain_ipv6,
126                 [NF_INET_PRE_ROUTING]   = nft_do_chain_ipv6,
127                 [NF_INET_POST_ROUTING]  = nft_do_chain_ipv6,
128         },
129 };
130
131 static void nft_chain_filter_ipv6_init(void)
132 {
133         nft_register_chain_type(&nft_chain_filter_ipv6);
134 }
135
136 static void nft_chain_filter_ipv6_fini(void)
137 {
138         nft_unregister_chain_type(&nft_chain_filter_ipv6);
139 }
140 #else
141 static inline void nft_chain_filter_ipv6_init(void) {}
142 static inline void nft_chain_filter_ipv6_fini(void) {}
143 #endif /* CONFIG_NF_TABLES_IPV6 */
144
145 #ifdef CONFIG_NF_TABLES_INET
146 static unsigned int nft_do_chain_inet(void *priv, struct sk_buff *skb,
147                                       const struct nf_hook_state *state)
148 {
149         struct nft_pktinfo pkt;
150
151         nft_set_pktinfo(&pkt, skb, state);
152
153         switch (state->pf) {
154         case NFPROTO_IPV4:
155                 nft_set_pktinfo_ipv4(&pkt, skb);
156                 break;
157         case NFPROTO_IPV6:
158                 nft_set_pktinfo_ipv6(&pkt, skb);
159                 break;
160         default:
161                 break;
162         }
163
164         return nft_do_chain(&pkt, priv);
165 }
166
167 static unsigned int nft_do_chain_inet_ingress(void *priv, struct sk_buff *skb,
168                                               const struct nf_hook_state *state)
169 {
170         struct nf_hook_state ingress_state = *state;
171         struct nft_pktinfo pkt;
172
173         switch (skb->protocol) {
174         case htons(ETH_P_IP):
175                 /* Original hook is NFPROTO_NETDEV and NF_NETDEV_INGRESS. */
176                 ingress_state.pf = NFPROTO_IPV4;
177                 ingress_state.hook = NF_INET_INGRESS;
178                 nft_set_pktinfo(&pkt, skb, &ingress_state);
179
180                 if (nft_set_pktinfo_ipv4_ingress(&pkt, skb) < 0)
181                         return NF_DROP;
182                 break;
183         case htons(ETH_P_IPV6):
184                 ingress_state.pf = NFPROTO_IPV6;
185                 ingress_state.hook = NF_INET_INGRESS;
186                 nft_set_pktinfo(&pkt, skb, &ingress_state);
187
188                 if (nft_set_pktinfo_ipv6_ingress(&pkt, skb) < 0)
189                         return NF_DROP;
190                 break;
191         default:
192                 return NF_ACCEPT;
193         }
194
195         return nft_do_chain(&pkt, priv);
196 }
197
198 static const struct nft_chain_type nft_chain_filter_inet = {
199         .name           = "filter",
200         .type           = NFT_CHAIN_T_DEFAULT,
201         .family         = NFPROTO_INET,
202         .hook_mask      = (1 << NF_INET_INGRESS) |
203                           (1 << NF_INET_LOCAL_IN) |
204                           (1 << NF_INET_LOCAL_OUT) |
205                           (1 << NF_INET_FORWARD) |
206                           (1 << NF_INET_PRE_ROUTING) |
207                           (1 << NF_INET_POST_ROUTING),
208         .hooks          = {
209                 [NF_INET_INGRESS]       = nft_do_chain_inet_ingress,
210                 [NF_INET_LOCAL_IN]      = nft_do_chain_inet,
211                 [NF_INET_LOCAL_OUT]     = nft_do_chain_inet,
212                 [NF_INET_FORWARD]       = nft_do_chain_inet,
213                 [NF_INET_PRE_ROUTING]   = nft_do_chain_inet,
214                 [NF_INET_POST_ROUTING]  = nft_do_chain_inet,
215         },
216 };
217
218 static void nft_chain_filter_inet_init(void)
219 {
220         nft_register_chain_type(&nft_chain_filter_inet);
221 }
222
223 static void nft_chain_filter_inet_fini(void)
224 {
225         nft_unregister_chain_type(&nft_chain_filter_inet);
226 }
227 #else
228 static inline void nft_chain_filter_inet_init(void) {}
229 static inline void nft_chain_filter_inet_fini(void) {}
230 #endif /* CONFIG_NF_TABLES_IPV6 */
231
232 #if IS_ENABLED(CONFIG_NF_TABLES_BRIDGE)
233 static unsigned int
234 nft_do_chain_bridge(void *priv,
235                     struct sk_buff *skb,
236                     const struct nf_hook_state *state)
237 {
238         struct nft_pktinfo pkt;
239
240         nft_set_pktinfo(&pkt, skb, state);
241
242         switch (eth_hdr(skb)->h_proto) {
243         case htons(ETH_P_IP):
244                 nft_set_pktinfo_ipv4_validate(&pkt, skb);
245                 break;
246         case htons(ETH_P_IPV6):
247                 nft_set_pktinfo_ipv6_validate(&pkt, skb);
248                 break;
249         default:
250                 nft_set_pktinfo_unspec(&pkt, skb);
251                 break;
252         }
253
254         return nft_do_chain(&pkt, priv);
255 }
256
257 static const struct nft_chain_type nft_chain_filter_bridge = {
258         .name           = "filter",
259         .type           = NFT_CHAIN_T_DEFAULT,
260         .family         = NFPROTO_BRIDGE,
261         .hook_mask      = (1 << NF_BR_PRE_ROUTING) |
262                           (1 << NF_BR_LOCAL_IN) |
263                           (1 << NF_BR_FORWARD) |
264                           (1 << NF_BR_LOCAL_OUT) |
265                           (1 << NF_BR_POST_ROUTING),
266         .hooks          = {
267                 [NF_BR_PRE_ROUTING]     = nft_do_chain_bridge,
268                 [NF_BR_LOCAL_IN]        = nft_do_chain_bridge,
269                 [NF_BR_FORWARD]         = nft_do_chain_bridge,
270                 [NF_BR_LOCAL_OUT]       = nft_do_chain_bridge,
271                 [NF_BR_POST_ROUTING]    = nft_do_chain_bridge,
272         },
273 };
274
275 static void nft_chain_filter_bridge_init(void)
276 {
277         nft_register_chain_type(&nft_chain_filter_bridge);
278 }
279
280 static void nft_chain_filter_bridge_fini(void)
281 {
282         nft_unregister_chain_type(&nft_chain_filter_bridge);
283 }
284 #else
285 static inline void nft_chain_filter_bridge_init(void) {}
286 static inline void nft_chain_filter_bridge_fini(void) {}
287 #endif /* CONFIG_NF_TABLES_BRIDGE */
288
289 #ifdef CONFIG_NF_TABLES_NETDEV
290 static unsigned int nft_do_chain_netdev(void *priv, struct sk_buff *skb,
291                                         const struct nf_hook_state *state)
292 {
293         struct nft_pktinfo pkt;
294
295         nft_set_pktinfo(&pkt, skb, state);
296
297         switch (skb->protocol) {
298         case htons(ETH_P_IP):
299                 nft_set_pktinfo_ipv4_validate(&pkt, skb);
300                 break;
301         case htons(ETH_P_IPV6):
302                 nft_set_pktinfo_ipv6_validate(&pkt, skb);
303                 break;
304         default:
305                 nft_set_pktinfo_unspec(&pkt, skb);
306                 break;
307         }
308
309         return nft_do_chain(&pkt, priv);
310 }
311
312 static const struct nft_chain_type nft_chain_filter_netdev = {
313         .name           = "filter",
314         .type           = NFT_CHAIN_T_DEFAULT,
315         .family         = NFPROTO_NETDEV,
316         .hook_mask      = (1 << NF_NETDEV_INGRESS),
317         .hooks          = {
318                 [NF_NETDEV_INGRESS]     = nft_do_chain_netdev,
319         },
320 };
321
322 static void nft_netdev_event(unsigned long event, struct net_device *dev,
323                              struct nft_ctx *ctx)
324 {
325         struct nft_base_chain *basechain = nft_base_chain(ctx->chain);
326         struct nft_hook *hook, *found = NULL;
327         int n = 0;
328
329         if (event != NETDEV_UNREGISTER)
330                 return;
331
332         list_for_each_entry(hook, &basechain->hook_list, list) {
333                 if (hook->ops.dev == dev)
334                         found = hook;
335
336                 n++;
337         }
338         if (!found)
339                 return;
340
341         if (n > 1) {
342                 nf_unregister_net_hook(ctx->net, &found->ops);
343                 list_del_rcu(&found->list);
344                 kfree_rcu(found, rcu);
345                 return;
346         }
347
348         /* UNREGISTER events are also happening on netns exit.
349          *
350          * Although nf_tables core releases all tables/chains, only this event
351          * handler provides guarantee that hook->ops.dev is still accessible,
352          * so we cannot skip exiting net namespaces.
353          */
354         __nft_release_basechain(ctx);
355 }
356
357 static int nf_tables_netdev_event(struct notifier_block *this,
358                                   unsigned long event, void *ptr)
359 {
360         struct net_device *dev = netdev_notifier_info_to_dev(ptr);
361         struct nftables_pernet *nft_net;
362         struct nft_table *table;
363         struct nft_chain *chain, *nr;
364         struct nft_ctx ctx = {
365                 .net    = dev_net(dev),
366         };
367
368         if (event != NETDEV_UNREGISTER &&
369             event != NETDEV_CHANGENAME)
370                 return NOTIFY_DONE;
371
372         nft_net = net_generic(ctx.net, nf_tables_net_id);
373         mutex_lock(&nft_net->commit_mutex);
374         list_for_each_entry(table, &nft_net->tables, list) {
375                 if (table->family != NFPROTO_NETDEV)
376                         continue;
377
378                 ctx.family = table->family;
379                 ctx.table = table;
380                 list_for_each_entry_safe(chain, nr, &table->chains, list) {
381                         if (!nft_is_base_chain(chain))
382                                 continue;
383
384                         ctx.chain = chain;
385                         nft_netdev_event(event, dev, &ctx);
386                 }
387         }
388         mutex_unlock(&nft_net->commit_mutex);
389
390         return NOTIFY_DONE;
391 }
392
393 static struct notifier_block nf_tables_netdev_notifier = {
394         .notifier_call  = nf_tables_netdev_event,
395 };
396
397 static int nft_chain_filter_netdev_init(void)
398 {
399         int err;
400
401         nft_register_chain_type(&nft_chain_filter_netdev);
402
403         err = register_netdevice_notifier(&nf_tables_netdev_notifier);
404         if (err)
405                 goto err_register_netdevice_notifier;
406
407         return 0;
408
409 err_register_netdevice_notifier:
410         nft_unregister_chain_type(&nft_chain_filter_netdev);
411
412         return err;
413 }
414
415 static void nft_chain_filter_netdev_fini(void)
416 {
417         nft_unregister_chain_type(&nft_chain_filter_netdev);
418         unregister_netdevice_notifier(&nf_tables_netdev_notifier);
419 }
420 #else
421 static inline int nft_chain_filter_netdev_init(void) { return 0; }
422 static inline void nft_chain_filter_netdev_fini(void) {}
423 #endif /* CONFIG_NF_TABLES_NETDEV */
424
425 int __init nft_chain_filter_init(void)
426 {
427         int err;
428
429         err = nft_chain_filter_netdev_init();
430         if (err < 0)
431                 return err;
432
433         nft_chain_filter_ipv4_init();
434         nft_chain_filter_ipv6_init();
435         nft_chain_filter_arp_init();
436         nft_chain_filter_inet_init();
437         nft_chain_filter_bridge_init();
438
439         return 0;
440 }
441
442 void nft_chain_filter_fini(void)
443 {
444         nft_chain_filter_bridge_fini();
445         nft_chain_filter_inet_fini();
446         nft_chain_filter_arp_fini();
447         nft_chain_filter_ipv6_fini();
448         nft_chain_filter_ipv4_fini();
449         nft_chain_filter_netdev_fini();
450 }