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.21-rc7 394 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#include <asm/unaligned.h> 36 37#include <net/ip_vs.h> 38 39 40#define SERVER_STRING "227 Entering Passive Mode (" 41#define CLIENT_STRING "PORT " 42 43 44/* 45 * List of ports (up to IP_VS_APP_MAX_PORTS) to be handled by helper 46 * First port is set to the default port. 47 */ 48static unsigned short ports[IP_VS_APP_MAX_PORTS] = {21, 0}; 49module_param_array(ports, ushort, NULL, 0); 50MODULE_PARM_DESC(ports, "Ports to monitor for FTP control commands"); 51 52 53/* Dummy variable */ 54static int ip_vs_ftp_pasv; 55 56 57static int 58ip_vs_ftp_init_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) 59{ 60 return 0; 61} 62 63 64static int 65ip_vs_ftp_done_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) 66{ 67 return 0; 68} 69 70 71/* 72 * Get <addr,port> from the string "xxx.xxx.xxx.xxx,ppp,ppp", started 73 * with the "pattern" and terminated with the "term" character. 74 * <addr,port> is in network order. 75 */ 76static int ip_vs_ftp_get_addrport(char *data, char *data_limit, 77 const char *pattern, size_t plen, char term, 78 __be32 *addr, __be16 *port, 79 char **start, char **end) 80{ 81 unsigned char p[6]; 82 int i = 0; 83 84 if (data_limit - data < plen) { 85 /* check if there is partial match */ 86 if (strnicmp(data, pattern, data_limit - data) == 0) 87 return -1; 88 else 89 return 0; 90 } 91 92 if (strnicmp(data, pattern, plen) != 0) { 93 return 0; 94 } 95 *start = data + plen; 96 97 for (data = *start; *data != term; data++) { 98 if (data == data_limit) 99 return -1; 100 } 101 *end = data; 102 103 memset(p, 0, sizeof(p)); 104 for (data = *start; data != *end; data++) { 105 if (*data >= '0' && *data <= '9') { 106 p[i] = p[i]*10 + *data - '0'; 107 } else if (*data == ',' && i < 5) { 108 i++; 109 } else { 110 /* unexpected character */ 111 return -1; 112 } 113 } 114 115 if (i != 5) 116 return -1; 117 118 *addr = get_unaligned((__be32 *)p); 119 *port = get_unaligned((__be16 *)(p + 4)); 120 return 1; 121} 122 123 124/* 125 * Look at outgoing ftp packets to catch the response to a PASV command 126 * from the server (inside-to-outside). 127 * When we see one, we build a connection entry with the client address, 128 * client port 0 (unknown at the moment), the server address and the 129 * server port. Mark the current connection entry as a control channel 130 * of the new entry. All this work is just to make the data connection 131 * can be scheduled to the right server later. 132 * 133 * The outgoing packet should be something like 134 * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)". 135 * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number. 136 */ 137static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, 138 struct sk_buff **pskb, int *diff) 139{ 140 struct iphdr *iph; 141 struct tcphdr *th; 142 char *data, *data_limit; 143 char *start, *end; 144 __be32 from; 145 __be16 port; 146 struct ip_vs_conn *n_cp; 147 char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ 148 unsigned buf_len; 149 int ret; 150 151 *diff = 0; 152 153 /* Only useful for established sessions */ 154 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 155 return 1; 156 157 /* Linear packets are much easier to deal with. */ 158 if (!ip_vs_make_skb_writable(pskb, (*pskb)->len)) 159 return 0; 160 161 if (cp->app_data == &ip_vs_ftp_pasv) { 162 iph = (*pskb)->nh.iph; 163 th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 164 data = (char *)th + (th->doff << 2); 165 data_limit = (*pskb)->tail; 166 167 if (ip_vs_ftp_get_addrport(data, data_limit, 168 SERVER_STRING, 169 sizeof(SERVER_STRING)-1, ')', 170 &from, &port, 171 &start, &end) != 1) 172 return 1; 173 174 IP_VS_DBG(7, "PASV response (%u.%u.%u.%u:%d) -> " 175 "%u.%u.%u.%u:%d detected\n", 176 NIPQUAD(from), ntohs(port), NIPQUAD(cp->caddr), 0); 177 178 /* 179 * Now update or create an connection entry for it 180 */ 181 n_cp = ip_vs_conn_out_get(iph->protocol, from, port, 182 cp->caddr, 0); 183 if (!n_cp) { 184 n_cp = ip_vs_conn_new(IPPROTO_TCP, 185 cp->caddr, 0, 186 cp->vaddr, port, 187 from, port, 188 IP_VS_CONN_F_NO_CPORT, 189 cp->dest); 190 if (!n_cp) 191 return 0; 192 193 /* add its controller */ 194 ip_vs_control_add(n_cp, cp); 195 } 196 197 /* 198 * Replace the old passive address with the new one 199 */ 200 from = n_cp->vaddr; 201 port = n_cp->vport; 202 sprintf(buf,"%d,%d,%d,%d,%d,%d", NIPQUAD(from), 203 (ntohs(port)>>8)&255, ntohs(port)&255); 204 buf_len = strlen(buf); 205 206 /* 207 * Calculate required delta-offset to keep TCP happy 208 */ 209 *diff = buf_len - (end-start); 210 211 if (*diff == 0) { 212 /* simply replace it with new passive address */ 213 memcpy(start, buf, buf_len); 214 ret = 1; 215 } else { 216 ret = !ip_vs_skb_replace(*pskb, GFP_ATOMIC, start, 217 end-start, buf, buf_len); 218 } 219 220 cp->app_data = NULL; 221 ip_vs_tcp_conn_listen(n_cp); 222 ip_vs_conn_put(n_cp); 223 return ret; 224 } 225 return 1; 226} 227 228 229/* 230 * Look at incoming ftp packets to catch the PASV/PORT command 231 * (outside-to-inside). 232 * 233 * The incoming packet having the PORT command should be something like 234 * "PORT xxx,xxx,xxx,xxx,ppp,ppp\n". 235 * xxx,xxx,xxx,xxx is the client address, ppp,ppp is the client port number. 236 * In this case, we create a connection entry using the client address and 237 * port, so that the active ftp data connection from the server can reach 238 * the client. 239 */ 240static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, 241 struct sk_buff **pskb, int *diff) 242{ 243 struct iphdr *iph; 244 struct tcphdr *th; 245 char *data, *data_start, *data_limit; 246 char *start, *end; 247 __be32 to; 248 __be16 port; 249 struct ip_vs_conn *n_cp; 250 251 /* no diff required for incoming packets */ 252 *diff = 0; 253 254 /* Only useful for established sessions */ 255 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 256 return 1; 257 258 /* Linear packets are much easier to deal with. */ 259 if (!ip_vs_make_skb_writable(pskb, (*pskb)->len)) 260 return 0; 261 262 /* 263 * Detecting whether it is passive 264 */ 265 iph = (*pskb)->nh.iph; 266 th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 267 268 /* Since there may be OPTIONS in the TCP packet and the HLEN is 269 the length of the header in 32-bit multiples, it is accurate 270 to calculate data address by th+HLEN*4 */ 271 data = data_start = (char *)th + (th->doff << 2); 272 data_limit = (*pskb)->tail; 273 274 while (data <= data_limit - 6) { 275 if (strnicmp(data, "PASV\r\n", 6) == 0) { 276 /* Passive mode on */ 277 IP_VS_DBG(7, "got PASV at %td of %td\n", 278 data - data_start, 279 data_limit - data_start); 280 cp->app_data = &ip_vs_ftp_pasv; 281 return 1; 282 } 283 data++; 284 } 285 286 /* 287 * To support virtual FTP server, the scenerio is as follows: 288 * FTP client ----> Load Balancer ----> FTP server 289 * First detect the port number in the application data, 290 * then create a new connection entry for the coming data 291 * connection. 292 */ 293 if (ip_vs_ftp_get_addrport(data_start, data_limit, 294 CLIENT_STRING, sizeof(CLIENT_STRING)-1, 295 '\r', &to, &port, 296 &start, &end) != 1) 297 return 1; 298 299 IP_VS_DBG(7, "PORT %u.%u.%u.%u:%d detected\n", 300 NIPQUAD(to), ntohs(port)); 301 302 /* Passive mode off */ 303 cp->app_data = NULL; 304 305 /* 306 * Now update or create a connection entry for it 307 */ 308 IP_VS_DBG(7, "protocol %s %u.%u.%u.%u:%d %u.%u.%u.%u:%d\n", 309 ip_vs_proto_name(iph->protocol), 310 NIPQUAD(to), ntohs(port), NIPQUAD(cp->vaddr), 0); 311 312 n_cp = ip_vs_conn_in_get(iph->protocol, 313 to, port, 314 cp->vaddr, htons(ntohs(cp->vport)-1)); 315 if (!n_cp) { 316 n_cp = ip_vs_conn_new(IPPROTO_TCP, 317 to, port, 318 cp->vaddr, htons(ntohs(cp->vport)-1), 319 cp->daddr, htons(ntohs(cp->dport)-1), 320 0, 321 cp->dest); 322 if (!n_cp) 323 return 0; 324 325 /* add its controller */ 326 ip_vs_control_add(n_cp, cp); 327 } 328 329 /* 330 * Move tunnel to listen state 331 */ 332 ip_vs_tcp_conn_listen(n_cp); 333 ip_vs_conn_put(n_cp); 334 335 return 1; 336} 337 338 339static struct ip_vs_app ip_vs_ftp = { 340 .name = "ftp", 341 .type = IP_VS_APP_TYPE_FTP, 342 .protocol = IPPROTO_TCP, 343 .module = THIS_MODULE, 344 .incs_list = LIST_HEAD_INIT(ip_vs_ftp.incs_list), 345 .init_conn = ip_vs_ftp_init_conn, 346 .done_conn = ip_vs_ftp_done_conn, 347 .bind_conn = NULL, 348 .unbind_conn = NULL, 349 .pkt_out = ip_vs_ftp_out, 350 .pkt_in = ip_vs_ftp_in, 351}; 352 353 354/* 355 * ip_vs_ftp initialization 356 */ 357static int __init ip_vs_ftp_init(void) 358{ 359 int i, ret; 360 struct ip_vs_app *app = &ip_vs_ftp; 361 362 ret = register_ip_vs_app(app); 363 if (ret) 364 return ret; 365 366 for (i=0; i<IP_VS_APP_MAX_PORTS; i++) { 367 if (!ports[i]) 368 continue; 369 ret = register_ip_vs_app_inc(app, app->protocol, ports[i]); 370 if (ret) 371 break; 372 IP_VS_INFO("%s: loaded support on port[%d] = %d\n", 373 app->name, i, ports[i]); 374 } 375 376 if (ret) 377 unregister_ip_vs_app(app); 378 379 return ret; 380} 381 382 383/* 384 * ip_vs_ftp finish. 385 */ 386static void __exit ip_vs_ftp_exit(void) 387{ 388 unregister_ip_vs_app(&ip_vs_ftp); 389} 390 391 392module_init(ip_vs_ftp_init); 393module_exit(ip_vs_ftp_exit); 394MODULE_LICENSE("GPL");