Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268// SPDX-License-Identifier: GPL-2.0-or-later/* * Rusty Russell (C)2000 -- This code is GPL. * Patrick McHardy (c) 2006-2012 */
#include <linux/kernel.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/module.h>#include <linux/proc_fs.h>#include <linux/skbuff.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv4.h>#include <linux/netfilter_ipv6.h>#include <linux/netfilter_bridge.h>#include <linux/seq_file.h>#include <linux/rcupdate.h>#include <net/protocol.h>#include <net/netfilter/nf_queue.h>#include <net/dst.h>
#include "nf_internals.h"
static const struct nf_queue_handler __rcu *nf_queue_handler;
/* * Hook for nfnetlink_queue to register its queue handler. * We do this so that most of the NFQUEUE code can be modular. * * Once the queue is registered it must reinject all packets it * receives, no matter what. */
void nf_register_queue_handler(const struct nf_queue_handler *qh){ /* should never happen, we only have one queueing backend in kernel */ WARN_ON(rcu_access_pointer(nf_queue_handler)); rcu_assign_pointer(nf_queue_handler, qh);}EXPORT_SYMBOL(nf_register_queue_handler);
/* The caller must flush their queue before this */void nf_unregister_queue_handler(void){ RCU_INIT_POINTER(nf_queue_handler, NULL);}EXPORT_SYMBOL(nf_unregister_queue_handler);
static void nf_queue_sock_put(struct sock *sk){#ifdef CONFIG_INET sock_gen_put(sk);#else sock_put(sk);#endif}
static void nf_queue_entry_release_refs(struct nf_queue_entry *entry){ struct nf_hook_state *state = &entry->state;
/* Release those devices we held, or Alexey will kill me. */ dev_put(entry->skb_dev); dev_put(state->in); dev_put(state->out); if (state->sk) nf_queue_sock_put(state->sk);
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) dev_put(entry->bridge_dev); dev_put(entry->physin); dev_put(entry->physout);#endif}
void nf_queue_entry_free(struct nf_queue_entry *entry){ nf_queue_entry_release_refs(entry); kfree(entry);}EXPORT_SYMBOL_GPL(nf_queue_entry_free);
static void __nf_queue_entry_init_physdevs(struct nf_queue_entry *entry){#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) const struct sk_buff *skb = entry->skb; struct dst_entry *dst = skb_dst(skb); struct net_device *dev = NULL;
if (nf_bridge_info_exists(skb)) { entry->physin = nf_bridge_get_physindev(skb, entry->state.net); entry->physout = nf_bridge_get_physoutdev(skb); } else { entry->physin = NULL; entry->physout = NULL; }
if (entry->state.pf == NFPROTO_BRIDGE && dst && (dst->flags & DST_FAKE_RTABLE)) dev = dst_dev_rcu(dst);
/* Must hold a reference on the bridge device: dst_hold() protects * the dst itself, but the fake rtable is embedded in bridge-private * storage that netdevice teardown can free independently. */ entry->bridge_dev = dev;#endif}
/* Bump dev refs so they don't vanish while packet is out */bool nf_queue_entry_get_refs(struct nf_queue_entry *entry){ struct nf_hook_state *state = &entry->state;
if (state->sk && !refcount_inc_not_zero(&state->sk->sk_refcnt)) return false;
dev_hold(entry->skb_dev); dev_hold(state->in); dev_hold(state->out);
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER) dev_hold(entry->bridge_dev); dev_hold(entry->physin); dev_hold(entry->physout);#endif return true;}EXPORT_SYMBOL_GPL(nf_queue_entry_get_refs);
void nf_queue_nf_hook_drop(struct net *net){ const struct nf_queue_handler *qh;
rcu_read_lock(); qh = rcu_dereference(nf_queue_handler); if (qh) qh->nf_hook_drop(net); rcu_read_unlock();}EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop);
static void nf_ip_saveroute(const struct sk_buff *skb, struct nf_queue_entry *entry){ struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
if (entry->state.hook == NF_INET_LOCAL_OUT) { const struct iphdr *iph = ip_hdr(skb);
rt_info->tos = iph->tos; rt_info->daddr = iph->daddr; rt_info->saddr = iph->saddr; rt_info->mark = skb->mark; }}
static void nf_ip6_saveroute(const struct sk_buff *skb, struct nf_queue_entry *entry){ struct ip6_rt_info *rt_info = nf_queue_entry_reroute(entry);
if (entry->state.hook == NF_INET_LOCAL_OUT) { const struct ipv6hdr *iph = ipv6_hdr(skb);
rt_info->daddr = iph->daddr; rt_info->saddr = iph->saddr; rt_info->mark = skb->mark; }}
static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, unsigned int index, unsigned int queuenum){ struct nf_queue_entry *entry = NULL; const struct nf_queue_handler *qh; unsigned int route_key_size; int status;
/* QUEUE == DROP if no one is waiting, to be safe. */ qh = rcu_dereference(nf_queue_handler); if (!qh) return -ESRCH;
switch (state->pf) { case AF_INET: route_key_size = sizeof(struct ip_rt_info); break; case AF_INET6: route_key_size = sizeof(struct ip6_rt_info); break; default: route_key_size = 0; break; }
if (skb_sk_is_prefetched(skb)) { struct sock *sk = skb->sk;
if (!sk_is_refcounted(sk)) { if (!refcount_inc_not_zero(&sk->sk_refcnt)) return -ENOTCONN;
/* drop refcount on skb_orphan */ skb->destructor = sock_edemux; } }
entry = kmalloc(sizeof(*entry) + route_key_size, GFP_ATOMIC); if (!entry) return -ENOMEM;
if (skb_dst(skb) && !skb_dst_force(skb)) { kfree(entry); return -ENETDOWN; }
*entry = (struct nf_queue_entry) { .skb = skb, .skb_dev = skb->dev, .state = *state, .hook_index = index, .size = sizeof(*entry) + route_key_size, }; __nf_queue_entry_init_physdevs(entry);
if (!nf_queue_entry_get_refs(entry)) { kfree(entry); return -ENOTCONN; }
switch (entry->state.pf) { case AF_INET: nf_ip_saveroute(skb, entry); break; case AF_INET6: nf_ip6_saveroute(skb, entry); break; }
status = qh->outfn(entry, queuenum); if (status < 0) { nf_queue_entry_free(entry); return status; }
return 0;}
/* Packets leaving via this function must come back through nf_reinject(). */int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, unsigned int index, unsigned int verdict){ int ret;
ret = __nf_queue(skb, state, index, verdict >> NF_VERDICT_QBITS); if (ret < 0) { if (ret == -ESRCH && (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) return 1; kfree_skb(skb); }
return 0;}EXPORT_SYMBOL_GPL(nf_queue);