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