aboutsummaryrefslogtreecommitdiff
path: root/net/ipv4/netfilter/ip_queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/ipv4/netfilter/ip_queue.c')
-rw-r--r--net/ipv4/netfilter/ip_queue.c210
1 files changed, 64 insertions, 146 deletions
diff --git a/net/ipv4/netfilter/ip_queue.c b/net/ipv4/netfilter/ip_queue.c
index 14d64a383db..5109839da22 100644
--- a/net/ipv4/netfilter/ip_queue.c
+++ b/net/ipv4/netfilter/ip_queue.c
@@ -28,19 +28,15 @@
#include <net/net_namespace.h>
#include <net/sock.h>
#include <net/route.h>
+#include <net/netfilter/nf_queue.h>
+#include <net/ip.h>
#define IPQ_QMAX_DEFAULT 1024
#define IPQ_PROC_FS_NAME "ip_queue"
#define NET_IPQ_QMAX 2088
#define NET_IPQ_QMAX_NAME "ip_queue_maxlen"
-struct ipq_queue_entry {
- struct list_head list;
- struct nf_info *info;
- struct sk_buff *skb;
-};
-
-typedef int (*ipq_cmpfn)(struct ipq_queue_entry *, unsigned long);
+typedef int (*ipq_cmpfn)(struct nf_queue_entry *, unsigned long);
static unsigned char copy_mode __read_mostly = IPQ_COPY_NONE;
static unsigned int queue_maxlen __read_mostly = IPQ_QMAX_DEFAULT;
@@ -54,76 +50,13 @@ static struct sock *ipqnl __read_mostly;
static LIST_HEAD(queue_list);
static DEFINE_MUTEX(ipqnl_mutex);
-static void
-ipq_issue_verdict(struct ipq_queue_entry *entry, int verdict)
-{
- /* TCP input path (and probably other bits) assume to be called
- * from softirq context, not from syscall, like ipq_issue_verdict is
- * called. TCP input path deadlocks with locks taken from timer
- * softirq, e.g. We therefore emulate this by local_bh_disable() */
-
- local_bh_disable();
- nf_reinject(entry->skb, entry->info, verdict);
- local_bh_enable();
-
- kfree(entry);
-}
-
static inline void
-__ipq_enqueue_entry(struct ipq_queue_entry *entry)
+__ipq_enqueue_entry(struct nf_queue_entry *entry)
{
- list_add(&entry->list, &queue_list);
+ list_add_tail(&entry->list, &queue_list);
queue_total++;
}
-/*
- * Find and return a queued entry matched by cmpfn, or return the last
- * entry if cmpfn is NULL.
- */
-static inline struct ipq_queue_entry *
-__ipq_find_entry(ipq_cmpfn cmpfn, unsigned long data)
-{
- struct list_head *p;
-
- list_for_each_prev(p, &queue_list) {
- struct ipq_queue_entry *entry = (struct ipq_queue_entry *)p;
-
- if (!cmpfn || cmpfn(entry, data))
- return entry;
- }
- return NULL;
-}
-
-static inline void
-__ipq_dequeue_entry(struct ipq_queue_entry *entry)
-{
- list_del(&entry->list);
- queue_total--;
-}
-
-static inline struct ipq_queue_entry *
-__ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data)
-{
- struct ipq_queue_entry *entry;
-
- entry = __ipq_find_entry(cmpfn, data);
- if (entry == NULL)
- return NULL;
-
- __ipq_dequeue_entry(entry);
- return entry;
-}
-
-
-static inline void
-__ipq_flush(int verdict)
-{
- struct ipq_queue_entry *entry;
-
- while ((entry = __ipq_find_dequeue_entry(NULL, 0)))
- ipq_issue_verdict(entry, verdict);
-}
-
static inline int
__ipq_set_mode(unsigned char mode, unsigned int range)
{
@@ -150,36 +83,64 @@ __ipq_set_mode(unsigned char mode, unsigned int range)
return status;
}
+static void __ipq_flush(ipq_cmpfn cmpfn, unsigned long data);
+
static inline void
__ipq_reset(void)
{
peer_pid = 0;
net_disable_timestamp();
__ipq_set_mode(IPQ_COPY_NONE, 0);
- __ipq_flush(NF_DROP);
+ __ipq_flush(NULL, 0);
}
-static struct ipq_queue_entry *
-ipq_find_dequeue_entry(ipq_cmpfn cmpfn, unsigned long data)
+static struct nf_queue_entry *
+ipq_find_dequeue_entry(unsigned long id)
{
- struct ipq_queue_entry *entry;
+ struct nf_queue_entry *entry = NULL, *i;
write_lock_bh(&queue_lock);
- entry = __ipq_find_dequeue_entry(cmpfn, data);
+
+ list_for_each_entry(i, &queue_list, list) {
+ if ((unsigned long)i == id) {
+ entry = i;
+ break;
+ }
+ }
+
+ if (entry) {
+ list_del(&entry->list);
+ queue_total--;
+ }
+
write_unlock_bh(&queue_lock);
return entry;
}
static void
-ipq_flush(int verdict)
+__ipq_flush(ipq_cmpfn cmpfn, unsigned long data)
+{
+ struct nf_queue_entry *entry, *next;
+
+ list_for_each_entry_safe(entry, next, &queue_list, list) {
+ if (!cmpfn || cmpfn(entry, data)) {
+ list_del(&entry->list);
+ queue_total--;
+ nf_reinject(entry, NF_DROP);
+ }
+ }
+}
+
+static void
+ipq_flush(ipq_cmpfn cmpfn, unsigned long data)
{
write_lock_bh(&queue_lock);
- __ipq_flush(verdict);
+ __ipq_flush(cmpfn, data);
write_unlock_bh(&queue_lock);
}
static struct sk_buff *
-ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp)
+ipq_build_packet_message(struct nf_queue_entry *entry, int *errp)
{
sk_buff_data_t old_tail;
size_t size = 0;
@@ -236,20 +197,20 @@ ipq_build_packet_message(struct ipq_queue_entry *entry, int *errp)
pmsg->timestamp_sec = tv.tv_sec;
pmsg->timestamp_usec = tv.tv_usec;
pmsg->mark = entry->skb->mark;
- pmsg->hook = entry->info->hook;
+ pmsg->hook = entry->hook;
pmsg->hw_protocol = entry->skb->protocol;
- if (entry->info->indev)
- strcpy(pmsg->indev_name, entry->info->indev->name);
+ if (entry->indev)
+ strcpy(pmsg->indev_name, entry->indev->name);
else
pmsg->indev_name[0] = '\0';
- if (entry->info->outdev)
- strcpy(pmsg->outdev_name, entry->info->outdev->name);
+ if (entry->outdev)
+ strcpy(pmsg->outdev_name, entry->outdev->name);
else
pmsg->outdev_name[0] = '\0';
- if (entry->info->indev && entry->skb->dev) {
+ if (entry->indev && entry->skb->dev) {
pmsg->hw_type = entry->skb->dev->type;
pmsg->hw_addrlen = dev_parse_header(entry->skb,
pmsg->hw_addr);
@@ -271,28 +232,17 @@ nlmsg_failure:
}
static int
-ipq_enqueue_packet(struct sk_buff *skb, struct nf_info *info,
- unsigned int queuenum, void *data)
+ipq_enqueue_packet(struct nf_queue_entry *entry, unsigned int queuenum)
{
int status = -EINVAL;
struct sk_buff *nskb;
- struct ipq_queue_entry *entry;
if (copy_mode == IPQ_COPY_NONE)
return -EAGAIN;
- entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
- if (entry == NULL) {
- printk(KERN_ERR "ip_queue: OOM in ipq_enqueue_packet()\n");
- return -ENOMEM;
- }
-
- entry->info = info;
- entry->skb = skb;
-
nskb = ipq_build_packet_message(entry, &status);
if (nskb == NULL)
- goto err_out_free;
+ return status;
write_lock_bh(&queue_lock);
@@ -326,14 +276,11 @@ err_out_free_nskb:
err_out_unlock:
write_unlock_bh(&queue_lock);
-
-err_out_free:
- kfree(entry);
return status;
}
static int
-ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
+ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct nf_queue_entry *e)
{
int diff;
int err;
@@ -368,21 +315,15 @@ ipq_mangle_ipv4(ipq_verdict_msg_t *v, struct ipq_queue_entry *e)
return 0;
}
-static inline int
-id_cmp(struct ipq_queue_entry *e, unsigned long id)
-{
- return (id == (unsigned long )e);
-}
-
static int
ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len)
{
- struct ipq_queue_entry *entry;
+ struct nf_queue_entry *entry;
if (vmsg->value > NF_MAX_VERDICT)
return -EINVAL;
- entry = ipq_find_dequeue_entry(id_cmp, vmsg->id);
+ entry = ipq_find_dequeue_entry(vmsg->id);
if (entry == NULL)
return -ENOENT;
else {
@@ -392,7 +333,7 @@ ipq_set_verdict(struct ipq_verdict_msg *vmsg, unsigned int len)
if (ipq_mangle_ipv4(vmsg, entry) < 0)
verdict = NF_DROP;
- ipq_issue_verdict(entry, verdict);
+ nf_reinject(entry, verdict);
return 0;
}
}
@@ -437,13 +378,13 @@ ipq_receive_peer(struct ipq_peer_msg *pmsg,
}
static int
-dev_cmp(struct ipq_queue_entry *entry, unsigned long ifindex)
+dev_cmp(struct nf_queue_entry *entry, unsigned long ifindex)
{
- if (entry->info->indev)
- if (entry->info->indev->ifindex == ifindex)
+ if (entry->indev)
+ if (entry->indev->ifindex == ifindex)
return 1;
- if (entry->info->outdev)
- if (entry->info->outdev->ifindex == ifindex)
+ if (entry->outdev)
+ if (entry->outdev->ifindex == ifindex)
return 1;
#ifdef CONFIG_BRIDGE_NETFILTER
if (entry->skb->nf_bridge) {
@@ -461,10 +402,7 @@ dev_cmp(struct ipq_queue_entry *entry, unsigned long ifindex)
static void
ipq_dev_drop(int ifindex)
{
- struct ipq_queue_entry *entry;
-
- while ((entry = ipq_find_dequeue_entry(dev_cmp, ifindex)) != NULL)
- ipq_issue_verdict(entry, NF_DROP);
+ ipq_flush(dev_cmp, ifindex);
}
#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0)
@@ -588,26 +526,6 @@ static ctl_table ipq_table[] = {
{ .ctl_name = 0 }
};
-static ctl_table ipq_dir_table[] = {
- {
- .ctl_name = NET_IPV4,
- .procname = "ipv4",
- .mode = 0555,
- .child = ipq_table
- },
- { .ctl_name = 0 }
-};
-
-static ctl_table ipq_root_table[] = {
- {
- .ctl_name = CTL_NET,
- .procname = "net",
- .mode = 0555,
- .child = ipq_dir_table
- },
- { .ctl_name = 0 }
-};
-
static int ip_queue_show(struct seq_file *m, void *v)
{
read_lock_bh(&queue_lock);
@@ -645,7 +563,7 @@ static const struct file_operations ip_queue_proc_fops = {
.owner = THIS_MODULE,
};
-static struct nf_queue_handler nfqh = {
+static const struct nf_queue_handler nfqh = {
.name = "ip_queue",
.outfn = &ipq_enqueue_packet,
};
@@ -673,7 +591,7 @@ static int __init ip_queue_init(void)
}
register_netdevice_notifier(&ipq_dev_notifier);
- ipq_sysctl_header = register_sysctl_table(ipq_root_table);
+ ipq_sysctl_header = register_sysctl_paths(net_ipv4_ctl_path, ipq_table);
status = nf_register_queue_handler(PF_INET, &nfqh);
if (status < 0) {
@@ -687,7 +605,7 @@ cleanup_sysctl:
unregister_netdevice_notifier(&ipq_dev_notifier);
proc_net_remove(&init_net, IPQ_PROC_FS_NAME);
cleanup_ipqnl:
- sock_release(ipqnl->sk_socket);
+ netlink_kernel_release(ipqnl);
mutex_lock(&ipqnl_mutex);
mutex_unlock(&ipqnl_mutex);
@@ -700,13 +618,13 @@ static void __exit ip_queue_fini(void)
{
nf_unregister_queue_handlers(&nfqh);
synchronize_net();
- ipq_flush(NF_DROP);
+ ipq_flush(NULL, 0);
unregister_sysctl_table(ipq_sysctl_header);
unregister_netdevice_notifier(&ipq_dev_notifier);
proc_net_remove(&init_net, IPQ_PROC_FS_NAME);
- sock_release(ipqnl->sk_socket);
+ netlink_kernel_release(ipqnl);
mutex_lock(&ipqnl_mutex);
mutex_unlock(&ipqnl_mutex);