diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/ipv4/ipvs/ip_vs_ftp.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/ipv4/ipvs/ip_vs_ftp.c')
-rw-r--r-- | net/ipv4/ipvs/ip_vs_ftp.c | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/net/ipv4/ipvs/ip_vs_ftp.c b/net/ipv4/ipvs/ip_vs_ftp.c new file mode 100644 index 00000000000..a19a33ceb81 --- /dev/null +++ b/net/ipv4/ipvs/ip_vs_ftp.c | |||
@@ -0,0 +1,400 @@ | |||
1 | /* | ||
2 | * ip_vs_ftp.c: IPVS ftp application module | ||
3 | * | ||
4 | * Version: $Id: ip_vs_ftp.c,v 1.13 2002/09/15 08:14:08 wensong Exp $ | ||
5 | * | ||
6 | * Authors: Wensong Zhang <wensong@linuxvirtualserver.org> | ||
7 | * | ||
8 | * Changes: | ||
9 | * | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or | ||
12 | * modify it under the terms of the GNU General Public License | ||
13 | * as published by the Free Software Foundation; either version | ||
14 | * 2 of the License, or (at your option) any later version. | ||
15 | * | ||
16 | * Most code here is taken from ip_masq_ftp.c in kernel 2.2. The difference | ||
17 | * is that ip_vs_ftp module handles the reverse direction to ip_masq_ftp. | ||
18 | * | ||
19 | * IP_MASQ_FTP ftp masquerading module | ||
20 | * | ||
21 | * Version: @(#)ip_masq_ftp.c 0.04 02/05/96 | ||
22 | * | ||
23 | * Author: Wouter Gadeyne | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/moduleparam.h> | ||
29 | #include <linux/kernel.h> | ||
30 | #include <linux/skbuff.h> | ||
31 | #include <linux/in.h> | ||
32 | #include <linux/ip.h> | ||
33 | #include <net/protocol.h> | ||
34 | #include <net/tcp.h> | ||
35 | |||
36 | #include <net/ip_vs.h> | ||
37 | |||
38 | |||
39 | #define SERVER_STRING "227 Entering Passive Mode (" | ||
40 | #define CLIENT_STRING "PORT " | ||
41 | |||
42 | |||
43 | /* | ||
44 | * List of ports (up to IP_VS_APP_MAX_PORTS) to be handled by helper | ||
45 | * First port is set to the default port. | ||
46 | */ | ||
47 | static int ports[IP_VS_APP_MAX_PORTS] = {21, 0}; | ||
48 | module_param_array(ports, int, NULL, 0); | ||
49 | |||
50 | /* | ||
51 | * Debug level | ||
52 | */ | ||
53 | #ifdef CONFIG_IP_VS_DEBUG | ||
54 | static int debug=0; | ||
55 | module_param(debug, int, 0); | ||
56 | #endif | ||
57 | |||
58 | |||
59 | /* Dummy variable */ | ||
60 | static int ip_vs_ftp_pasv; | ||
61 | |||
62 | |||
63 | static int | ||
64 | ip_vs_ftp_init_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) | ||
65 | { | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | |||
70 | static int | ||
71 | ip_vs_ftp_done_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) | ||
72 | { | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | |||
77 | /* | ||
78 | * Get <addr,port> from the string "xxx.xxx.xxx.xxx,ppp,ppp", started | ||
79 | * with the "pattern" and terminated with the "term" character. | ||
80 | * <addr,port> is in network order. | ||
81 | */ | ||
82 | static int ip_vs_ftp_get_addrport(char *data, char *data_limit, | ||
83 | const char *pattern, size_t plen, char term, | ||
84 | __u32 *addr, __u16 *port, | ||
85 | char **start, char **end) | ||
86 | { | ||
87 | unsigned char p[6]; | ||
88 | int i = 0; | ||
89 | |||
90 | if (data_limit - data < plen) { | ||
91 | /* check if there is partial match */ | ||
92 | if (strnicmp(data, pattern, data_limit - data) == 0) | ||
93 | return -1; | ||
94 | else | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | if (strnicmp(data, pattern, plen) != 0) { | ||
99 | return 0; | ||
100 | } | ||
101 | *start = data + plen; | ||
102 | |||
103 | for (data = *start; *data != term; data++) { | ||
104 | if (data == data_limit) | ||
105 | return -1; | ||
106 | } | ||
107 | *end = data; | ||
108 | |||
109 | memset(p, 0, sizeof(p)); | ||
110 | for (data = *start; data != *end; data++) { | ||
111 | if (*data >= '0' && *data <= '9') { | ||
112 | p[i] = p[i]*10 + *data - '0'; | ||
113 | } else if (*data == ',' && i < 5) { | ||
114 | i++; | ||
115 | } else { | ||
116 | /* unexpected character */ | ||
117 | return -1; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | if (i != 5) | ||
122 | return -1; | ||
123 | |||
124 | *addr = (p[3]<<24) | (p[2]<<16) | (p[1]<<8) | p[0]; | ||
125 | *port = (p[5]<<8) | p[4]; | ||
126 | return 1; | ||
127 | } | ||
128 | |||
129 | |||
130 | /* | ||
131 | * Look at outgoing ftp packets to catch the response to a PASV command | ||
132 | * from the server (inside-to-outside). | ||
133 | * When we see one, we build a connection entry with the client address, | ||
134 | * client port 0 (unknown at the moment), the server address and the | ||
135 | * server port. Mark the current connection entry as a control channel | ||
136 | * of the new entry. All this work is just to make the data connection | ||
137 | * can be scheduled to the right server later. | ||
138 | * | ||
139 | * The outgoing packet should be something like | ||
140 | * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)". | ||
141 | * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number. | ||
142 | */ | ||
143 | static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, | ||
144 | struct sk_buff **pskb, int *diff) | ||
145 | { | ||
146 | struct iphdr *iph; | ||
147 | struct tcphdr *th; | ||
148 | char *data, *data_limit; | ||
149 | char *start, *end; | ||
150 | __u32 from; | ||
151 | __u16 port; | ||
152 | struct ip_vs_conn *n_cp; | ||
153 | char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ | ||
154 | unsigned buf_len; | ||
155 | int ret; | ||
156 | |||
157 | *diff = 0; | ||
158 | |||
159 | /* Only useful for established sessions */ | ||
160 | if (cp->state != IP_VS_TCP_S_ESTABLISHED) | ||
161 | return 1; | ||
162 | |||
163 | /* Linear packets are much easier to deal with. */ | ||
164 | if (!ip_vs_make_skb_writable(pskb, (*pskb)->len)) | ||
165 | return 0; | ||
166 | |||
167 | if (cp->app_data == &ip_vs_ftp_pasv) { | ||
168 | iph = (*pskb)->nh.iph; | ||
169 | th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); | ||
170 | data = (char *)th + (th->doff << 2); | ||
171 | data_limit = (*pskb)->tail; | ||
172 | |||
173 | if (ip_vs_ftp_get_addrport(data, data_limit, | ||
174 | SERVER_STRING, | ||
175 | sizeof(SERVER_STRING)-1, ')', | ||
176 | &from, &port, | ||
177 | &start, &end) != 1) | ||
178 | return 1; | ||
179 | |||
180 | IP_VS_DBG(1-debug, "PASV response (%u.%u.%u.%u:%d) -> " | ||
181 | "%u.%u.%u.%u:%d detected\n", | ||
182 | NIPQUAD(from), ntohs(port), NIPQUAD(cp->caddr), 0); | ||
183 | |||
184 | /* | ||
185 | * Now update or create an connection entry for it | ||
186 | */ | ||
187 | n_cp = ip_vs_conn_out_get(iph->protocol, from, port, | ||
188 | cp->caddr, 0); | ||
189 | if (!n_cp) { | ||
190 | n_cp = ip_vs_conn_new(IPPROTO_TCP, | ||
191 | cp->caddr, 0, | ||
192 | cp->vaddr, port, | ||
193 | from, port, | ||
194 | IP_VS_CONN_F_NO_CPORT, | ||
195 | cp->dest); | ||
196 | if (!n_cp) | ||
197 | return 0; | ||
198 | |||
199 | /* add its controller */ | ||
200 | ip_vs_control_add(n_cp, cp); | ||
201 | } | ||
202 | |||
203 | /* | ||
204 | * Replace the old passive address with the new one | ||
205 | */ | ||
206 | from = n_cp->vaddr; | ||
207 | port = n_cp->vport; | ||
208 | sprintf(buf,"%d,%d,%d,%d,%d,%d", NIPQUAD(from), | ||
209 | port&255, (port>>8)&255); | ||
210 | buf_len = strlen(buf); | ||
211 | |||
212 | /* | ||
213 | * Calculate required delta-offset to keep TCP happy | ||
214 | */ | ||
215 | *diff = buf_len - (end-start); | ||
216 | |||
217 | if (*diff == 0) { | ||
218 | /* simply replace it with new passive address */ | ||
219 | memcpy(start, buf, buf_len); | ||
220 | ret = 1; | ||
221 | } else { | ||
222 | ret = !ip_vs_skb_replace(*pskb, GFP_ATOMIC, start, | ||
223 | end-start, buf, buf_len); | ||
224 | } | ||
225 | |||
226 | cp->app_data = NULL; | ||
227 | ip_vs_tcp_conn_listen(n_cp); | ||
228 | ip_vs_conn_put(n_cp); | ||
229 | return ret; | ||
230 | } | ||
231 | return 1; | ||
232 | } | ||
233 | |||
234 | |||
235 | /* | ||
236 | * Look at incoming ftp packets to catch the PASV/PORT command | ||
237 | * (outside-to-inside). | ||
238 | * | ||
239 | * The incoming packet having the PORT command should be something like | ||
240 | * "PORT xxx,xxx,xxx,xxx,ppp,ppp\n". | ||
241 | * xxx,xxx,xxx,xxx is the client address, ppp,ppp is the client port number. | ||
242 | * In this case, we create a connection entry using the client address and | ||
243 | * port, so that the active ftp data connection from the server can reach | ||
244 | * the client. | ||
245 | */ | ||
246 | static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, | ||
247 | struct sk_buff **pskb, int *diff) | ||
248 | { | ||
249 | struct iphdr *iph; | ||
250 | struct tcphdr *th; | ||
251 | char *data, *data_start, *data_limit; | ||
252 | char *start, *end; | ||
253 | __u32 to; | ||
254 | __u16 port; | ||
255 | struct ip_vs_conn *n_cp; | ||
256 | |||
257 | /* no diff required for incoming packets */ | ||
258 | *diff = 0; | ||
259 | |||
260 | /* Only useful for established sessions */ | ||
261 | if (cp->state != IP_VS_TCP_S_ESTABLISHED) | ||
262 | return 1; | ||
263 | |||
264 | /* Linear packets are much easier to deal with. */ | ||
265 | if (!ip_vs_make_skb_writable(pskb, (*pskb)->len)) | ||
266 | return 0; | ||
267 | |||
268 | /* | ||
269 | * Detecting whether it is passive | ||
270 | */ | ||
271 | iph = (*pskb)->nh.iph; | ||
272 | th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); | ||
273 | |||
274 | /* Since there may be OPTIONS in the TCP packet and the HLEN is | ||
275 | the length of the header in 32-bit multiples, it is accurate | ||
276 | to calculate data address by th+HLEN*4 */ | ||
277 | data = data_start = (char *)th + (th->doff << 2); | ||
278 | data_limit = (*pskb)->tail; | ||
279 | |||
280 | while (data <= data_limit - 6) { | ||
281 | if (strnicmp(data, "PASV\r\n", 6) == 0) { | ||
282 | /* Passive mode on */ | ||
283 | IP_VS_DBG(1-debug, "got PASV at %zd of %zd\n", | ||
284 | data - data_start, | ||
285 | data_limit - data_start); | ||
286 | cp->app_data = &ip_vs_ftp_pasv; | ||
287 | return 1; | ||
288 | } | ||
289 | data++; | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * To support virtual FTP server, the scenerio is as follows: | ||
294 | * FTP client ----> Load Balancer ----> FTP server | ||
295 | * First detect the port number in the application data, | ||
296 | * then create a new connection entry for the coming data | ||
297 | * connection. | ||
298 | */ | ||
299 | if (ip_vs_ftp_get_addrport(data_start, data_limit, | ||
300 | CLIENT_STRING, sizeof(CLIENT_STRING)-1, | ||
301 | '\r', &to, &port, | ||
302 | &start, &end) != 1) | ||
303 | return 1; | ||
304 | |||
305 | IP_VS_DBG(1-debug, "PORT %u.%u.%u.%u:%d detected\n", | ||
306 | NIPQUAD(to), ntohs(port)); | ||
307 | |||
308 | /* Passive mode off */ | ||
309 | cp->app_data = NULL; | ||
310 | |||
311 | /* | ||
312 | * Now update or create a connection entry for it | ||
313 | */ | ||
314 | IP_VS_DBG(1-debug, "protocol %s %u.%u.%u.%u:%d %u.%u.%u.%u:%d\n", | ||
315 | ip_vs_proto_name(iph->protocol), | ||
316 | NIPQUAD(to), ntohs(port), NIPQUAD(cp->vaddr), 0); | ||
317 | |||
318 | n_cp = ip_vs_conn_in_get(iph->protocol, | ||
319 | to, port, | ||
320 | cp->vaddr, htons(ntohs(cp->vport)-1)); | ||
321 | if (!n_cp) { | ||
322 | n_cp = ip_vs_conn_new(IPPROTO_TCP, | ||
323 | to, port, | ||
324 | cp->vaddr, htons(ntohs(cp->vport)-1), | ||
325 | cp->daddr, htons(ntohs(cp->dport)-1), | ||
326 | 0, | ||
327 | cp->dest); | ||
328 | if (!n_cp) | ||
329 | return 0; | ||
330 | |||
331 | /* add its controller */ | ||
332 | ip_vs_control_add(n_cp, cp); | ||
333 | } | ||
334 | |||
335 | /* | ||
336 | * Move tunnel to listen state | ||
337 | */ | ||
338 | ip_vs_tcp_conn_listen(n_cp); | ||
339 | ip_vs_conn_put(n_cp); | ||
340 | |||
341 | return 1; | ||
342 | } | ||
343 | |||
344 | |||
345 | static struct ip_vs_app ip_vs_ftp = { | ||
346 | .name = "ftp", | ||
347 | .type = IP_VS_APP_TYPE_FTP, | ||
348 | .protocol = IPPROTO_TCP, | ||
349 | .module = THIS_MODULE, | ||
350 | .incs_list = LIST_HEAD_INIT(ip_vs_ftp.incs_list), | ||
351 | .init_conn = ip_vs_ftp_init_conn, | ||
352 | .done_conn = ip_vs_ftp_done_conn, | ||
353 | .bind_conn = NULL, | ||
354 | .unbind_conn = NULL, | ||
355 | .pkt_out = ip_vs_ftp_out, | ||
356 | .pkt_in = ip_vs_ftp_in, | ||
357 | }; | ||
358 | |||
359 | |||
360 | /* | ||
361 | * ip_vs_ftp initialization | ||
362 | */ | ||
363 | static int __init ip_vs_ftp_init(void) | ||
364 | { | ||
365 | int i, ret; | ||
366 | struct ip_vs_app *app = &ip_vs_ftp; | ||
367 | |||
368 | ret = register_ip_vs_app(app); | ||
369 | if (ret) | ||
370 | return ret; | ||
371 | |||
372 | for (i=0; i<IP_VS_APP_MAX_PORTS; i++) { | ||
373 | if (!ports[i]) | ||
374 | continue; | ||
375 | ret = register_ip_vs_app_inc(app, app->protocol, ports[i]); | ||
376 | if (ret) | ||
377 | break; | ||
378 | IP_VS_DBG(1-debug, "%s: loaded support on port[%d] = %d\n", | ||
379 | app->name, i, ports[i]); | ||
380 | } | ||
381 | |||
382 | if (ret) | ||
383 | unregister_ip_vs_app(app); | ||
384 | |||
385 | return ret; | ||
386 | } | ||
387 | |||
388 | |||
389 | /* | ||
390 | * ip_vs_ftp finish. | ||
391 | */ | ||
392 | static void __exit ip_vs_ftp_exit(void) | ||
393 | { | ||
394 | unregister_ip_vs_app(&ip_vs_ftp); | ||
395 | } | ||
396 | |||
397 | |||
398 | module_init(ip_vs_ftp_init); | ||
399 | module_exit(ip_vs_ftp_exit); | ||
400 | MODULE_LICENSE("GPL"); | ||