linux/net/netfilter/nf_queue.c
Arjan van de Ven da7071d7e3 [PATCH] mark struct file_operations const 8
Many struct file_operations in the kernel can be "const".  Marking them const
moves these to the .rodata section, which avoids false sharing with potential
dirty data.  In addition it'll catch accidental writes at compile time to
these shared resources.

Signed-off-by: Arjan van de Ven <arjan@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-02-12 09:48:46 -08:00

357 lines
7.6 KiB
C

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/seq_file.h>
#include <linux/rcupdate.h>
#include <net/protocol.h>
#include "nf_internals.h"
/*
* A queue handler may be registered for each protocol. Each is protected by
* long term mutex. The handler must provide an an outfn() to accept packets
* for queueing and must reinject all packets it receives, no matter what.
*/
static struct nf_queue_handler *queue_handler[NPROTO];
static DEFINE_RWLOCK(queue_handler_lock);
/* return EBUSY when somebody else is registered, return EEXIST if the
* same handler is registered, return 0 in case of success. */
int nf_register_queue_handler(int pf, struct nf_queue_handler *qh)
{
int ret;
if (pf >= NPROTO)
return -EINVAL;
write_lock_bh(&queue_handler_lock);
if (queue_handler[pf] == qh)
ret = -EEXIST;
else if (queue_handler[pf])
ret = -EBUSY;
else {
queue_handler[pf] = qh;
ret = 0;
}
write_unlock_bh(&queue_handler_lock);
return ret;
}
EXPORT_SYMBOL(nf_register_queue_handler);
/* The caller must flush their queue before this */
int nf_unregister_queue_handler(int pf)
{
if (pf >= NPROTO)
return -EINVAL;
write_lock_bh(&queue_handler_lock);
queue_handler[pf] = NULL;
write_unlock_bh(&queue_handler_lock);
return 0;
}
EXPORT_SYMBOL(nf_unregister_queue_handler);
void nf_unregister_queue_handlers(struct nf_queue_handler *qh)
{
int pf;
write_lock_bh(&queue_handler_lock);
for (pf = 0; pf < NPROTO; pf++) {
if (queue_handler[pf] == qh)
queue_handler[pf] = NULL;
}
write_unlock_bh(&queue_handler_lock);
}
EXPORT_SYMBOL_GPL(nf_unregister_queue_handlers);
/*
* Any packet that leaves via this function must come back
* through nf_reinject().
*/
static int __nf_queue(struct sk_buff *skb,
struct list_head *elem,
int pf, unsigned int hook,
struct net_device *indev,
struct net_device *outdev,
int (*okfn)(struct sk_buff *),
unsigned int queuenum)
{
int status;
struct nf_info *info;
#ifdef CONFIG_BRIDGE_NETFILTER
struct net_device *physindev = NULL;
struct net_device *physoutdev = NULL;
#endif
struct nf_afinfo *afinfo;
/* QUEUE == DROP if noone is waiting, to be safe. */
read_lock(&queue_handler_lock);
if (!queue_handler[pf]) {
read_unlock(&queue_handler_lock);
kfree_skb(skb);
return 1;
}
afinfo = nf_get_afinfo(pf);
if (!afinfo) {
read_unlock(&queue_handler_lock);
kfree_skb(skb);
return 1;
}
info = kmalloc(sizeof(*info) + afinfo->route_key_size, GFP_ATOMIC);
if (!info) {
if (net_ratelimit())
printk(KERN_ERR "OOM queueing packet %p\n",
skb);
read_unlock(&queue_handler_lock);
kfree_skb(skb);
return 1;
}
*info = (struct nf_info) {
(struct nf_hook_ops *)elem, pf, hook, indev, outdev, okfn };
/* If it's going away, ignore hook. */
if (!try_module_get(info->elem->owner)) {
read_unlock(&queue_handler_lock);
kfree(info);
return 0;
}
/* Bump dev refs so they don't vanish while packet is out */
if (indev) dev_hold(indev);
if (outdev) dev_hold(outdev);
#ifdef CONFIG_BRIDGE_NETFILTER
if (skb->nf_bridge) {
physindev = skb->nf_bridge->physindev;
if (physindev) dev_hold(physindev);
physoutdev = skb->nf_bridge->physoutdev;
if (physoutdev) dev_hold(physoutdev);
}
#endif
afinfo->saveroute(skb, info);
status = queue_handler[pf]->outfn(skb, info, queuenum,
queue_handler[pf]->data);
read_unlock(&queue_handler_lock);
if (status < 0) {
/* James M doesn't say fuck enough. */
if (indev) dev_put(indev);
if (outdev) dev_put(outdev);
#ifdef CONFIG_BRIDGE_NETFILTER
if (physindev) dev_put(physindev);
if (physoutdev) dev_put(physoutdev);
#endif
module_put(info->elem->owner);
kfree(info);
kfree_skb(skb);
return 1;
}
return 1;
}
int nf_queue(struct sk_buff *skb,
struct list_head *elem,
int pf, unsigned int hook,
struct net_device *indev,
struct net_device *outdev,
int (*okfn)(struct sk_buff *),
unsigned int queuenum)
{
struct sk_buff *segs;
if (!skb_is_gso(skb))
return __nf_queue(skb, elem, pf, hook, indev, outdev, okfn,
queuenum);
switch (pf) {
case AF_INET:
skb->protocol = htons(ETH_P_IP);
break;
case AF_INET6:
skb->protocol = htons(ETH_P_IPV6);
break;
}
segs = skb_gso_segment(skb, 0);
kfree_skb(skb);
if (unlikely(IS_ERR(segs)))
return 1;
do {
struct sk_buff *nskb = segs->next;
segs->next = NULL;
if (!__nf_queue(segs, elem, pf, hook, indev, outdev, okfn,
queuenum))
kfree_skb(segs);
segs = nskb;
} while (segs);
return 1;
}
void nf_reinject(struct sk_buff *skb, struct nf_info *info,
unsigned int verdict)
{
struct list_head *elem = &info->elem->list;
struct list_head *i;
struct nf_afinfo *afinfo;
rcu_read_lock();
/* Release those devices we held, or Alexey will kill me. */
if (info->indev) dev_put(info->indev);
if (info->outdev) dev_put(info->outdev);
#ifdef CONFIG_BRIDGE_NETFILTER
if (skb->nf_bridge) {
if (skb->nf_bridge->physindev)
dev_put(skb->nf_bridge->physindev);
if (skb->nf_bridge->physoutdev)
dev_put(skb->nf_bridge->physoutdev);
}
#endif
/* Drop reference to owner of hook which queued us. */
module_put(info->elem->owner);
list_for_each_rcu(i, &nf_hooks[info->pf][info->hook]) {
if (i == elem)
break;
}
if (i == &nf_hooks[info->pf][info->hook]) {
/* The module which sent it to userspace is gone. */
NFDEBUG("%s: module disappeared, dropping packet.\n",
__FUNCTION__);
verdict = NF_DROP;
}
/* Continue traversal iff userspace said ok... */
if (verdict == NF_REPEAT) {
elem = elem->prev;
verdict = NF_ACCEPT;
}
if (verdict == NF_ACCEPT) {
afinfo = nf_get_afinfo(info->pf);
if (!afinfo || afinfo->reroute(&skb, info) < 0)
verdict = NF_DROP;
}
if (verdict == NF_ACCEPT) {
next_hook:
verdict = nf_iterate(&nf_hooks[info->pf][info->hook],
&skb, info->hook,
info->indev, info->outdev, &elem,
info->okfn, INT_MIN);
}
switch (verdict & NF_VERDICT_MASK) {
case NF_ACCEPT:
case NF_STOP:
info->okfn(skb);
case NF_STOLEN:
break;
case NF_QUEUE:
if (!__nf_queue(skb, elem, info->pf, info->hook,
info->indev, info->outdev, info->okfn,
verdict >> NF_VERDICT_BITS))
goto next_hook;
break;
default:
kfree_skb(skb);
}
rcu_read_unlock();
kfree(info);
return;
}
EXPORT_SYMBOL(nf_reinject);
#ifdef CONFIG_PROC_FS
static void *seq_start(struct seq_file *seq, loff_t *pos)
{
if (*pos >= NPROTO)
return NULL;
return pos;
}
static void *seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
if (*pos >= NPROTO)
return NULL;
return pos;
}
static void seq_stop(struct seq_file *s, void *v)
{
}
static int seq_show(struct seq_file *s, void *v)
{
int ret;
loff_t *pos = v;
struct nf_queue_handler *qh;
read_lock_bh(&queue_handler_lock);
qh = queue_handler[*pos];
if (!qh)
ret = seq_printf(s, "%2lld NONE\n", *pos);
else
ret = seq_printf(s, "%2lld %s\n", *pos, qh->name);
read_unlock_bh(&queue_handler_lock);
return ret;
}
static struct seq_operations nfqueue_seq_ops = {
.start = seq_start,
.next = seq_next,
.stop = seq_stop,
.show = seq_show,
};
static int nfqueue_open(struct inode *inode, struct file *file)
{
return seq_open(file, &nfqueue_seq_ops);
}
static const struct file_operations nfqueue_file_ops = {
.owner = THIS_MODULE,
.open = nfqueue_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
#endif /* PROC_FS */
int __init netfilter_queue_init(void)
{
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *pde;
pde = create_proc_entry("nf_queue", S_IRUGO, proc_net_netfilter);
if (!pde)
return -1;
pde->proc_fops = &nfqueue_file_ops;
#endif
return 0;
}