xref: /linux-6.15/net/netfilter/nf_queue.c (revision 3f801968)
1f229f6ceSPatrick McHardy /*
2f229f6ceSPatrick McHardy  * Rusty Russell (C)2000 -- This code is GPL.
3f229f6ceSPatrick McHardy  * Patrick McHardy (c) 2006-2012
4f229f6ceSPatrick McHardy  */
5f229f6ceSPatrick McHardy 
6f6ebe77fSHarald Welte #include <linux/kernel.h>
75a0e3ad6STejun Heo #include <linux/slab.h>
8f6ebe77fSHarald Welte #include <linux/init.h>
9f6ebe77fSHarald Welte #include <linux/module.h>
10f6ebe77fSHarald Welte #include <linux/proc_fs.h>
11f6ebe77fSHarald Welte #include <linux/skbuff.h>
12f6ebe77fSHarald Welte #include <linux/netfilter.h>
137db9a51eSPablo Neira Ayuso #include <linux/netfilter_ipv4.h>
147db9a51eSPablo Neira Ayuso #include <linux/netfilter_ipv6.h>
15c737b7c4SFlorian Westphal #include <linux/netfilter_bridge.h>
16bbd86b9fSHarald Welte #include <linux/seq_file.h>
177a11b984SPatrick McHardy #include <linux/rcupdate.h>
18f6ebe77fSHarald Welte #include <net/protocol.h>
19c01cd429SPatrick McHardy #include <net/netfilter/nf_queue.h>
207fee226aSEric Dumazet #include <net/dst.h>
21f6ebe77fSHarald Welte 
22f6ebe77fSHarald Welte #include "nf_internals.h"
23f6ebe77fSHarald Welte 
2487029970SFlorian Westphal static const struct nf_queue_handler __rcu *nf_queue_handler;
2587029970SFlorian Westphal 
26f6ebe77fSHarald Welte /*
270360ae41SFlorian Westphal  * Hook for nfnetlink_queue to register its queue handler.
280360ae41SFlorian Westphal  * We do this so that most of the NFQUEUE code can be modular.
290360ae41SFlorian Westphal  *
300360ae41SFlorian Westphal  * Once the queue is registered it must reinject all packets it
310360ae41SFlorian Westphal  * receives, no matter what.
32f6ebe77fSHarald Welte  */
33f6ebe77fSHarald Welte 
nf_register_queue_handler(const struct nf_queue_handler * qh)3487029970SFlorian Westphal void nf_register_queue_handler(const struct nf_queue_handler *qh)
35f6ebe77fSHarald Welte {
360360ae41SFlorian Westphal 	/* should never happen, we only have one queueing backend in kernel */
3787029970SFlorian Westphal 	WARN_ON(rcu_access_pointer(nf_queue_handler));
3887029970SFlorian Westphal 	rcu_assign_pointer(nf_queue_handler, qh);
39f6ebe77fSHarald Welte }
40f6ebe77fSHarald Welte EXPORT_SYMBOL(nf_register_queue_handler);
41f6ebe77fSHarald Welte 
42f6ebe77fSHarald Welte /* The caller must flush their queue before this */
nf_unregister_queue_handler(void)4387029970SFlorian Westphal void nf_unregister_queue_handler(void)
44f6ebe77fSHarald Welte {
4587029970SFlorian Westphal 	RCU_INIT_POINTER(nf_queue_handler, NULL);
46f6ebe77fSHarald Welte }
47f6ebe77fSHarald Welte EXPORT_SYMBOL(nf_unregister_queue_handler);
48f6ebe77fSHarald Welte 
nf_queue_sock_put(struct sock * sk)49747670fdSFlorian Westphal static void nf_queue_sock_put(struct sock *sk)
50747670fdSFlorian Westphal {
51747670fdSFlorian Westphal #ifdef CONFIG_INET
52747670fdSFlorian Westphal 	sock_gen_put(sk);
53747670fdSFlorian Westphal #else
54747670fdSFlorian Westphal 	sock_put(sk);
55747670fdSFlorian Westphal #endif
56747670fdSFlorian Westphal }
57747670fdSFlorian Westphal 
nf_queue_entry_release_refs(struct nf_queue_entry * entry)58dd3cc111SFlorian Westphal static void nf_queue_entry_release_refs(struct nf_queue_entry *entry)
59daaa8be2SPatrick McHardy {
601d1de89bSDavid S. Miller 	struct nf_hook_state *state = &entry->state;
611d1de89bSDavid S. Miller 
62daaa8be2SPatrick McHardy 	/* Release those devices we held, or Alexey will kill me. */
631d1de89bSDavid S. Miller 	dev_put(state->in);
641d1de89bSDavid S. Miller 	dev_put(state->out);
651c984f8aSDavid Miller 	if (state->sk)
66747670fdSFlorian Westphal 		nf_queue_sock_put(state->sk);
67c4b0e771SFlorian Westphal 
68119e52e6SFlorian Westphal #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
69119e52e6SFlorian Westphal 	dev_put(entry->physin);
70119e52e6SFlorian Westphal 	dev_put(entry->physout);
71119e52e6SFlorian Westphal #endif
72c4b0e771SFlorian Westphal }
73dd3cc111SFlorian Westphal 
nf_queue_entry_free(struct nf_queue_entry * entry)74dd3cc111SFlorian Westphal void nf_queue_entry_free(struct nf_queue_entry *entry)
75dd3cc111SFlorian Westphal {
76dd3cc111SFlorian Westphal 	nf_queue_entry_release_refs(entry);
77dd3cc111SFlorian Westphal 	kfree(entry);
78dd3cc111SFlorian Westphal }
79dd3cc111SFlorian Westphal EXPORT_SYMBOL_GPL(nf_queue_entry_free);
80c4b0e771SFlorian Westphal 
__nf_queue_entry_init_physdevs(struct nf_queue_entry * entry)81119e52e6SFlorian Westphal static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry)
82c4b0e771SFlorian Westphal {
831109a90cSPablo Neira Ayuso #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
84119e52e6SFlorian Westphal 	const struct sk_buff *skb = entry->skb;
85c4b0e771SFlorian Westphal 
86aeaa4407SPavel Tikhomirov 	if (nf_bridge_info_exists(skb)) {
87*a54e7219SPavel Tikhomirov 		entry->physin = nf_bridge_get_physindev(skb, entry->state.net);
88119e52e6SFlorian Westphal 		entry->physout = nf_bridge_get_physoutdev(skb);
89119e52e6SFlorian Westphal 	} else {
90119e52e6SFlorian Westphal 		entry->physin = NULL;
91119e52e6SFlorian Westphal 		entry->physout = NULL;
92daaa8be2SPatrick McHardy 	}
93daaa8be2SPatrick McHardy #endif
94daaa8be2SPatrick McHardy }
95daaa8be2SPatrick McHardy 
964bd60443SFlorian Westphal /* Bump dev refs so they don't vanish while packet is out */
nf_queue_entry_get_refs(struct nf_queue_entry * entry)97c3873070SFlorian Westphal bool nf_queue_entry_get_refs(struct nf_queue_entry *entry)
984bd60443SFlorian Westphal {
991d1de89bSDavid S. Miller 	struct nf_hook_state *state = &entry->state;
1001d1de89bSDavid S. Miller 
101c3873070SFlorian Westphal 	if (state->sk && !refcount_inc_not_zero(&state->sk->sk_refcnt))
102c3873070SFlorian Westphal 		return false;
103c3873070SFlorian Westphal 
1041d1de89bSDavid S. Miller 	dev_hold(state->in);
1051d1de89bSDavid S. Miller 	dev_hold(state->out);
1064bd60443SFlorian Westphal 
107119e52e6SFlorian Westphal #if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
108119e52e6SFlorian Westphal 	dev_hold(entry->physin);
109119e52e6SFlorian Westphal 	dev_hold(entry->physout);
110119e52e6SFlorian Westphal #endif
111c3873070SFlorian Westphal 	return true;
1124bd60443SFlorian Westphal }
113a5fedd43SFlorian Westphal EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
1144bd60443SFlorian Westphal 
nf_queue_nf_hook_drop(struct net * net)11526888dfdSFlorian Westphal void nf_queue_nf_hook_drop(struct net *net)
1168405a8ffSEric W. Biederman {
1178405a8ffSEric W. Biederman 	const struct nf_queue_handler *qh;
1188405a8ffSEric W. Biederman 
1198405a8ffSEric W. Biederman 	rcu_read_lock();
12087029970SFlorian Westphal 	qh = rcu_dereference(nf_queue_handler);
1212385eb0cSPablo Neira Ayuso 	if (qh)
12226888dfdSFlorian Westphal 		qh->nf_hook_drop(net);
1238405a8ffSEric W. Biederman 	rcu_read_unlock();
1248405a8ffSEric W. Biederman }
125e2a75007SFlorian Westphal EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop);
1268405a8ffSEric W. Biederman 
nf_ip_saveroute(const struct sk_buff * skb,struct nf_queue_entry * entry)1277db9a51eSPablo Neira Ayuso static void nf_ip_saveroute(const struct sk_buff *skb,
1287db9a51eSPablo Neira Ayuso 			    struct nf_queue_entry *entry)
1297db9a51eSPablo Neira Ayuso {
1307db9a51eSPablo Neira Ayuso 	struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
1317db9a51eSPablo Neira Ayuso 
1327db9a51eSPablo Neira Ayuso 	if (entry->state.hook == NF_INET_LOCAL_OUT) {
1337db9a51eSPablo Neira Ayuso 		const struct iphdr *iph = ip_hdr(skb);
1347db9a51eSPablo Neira Ayuso 
1357db9a51eSPablo Neira Ayuso 		rt_info->tos = iph->tos;
1367db9a51eSPablo Neira Ayuso 		rt_info->daddr = iph->daddr;
1377db9a51eSPablo Neira Ayuso 		rt_info->saddr = iph->saddr;
1387db9a51eSPablo Neira Ayuso 		rt_info->mark = skb->mark;
1397db9a51eSPablo Neira Ayuso 	}
1407db9a51eSPablo Neira Ayuso }
1417db9a51eSPablo Neira Ayuso 
nf_ip6_saveroute(const struct sk_buff * skb,struct nf_queue_entry * entry)1427db9a51eSPablo Neira Ayuso static void nf_ip6_saveroute(const struct sk_buff *skb,
1437db9a51eSPablo Neira Ayuso 			     struct nf_queue_entry *entry)
1447db9a51eSPablo Neira Ayuso {
1457db9a51eSPablo Neira Ayuso 	struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry);
1467db9a51eSPablo Neira Ayuso 
1477db9a51eSPablo Neira Ayuso 	if (entry->state.hook == NF_INET_LOCAL_OUT) {
1487db9a51eSPablo Neira Ayuso 		const struct ipv6hdr *iph = ipv6_hdr(skb);
1497db9a51eSPablo Neira Ayuso 
1507db9a51eSPablo Neira Ayuso 		rt_info->daddr = iph->daddr;
1517db9a51eSPablo Neira Ayuso 		rt_info->saddr = iph->saddr;
1527db9a51eSPablo Neira Ayuso 		rt_info->mark = skb->mark;
1537db9a51eSPablo Neira Ayuso 	}
1547db9a51eSPablo Neira Ayuso }
1557db9a51eSPablo Neira Ayuso 
__nf_queue(struct sk_buff * skb,const struct nf_hook_state * state,unsigned int index,unsigned int queuenum)1567034b566SPablo Neira Ayuso static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state,
157960632ecSAaron Conole 		      unsigned int index, unsigned int queuenum)
158f6ebe77fSHarald Welte {
159daaa8be2SPatrick McHardy 	struct nf_queue_entry *entry = NULL;
160e3ac5298SPatrick McHardy 	const struct nf_queue_handler *qh;
16146435623SPablo Neira Ayuso 	unsigned int route_key_size;
16228f715b9SFlorian Westphal 	int status;
163f6ebe77fSHarald Welte 
164f6ebe77fSHarald Welte 	/* QUEUE == DROP if no one is waiting, to be safe. */
16587029970SFlorian Westphal 	qh = rcu_dereference(nf_queue_handler);
16628f715b9SFlorian Westphal 	if (!qh)
16728f715b9SFlorian Westphal 		return -ESRCH;
168f6ebe77fSHarald Welte 
16946435623SPablo Neira Ayuso 	switch (state->pf) {
17046435623SPablo Neira Ayuso 	case AF_INET:
17146435623SPablo Neira Ayuso 		route_key_size = sizeof(struct ip_rt_info);
17246435623SPablo Neira Ayuso 		break;
17346435623SPablo Neira Ayuso 	case AF_INET6:
17446435623SPablo Neira Ayuso 		route_key_size = sizeof(struct ip6_rt_info);
17546435623SPablo Neira Ayuso 		break;
17646435623SPablo Neira Ayuso 	default:
17746435623SPablo Neira Ayuso 		route_key_size = 0;
17846435623SPablo Neira Ayuso 		break;
17946435623SPablo Neira Ayuso 	}
180bce8032eSPatrick McHardy 
1813b836da4SFlorian Westphal 	if (skb_sk_is_prefetched(skb)) {
1823b836da4SFlorian Westphal 		struct sock *sk = skb->sk;
1833b836da4SFlorian Westphal 
1843b836da4SFlorian Westphal 		if (!sk_is_refcounted(sk)) {
1853b836da4SFlorian Westphal 			if (!refcount_inc_not_zero(&sk->sk_refcnt))
1863b836da4SFlorian Westphal 				return -ENOTCONN;
1873b836da4SFlorian Westphal 
1883b836da4SFlorian Westphal 			/* drop refcount on skb_orphan */
1893b836da4SFlorian Westphal 			skb->destructor = sock_edemux;
1903b836da4SFlorian Westphal 		}
1913b836da4SFlorian Westphal 	}
1923b836da4SFlorian Westphal 
19346435623SPablo Neira Ayuso 	entry = kmalloc(sizeof(*entry) + route_key_size, GFP_ATOMIC);
19428f715b9SFlorian Westphal 	if (!entry)
19528f715b9SFlorian Westphal 		return -ENOMEM;
196f6ebe77fSHarald Welte 
1970b9173f4SMarco Oliverio 	if (skb_dst(skb) && !skb_dst_force(skb)) {
19828f715b9SFlorian Westphal 		kfree(entry);
19928f715b9SFlorian Westphal 		return -ENETDOWN;
200b60a7738SFlorian Westphal 	}
201b60a7738SFlorian Westphal 
20202f014d8SPatrick McHardy 	*entry = (struct nf_queue_entry) {
20302f014d8SPatrick McHardy 		.skb	= skb,
2041d1de89bSDavid S. Miller 		.state	= *state,
205960632ecSAaron Conole 		.hook_index = index,
20646435623SPablo Neira Ayuso 		.size	= sizeof(*entry) + route_key_size,
20702f014d8SPatrick McHardy 	};
208f6ebe77fSHarald Welte 
209119e52e6SFlorian Westphal 	__nf_queue_entry_init_physdevs(entry);
210119e52e6SFlorian Westphal 
211c3873070SFlorian Westphal 	if (!nf_queue_entry_get_refs(entry)) {
212c3873070SFlorian Westphal 		kfree(entry);
213c3873070SFlorian Westphal 		return -ENOTCONN;
214c3873070SFlorian Westphal 	}
2157db9a51eSPablo Neira Ayuso 
2167db9a51eSPablo Neira Ayuso 	switch (entry->state.pf) {
2177db9a51eSPablo Neira Ayuso 	case AF_INET:
2187db9a51eSPablo Neira Ayuso 		nf_ip_saveroute(skb, entry);
2197db9a51eSPablo Neira Ayuso 		break;
2207db9a51eSPablo Neira Ayuso 	case AF_INET6:
2217db9a51eSPablo Neira Ayuso 		nf_ip6_saveroute(skb, entry);
2227db9a51eSPablo Neira Ayuso 		break;
2237db9a51eSPablo Neira Ayuso 	}
2247db9a51eSPablo Neira Ayuso 
22502f014d8SPatrick McHardy 	status = qh->outfn(entry, queuenum);
226f6ebe77fSHarald Welte 	if (status < 0) {
22728f715b9SFlorian Westphal 		nf_queue_entry_free(entry);
22828f715b9SFlorian Westphal 		return status;
229f6ebe77fSHarald Welte 	}
230f6ebe77fSHarald Welte 
231f1585086SFlorian Westphal 	return 0;
232f6ebe77fSHarald Welte }
233f6ebe77fSHarald Welte 
2347034b566SPablo Neira Ayuso /* Packets leaving via this function must come back through nf_reinject(). */
nf_queue(struct sk_buff * skb,struct nf_hook_state * state,unsigned int index,unsigned int verdict)2357034b566SPablo Neira Ayuso int nf_queue(struct sk_buff *skb, struct nf_hook_state *state,
2360d9cb300SFlorian Westphal 	     unsigned int index, unsigned int verdict)
2377034b566SPablo Neira Ayuso {
2387034b566SPablo Neira Ayuso 	int ret;
2397034b566SPablo Neira Ayuso 
2400d9cb300SFlorian Westphal 	ret = __nf_queue(skb, state, index, verdict >> NF_VERDICT_QBITS);
2417034b566SPablo Neira Ayuso 	if (ret < 0) {
2427034b566SPablo Neira Ayuso 		if (ret == -ESRCH &&
243960632ecSAaron Conole 		    (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS))
2447034b566SPablo Neira Ayuso 			return 1;
2457034b566SPablo Neira Ayuso 		kfree_skb(skb);
2467034b566SPablo Neira Ayuso 	}
2477034b566SPablo Neira Ayuso 
2487034b566SPablo Neira Ayuso 	return 0;
2497034b566SPablo Neira Ayuso }
250971502d7SFlorian Westphal EXPORT_SYMBOL_GPL(nf_queue);
251