1/* 2 * ip_vs_ftp.c: IPVS ftp application module 3 * 4 * Version: $Id: ip_vs_ftp.c,v 1.1.1.1 2007/08/03 18:53:51 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 71static int ip_vs_ftp_get_addrport(char *data, char *data_limit, 72 const char *pattern, size_t plen, char term, 73 __be32 *addr, __be16 *port, 74 char **start, char **end) 75{ 76 unsigned char p[6]; 77 int i = 0; 78 79 if (data_limit - data < plen) { 80 /* check if there is partial match */ 81 if (strnicmp(data, pattern, data_limit - data) == 0) 82 return -1; 83 else 84 return 0; 85 } 86 87 if (strnicmp(data, pattern, plen) != 0) { 88 return 0; 89 } 90 *start = data + plen; 91 92 for (data = *start; *data != term; data++) { 93 if (data == data_limit) 94 return -1; 95 } 96 *end = data; 97 98 memset(p, 0, sizeof(p)); 99 for (data = *start; data != *end; data++) { 100 if (*data >= '0' && *data <= '9') { 101 p[i] = p[i]*10 + *data - '0'; 102 } else if (*data == ',' && i < 5) { 103 i++; 104 } else { 105 /* unexpected character */ 106 return -1; 107 } 108 } 109 110 if (i != 5) 111 return -1; 112 113 *addr = get_unaligned((__be32 *)p); 114 *port = get_unaligned((__be16 *)(p + 4)); 115 return 1; 116} 117 118 119static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, 120 struct sk_buff **pskb, int *diff) 121{ 122 struct iphdr *iph; 123 struct tcphdr *th; 124 char *data, *data_limit; 125 char *start, *end; 126 __be32 from; 127 __be16 port; 128 struct ip_vs_conn *n_cp; 129 char buf[24]; 130 unsigned buf_len; 131 int ret; 132 133 *diff = 0; 134 135 /* Only useful for established sessions */ 136 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 137 return 1; 138 139 /* Linear packets are much easier to deal with. */ 140 if (!ip_vs_make_skb_writable(pskb, (*pskb)->len)) 141 return 0; 142 143 if (cp->app_data == &ip_vs_ftp_pasv) { 144 iph = ip_hdr(*pskb); 145 th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 146 data = (char *)th + (th->doff << 2); 147 data_limit = skb_tail_pointer(*pskb); 148 149 if (ip_vs_ftp_get_addrport(data, data_limit, 150 SERVER_STRING, 151 sizeof(SERVER_STRING)-1, ')', 152 &from, &port, 153 &start, &end) != 1) 154 return 1; 155 156 IP_VS_DBG(7, "PASV response (%u.%u.%u.%u:%d) -> " 157 "%u.%u.%u.%u:%d detected\n", 158 NIPQUAD(from), ntohs(port), NIPQUAD(cp->caddr), 0); 159 160 /* 161 * Now update or create an connection entry for it 162 */ 163 n_cp = ip_vs_conn_out_get(iph->protocol, from, port, 164 cp->caddr, 0); 165 if (!n_cp) { 166 n_cp = ip_vs_conn_new(IPPROTO_TCP, 167 cp->caddr, 0, 168 cp->vaddr, port, 169 from, port, 170 IP_VS_CONN_F_NO_CPORT, 171 cp->dest); 172 if (!n_cp) 173 return 0; 174 175 /* add its controller */ 176 ip_vs_control_add(n_cp, cp); 177 } 178 179 /* 180 * Replace the old passive address with the new one 181 */ 182 from = n_cp->vaddr; 183 port = n_cp->vport; 184 sprintf(buf,"%d,%d,%d,%d,%d,%d", NIPQUAD(from), 185 (ntohs(port)>>8)&255, ntohs(port)&255); 186 buf_len = strlen(buf); 187 188 /* 189 * Calculate required delta-offset to keep TCP happy 190 */ 191 *diff = buf_len - (end-start); 192 193 if (*diff == 0) { 194 /* simply replace it with new passive address */ 195 memcpy(start, buf, buf_len); 196 ret = 1; 197 } else { 198 ret = !ip_vs_skb_replace(*pskb, GFP_ATOMIC, start, 199 end-start, buf, buf_len); 200 } 201 202 cp->app_data = NULL; 203 ip_vs_tcp_conn_listen(n_cp); 204 ip_vs_conn_put(n_cp); 205 return ret; 206 } 207 return 1; 208} 209 210 211static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, 212 struct sk_buff **pskb, int *diff) 213{ 214 struct iphdr *iph; 215 struct tcphdr *th; 216 char *data, *data_start, *data_limit; 217 char *start, *end; 218 __be32 to; 219 __be16 port; 220 struct ip_vs_conn *n_cp; 221 222 /* no diff required for incoming packets */ 223 *diff = 0; 224 225 /* Only useful for established sessions */ 226 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 227 return 1; 228 229 /* Linear packets are much easier to deal with. */ 230 if (!ip_vs_make_skb_writable(pskb, (*pskb)->len)) 231 return 0; 232 233 /* 234 * Detecting whether it is passive 235 */ 236 iph = ip_hdr(*pskb); 237 th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 238 239 /* Since there may be OPTIONS in the TCP packet and the HLEN is 240 the length of the header in 32-bit multiples, it is accurate 241 to calculate data address by th+HLEN*4 */ 242 data = data_start = (char *)th + (th->doff << 2); 243 data_limit = skb_tail_pointer(*pskb); 244 245 while (data <= data_limit - 6) { 246 if (strnicmp(data, "PASV\r\n", 6) == 0) { 247 /* Passive mode on */ 248 IP_VS_DBG(7, "got PASV at %td of %td\n", 249 data - data_start, 250 data_limit - data_start); 251 cp->app_data = &ip_vs_ftp_pasv; 252 return 1; 253 } 254 data++; 255 } 256 257 /* 258 * To support virtual FTP server, the scenerio is as follows: 259 * FTP client ----> Load Balancer ----> FTP server 260 * First detect the port number in the application data, 261 * then create a new connection entry for the coming data 262 * connection. 263 */ 264 if (ip_vs_ftp_get_addrport(data_start, data_limit, 265 CLIENT_STRING, sizeof(CLIENT_STRING)-1, 266 '\r', &to, &port, 267 &start, &end) != 1) 268 return 1; 269 270 IP_VS_DBG(7, "PORT %u.%u.%u.%u:%d detected\n", 271 NIPQUAD(to), ntohs(port)); 272 273 /* Passive mode off */ 274 cp->app_data = NULL; 275 276 /* 277 * Now update or create a connection entry for it 278 */ 279 IP_VS_DBG(7, "protocol %s %u.%u.%u.%u:%d %u.%u.%u.%u:%d\n", 280 ip_vs_proto_name(iph->protocol), 281 NIPQUAD(to), ntohs(port), NIPQUAD(cp->vaddr), 0); 282 283 n_cp = ip_vs_conn_in_get(iph->protocol, 284 to, port, 285 cp->vaddr, htons(ntohs(cp->vport)-1)); 286 if (!n_cp) { 287 n_cp = ip_vs_conn_new(IPPROTO_TCP, 288 to, port, 289 cp->vaddr, htons(ntohs(cp->vport)-1), 290 cp->daddr, htons(ntohs(cp->dport)-1), 291 0, 292 cp->dest); 293 if (!n_cp) 294 return 0; 295 296 /* add its controller */ 297 ip_vs_control_add(n_cp, cp); 298 } 299 300 /* 301 * Move tunnel to listen state 302 */ 303 ip_vs_tcp_conn_listen(n_cp); 304 ip_vs_conn_put(n_cp); 305 306 return 1; 307} 308 309 310static struct ip_vs_app ip_vs_ftp = { 311 .name = "ftp", 312 .type = IP_VS_APP_TYPE_FTP, 313 .protocol = IPPROTO_TCP, 314 .module = THIS_MODULE, 315 .incs_list = LIST_HEAD_INIT(ip_vs_ftp.incs_list), 316 .init_conn = ip_vs_ftp_init_conn, 317 .done_conn = ip_vs_ftp_done_conn, 318 .bind_conn = NULL, 319 .unbind_conn = NULL, 320 .pkt_out = ip_vs_ftp_out, 321 .pkt_in = ip_vs_ftp_in, 322}; 323 324 325/* 326 * ip_vs_ftp initialization 327 */ 328static int __init ip_vs_ftp_init(void) 329{ 330 int i, ret; 331 struct ip_vs_app *app = &ip_vs_ftp; 332 333 ret = register_ip_vs_app(app); 334 if (ret) 335 return ret; 336 337 for (i=0; i<IP_VS_APP_MAX_PORTS; i++) { 338 if (!ports[i]) 339 continue; 340 ret = register_ip_vs_app_inc(app, app->protocol, ports[i]); 341 if (ret) 342 break; 343 IP_VS_INFO("%s: loaded support on port[%d] = %d\n", 344 app->name, i, ports[i]); 345 } 346 347 if (ret) 348 unregister_ip_vs_app(app); 349 350 return ret; 351} 352 353 354/* 355 * ip_vs_ftp finish. 356 */ 357static void __exit ip_vs_ftp_exit(void) 358{ 359 unregister_ip_vs_app(&ip_vs_ftp); 360} 361 362 363module_init(ip_vs_ftp_init); 364module_exit(ip_vs_ftp_exit); 365MODULE_LICENSE("GPL"); 366