File Index
Files and directories
Sign in
FILE

nft_chain_filter.c

File preview with syntax highlighting.

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