diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/Kconfig | 13 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/nf_conntrack_sane.c | 242 |
3 files changed, 256 insertions, 0 deletions
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 80107d4909c5..614c92c17835 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -235,6 +235,19 @@ config NF_CONNTRACK_PPTP | |||
235 | 235 | ||
236 | To compile it as a module, choose M here. If unsure, say N. | 236 | To compile it as a module, choose M here. If unsure, say N. |
237 | 237 | ||
238 | config NF_CONNTRACK_SANE | ||
239 | tristate "SANE protocol support (EXPERIMENTAL)" | ||
240 | depends on EXPERIMENTAL && NF_CONNTRACK | ||
241 | help | ||
242 | SANE is a protocol for remote access to scanners as implemented | ||
243 | by the 'saned' daemon. Like FTP, it uses separate control and | ||
244 | data connections. | ||
245 | |||
246 | With this module you can support SANE on a connection tracking | ||
247 | firewall. | ||
248 | |||
249 | To compile it as a module, choose M here. If unsure, say N. | ||
250 | |||
238 | config NF_CONNTRACK_SIP | 251 | config NF_CONNTRACK_SIP |
239 | tristate "SIP protocol support (EXPERIMENTAL)" | 252 | tristate "SIP protocol support (EXPERIMENTAL)" |
240 | depends on EXPERIMENTAL && NF_CONNTRACK | 253 | depends on EXPERIMENTAL && NF_CONNTRACK |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 5dc5574f7e99..5054b0ff8096 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -29,6 +29,7 @@ obj-$(CONFIG_NF_CONNTRACK_H323) += nf_conntrack_h323.o | |||
29 | obj-$(CONFIG_NF_CONNTRACK_IRC) += nf_conntrack_irc.o | 29 | obj-$(CONFIG_NF_CONNTRACK_IRC) += nf_conntrack_irc.o |
30 | obj-$(CONFIG_NF_CONNTRACK_NETBIOS_NS) += nf_conntrack_netbios_ns.o | 30 | obj-$(CONFIG_NF_CONNTRACK_NETBIOS_NS) += nf_conntrack_netbios_ns.o |
31 | obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_conntrack_pptp.o | 31 | obj-$(CONFIG_NF_CONNTRACK_PPTP) += nf_conntrack_pptp.o |
32 | obj-$(CONFIG_NF_CONNTRACK_SANE) += nf_conntrack_sane.o | ||
32 | obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o | 33 | obj-$(CONFIG_NF_CONNTRACK_SIP) += nf_conntrack_sip.o |
33 | obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o | 34 | obj-$(CONFIG_NF_CONNTRACK_TFTP) += nf_conntrack_tftp.o |
34 | 35 | ||
diff --git a/net/netfilter/nf_conntrack_sane.c b/net/netfilter/nf_conntrack_sane.c new file mode 100644 index 000000000000..eb2d1dc46d45 --- /dev/null +++ b/net/netfilter/nf_conntrack_sane.c | |||
@@ -0,0 +1,242 @@ | |||
1 | /* SANE connection tracking helper | ||
2 | * (SANE = Scanner Access Now Easy) | ||
3 | * For documentation about the SANE network protocol see | ||
4 | * http://www.sane-project.org/html/doc015.html | ||
5 | */ | ||
6 | |||
7 | /* Copyright (C) 2007 Red Hat, Inc. | ||
8 | * Author: Michal Schmidt <mschmidt@redhat.com> | ||
9 | * Based on the FTP conntrack helper (net/netfilter/nf_conntrack_ftp.c): | ||
10 | * (C) 1999-2001 Paul `Rusty' Russell | ||
11 | * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org> | ||
12 | * (C) 2003,2004 USAGI/WIDE Project <http://www.linux-ipv6.org> | ||
13 | * (C) 2003 Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp> | ||
14 | * | ||
15 | * This program is free software; you can redistribute it and/or modify | ||
16 | * it under the terms of the GNU General Public License version 2 as | ||
17 | * published by the Free Software Foundation. | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/moduleparam.h> | ||
22 | #include <linux/netfilter.h> | ||
23 | #include <linux/in.h> | ||
24 | #include <linux/tcp.h> | ||
25 | #include <net/netfilter/nf_conntrack.h> | ||
26 | #include <net/netfilter/nf_conntrack_helper.h> | ||
27 | #include <net/netfilter/nf_conntrack_expect.h> | ||
28 | #include <linux/netfilter/nf_conntrack_sane.h> | ||
29 | |||
30 | MODULE_LICENSE("GPL"); | ||
31 | MODULE_AUTHOR("Michal Schmidt <mschmidt@redhat.com>"); | ||
32 | MODULE_DESCRIPTION("SANE connection tracking helper"); | ||
33 | |||
34 | static char *sane_buffer; | ||
35 | |||
36 | static DEFINE_SPINLOCK(nf_sane_lock); | ||
37 | |||
38 | #define MAX_PORTS 8 | ||
39 | static u_int16_t ports[MAX_PORTS]; | ||
40 | static unsigned int ports_c; | ||
41 | module_param_array(ports, ushort, &ports_c, 0400); | ||
42 | |||
43 | #if 0 | ||
44 | #define DEBUGP printk | ||
45 | #else | ||
46 | #define DEBUGP(format, args...) | ||
47 | #endif | ||
48 | |||
49 | struct sane_request { | ||
50 | __be32 RPC_code; | ||
51 | #define SANE_NET_START 7 /* RPC code */ | ||
52 | |||
53 | __be32 handle; | ||
54 | }; | ||
55 | |||
56 | struct sane_reply_net_start { | ||
57 | __be32 status; | ||
58 | #define SANE_STATUS_SUCCESS 0 | ||
59 | |||
60 | __be16 zero; | ||
61 | __be16 port; | ||
62 | /* other fields aren't interesting for conntrack */ | ||
63 | }; | ||
64 | |||
65 | static int help(struct sk_buff **pskb, | ||
66 | unsigned int protoff, | ||
67 | struct nf_conn *ct, | ||
68 | enum ip_conntrack_info ctinfo) | ||
69 | { | ||
70 | unsigned int dataoff, datalen; | ||
71 | struct tcphdr _tcph, *th; | ||
72 | char *sb_ptr; | ||
73 | int ret = NF_ACCEPT; | ||
74 | int dir = CTINFO2DIR(ctinfo); | ||
75 | struct nf_ct_sane_master *ct_sane_info; | ||
76 | struct nf_conntrack_expect *exp; | ||
77 | struct nf_conntrack_tuple *tuple; | ||
78 | struct sane_request *req; | ||
79 | struct sane_reply_net_start *reply; | ||
80 | int family = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.l3num; | ||
81 | |||
82 | ct_sane_info = &nfct_help(ct)->help.ct_sane_info; | ||
83 | /* Until there's been traffic both ways, don't look in packets. */ | ||
84 | if (ctinfo != IP_CT_ESTABLISHED && | ||
85 | ctinfo != IP_CT_ESTABLISHED+IP_CT_IS_REPLY) | ||
86 | return NF_ACCEPT; | ||
87 | |||
88 | /* Not a full tcp header? */ | ||
89 | th = skb_header_pointer(*pskb, protoff, sizeof(_tcph), &_tcph); | ||
90 | if (th == NULL) | ||
91 | return NF_ACCEPT; | ||
92 | |||
93 | /* No data? */ | ||
94 | dataoff = protoff + th->doff * 4; | ||
95 | if (dataoff >= (*pskb)->len) | ||
96 | return NF_ACCEPT; | ||
97 | |||
98 | datalen = (*pskb)->len - dataoff; | ||
99 | |||
100 | spin_lock_bh(&nf_sane_lock); | ||
101 | sb_ptr = skb_header_pointer(*pskb, dataoff, datalen, sane_buffer); | ||
102 | BUG_ON(sb_ptr == NULL); | ||
103 | |||
104 | if (dir == IP_CT_DIR_ORIGINAL) { | ||
105 | if (datalen != sizeof(struct sane_request)) | ||
106 | goto out; | ||
107 | |||
108 | req = (struct sane_request *)sb_ptr; | ||
109 | if (req->RPC_code != htonl(SANE_NET_START)) { | ||
110 | /* Not an interesting command */ | ||
111 | ct_sane_info->state = SANE_STATE_NORMAL; | ||
112 | goto out; | ||
113 | } | ||
114 | |||
115 | /* We're interested in the next reply */ | ||
116 | ct_sane_info->state = SANE_STATE_START_REQUESTED; | ||
117 | goto out; | ||
118 | } | ||
119 | |||
120 | /* Is it a reply to an uninteresting command? */ | ||
121 | if (ct_sane_info->state != SANE_STATE_START_REQUESTED) | ||
122 | goto out; | ||
123 | |||
124 | /* It's a reply to SANE_NET_START. */ | ||
125 | ct_sane_info->state = SANE_STATE_NORMAL; | ||
126 | |||
127 | if (datalen < sizeof(struct sane_reply_net_start)) { | ||
128 | DEBUGP("nf_ct_sane: NET_START reply too short\n"); | ||
129 | goto out; | ||
130 | } | ||
131 | |||
132 | reply = (struct sane_reply_net_start *)sb_ptr; | ||
133 | if (reply->status != htonl(SANE_STATUS_SUCCESS)) { | ||
134 | /* saned refused the command */ | ||
135 | DEBUGP("nf_ct_sane: unsuccessful SANE_STATUS = %u\n", | ||
136 | ntohl(reply->status)); | ||
137 | goto out; | ||
138 | } | ||
139 | |||
140 | /* Invalid saned reply? Ignore it. */ | ||
141 | if (reply->zero != 0) | ||
142 | goto out; | ||
143 | |||
144 | exp = nf_conntrack_expect_alloc(ct); | ||
145 | if (exp == NULL) { | ||
146 | ret = NF_DROP; | ||
147 | goto out; | ||
148 | } | ||
149 | |||
150 | tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple; | ||
151 | nf_conntrack_expect_init(exp, family, | ||
152 | &tuple->src.u3, &tuple->dst.u3, | ||
153 | IPPROTO_TCP, | ||
154 | NULL, &reply->port); | ||
155 | |||
156 | DEBUGP("nf_ct_sane: expect: "); | ||
157 | NF_CT_DUMP_TUPLE(&exp->tuple); | ||
158 | NF_CT_DUMP_TUPLE(&exp->mask); | ||
159 | |||
160 | /* Can't expect this? Best to drop packet now. */ | ||
161 | if (nf_conntrack_expect_related(exp) != 0) | ||
162 | ret = NF_DROP; | ||
163 | |||
164 | nf_conntrack_expect_put(exp); | ||
165 | |||
166 | out: | ||
167 | spin_unlock_bh(&nf_sane_lock); | ||
168 | return ret; | ||
169 | } | ||
170 | |||
171 | static struct nf_conntrack_helper sane[MAX_PORTS][2]; | ||
172 | static char sane_names[MAX_PORTS][2][sizeof("sane-65535")]; | ||
173 | |||
174 | /* don't make this __exit, since it's called from __init ! */ | ||
175 | static void nf_conntrack_sane_fini(void) | ||
176 | { | ||
177 | int i, j; | ||
178 | |||
179 | for (i = 0; i < ports_c; i++) { | ||
180 | for (j = 0; j < 2; j++) { | ||
181 | DEBUGP("nf_ct_sane: unregistering helper for pf: %d " | ||
182 | "port: %d\n", | ||
183 | sane[i][j].tuple.src.l3num, ports[i]); | ||
184 | nf_conntrack_helper_unregister(&sane[i][j]); | ||
185 | } | ||
186 | } | ||
187 | |||
188 | kfree(sane_buffer); | ||
189 | } | ||
190 | |||
191 | static int __init nf_conntrack_sane_init(void) | ||
192 | { | ||
193 | int i, j = -1, ret = 0; | ||
194 | char *tmpname; | ||
195 | |||
196 | sane_buffer = kmalloc(65536, GFP_KERNEL); | ||
197 | if (!sane_buffer) | ||
198 | return -ENOMEM; | ||
199 | |||
200 | if (ports_c == 0) | ||
201 | ports[ports_c++] = SANE_PORT; | ||
202 | |||
203 | /* FIXME should be configurable whether IPv4 and IPv6 connections | ||
204 | are tracked or not - YK */ | ||
205 | for (i = 0; i < ports_c; i++) { | ||
206 | sane[i][0].tuple.src.l3num = PF_INET; | ||
207 | sane[i][1].tuple.src.l3num = PF_INET6; | ||
208 | for (j = 0; j < 2; j++) { | ||
209 | sane[i][j].tuple.src.u.tcp.port = htons(ports[i]); | ||
210 | sane[i][j].tuple.dst.protonum = IPPROTO_TCP; | ||
211 | sane[i][j].mask.src.u.tcp.port = 0xFFFF; | ||
212 | sane[i][j].mask.dst.protonum = 0xFF; | ||
213 | sane[i][j].max_expected = 1; | ||
214 | sane[i][j].timeout = 5 * 60; /* 5 Minutes */ | ||
215 | sane[i][j].me = THIS_MODULE; | ||
216 | sane[i][j].help = help; | ||
217 | tmpname = &sane_names[i][j][0]; | ||
218 | if (ports[i] == SANE_PORT) | ||
219 | sprintf(tmpname, "sane"); | ||
220 | else | ||
221 | sprintf(tmpname, "sane-%d", ports[i]); | ||
222 | sane[i][j].name = tmpname; | ||
223 | |||
224 | DEBUGP("nf_ct_sane: registering helper for pf: %d " | ||
225 | "port: %d\n", | ||
226 | sane[i][j].tuple.src.l3num, ports[i]); | ||
227 | ret = nf_conntrack_helper_register(&sane[i][j]); | ||
228 | if (ret) { | ||
229 | printk(KERN_ERR "nf_ct_sane: failed to " | ||
230 | "register helper for pf: %d port: %d\n", | ||
231 | sane[i][j].tuple.src.l3num, ports[i]); | ||
232 | nf_conntrack_sane_fini(); | ||
233 | return ret; | ||
234 | } | ||
235 | } | ||
236 | } | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | module_init(nf_conntrack_sane_init); | ||
242 | module_exit(nf_conntrack_sane_fini); | ||