xref: /linux/net/netfilter/nft_chain_filter.c (revision dc1d9408c961c1c4d4b3b99a1d9390c17e13de71)
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