diff options
author | Harald Welte <laforge@netfilter.org> | 2005-08-09 22:44:15 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-08-29 18:36:56 -0400 |
commit | 7af4cc3fa158ff1dda6e7451c7e6afa6b0bb85cb (patch) | |
tree | 752acd1f26063b23e5629124ee0a33078d2fb9a1 | |
parent | 0ab43f84995f2c2fcc5cc58a9accaa1095e1317f (diff) |
[NETFILTER]: Add "nfnetlink_queue" netfilter queue handler over nfnetlink
- Add new nfnetlink_queue module
- Add new ipt_NFQUEUE and ip6t_NFQUEUE modules to access queue numbers 1-65535
- Mark ip_queue and ip6_queue Kconfig options as OBSOLETE
- Update feature-removal-schedule to remove ip[6]_queue in December
Signed-off-by: Harald Welte <laforge@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | Documentation/feature-removal-schedule.txt | 12 | ||||
-rw-r--r-- | include/linux/netfilter/nfnetlink_queue.h | 85 | ||||
-rw-r--r-- | include/linux/netfilter_ipv4/ipt_NFQUEUE.h | 16 | ||||
-rw-r--r-- | net/ipv4/netfilter/Kconfig | 6 | ||||
-rw-r--r-- | net/ipv4/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_NFQUEUE.c | 70 | ||||
-rw-r--r-- | net/ipv6/netfilter/Kconfig | 11 | ||||
-rw-r--r-- | net/ipv6/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_NFQUEUE.c | 70 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 8 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/nfnetlink_queue.c | 877 |
12 files changed, 1153 insertions, 5 deletions
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 8b1430b46655..0665cb12bd66 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt | |||
@@ -135,3 +135,15 @@ Why: With the 16-bit PCMCIA subsystem now behaving (almost) like a | |||
135 | pcmciautils package available at | 135 | pcmciautils package available at |
136 | http://kernel.org/pub/linux/utils/kernel/pcmcia/ | 136 | http://kernel.org/pub/linux/utils/kernel/pcmcia/ |
137 | Who: Dominik Brodowski <linux@brodo.de> | 137 | Who: Dominik Brodowski <linux@brodo.de> |
138 | |||
139 | --------------------------- | ||
140 | |||
141 | What: ip_queue and ip6_queue (old ipv4-only and ipv6-only netfilter queue) | ||
142 | When: December 2005 | ||
143 | Why: This interface has been obsoleted by the new layer3-independent | ||
144 | "nfnetlink_queue". The Kernel interface is compatible, so the old | ||
145 | ip[6]tables "QUEUE" targets still work and will transparently handle | ||
146 | all packets into nfnetlink queue number 0. Userspace users will have | ||
147 | to link against API-compatible library on top of libnfnetlink_queue | ||
148 | instead of the current 'libipq'. | ||
149 | Who: Harald Welte <laforge@netfilter.org> | ||
diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h new file mode 100644 index 000000000000..edb463a87eb4 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_queue.h | |||
@@ -0,0 +1,85 @@ | |||
1 | #ifndef _NFNETLINK_QUEUE_H | ||
2 | #define _NFNETLINK_QUEUE_H | ||
3 | |||
4 | #include <linux/netfilter/nfnetlink.h> | ||
5 | |||
6 | enum nfqnl_msg_types { | ||
7 | NFQNL_MSG_PACKET, /* packet from kernel to userspace */ | ||
8 | NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */ | ||
9 | NFQNL_MSG_CONFIG, /* connect to a particular queue */ | ||
10 | |||
11 | NFQNL_MSG_MAX | ||
12 | }; | ||
13 | |||
14 | struct nfqnl_msg_packet_hdr { | ||
15 | u_int32_t packet_id; /* unique ID of packet in queue */ | ||
16 | u_int16_t hw_protocol; /* hw protocol (network order) */ | ||
17 | u_int8_t hook; /* netfilter hook */ | ||
18 | } __attribute__ ((packed)); | ||
19 | |||
20 | struct nfqnl_msg_packet_hw { | ||
21 | u_int16_t hw_addrlen; | ||
22 | u_int16_t _pad; | ||
23 | u_int8_t hw_addr[8]; | ||
24 | } __attribute__ ((packed)); | ||
25 | |||
26 | struct nfqnl_msg_packet_timestamp { | ||
27 | u_int64_t sec; | ||
28 | u_int64_t usec; | ||
29 | } __attribute__ ((packed)); | ||
30 | |||
31 | enum nfqnl_attr_type { | ||
32 | NFQA_UNSPEC, | ||
33 | NFQA_PACKET_HDR, | ||
34 | NFQA_VERDICT_HDR, /* nfqnl_msg_verdict_hrd */ | ||
35 | NFQA_MARK, /* u_int32_t nfmark */ | ||
36 | NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */ | ||
37 | NFQA_IFINDEX_INDEV, /* u_int32_t ifindex */ | ||
38 | NFQA_IFINDEX_OUTDEV, /* u_int32_t ifindex */ | ||
39 | NFQA_HWADDR, /* nfqnl_msg_packet_hw */ | ||
40 | NFQA_PAYLOAD, /* opaque data payload */ | ||
41 | |||
42 | __NFQA_MAX | ||
43 | }; | ||
44 | #define NFQA_MAX (__NFQA_MAX - 1) | ||
45 | |||
46 | struct nfqnl_msg_verdict_hdr { | ||
47 | u_int32_t verdict; | ||
48 | u_int32_t id; | ||
49 | } __attribute__ ((packed)); | ||
50 | |||
51 | |||
52 | enum nfqnl_msg_config_cmds { | ||
53 | NFQNL_CFG_CMD_NONE, | ||
54 | NFQNL_CFG_CMD_BIND, | ||
55 | NFQNL_CFG_CMD_UNBIND, | ||
56 | NFQNL_CFG_CMD_PF_BIND, | ||
57 | NFQNL_CFG_CMD_PF_UNBIND, | ||
58 | }; | ||
59 | |||
60 | struct nfqnl_msg_config_cmd { | ||
61 | u_int8_t command; /* nfqnl_msg_config_cmds */ | ||
62 | u_int8_t _pad; | ||
63 | u_int16_t pf; /* AF_xxx for PF_[UN]BIND */ | ||
64 | } __attribute__ ((packed)); | ||
65 | |||
66 | enum nfqnl_config_mode { | ||
67 | NFQNL_COPY_NONE, | ||
68 | NFQNL_COPY_META, | ||
69 | NFQNL_COPY_PACKET, | ||
70 | }; | ||
71 | |||
72 | struct nfqnl_msg_config_params { | ||
73 | u_int32_t copy_range; | ||
74 | u_int8_t copy_mode; /* enum nfqnl_config_mode */ | ||
75 | } __attribute__ ((packed)); | ||
76 | |||
77 | |||
78 | enum nfqnl_attr_config { | ||
79 | NFQA_CFG_UNSPEC, | ||
80 | NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */ | ||
81 | NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */ | ||
82 | __NFQA_CFG_MAX | ||
83 | }; | ||
84 | |||
85 | #endif /* _NFNETLINK_QUEUE_H */ | ||
diff --git a/include/linux/netfilter_ipv4/ipt_NFQUEUE.h b/include/linux/netfilter_ipv4/ipt_NFQUEUE.h new file mode 100644 index 000000000000..b5b2943b0c66 --- /dev/null +++ b/include/linux/netfilter_ipv4/ipt_NFQUEUE.h | |||
@@ -0,0 +1,16 @@ | |||
1 | /* iptables module for using NFQUEUE mechanism | ||
2 | * | ||
3 | * (C) 2005 Harald Welte <laforge@netfilter.org> | ||
4 | * | ||
5 | * This software is distributed under GNU GPL v2, 1991 | ||
6 | * | ||
7 | */ | ||
8 | #ifndef _IPT_NFQ_TARGET_H | ||
9 | #define _IPT_NFQ_TARGET_H | ||
10 | |||
11 | /* target info */ | ||
12 | struct ipt_NFQ_info { | ||
13 | u_int16_t queuenum; | ||
14 | }; | ||
15 | |||
16 | #endif /* _IPT_DSCP_TARGET_H */ | ||
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index e47ba39eb657..2fa26a41fa47 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig | |||
@@ -110,11 +110,15 @@ config IP_NF_AMANDA | |||
110 | To compile it as a module, choose M here. If unsure, say Y. | 110 | To compile it as a module, choose M here. If unsure, say Y. |
111 | 111 | ||
112 | config IP_NF_QUEUE | 112 | config IP_NF_QUEUE |
113 | tristate "Userspace queueing via NETLINK" | 113 | tristate "IP Userspace queueing via NETLINK (OBSOLETE)" |
114 | help | 114 | help |
115 | Netfilter has the ability to queue packets to user space: the | 115 | Netfilter has the ability to queue packets to user space: the |
116 | netlink device can be used to access them using this driver. | 116 | netlink device can be used to access them using this driver. |
117 | 117 | ||
118 | This option enables the old IPv4-only "ip_queue" implementation | ||
119 | which has been obsoleted by the new "nfnetlink_queue" code (see | ||
120 | CONFIG_NETFILTER_NETLINK_QUEUE). | ||
121 | |||
118 | To compile it as a module, choose M here. If unsure, say N. | 122 | To compile it as a module, choose M here. If unsure, say N. |
119 | 123 | ||
120 | config IP_NF_IPTABLES | 124 | config IP_NF_IPTABLES |
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index abf2a7d1a584..c2ae663b723f 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile | |||
@@ -91,3 +91,4 @@ obj-$(CONFIG_IP_NF_ARP_MANGLE) += arpt_mangle.o | |||
91 | obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o | 91 | obj-$(CONFIG_IP_NF_ARPFILTER) += arptable_filter.o |
92 | 92 | ||
93 | obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o | 93 | obj-$(CONFIG_IP_NF_QUEUE) += ip_queue.o |
94 | obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += ipt_NFQUEUE.o | ||
diff --git a/net/ipv4/netfilter/ipt_NFQUEUE.c b/net/ipv4/netfilter/ipt_NFQUEUE.c new file mode 100644 index 000000000000..3cedc9be8807 --- /dev/null +++ b/net/ipv4/netfilter/ipt_NFQUEUE.c | |||
@@ -0,0 +1,70 @@ | |||
1 | /* iptables module for using new netfilter netlink queue | ||
2 | * | ||
3 | * (C) 2005 by Harald Welte <laforge@netfilter.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/skbuff.h> | ||
13 | |||
14 | #include <linux/netfilter.h> | ||
15 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
16 | #include <linux/netfilter_ipv4/ipt_NFQUEUE.h> | ||
17 | |||
18 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | ||
19 | MODULE_DESCRIPTION("iptables NFQUEUE target"); | ||
20 | MODULE_LICENSE("GPL"); | ||
21 | |||
22 | static unsigned int | ||
23 | target(struct sk_buff **pskb, | ||
24 | const struct net_device *in, | ||
25 | const struct net_device *out, | ||
26 | unsigned int hooknum, | ||
27 | const void *targinfo, | ||
28 | void *userinfo) | ||
29 | { | ||
30 | const struct ipt_NFQ_info *tinfo = targinfo; | ||
31 | |||
32 | return NF_QUEUE_NR(tinfo->queuenum); | ||
33 | } | ||
34 | |||
35 | static int | ||
36 | checkentry(const char *tablename, | ||
37 | const struct ipt_entry *e, | ||
38 | void *targinfo, | ||
39 | unsigned int targinfosize, | ||
40 | unsigned int hook_mask) | ||
41 | { | ||
42 | if (targinfosize != IPT_ALIGN(sizeof(struct ipt_NFQ_info))) { | ||
43 | printk(KERN_WARNING "NFQUEUE: targinfosize %u != %Zu\n", | ||
44 | targinfosize, | ||
45 | IPT_ALIGN(sizeof(struct ipt_NFQ_info))); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | return 1; | ||
50 | } | ||
51 | |||
52 | static struct ipt_target ipt_NFQ_reg = { | ||
53 | .name = "NFQUEUE", | ||
54 | .target = target, | ||
55 | .checkentry = checkentry, | ||
56 | .me = THIS_MODULE, | ||
57 | }; | ||
58 | |||
59 | static int __init init(void) | ||
60 | { | ||
61 | return ipt_register_target(&ipt_NFQ_reg); | ||
62 | } | ||
63 | |||
64 | static void __exit fini(void) | ||
65 | { | ||
66 | ipt_unregister_target(&ipt_NFQ_reg); | ||
67 | } | ||
68 | |||
69 | module_init(init); | ||
70 | module_exit(fini); | ||
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 77ec704c9ee3..cd1551983c63 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig | |||
@@ -10,13 +10,16 @@ menu "IPv6: Netfilter Configuration (EXPERIMENTAL)" | |||
10 | # dep_tristate ' FTP protocol support' CONFIG_IP6_NF_FTP $CONFIG_IP6_NF_CONNTRACK | 10 | # dep_tristate ' FTP protocol support' CONFIG_IP6_NF_FTP $CONFIG_IP6_NF_CONNTRACK |
11 | #fi | 11 | #fi |
12 | config IP6_NF_QUEUE | 12 | config IP6_NF_QUEUE |
13 | tristate "Userspace queueing via NETLINK" | 13 | tristate "IP6 Userspace queueing via NETLINK (OBSOLETE)" |
14 | ---help--- | 14 | ---help--- |
15 | 15 | ||
16 | This option adds a queue handler to the kernel for IPv6 | 16 | This option adds a queue handler to the kernel for IPv6 |
17 | packets which lets us to receive the filtered packets | 17 | packets which enables users to receive the filtered packets |
18 | with QUEUE target using libiptc as we can do with | 18 | with QUEUE target using libipq. |
19 | the IPv4 now. | 19 | |
20 | THis option enables the old IPv6-only "ip6_queue" implementation | ||
21 | which has been obsoleted by the new "nfnetlink_queue" code (see | ||
22 | CONFIG_NETFILTER_NETLINK_QUEUE). | ||
20 | 23 | ||
21 | (C) Fernando Anton 2001 | 24 | (C) Fernando Anton 2001 |
22 | IPv64 Project - Work based in IPv64 draft by Arturo Azcorra. | 25 | IPv64 Project - Work based in IPv64 draft by Arturo Azcorra. |
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 2e51714953b6..847651dbcd2a 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
@@ -24,3 +24,4 @@ obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o | |||
24 | obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o | 24 | obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o |
25 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o | 25 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o |
26 | obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o | 26 | obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o |
27 | obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += ip6t_NFQUEUE.o | ||
diff --git a/net/ipv6/netfilter/ip6t_NFQUEUE.c b/net/ipv6/netfilter/ip6t_NFQUEUE.c new file mode 100644 index 000000000000..c6e3730e7409 --- /dev/null +++ b/net/ipv6/netfilter/ip6t_NFQUEUE.c | |||
@@ -0,0 +1,70 @@ | |||
1 | /* ip6tables module for using new netfilter netlink queue | ||
2 | * | ||
3 | * (C) 2005 by Harald Welte <laforge@netfilter.org> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/skbuff.h> | ||
13 | |||
14 | #include <linux/netfilter.h> | ||
15 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
16 | #include <linux/netfilter_ipv4/ipt_NFQUEUE.h> | ||
17 | |||
18 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | ||
19 | MODULE_DESCRIPTION("ip6tables NFQUEUE target"); | ||
20 | MODULE_LICENSE("GPL"); | ||
21 | |||
22 | static unsigned int | ||
23 | target(struct sk_buff **pskb, | ||
24 | const struct net_device *in, | ||
25 | const struct net_device *out, | ||
26 | unsigned int hooknum, | ||
27 | const void *targinfo, | ||
28 | void *userinfo) | ||
29 | { | ||
30 | const struct ipt_NFQ_info *tinfo = targinfo; | ||
31 | |||
32 | return NF_QUEUE_NR(tinfo->queuenum); | ||
33 | } | ||
34 | |||
35 | static int | ||
36 | checkentry(const char *tablename, | ||
37 | const struct ip6t_entry *e, | ||
38 | void *targinfo, | ||
39 | unsigned int targinfosize, | ||
40 | unsigned int hook_mask) | ||
41 | { | ||
42 | if (targinfosize != IP6T_ALIGN(sizeof(struct ipt_NFQ_info))) { | ||
43 | printk(KERN_WARNING "NFQUEUE: targinfosize %u != %Zu\n", | ||
44 | targinfosize, | ||
45 | IP6T_ALIGN(sizeof(struct ipt_NFQ_info))); | ||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | return 1; | ||
50 | } | ||
51 | |||
52 | static struct ip6t_target ipt_NFQ_reg = { | ||
53 | .name = "NFQUEUE", | ||
54 | .target = target, | ||
55 | .checkentry = checkentry, | ||
56 | .me = THIS_MODULE, | ||
57 | }; | ||
58 | |||
59 | static int __init init(void) | ||
60 | { | ||
61 | return ip6t_register_target(&ipt_NFQ_reg); | ||
62 | } | ||
63 | |||
64 | static void __exit fini(void) | ||
65 | { | ||
66 | ip6t_unregister_target(&ipt_NFQ_reg); | ||
67 | } | ||
68 | |||
69 | module_init(init); | ||
70 | module_exit(fini); | ||
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 3629d3d1776d..f0eb23e5c5f1 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -3,3 +3,11 @@ config NETFILTER_NETLINK | |||
3 | help | 3 | help |
4 | If this option is enabled, the kernel will include support | 4 | If this option is enabled, the kernel will include support |
5 | for the new netfilter netlink interface. | 5 | for the new netfilter netlink interface. |
6 | |||
7 | config NETFILTER_NETLINK_QUEUE | ||
8 | tristate "Netfilter NFQUEUE over NFNETLINK interface" | ||
9 | depends on NETFILTER_NETLINK | ||
10 | help | ||
11 | If this option isenabled, the kernel will include support | ||
12 | for queueing packets via NFNETLINK. | ||
13 | |||
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 02e67d371941..14a0b187e75e 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o | 1 | obj-$(CONFIG_NETFILTER_NETLINK) += nfnetlink.o |
2 | obj-$(CONFIG_NETFILTER_NETLINK_QUEUE) += nfnetlink_queue.o | ||
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c new file mode 100644 index 000000000000..24032610c425 --- /dev/null +++ b/net/netfilter/nfnetlink_queue.c | |||
@@ -0,0 +1,877 @@ | |||
1 | /* | ||
2 | * This is a module which is used for queueing packets and communicating with | ||
3 | * userspace via nfetlink. | ||
4 | * | ||
5 | * (C) 2005 by Harald Welte <laforge@netfilter.org> | ||
6 | * | ||
7 | * Based on the old ipv4-only ip_queue.c: | ||
8 | * (C) 2000-2002 James Morris <jmorris@intercode.com.au> | ||
9 | * (C) 2003-2005 Netfilter Core Team <coreteam@netfilter.org> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | * | ||
15 | */ | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/skbuff.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/spinlock.h> | ||
20 | #include <linux/notifier.h> | ||
21 | #include <linux/netdevice.h> | ||
22 | #include <linux/netfilter.h> | ||
23 | #include <linux/netfilter_ipv4.h> | ||
24 | #include <linux/netfilter_ipv6.h> | ||
25 | #include <linux/netfilter/nfnetlink.h> | ||
26 | #include <linux/netfilter/nfnetlink_queue.h> | ||
27 | #include <linux/list.h> | ||
28 | #include <net/sock.h> | ||
29 | |||
30 | #include <asm/atomic.h> | ||
31 | |||
32 | #define NFQNL_QMAX_DEFAULT 1024 | ||
33 | |||
34 | #if 0 | ||
35 | #define QDEBUG(x, args ...) printk(KERN_DEBUG "%s(%d):%s(): " x, \ | ||
36 | __FILE__, __LINE__, __FUNCTION__, \ | ||
37 | ## args) | ||
38 | #else | ||
39 | #define QDEBUG(x, ...) | ||
40 | #endif | ||
41 | |||
42 | struct nfqnl_queue_entry { | ||
43 | struct list_head list; | ||
44 | struct nf_info *info; | ||
45 | struct sk_buff *skb; | ||
46 | unsigned int id; | ||
47 | }; | ||
48 | |||
49 | struct nfqnl_instance { | ||
50 | struct hlist_node hlist; /* global list of queues */ | ||
51 | |||
52 | int peer_pid; | ||
53 | unsigned int queue_maxlen; | ||
54 | unsigned int copy_range; | ||
55 | unsigned int queue_total; | ||
56 | unsigned int queue_dropped; | ||
57 | unsigned int queue_user_dropped; | ||
58 | |||
59 | atomic_t id_sequence; /* 'sequence' of pkt ids */ | ||
60 | |||
61 | u_int16_t queue_num; /* number of this queue */ | ||
62 | u_int8_t copy_mode; | ||
63 | |||
64 | spinlock_t lock; | ||
65 | |||
66 | struct list_head queue_list; /* packets in queue */ | ||
67 | }; | ||
68 | |||
69 | typedef int (*nfqnl_cmpfn)(struct nfqnl_queue_entry *, unsigned long); | ||
70 | |||
71 | static DEFINE_RWLOCK(instances_lock); | ||
72 | |||
73 | u_int64_t htonll(u_int64_t in) | ||
74 | { | ||
75 | u_int64_t out; | ||
76 | int i; | ||
77 | |||
78 | for (i = 0; i < sizeof(u_int64_t); i++) | ||
79 | ((u_int8_t *)&out)[sizeof(u_int64_t)-1] = ((u_int8_t *)&in)[i]; | ||
80 | |||
81 | return out; | ||
82 | } | ||
83 | |||
84 | #define INSTANCE_BUCKETS 16 | ||
85 | static struct hlist_head instance_table[INSTANCE_BUCKETS]; | ||
86 | |||
87 | static inline u_int8_t instance_hashfn(u_int16_t queue_num) | ||
88 | { | ||
89 | return ((queue_num >> 8) | queue_num) % INSTANCE_BUCKETS; | ||
90 | } | ||
91 | |||
92 | static struct nfqnl_instance * | ||
93 | __instance_lookup(u_int16_t queue_num) | ||
94 | { | ||
95 | struct hlist_head *head; | ||
96 | struct hlist_node *pos; | ||
97 | struct nfqnl_instance *inst; | ||
98 | |||
99 | head = &instance_table[instance_hashfn(queue_num)]; | ||
100 | hlist_for_each_entry(inst, pos, head, hlist) { | ||
101 | if (inst->queue_num == queue_num) | ||
102 | return inst; | ||
103 | } | ||
104 | return NULL; | ||
105 | } | ||
106 | |||
107 | static struct nfqnl_instance * | ||
108 | instance_lookup(u_int16_t queue_num) | ||
109 | { | ||
110 | struct nfqnl_instance *inst; | ||
111 | |||
112 | read_lock_bh(&instances_lock); | ||
113 | inst = __instance_lookup(queue_num); | ||
114 | read_unlock_bh(&instances_lock); | ||
115 | |||
116 | return inst; | ||
117 | } | ||
118 | |||
119 | static struct nfqnl_instance * | ||
120 | instance_create(u_int16_t queue_num, int pid) | ||
121 | { | ||
122 | struct nfqnl_instance *inst; | ||
123 | |||
124 | QDEBUG("entering for queue_num=%u, pid=%d\n", queue_num, pid); | ||
125 | |||
126 | write_lock_bh(&instances_lock); | ||
127 | if (__instance_lookup(queue_num)) { | ||
128 | inst = NULL; | ||
129 | QDEBUG("aborting, instance already exists\n"); | ||
130 | goto out_unlock; | ||
131 | } | ||
132 | |||
133 | inst = kmalloc(sizeof(*inst), GFP_ATOMIC); | ||
134 | if (!inst) | ||
135 | goto out_unlock; | ||
136 | |||
137 | memset(inst, 0, sizeof(*inst)); | ||
138 | inst->queue_num = queue_num; | ||
139 | inst->peer_pid = pid; | ||
140 | inst->queue_maxlen = NFQNL_QMAX_DEFAULT; | ||
141 | inst->copy_range = 0xfffff; | ||
142 | inst->copy_mode = NFQNL_COPY_NONE; | ||
143 | atomic_set(&inst->id_sequence, 0); | ||
144 | inst->lock = SPIN_LOCK_UNLOCKED; | ||
145 | INIT_LIST_HEAD(&inst->queue_list); | ||
146 | |||
147 | if (!try_module_get(THIS_MODULE)) | ||
148 | goto out_free; | ||
149 | |||
150 | hlist_add_head(&inst->hlist, | ||
151 | &instance_table[instance_hashfn(queue_num)]); | ||
152 | |||
153 | write_unlock_bh(&instances_lock); | ||
154 | |||
155 | QDEBUG("successfully created new instance\n"); | ||
156 | |||
157 | return inst; | ||
158 | |||
159 | out_free: | ||
160 | kfree(inst); | ||
161 | out_unlock: | ||
162 | write_unlock_bh(&instances_lock); | ||
163 | return NULL; | ||
164 | } | ||
165 | |||
166 | static void nfqnl_flush(struct nfqnl_instance *queue, int verdict); | ||
167 | |||
168 | static void | ||
169 | _instance_destroy2(struct nfqnl_instance *inst, int lock) | ||
170 | { | ||
171 | /* first pull it out of the global list */ | ||
172 | if (lock) | ||
173 | write_lock_bh(&instances_lock); | ||
174 | |||
175 | QDEBUG("removing instance %p (queuenum=%u) from hash\n", | ||
176 | inst, inst->queue_num); | ||
177 | hlist_del(&inst->hlist); | ||
178 | |||
179 | if (lock) | ||
180 | write_unlock_bh(&instances_lock); | ||
181 | |||
182 | /* then flush all pending skbs from the queue */ | ||
183 | nfqnl_flush(inst, NF_DROP); | ||
184 | |||
185 | /* and finally free the data structure */ | ||
186 | kfree(inst); | ||
187 | |||
188 | module_put(THIS_MODULE); | ||
189 | } | ||
190 | |||
191 | static inline void | ||
192 | __instance_destroy(struct nfqnl_instance *inst) | ||
193 | { | ||
194 | _instance_destroy2(inst, 0); | ||
195 | } | ||
196 | |||
197 | static inline void | ||
198 | instance_destroy(struct nfqnl_instance *inst) | ||
199 | { | ||
200 | _instance_destroy2(inst, 1); | ||
201 | } | ||
202 | |||
203 | |||
204 | |||
205 | static void | ||
206 | issue_verdict(struct nfqnl_queue_entry *entry, int verdict) | ||
207 | { | ||
208 | QDEBUG("entering for entry %p, verdict %u\n", entry, verdict); | ||
209 | |||
210 | /* TCP input path (and probably other bits) assume to be called | ||
211 | * from softirq context, not from syscall, like issue_verdict is | ||
212 | * called. TCP input path deadlocks with locks taken from timer | ||
213 | * softirq, e.g. We therefore emulate this by local_bh_disable() */ | ||
214 | |||
215 | local_bh_disable(); | ||
216 | nf_reinject(entry->skb, entry->info, verdict); | ||
217 | local_bh_enable(); | ||
218 | |||
219 | kfree(entry); | ||
220 | } | ||
221 | |||
222 | static inline void | ||
223 | __enqueue_entry(struct nfqnl_instance *queue, | ||
224 | struct nfqnl_queue_entry *entry) | ||
225 | { | ||
226 | list_add(&entry->list, &queue->queue_list); | ||
227 | queue->queue_total++; | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * Find and return a queued entry matched by cmpfn, or return the last | ||
232 | * entry if cmpfn is NULL. | ||
233 | */ | ||
234 | static inline struct nfqnl_queue_entry * | ||
235 | __find_entry(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, | ||
236 | unsigned long data) | ||
237 | { | ||
238 | struct list_head *p; | ||
239 | |||
240 | list_for_each_prev(p, &queue->queue_list) { | ||
241 | struct nfqnl_queue_entry *entry = (struct nfqnl_queue_entry *)p; | ||
242 | |||
243 | if (!cmpfn || cmpfn(entry, data)) | ||
244 | return entry; | ||
245 | } | ||
246 | return NULL; | ||
247 | } | ||
248 | |||
249 | static inline void | ||
250 | __dequeue_entry(struct nfqnl_instance *q, struct nfqnl_queue_entry *entry) | ||
251 | { | ||
252 | list_del(&entry->list); | ||
253 | q->queue_total--; | ||
254 | } | ||
255 | |||
256 | static inline struct nfqnl_queue_entry * | ||
257 | __find_dequeue_entry(struct nfqnl_instance *queue, | ||
258 | nfqnl_cmpfn cmpfn, unsigned long data) | ||
259 | { | ||
260 | struct nfqnl_queue_entry *entry; | ||
261 | |||
262 | entry = __find_entry(queue, cmpfn, data); | ||
263 | if (entry == NULL) | ||
264 | return NULL; | ||
265 | |||
266 | __dequeue_entry(queue, entry); | ||
267 | return entry; | ||
268 | } | ||
269 | |||
270 | |||
271 | static inline void | ||
272 | __nfqnl_flush(struct nfqnl_instance *queue, int verdict) | ||
273 | { | ||
274 | struct nfqnl_queue_entry *entry; | ||
275 | |||
276 | while ((entry = __find_dequeue_entry(queue, NULL, 0))) | ||
277 | issue_verdict(entry, verdict); | ||
278 | } | ||
279 | |||
280 | static inline int | ||
281 | __nfqnl_set_mode(struct nfqnl_instance *queue, | ||
282 | unsigned char mode, unsigned int range) | ||
283 | { | ||
284 | int status = 0; | ||
285 | |||
286 | switch (mode) { | ||
287 | case NFQNL_COPY_NONE: | ||
288 | case NFQNL_COPY_META: | ||
289 | queue->copy_mode = mode; | ||
290 | queue->copy_range = 0; | ||
291 | break; | ||
292 | |||
293 | case NFQNL_COPY_PACKET: | ||
294 | queue->copy_mode = mode; | ||
295 | /* we're using struct nfattr which has 16bit nfa_len */ | ||
296 | if (range > 0xffff) | ||
297 | queue->copy_range = 0xffff; | ||
298 | else | ||
299 | queue->copy_range = range; | ||
300 | break; | ||
301 | |||
302 | default: | ||
303 | status = -EINVAL; | ||
304 | |||
305 | } | ||
306 | return status; | ||
307 | } | ||
308 | |||
309 | static struct nfqnl_queue_entry * | ||
310 | find_dequeue_entry(struct nfqnl_instance *queue, | ||
311 | nfqnl_cmpfn cmpfn, unsigned long data) | ||
312 | { | ||
313 | struct nfqnl_queue_entry *entry; | ||
314 | |||
315 | spin_lock_bh(&queue->lock); | ||
316 | entry = __find_dequeue_entry(queue, cmpfn, data); | ||
317 | spin_unlock_bh(&queue->lock); | ||
318 | |||
319 | return entry; | ||
320 | } | ||
321 | |||
322 | static void | ||
323 | nfqnl_flush(struct nfqnl_instance *queue, int verdict) | ||
324 | { | ||
325 | spin_lock_bh(&queue->lock); | ||
326 | __nfqnl_flush(queue, verdict); | ||
327 | spin_unlock_bh(&queue->lock); | ||
328 | } | ||
329 | |||
330 | static struct sk_buff * | ||
331 | nfqnl_build_packet_message(struct nfqnl_instance *queue, | ||
332 | struct nfqnl_queue_entry *entry, int *errp) | ||
333 | { | ||
334 | unsigned char *old_tail; | ||
335 | size_t size; | ||
336 | size_t data_len = 0; | ||
337 | struct sk_buff *skb; | ||
338 | struct nfqnl_msg_packet_hdr pmsg; | ||
339 | struct nlmsghdr *nlh; | ||
340 | struct nfgenmsg *nfmsg; | ||
341 | unsigned int tmp_uint; | ||
342 | |||
343 | QDEBUG("entered\n"); | ||
344 | |||
345 | /* all macros expand to constant values at compile time */ | ||
346 | size = NLMSG_SPACE(sizeof(struct nfqnl_msg_packet_hdr)) | ||
347 | + NLMSG_SPACE(sizeof(u_int32_t)) /* ifindex */ | ||
348 | + NLMSG_SPACE(sizeof(u_int32_t)) /* ifindex */ | ||
349 | + NLMSG_SPACE(sizeof(u_int32_t)) /* mark */ | ||
350 | + NLMSG_SPACE(sizeof(struct nfqnl_msg_packet_hw)) | ||
351 | + NLMSG_SPACE(sizeof(struct nfqnl_msg_packet_timestamp)); | ||
352 | |||
353 | spin_lock_bh(&queue->lock); | ||
354 | |||
355 | switch (queue->copy_mode) { | ||
356 | case NFQNL_COPY_META: | ||
357 | case NFQNL_COPY_NONE: | ||
358 | data_len = 0; | ||
359 | break; | ||
360 | |||
361 | case NFQNL_COPY_PACKET: | ||
362 | if (queue->copy_range == 0 | ||
363 | || queue->copy_range > entry->skb->len) | ||
364 | data_len = entry->skb->len; | ||
365 | else | ||
366 | data_len = queue->copy_range; | ||
367 | |||
368 | size += NLMSG_SPACE(data_len); | ||
369 | break; | ||
370 | |||
371 | default: | ||
372 | *errp = -EINVAL; | ||
373 | spin_unlock_bh(&queue->lock); | ||
374 | return NULL; | ||
375 | } | ||
376 | |||
377 | spin_unlock_bh(&queue->lock); | ||
378 | |||
379 | skb = alloc_skb(size, GFP_ATOMIC); | ||
380 | if (!skb) | ||
381 | goto nlmsg_failure; | ||
382 | |||
383 | old_tail= skb->tail; | ||
384 | nlh = NLMSG_PUT(skb, 0, 0, | ||
385 | NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET, | ||
386 | sizeof(struct nfgenmsg)); | ||
387 | nfmsg = NLMSG_DATA(nlh); | ||
388 | nfmsg->nfgen_family = entry->info->pf; | ||
389 | nfmsg->version = NFNETLINK_V0; | ||
390 | nfmsg->res_id = htons(queue->queue_num); | ||
391 | |||
392 | pmsg.packet_id = htonl(entry->id); | ||
393 | pmsg.hw_protocol = htons(entry->skb->protocol); | ||
394 | pmsg.hook = entry->info->hook; | ||
395 | |||
396 | NFA_PUT(skb, NFQA_PACKET_HDR, sizeof(pmsg), &pmsg); | ||
397 | |||
398 | if (entry->info->indev) { | ||
399 | tmp_uint = htonl(entry->info->indev->ifindex); | ||
400 | NFA_PUT(skb, NFQA_IFINDEX_INDEV, sizeof(tmp_uint), &tmp_uint); | ||
401 | } | ||
402 | |||
403 | if (entry->info->outdev) { | ||
404 | tmp_uint = htonl(entry->info->outdev->ifindex); | ||
405 | NFA_PUT(skb, NFQA_IFINDEX_OUTDEV, sizeof(tmp_uint), &tmp_uint); | ||
406 | } | ||
407 | |||
408 | if (entry->skb->nfmark) { | ||
409 | tmp_uint = htonl(entry->skb->nfmark); | ||
410 | NFA_PUT(skb, NFQA_MARK, sizeof(u_int32_t), &tmp_uint); | ||
411 | } | ||
412 | |||
413 | if (entry->info->indev && entry->skb->dev | ||
414 | && entry->skb->dev->hard_header_parse) { | ||
415 | struct nfqnl_msg_packet_hw phw; | ||
416 | |||
417 | phw.hw_addrlen = | ||
418 | entry->skb->dev->hard_header_parse(entry->skb, | ||
419 | phw.hw_addr); | ||
420 | phw.hw_addrlen = htons(phw.hw_addrlen); | ||
421 | NFA_PUT(skb, NFQA_HWADDR, sizeof(phw), &phw); | ||
422 | } | ||
423 | |||
424 | if (entry->skb->stamp.tv_sec) { | ||
425 | struct nfqnl_msg_packet_timestamp ts; | ||
426 | |||
427 | ts.sec = htonll(entry->skb->stamp.tv_sec); | ||
428 | ts.usec = htonll(entry->skb->stamp.tv_usec); | ||
429 | |||
430 | NFA_PUT(skb, NFQA_TIMESTAMP, sizeof(ts), &ts); | ||
431 | } | ||
432 | |||
433 | if (data_len) { | ||
434 | struct nfattr *nfa; | ||
435 | int size = NFA_LENGTH(data_len); | ||
436 | |||
437 | if (skb_tailroom(skb) < (int)NFA_SPACE(data_len)) { | ||
438 | printk(KERN_WARNING "nf_queue: no tailroom!\n"); | ||
439 | goto nlmsg_failure; | ||
440 | } | ||
441 | |||
442 | nfa = (struct nfattr *)skb_put(skb, NFA_ALIGN(size)); | ||
443 | nfa->nfa_type = NFQA_PAYLOAD; | ||
444 | nfa->nfa_len = size; | ||
445 | |||
446 | if (skb_copy_bits(entry->skb, 0, NFA_DATA(nfa), data_len)) | ||
447 | BUG(); | ||
448 | } | ||
449 | |||
450 | nlh->nlmsg_len = skb->tail - old_tail; | ||
451 | return skb; | ||
452 | |||
453 | nlmsg_failure: | ||
454 | nfattr_failure: | ||
455 | if (skb) | ||
456 | kfree_skb(skb); | ||
457 | *errp = -EINVAL; | ||
458 | if (net_ratelimit()) | ||
459 | printk(KERN_ERR "nf_queue: error creating packet message\n"); | ||
460 | return NULL; | ||
461 | } | ||
462 | |||
463 | static int | ||
464 | nfqnl_enqueue_packet(struct sk_buff *skb, struct nf_info *info, | ||
465 | unsigned int queuenum, void *data) | ||
466 | { | ||
467 | int status = -EINVAL; | ||
468 | struct sk_buff *nskb; | ||
469 | struct nfqnl_instance *queue; | ||
470 | struct nfqnl_queue_entry *entry; | ||
471 | |||
472 | QDEBUG("entered\n"); | ||
473 | |||
474 | queue = instance_lookup(queuenum); | ||
475 | if (!queue) { | ||
476 | QDEBUG("no queue instance matching\n"); | ||
477 | return -EINVAL; | ||
478 | } | ||
479 | |||
480 | if (queue->copy_mode == NFQNL_COPY_NONE) { | ||
481 | QDEBUG("mode COPY_NONE, aborting\n"); | ||
482 | return -EAGAIN; | ||
483 | } | ||
484 | |||
485 | entry = kmalloc(sizeof(*entry), GFP_ATOMIC); | ||
486 | if (entry == NULL) { | ||
487 | if (net_ratelimit()) | ||
488 | printk(KERN_ERR | ||
489 | "nf_queue: OOM in nfqnl_enqueue_packet()\n"); | ||
490 | return -ENOMEM; | ||
491 | } | ||
492 | |||
493 | entry->info = info; | ||
494 | entry->skb = skb; | ||
495 | entry->id = atomic_inc_return(&queue->id_sequence); | ||
496 | |||
497 | nskb = nfqnl_build_packet_message(queue, entry, &status); | ||
498 | if (nskb == NULL) | ||
499 | goto err_out_free; | ||
500 | |||
501 | spin_lock_bh(&queue->lock); | ||
502 | |||
503 | if (!queue->peer_pid) | ||
504 | goto err_out_free_nskb; | ||
505 | |||
506 | if (queue->queue_total >= queue->queue_maxlen) { | ||
507 | queue->queue_dropped++; | ||
508 | status = -ENOSPC; | ||
509 | if (net_ratelimit()) | ||
510 | printk(KERN_WARNING "ip_queue: full at %d entries, " | ||
511 | "dropping packets(s). Dropped: %d\n", | ||
512 | queue->queue_total, queue->queue_dropped); | ||
513 | goto err_out_free_nskb; | ||
514 | } | ||
515 | |||
516 | /* nfnetlink_unicast will either free the nskb or add it to a socket */ | ||
517 | status = nfnetlink_unicast(nskb, queue->peer_pid, MSG_DONTWAIT); | ||
518 | if (status < 0) { | ||
519 | queue->queue_user_dropped++; | ||
520 | goto err_out_unlock; | ||
521 | } | ||
522 | |||
523 | __enqueue_entry(queue, entry); | ||
524 | |||
525 | spin_unlock_bh(&queue->lock); | ||
526 | return status; | ||
527 | |||
528 | err_out_free_nskb: | ||
529 | kfree_skb(nskb); | ||
530 | |||
531 | err_out_unlock: | ||
532 | spin_unlock_bh(&queue->lock); | ||
533 | |||
534 | err_out_free: | ||
535 | kfree(entry); | ||
536 | return status; | ||
537 | } | ||
538 | |||
539 | static int | ||
540 | nfqnl_mangle(void *data, int data_len, struct nfqnl_queue_entry *e) | ||
541 | { | ||
542 | int diff; | ||
543 | |||
544 | diff = data_len - e->skb->len; | ||
545 | if (diff < 0) | ||
546 | skb_trim(e->skb, data_len); | ||
547 | else if (diff > 0) { | ||
548 | if (data_len > 0xFFFF) | ||
549 | return -EINVAL; | ||
550 | if (diff > skb_tailroom(e->skb)) { | ||
551 | struct sk_buff *newskb; | ||
552 | |||
553 | newskb = skb_copy_expand(e->skb, | ||
554 | skb_headroom(e->skb), | ||
555 | diff, | ||
556 | GFP_ATOMIC); | ||
557 | if (newskb == NULL) { | ||
558 | printk(KERN_WARNING "ip_queue: OOM " | ||
559 | "in mangle, dropping packet\n"); | ||
560 | return -ENOMEM; | ||
561 | } | ||
562 | if (e->skb->sk) | ||
563 | skb_set_owner_w(newskb, e->skb->sk); | ||
564 | kfree_skb(e->skb); | ||
565 | e->skb = newskb; | ||
566 | } | ||
567 | skb_put(e->skb, diff); | ||
568 | } | ||
569 | if (!skb_make_writable(&e->skb, data_len)) | ||
570 | return -ENOMEM; | ||
571 | memcpy(e->skb->data, data, data_len); | ||
572 | |||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | static inline int | ||
577 | id_cmp(struct nfqnl_queue_entry *e, unsigned long id) | ||
578 | { | ||
579 | return (id == e->id); | ||
580 | } | ||
581 | |||
582 | static int | ||
583 | nfqnl_set_mode(struct nfqnl_instance *queue, | ||
584 | unsigned char mode, unsigned int range) | ||
585 | { | ||
586 | int status; | ||
587 | |||
588 | spin_lock_bh(&queue->lock); | ||
589 | status = __nfqnl_set_mode(queue, mode, range); | ||
590 | spin_unlock_bh(&queue->lock); | ||
591 | |||
592 | return status; | ||
593 | } | ||
594 | |||
595 | static int | ||
596 | dev_cmp(struct nfqnl_queue_entry *entry, unsigned long ifindex) | ||
597 | { | ||
598 | if (entry->info->indev) | ||
599 | if (entry->info->indev->ifindex == ifindex) | ||
600 | return 1; | ||
601 | |||
602 | if (entry->info->outdev) | ||
603 | if (entry->info->outdev->ifindex == ifindex) | ||
604 | return 1; | ||
605 | |||
606 | return 0; | ||
607 | } | ||
608 | |||
609 | /* drop all packets with either indev or outdev == ifindex from all queue | ||
610 | * instances */ | ||
611 | static void | ||
612 | nfqnl_dev_drop(int ifindex) | ||
613 | { | ||
614 | int i; | ||
615 | |||
616 | QDEBUG("entering for ifindex %u\n", ifindex); | ||
617 | |||
618 | /* this only looks like we have to hold the readlock for a way too long | ||
619 | * time, issue_verdict(), nf_reinject(), ... - but we always only | ||
620 | * issue NF_DROP, which is processed directly in nf_reinject() */ | ||
621 | read_lock_bh(&instances_lock); | ||
622 | |||
623 | for (i = 0; i < INSTANCE_BUCKETS; i++) { | ||
624 | struct hlist_node *tmp; | ||
625 | struct nfqnl_instance *inst; | ||
626 | struct hlist_head *head = &instance_table[i]; | ||
627 | |||
628 | hlist_for_each_entry(inst, tmp, head, hlist) { | ||
629 | struct nfqnl_queue_entry *entry; | ||
630 | while ((entry = find_dequeue_entry(inst, dev_cmp, | ||
631 | ifindex)) != NULL) | ||
632 | issue_verdict(entry, NF_DROP); | ||
633 | } | ||
634 | } | ||
635 | |||
636 | read_unlock_bh(&instances_lock); | ||
637 | } | ||
638 | |||
639 | #define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0) | ||
640 | |||
641 | static int | ||
642 | nfqnl_rcv_dev_event(struct notifier_block *this, | ||
643 | unsigned long event, void *ptr) | ||
644 | { | ||
645 | struct net_device *dev = ptr; | ||
646 | |||
647 | /* Drop any packets associated with the downed device */ | ||
648 | if (event == NETDEV_DOWN) | ||
649 | nfqnl_dev_drop(dev->ifindex); | ||
650 | return NOTIFY_DONE; | ||
651 | } | ||
652 | |||
653 | static struct notifier_block nfqnl_dev_notifier = { | ||
654 | .notifier_call = nfqnl_rcv_dev_event, | ||
655 | }; | ||
656 | |||
657 | static int | ||
658 | nfqnl_rcv_nl_event(struct notifier_block *this, | ||
659 | unsigned long event, void *ptr) | ||
660 | { | ||
661 | struct netlink_notify *n = ptr; | ||
662 | |||
663 | if (event == NETLINK_URELEASE && | ||
664 | n->protocol == NETLINK_NETFILTER && n->pid) { | ||
665 | int i; | ||
666 | |||
667 | /* destroy all instances for this pid */ | ||
668 | write_lock_bh(&instances_lock); | ||
669 | for (i = 0; i < INSTANCE_BUCKETS; i++) { | ||
670 | struct hlist_node *tmp, *t2; | ||
671 | struct nfqnl_instance *inst; | ||
672 | struct hlist_head *head = &instance_table[i]; | ||
673 | |||
674 | hlist_for_each_entry_safe(inst, tmp, t2, head, hlist) { | ||
675 | if (n->pid == inst->peer_pid) | ||
676 | __instance_destroy(inst); | ||
677 | } | ||
678 | } | ||
679 | write_unlock_bh(&instances_lock); | ||
680 | } | ||
681 | return NOTIFY_DONE; | ||
682 | } | ||
683 | |||
684 | static struct notifier_block nfqnl_rtnl_notifier = { | ||
685 | .notifier_call = nfqnl_rcv_nl_event, | ||
686 | }; | ||
687 | |||
688 | static int | ||
689 | nfqnl_recv_verdict(struct sock *ctnl, struct sk_buff *skb, | ||
690 | struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) | ||
691 | { | ||
692 | struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); | ||
693 | u_int16_t queue_num = ntohs(nfmsg->res_id); | ||
694 | |||
695 | struct nfqnl_msg_verdict_hdr *vhdr; | ||
696 | struct nfqnl_instance *queue; | ||
697 | unsigned int verdict; | ||
698 | struct nfqnl_queue_entry *entry; | ||
699 | |||
700 | queue = instance_lookup(queue_num); | ||
701 | if (!queue) | ||
702 | return -ENODEV; | ||
703 | |||
704 | if (queue->peer_pid != NETLINK_CB(skb).pid) | ||
705 | return -EPERM; | ||
706 | |||
707 | if (!nfqa[NFQA_VERDICT_HDR-1]) | ||
708 | return -EINVAL; | ||
709 | |||
710 | vhdr = NFA_DATA(nfqa[NFQA_VERDICT_HDR-1]); | ||
711 | verdict = ntohl(vhdr->verdict); | ||
712 | |||
713 | if ((verdict & NF_VERDICT_MASK) > NF_MAX_VERDICT) | ||
714 | return -EINVAL; | ||
715 | |||
716 | entry = find_dequeue_entry(queue, id_cmp, ntohl(vhdr->id)); | ||
717 | if (entry == NULL) | ||
718 | return -ENOENT; | ||
719 | |||
720 | if (nfqa[NFQA_PAYLOAD-1]) { | ||
721 | if (nfqnl_mangle(NFA_DATA(nfqa[NFQA_PAYLOAD-1]), | ||
722 | NFA_PAYLOAD(nfqa[NFQA_PAYLOAD-1]), entry) < 0) | ||
723 | verdict = NF_DROP; | ||
724 | } | ||
725 | |||
726 | if (nfqa[NFQA_MARK-1]) | ||
727 | skb->nfmark = ntohl(*(u_int32_t *)NFA_DATA(nfqa[NFQA_MARK-1])); | ||
728 | |||
729 | issue_verdict(entry, verdict); | ||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | static int | ||
734 | nfqnl_recv_unsupp(struct sock *ctnl, struct sk_buff *skb, | ||
735 | struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) | ||
736 | { | ||
737 | return -ENOTSUPP; | ||
738 | } | ||
739 | |||
740 | static int | ||
741 | nfqnl_recv_config(struct sock *ctnl, struct sk_buff *skb, | ||
742 | struct nlmsghdr *nlh, struct nfattr *nfqa[], int *errp) | ||
743 | { | ||
744 | struct nfgenmsg *nfmsg = NLMSG_DATA(nlh); | ||
745 | u_int16_t queue_num = ntohs(nfmsg->res_id); | ||
746 | struct nfqnl_instance *queue; | ||
747 | |||
748 | QDEBUG("entering for msg %u\n", NFNL_MSG_TYPE(nlh->nlmsg_type)); | ||
749 | |||
750 | queue = instance_lookup(queue_num); | ||
751 | if (nfqa[NFQA_CFG_CMD-1]) { | ||
752 | struct nfqnl_msg_config_cmd *cmd; | ||
753 | cmd = NFA_DATA(nfqa[NFQA_CFG_CMD-1]); | ||
754 | QDEBUG("found CFG_CMD\n"); | ||
755 | |||
756 | switch (cmd->command) { | ||
757 | case NFQNL_CFG_CMD_BIND: | ||
758 | if (queue) | ||
759 | return -EBUSY; | ||
760 | |||
761 | queue = instance_create(queue_num, NETLINK_CB(skb).pid); | ||
762 | if (!queue) | ||
763 | return -EINVAL; | ||
764 | break; | ||
765 | case NFQNL_CFG_CMD_UNBIND: | ||
766 | if (!queue) | ||
767 | return -ENODEV; | ||
768 | |||
769 | if (queue->peer_pid != NETLINK_CB(skb).pid) | ||
770 | return -EPERM; | ||
771 | |||
772 | instance_destroy(queue); | ||
773 | break; | ||
774 | case NFQNL_CFG_CMD_PF_BIND: | ||
775 | QDEBUG("registering queue handler for pf=%u\n", | ||
776 | ntohs(cmd->pf)); | ||
777 | return nf_register_queue_handler(ntohs(cmd->pf), | ||
778 | nfqnl_enqueue_packet, | ||
779 | NULL); | ||
780 | |||
781 | break; | ||
782 | case NFQNL_CFG_CMD_PF_UNBIND: | ||
783 | QDEBUG("unregistering queue handler for pf=%u\n", | ||
784 | ntohs(cmd->pf)); | ||
785 | /* This is a bug and a feature. We can unregister | ||
786 | * other handlers(!) */ | ||
787 | return nf_unregister_queue_handler(ntohs(cmd->pf)); | ||
788 | break; | ||
789 | default: | ||
790 | return -EINVAL; | ||
791 | } | ||
792 | } else { | ||
793 | if (!queue) { | ||
794 | QDEBUG("no config command, and no instance ENOENT\n"); | ||
795 | return -ENOENT; | ||
796 | } | ||
797 | |||
798 | if (queue->peer_pid != NETLINK_CB(skb).pid) { | ||
799 | QDEBUG("no config command, and wrong pid\n"); | ||
800 | return -EPERM; | ||
801 | } | ||
802 | } | ||
803 | |||
804 | if (nfqa[NFQA_CFG_PARAMS-1]) { | ||
805 | struct nfqnl_msg_config_params *params; | ||
806 | params = NFA_DATA(nfqa[NFQA_CFG_PARAMS-1]); | ||
807 | |||
808 | nfqnl_set_mode(queue, params->copy_mode, | ||
809 | ntohl(params->copy_range)); | ||
810 | } | ||
811 | |||
812 | return 0; | ||
813 | } | ||
814 | |||
815 | static struct nfnl_callback nfqnl_cb[NFQNL_MSG_MAX] = { | ||
816 | [NFQNL_MSG_PACKET] = { .call = nfqnl_recv_unsupp, | ||
817 | .cap_required = CAP_NET_ADMIN }, | ||
818 | [NFQNL_MSG_VERDICT] = { .call = nfqnl_recv_verdict, | ||
819 | .cap_required = CAP_NET_ADMIN }, | ||
820 | [NFQNL_MSG_CONFIG] = { .call = nfqnl_recv_config, | ||
821 | .cap_required = CAP_NET_ADMIN }, | ||
822 | }; | ||
823 | |||
824 | static struct nfnetlink_subsystem nfqnl_subsys = { | ||
825 | .name = "nf_queue", | ||
826 | .subsys_id = NFNL_SUBSYS_QUEUE, | ||
827 | .cb_count = NFQNL_MSG_MAX, | ||
828 | .attr_count = NFQA_MAX, | ||
829 | .cb = nfqnl_cb, | ||
830 | }; | ||
831 | |||
832 | static int | ||
833 | init_or_cleanup(int init) | ||
834 | { | ||
835 | int status = -ENOMEM; | ||
836 | |||
837 | if (!init) | ||
838 | goto cleanup; | ||
839 | |||
840 | netlink_register_notifier(&nfqnl_rtnl_notifier); | ||
841 | status = nfnetlink_subsys_register(&nfqnl_subsys); | ||
842 | if (status < 0) { | ||
843 | printk(KERN_ERR "nf_queue: failed to create netlink socket\n"); | ||
844 | goto cleanup_netlink_notifier; | ||
845 | } | ||
846 | |||
847 | register_netdevice_notifier(&nfqnl_dev_notifier); | ||
848 | return status; | ||
849 | |||
850 | cleanup: | ||
851 | nf_unregister_queue_handlers(nfqnl_enqueue_packet); | ||
852 | unregister_netdevice_notifier(&nfqnl_dev_notifier); | ||
853 | nfnetlink_subsys_unregister(&nfqnl_subsys); | ||
854 | |||
855 | cleanup_netlink_notifier: | ||
856 | netlink_unregister_notifier(&nfqnl_rtnl_notifier); | ||
857 | return status; | ||
858 | } | ||
859 | |||
860 | static int __init init(void) | ||
861 | { | ||
862 | |||
863 | return init_or_cleanup(1); | ||
864 | } | ||
865 | |||
866 | static void __exit fini(void) | ||
867 | { | ||
868 | init_or_cleanup(0); | ||
869 | } | ||
870 | |||
871 | MODULE_DESCRIPTION("netfilter packet queue handler"); | ||
872 | MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>"); | ||
873 | MODULE_LICENSE("GPL"); | ||
874 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_QUEUE); | ||
875 | |||
876 | module_init(init); | ||
877 | module_exit(fini); | ||