Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.12-rc6 400 lines 9.4 kB view raw
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 */ 47static int ports[IP_VS_APP_MAX_PORTS] = {21, 0}; 48module_param_array(ports, int, NULL, 0); 49 50/* 51 * Debug level 52 */ 53#ifdef CONFIG_IP_VS_DEBUG 54static int debug=0; 55module_param(debug, int, 0); 56#endif 57 58 59/* Dummy variable */ 60static int ip_vs_ftp_pasv; 61 62 63static int 64ip_vs_ftp_init_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) 65{ 66 return 0; 67} 68 69 70static int 71ip_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 */ 82static 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 */ 143static 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 */ 246static 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 345static 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 */ 363static 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 */ 392static void __exit ip_vs_ftp_exit(void) 393{ 394 unregister_ip_vs_app(&ip_vs_ftp); 395} 396 397 398module_init(ip_vs_ftp_init); 399module_exit(ip_vs_ftp_exit); 400MODULE_LICENSE("GPL");