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