diff options
Diffstat (limited to 'net/netfilter/xt_NFQUEUE.c')
| -rw-r--r-- | net/netfilter/xt_NFQUEUE.c | 175 |
1 files changed, 114 insertions, 61 deletions
diff --git a/net/netfilter/xt_NFQUEUE.c b/net/netfilter/xt_NFQUEUE.c index 8b76b6f8d1e..8f1779ff7e3 100644 --- a/net/netfilter/xt_NFQUEUE.c +++ b/net/netfilter/xt_NFQUEUE.c @@ -3,9 +3,9 @@ * (C) 2005 by Harald Welte <laforge@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 + * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * + * */ #include <linux/module.h> @@ -16,92 +16,145 @@ #include <linux/netfilter/x_tables.h> #include <linux/netfilter/xt_NFQUEUE.h> +#include <net/netfilter/nf_queue.h> + MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); -MODULE_DESCRIPTION("[ip,ip6,arp]_tables NFQUEUE target"); +MODULE_DESCRIPTION("Xtables: packet forwarding to netlink"); MODULE_LICENSE("GPL"); MODULE_ALIAS("ipt_NFQUEUE"); MODULE_ALIAS("ip6t_NFQUEUE"); MODULE_ALIAS("arpt_NFQUEUE"); +static u32 jhash_initval __read_mostly; + static unsigned int -target(struct sk_buff **pskb, - const struct net_device *in, - const struct net_device *out, - unsigned int hooknum, - const void *targinfo, - void *userinfo) +nfqueue_tg(struct sk_buff *skb, const struct xt_action_param *par) { - const struct xt_NFQ_info *tinfo = targinfo; + const struct xt_NFQ_info *tinfo = par->targinfo; return NF_QUEUE_NR(tinfo->queuenum); } -static int -checkentry(const char *tablename, - const void *entry, - void *targinfo, - unsigned int targinfosize, - unsigned int hook_mask) +static unsigned int +nfqueue_tg_v1(struct sk_buff *skb, const struct xt_action_param *par) { - if (targinfosize != XT_ALIGN(sizeof(struct xt_NFQ_info))) { - printk(KERN_WARNING "NFQUEUE: targinfosize %u != %Zu\n", - targinfosize, - XT_ALIGN(sizeof(struct xt_NFQ_info))); - return 0; + const struct xt_NFQ_info_v1 *info = par->targinfo; + u32 queue = info->queuenum; + + if (info->queues_total > 1) { + queue = nfqueue_hash(skb, queue, info->queues_total, + par->family, jhash_initval); } + return NF_QUEUE_NR(queue); +} + +static unsigned int +nfqueue_tg_v2(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct xt_NFQ_info_v2 *info = par->targinfo; + unsigned int ret = nfqueue_tg_v1(skb, par); - return 1; + if (info->bypass) + ret |= NF_VERDICT_FLAG_QUEUE_BYPASS; + return ret; } -static struct xt_target ipt_NFQ_reg = { - .name = "NFQUEUE", - .target = target, - .checkentry = checkentry, - .me = THIS_MODULE, -}; +static int nfqueue_tg_check(const struct xt_tgchk_param *par) +{ + const struct xt_NFQ_info_v3 *info = par->targinfo; + u32 maxid; -static struct xt_target ip6t_NFQ_reg = { - .name = "NFQUEUE", - .target = target, - .checkentry = checkentry, - .me = THIS_MODULE, -}; + init_hashrandom(&jhash_initval); -static struct xt_target arpt_NFQ_reg = { - .name = "NFQUEUE", - .target = target, - .checkentry = checkentry, - .me = THIS_MODULE, -}; + if (info->queues_total == 0) { + pr_err("NFQUEUE: number of total queues is 0\n"); + return -EINVAL; + } + maxid = info->queues_total - 1 + info->queuenum; + if (maxid > 0xffff) { + pr_err("NFQUEUE: number of queues (%u) out of range (got %u)\n", + info->queues_total, maxid); + return -ERANGE; + } + if (par->target->revision == 2 && info->flags > 1) + return -EINVAL; + if (par->target->revision == 3 && info->flags & ~NFQ_FLAG_MASK) + return -EINVAL; + + return 0; +} -static int __init init(void) +static unsigned int +nfqueue_tg_v3(struct sk_buff *skb, const struct xt_action_param *par) { + const struct xt_NFQ_info_v3 *info = par->targinfo; + u32 queue = info->queuenum; int ret; - ret = xt_register_target(AF_INET, &ipt_NFQ_reg); - if (ret) - return ret; - ret = xt_register_target(AF_INET6, &ip6t_NFQ_reg); - if (ret) - goto out_ip; - ret = xt_register_target(NF_ARP, &arpt_NFQ_reg); - if (ret) - goto out_ip6; - return ret; -out_ip6: - xt_unregister_target(AF_INET6, &ip6t_NFQ_reg); -out_ip: - xt_unregister_target(AF_INET, &ipt_NFQ_reg); + if (info->queues_total > 1) { + if (info->flags & NFQ_FLAG_CPU_FANOUT) { + int cpu = smp_processor_id(); + + queue = info->queuenum + cpu % info->queues_total; + } else { + queue = nfqueue_hash(skb, queue, info->queues_total, + par->family, jhash_initval); + } + } + + ret = NF_QUEUE_NR(queue); + if (info->flags & NFQ_FLAG_BYPASS) + ret |= NF_VERDICT_FLAG_QUEUE_BYPASS; return ret; } -static void __exit fini(void) +static struct xt_target nfqueue_tg_reg[] __read_mostly = { + { + .name = "NFQUEUE", + .family = NFPROTO_UNSPEC, + .target = nfqueue_tg, + .targetsize = sizeof(struct xt_NFQ_info), + .me = THIS_MODULE, + }, + { + .name = "NFQUEUE", + .revision = 1, + .family = NFPROTO_UNSPEC, + .checkentry = nfqueue_tg_check, + .target = nfqueue_tg_v1, + .targetsize = sizeof(struct xt_NFQ_info_v1), + .me = THIS_MODULE, + }, + { + .name = "NFQUEUE", + .revision = 2, + .family = NFPROTO_UNSPEC, + .checkentry = nfqueue_tg_check, + .target = nfqueue_tg_v2, + .targetsize = sizeof(struct xt_NFQ_info_v2), + .me = THIS_MODULE, + }, + { + .name = "NFQUEUE", + .revision = 3, + .family = NFPROTO_UNSPEC, + .checkentry = nfqueue_tg_check, + .target = nfqueue_tg_v3, + .targetsize = sizeof(struct xt_NFQ_info_v3), + .me = THIS_MODULE, + }, +}; + +static int __init nfqueue_tg_init(void) +{ + return xt_register_targets(nfqueue_tg_reg, ARRAY_SIZE(nfqueue_tg_reg)); +} + +static void __exit nfqueue_tg_exit(void) { - xt_unregister_target(NF_ARP, &arpt_NFQ_reg); - xt_unregister_target(AF_INET6, &ip6t_NFQ_reg); - xt_unregister_target(AF_INET, &ipt_NFQ_reg); + xt_unregister_targets(nfqueue_tg_reg, ARRAY_SIZE(nfqueue_tg_reg)); } -module_init(init); -module_exit(fini); +module_init(nfqueue_tg_init); +module_exit(nfqueue_tg_exit); |
