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