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#define KMSG_COMPONENT "IPVS" 26#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 27 28#include <linux/module.h> 29#include <linux/moduleparam.h> 30#include <linux/kernel.h> 31#include <linux/skbuff.h> 32#include <linux/in.h> 33#include <linux/ip.h> 34#include <linux/netfilter.h> 35#include <net/netfilter/nf_conntrack.h> 36#include <net/netfilter/nf_conntrack_expect.h> 37#include <net/netfilter/nf_nat.h> 38#include <net/netfilter/nf_nat_helper.h> 39#include <linux/gfp.h> 40#include <net/protocol.h> 41#include <net/tcp.h> 42#include <asm/unaligned.h> 43 44#include <net/ip_vs.h> 45 46 47#define SERVER_STRING "227 " 48#define CLIENT_STRING "PORT" 49 50 51/* 52 * List of ports (up to IP_VS_APP_MAX_PORTS) to be handled by helper 53 * First port is set to the default port. 54 */ 55static unsigned int ports_count = 1; 56static unsigned short ports[IP_VS_APP_MAX_PORTS] = {21, 0}; 57module_param_array(ports, ushort, &ports_count, 0444); 58MODULE_PARM_DESC(ports, "Ports to monitor for FTP control commands"); 59 60 61/* Dummy variable */ 62static int ip_vs_ftp_pasv; 63 64 65static int 66ip_vs_ftp_init_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) 67{ 68 /* We use connection tracking for the command connection */ 69 cp->flags |= IP_VS_CONN_F_NFCT; 70 return 0; 71} 72 73 74static int 75ip_vs_ftp_done_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) 76{ 77 return 0; 78} 79 80 81/* 82 * Get <addr,port> from the string "xxx.xxx.xxx.xxx,ppp,ppp", started 83 * with the "pattern", ignoring before "skip" and terminated with 84 * the "term" character. 85 * <addr,port> is in network order. 86 */ 87static int ip_vs_ftp_get_addrport(char *data, char *data_limit, 88 const char *pattern, size_t plen, 89 char skip, char term, 90 __be32 *addr, __be16 *port, 91 char **start, char **end) 92{ 93 char *s, c; 94 unsigned char p[6]; 95 int i = 0; 96 97 if (data_limit - data < plen) { 98 /* check if there is partial match */ 99 if (strncasecmp(data, pattern, data_limit - data) == 0) 100 return -1; 101 else 102 return 0; 103 } 104 105 if (strncasecmp(data, pattern, plen) != 0) { 106 return 0; 107 } 108 s = data + plen; 109 if (skip) { 110 int found = 0; 111 112 for (;; s++) { 113 if (s == data_limit) 114 return -1; 115 if (!found) { 116 if (*s == skip) 117 found = 1; 118 } else if (*s != skip) { 119 break; 120 } 121 } 122 } 123 124 for (data = s; ; data++) { 125 if (data == data_limit) 126 return -1; 127 if (*data == term) 128 break; 129 } 130 *end = data; 131 132 memset(p, 0, sizeof(p)); 133 for (data = s; ; data++) { 134 c = *data; 135 if (c == term) 136 break; 137 if (c >= '0' && c <= '9') { 138 p[i] = p[i]*10 + c - '0'; 139 } else if (c == ',' && i < 5) { 140 i++; 141 } else { 142 /* unexpected character */ 143 return -1; 144 } 145 } 146 147 if (i != 5) 148 return -1; 149 150 *start = s; 151 *addr = get_unaligned((__be32 *) p); 152 *port = get_unaligned((__be16 *) (p + 4)); 153 return 1; 154} 155 156/* 157 * Look at outgoing ftp packets to catch the response to a PASV command 158 * from the server (inside-to-outside). 159 * When we see one, we build a connection entry with the client address, 160 * client port 0 (unknown at the moment), the server address and the 161 * server port. Mark the current connection entry as a control channel 162 * of the new entry. All this work is just to make the data connection 163 * can be scheduled to the right server later. 164 * 165 * The outgoing packet should be something like 166 * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)". 167 * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number. 168 */ 169static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, 170 struct sk_buff *skb, int *diff) 171{ 172 struct iphdr *iph; 173 struct tcphdr *th; 174 char *data, *data_limit; 175 char *start, *end; 176 union nf_inet_addr from; 177 __be16 port; 178 struct ip_vs_conn *n_cp; 179 char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ 180 unsigned int buf_len; 181 int ret = 0; 182 enum ip_conntrack_info ctinfo; 183 struct nf_conn *ct; 184 struct net *net; 185 186#ifdef CONFIG_IP_VS_IPV6 187 /* This application helper doesn't work with IPv6 yet, 188 * so turn this into a no-op for IPv6 packets 189 */ 190 if (cp->af == AF_INET6) 191 return 1; 192#endif 193 194 *diff = 0; 195 196 /* Only useful for established sessions */ 197 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 198 return 1; 199 200 /* Linear packets are much easier to deal with. */ 201 if (!skb_make_writable(skb, skb->len)) 202 return 0; 203 204 if (cp->app_data == &ip_vs_ftp_pasv) { 205 iph = ip_hdr(skb); 206 th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 207 data = (char *)th + (th->doff << 2); 208 data_limit = skb_tail_pointer(skb); 209 210 if (ip_vs_ftp_get_addrport(data, data_limit, 211 SERVER_STRING, 212 sizeof(SERVER_STRING)-1, 213 '(', ')', 214 &from.ip, &port, 215 &start, &end) != 1) 216 return 1; 217 218 IP_VS_DBG(7, "PASV response (%pI4:%d) -> %pI4:%d detected\n", 219 &from.ip, ntohs(port), &cp->caddr.ip, 0); 220 221 /* 222 * Now update or create an connection entry for it 223 */ 224 { 225 struct ip_vs_conn_param p; 226 ip_vs_conn_fill_param(ip_vs_conn_net(cp), AF_INET, 227 iph->protocol, &from, port, 228 &cp->caddr, 0, &p); 229 n_cp = ip_vs_conn_out_get(&p); 230 } 231 if (!n_cp) { 232 struct ip_vs_conn_param p; 233 ip_vs_conn_fill_param(ip_vs_conn_net(cp), 234 AF_INET, IPPROTO_TCP, &cp->caddr, 235 0, &cp->vaddr, port, &p); 236 /* As above, this is ipv4 only */ 237 n_cp = ip_vs_conn_new(&p, AF_INET, &from, port, 238 IP_VS_CONN_F_NO_CPORT | 239 IP_VS_CONN_F_NFCT, 240 cp->dest, skb->mark); 241 if (!n_cp) 242 return 0; 243 244 /* add its controller */ 245 ip_vs_control_add(n_cp, cp); 246 } 247 248 /* 249 * Replace the old passive address with the new one 250 */ 251 from.ip = n_cp->vaddr.ip; 252 port = n_cp->vport; 253 snprintf(buf, sizeof(buf), "%u,%u,%u,%u,%u,%u", 254 ((unsigned char *)&from.ip)[0], 255 ((unsigned char *)&from.ip)[1], 256 ((unsigned char *)&from.ip)[2], 257 ((unsigned char *)&from.ip)[3], 258 ntohs(port) >> 8, 259 ntohs(port) & 0xFF); 260 261 buf_len = strlen(buf); 262 263 ct = nf_ct_get(skb, &ctinfo); 264 if (ct && !nf_ct_is_untracked(ct) && nfct_nat(ct)) { 265 /* If mangling fails this function will return 0 266 * which will cause the packet to be dropped. 267 * Mangling can only fail under memory pressure, 268 * hopefully it will succeed on the retransmitted 269 * packet. 270 */ 271 rcu_read_lock(); 272 ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, 273 iph->ihl * 4, 274 start-data, end-start, 275 buf, buf_len); 276 rcu_read_unlock(); 277 if (ret) { 278 ip_vs_nfct_expect_related(skb, ct, n_cp, 279 IPPROTO_TCP, 0, 0); 280 if (skb->ip_summed == CHECKSUM_COMPLETE) 281 skb->ip_summed = CHECKSUM_UNNECESSARY; 282 /* csum is updated */ 283 ret = 1; 284 } 285 } 286 287 /* 288 * Not setting 'diff' is intentional, otherwise the sequence 289 * would be adjusted twice. 290 */ 291 292 net = skb_net(skb); 293 cp->app_data = NULL; 294 ip_vs_tcp_conn_listen(net, n_cp); 295 ip_vs_conn_put(n_cp); 296 return ret; 297 } 298 return 1; 299} 300 301 302/* 303 * Look at incoming ftp packets to catch the PASV/PORT command 304 * (outside-to-inside). 305 * 306 * The incoming packet having the PORT command should be something like 307 * "PORT xxx,xxx,xxx,xxx,ppp,ppp\n". 308 * xxx,xxx,xxx,xxx is the client address, ppp,ppp is the client port number. 309 * In this case, we create a connection entry using the client address and 310 * port, so that the active ftp data connection from the server can reach 311 * the client. 312 */ 313static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, 314 struct sk_buff *skb, int *diff) 315{ 316 struct iphdr *iph; 317 struct tcphdr *th; 318 char *data, *data_start, *data_limit; 319 char *start, *end; 320 union nf_inet_addr to; 321 __be16 port; 322 struct ip_vs_conn *n_cp; 323 struct net *net; 324 325#ifdef CONFIG_IP_VS_IPV6 326 /* This application helper doesn't work with IPv6 yet, 327 * so turn this into a no-op for IPv6 packets 328 */ 329 if (cp->af == AF_INET6) 330 return 1; 331#endif 332 333 /* no diff required for incoming packets */ 334 *diff = 0; 335 336 /* Only useful for established sessions */ 337 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 338 return 1; 339 340 /* Linear packets are much easier to deal with. */ 341 if (!skb_make_writable(skb, skb->len)) 342 return 0; 343 344 /* 345 * Detecting whether it is passive 346 */ 347 iph = ip_hdr(skb); 348 th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 349 350 /* Since there may be OPTIONS in the TCP packet and the HLEN is 351 the length of the header in 32-bit multiples, it is accurate 352 to calculate data address by th+HLEN*4 */ 353 data = data_start = (char *)th + (th->doff << 2); 354 data_limit = skb_tail_pointer(skb); 355 356 while (data <= data_limit - 6) { 357 if (strncasecmp(data, "PASV\r\n", 6) == 0) { 358 /* Passive mode on */ 359 IP_VS_DBG(7, "got PASV at %td of %td\n", 360 data - data_start, 361 data_limit - data_start); 362 cp->app_data = &ip_vs_ftp_pasv; 363 return 1; 364 } 365 data++; 366 } 367 368 /* 369 * To support virtual FTP server, the scenerio is as follows: 370 * FTP client ----> Load Balancer ----> FTP server 371 * First detect the port number in the application data, 372 * then create a new connection entry for the coming data 373 * connection. 374 */ 375 if (ip_vs_ftp_get_addrport(data_start, data_limit, 376 CLIENT_STRING, sizeof(CLIENT_STRING)-1, 377 ' ', '\r', &to.ip, &port, 378 &start, &end) != 1) 379 return 1; 380 381 IP_VS_DBG(7, "PORT %pI4:%d detected\n", &to.ip, ntohs(port)); 382 383 /* Passive mode off */ 384 cp->app_data = NULL; 385 386 /* 387 * Now update or create a connection entry for it 388 */ 389 IP_VS_DBG(7, "protocol %s %pI4:%d %pI4:%d\n", 390 ip_vs_proto_name(iph->protocol), 391 &to.ip, ntohs(port), &cp->vaddr.ip, 0); 392 393 { 394 struct ip_vs_conn_param p; 395 ip_vs_conn_fill_param(ip_vs_conn_net(cp), AF_INET, 396 iph->protocol, &to, port, &cp->vaddr, 397 htons(ntohs(cp->vport)-1), &p); 398 n_cp = ip_vs_conn_in_get(&p); 399 if (!n_cp) { 400 /* This is ipv4 only */ 401 n_cp = ip_vs_conn_new(&p, AF_INET, &cp->daddr, 402 htons(ntohs(cp->dport)-1), 403 IP_VS_CONN_F_NFCT, cp->dest, 404 skb->mark); 405 if (!n_cp) 406 return 0; 407 408 /* add its controller */ 409 ip_vs_control_add(n_cp, cp); 410 } 411 } 412 413 /* 414 * Move tunnel to listen state 415 */ 416 net = skb_net(skb); 417 ip_vs_tcp_conn_listen(net, n_cp); 418 ip_vs_conn_put(n_cp); 419 420 return 1; 421} 422 423 424static struct ip_vs_app ip_vs_ftp = { 425 .name = "ftp", 426 .type = IP_VS_APP_TYPE_FTP, 427 .protocol = IPPROTO_TCP, 428 .module = THIS_MODULE, 429 .incs_list = LIST_HEAD_INIT(ip_vs_ftp.incs_list), 430 .init_conn = ip_vs_ftp_init_conn, 431 .done_conn = ip_vs_ftp_done_conn, 432 .bind_conn = NULL, 433 .unbind_conn = NULL, 434 .pkt_out = ip_vs_ftp_out, 435 .pkt_in = ip_vs_ftp_in, 436}; 437 438/* 439 * per netns ip_vs_ftp initialization 440 */ 441static int __net_init __ip_vs_ftp_init(struct net *net) 442{ 443 int i, ret; 444 struct ip_vs_app *app; 445 struct netns_ipvs *ipvs = net_ipvs(net); 446 447 if (!ipvs) 448 return -ENOENT; 449 450 app = register_ip_vs_app(net, &ip_vs_ftp); 451 if (IS_ERR(app)) 452 return PTR_ERR(app); 453 454 for (i = 0; i < ports_count; i++) { 455 if (!ports[i]) 456 continue; 457 ret = register_ip_vs_app_inc(net, app, app->protocol, ports[i]); 458 if (ret) 459 goto err_unreg; 460 pr_info("%s: loaded support on port[%d] = %d\n", 461 app->name, i, ports[i]); 462 } 463 return 0; 464 465err_unreg: 466 unregister_ip_vs_app(net, &ip_vs_ftp); 467 return ret; 468} 469/* 470 * netns exit 471 */ 472static void __ip_vs_ftp_exit(struct net *net) 473{ 474 unregister_ip_vs_app(net, &ip_vs_ftp); 475} 476 477static struct pernet_operations ip_vs_ftp_ops = { 478 .init = __ip_vs_ftp_init, 479 .exit = __ip_vs_ftp_exit, 480}; 481 482static int __init ip_vs_ftp_init(void) 483{ 484 int rv; 485 486 rv = register_pernet_subsys(&ip_vs_ftp_ops); 487 /* rcu_barrier() is called by netns on error */ 488 return rv; 489} 490 491/* 492 * ip_vs_ftp finish. 493 */ 494static void __exit ip_vs_ftp_exit(void) 495{ 496 unregister_pernet_subsys(&ip_vs_ftp_ops); 497 /* rcu_barrier() is called by netns */ 498} 499 500 501module_init(ip_vs_ftp_init); 502module_exit(ip_vs_ftp_exit); 503MODULE_LICENSE("GPL"); 504