/*
* This is a module which is used for queueing packets and communicating with
* userspace via nfetlink.
*
* (C) 2005 by Harald Welte <laforge@netfilter.org>
*
* Based on the old ipv4-only ip_queue.c:
* (C) 2000-2002 James Morris <jmorris@intercode.com.au>
* (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/proc_fs.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_queue.h>
#include <linux/list.h>
#include <net/sock.h>
#include <asm/atomic.h>
#ifdef CONFIG_BRIDGE_NETFILTER
#include "../bridge/br_private.h"
#endif
#define NFQNL_QMAX_DEFAULT 1024
#if 0
#define QDEBUG(x, args ...) printk(KERN_DEBUG "%s(%d):%s(): " x, \
__FILE__, __LINE__, __FUNCTION__, \
## args)
#else
#define QDEBUG(x, ...)
#endif
struct nfqnl_queue_entry {
struct list_head list;
struct nf_info *info;
struct sk_buff *skb;
unsigned int id;
};
struct nfqnl_instance {
struct hlist_node hlist; /* global list of queues */
atomic_t use;
int peer_pid;
unsigned int queue_maxlen;
unsigned int copy_range;
unsigned int queue_total;
unsigned int queue_dropped;
unsigned int queue_user_dropped;
atomic_t id_sequence; /* 'sequence' of pkt ids */
u_int16_t queue_num; /* number of this queue */
u_int8_t copy_mode;
spinlock_t lock;
struct list_head queue_list; /* packets in queue */
};
typedef int (*nfqnl_cmpfn)(struct nfqnl_queue_entry *, unsigned long);
static DEFINE_RWLOCK(instances_lock);
#define INSTANCE_BUCKETS 16
static struct hlist_head instance_table[INSTANCE_BUCKETS];
static inline u_int8_t instance_hashfn(u_int16_t queue_num)
{
return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS;
}
static struct nfqnl_instance *
__instance_lookup(u_int16_t queue_num)
{
struct hlist_head *head;
struct hlist_node *pos;
struct nfqnl_instance *inst;
head = &instance_table[instance_hashfn(queue_num)];
hlist_for_each_entry(inst, pos, head, hlist) {
if (inst->queue_num == queue_num)
return inst;
}
return NULL;
}
static struct nfqnl_instance *
instance_lookup_get(u_int16_t queue_num)
{
struct nfqnl_instance *inst;
read_lock_bh(&instances_lock);
inst = __instance_lookup(queue_num);
if (inst)
atomic_inc(&inst->use);
read_unlock_bh(&instances_lock);
return inst;
}
static void
instance_put(struct nfqnl_instance *inst)
{
if (inst && atomic_dec_and_test(&inst->use)) {
QDEBUG("kfree(inst=%p)\n", inst);
kfree(inst);
}
}
static struct nfqnl_instance *
instance_create(u_int16_t queue_num, int pid)
{
struct nfqnl_instance *inst;
QDEBUG("entering for queue_num=%u, pid=%d\n", queue_num, pid);
write_lock_bh(&instances_lock);
if (__instance_lookup(queue_num)) {
inst = NULL;
QDEBUG("aborting, instance already exists\n");
goto out_unlock;
}
inst = kzalloc(sizeof(*inst), GFP_ATOMIC);
if (!inst)
goto out_unlock;
inst->queue_num = queue_num;
inst->peer_pid = pid;
inst->queue_maxlen = NFQNL_QMAX_DEFAULT;
inst->copy_range = 0xfffff;