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 */ 55 static unsigned int ports_count = 1; 56 static unsigned short ports[IP_VS_APP_MAX_PORTS] = {21, 0}; 57 module_param_array(ports, ushort, &ports_count, 0444); 58 MODULE_PARM_DESC(ports, "Ports to monitor for FTP control commands"); 59 60 61 /* Dummy variable */ 62 static int ip_vs_ftp_pasv; 63 64 65 static int 66 ip_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 74 static int 75 ip_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 */ 87 static 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 (strnicmp(data, pattern, data_limit - data) == 0) 100 return -1; 101 else 102 return 0; 103 } 104 105 if (strnicmp(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 */ 169 static 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 n_cp = ip_vs_conn_new(&p, &from, port, 237 IP_VS_CONN_F_NO_CPORT | 238 IP_VS_CONN_F_NFCT, 239 cp->dest, skb->mark); 240 if (!n_cp) 241 return 0; 242 243 /* add its controller */ 244 ip_vs_control_add(n_cp, cp); 245 } 246 247 /* 248 * Replace the old passive address with the new one 249 */ 250 from.ip = n_cp->vaddr.ip; 251 port = n_cp->vport; 252 snprintf(buf, sizeof(buf), "%u,%u,%u,%u,%u,%u", 253 ((unsigned char *)&from.ip)[0], 254 ((unsigned char *)&from.ip)[1], 255 ((unsigned char *)&from.ip)[2], 256 ((unsigned char *)&from.ip)[3], 257 ntohs(port) >> 8, 258 ntohs(port) & 0xFF); 259 260 buf_len = strlen(buf); 261 262 ct = nf_ct_get(skb, &ctinfo); 263 if (ct && !nf_ct_is_untracked(ct) && nfct_nat(ct)) { 264 /* If mangling fails this function will return 0 265 * which will cause the packet to be dropped. 266 * Mangling can only fail under memory pressure, 267 * hopefully it will succeed on the retransmitted 268 * packet. 269 */ 270 ret = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, 271 iph->ihl * 4, 272 start-data, end-start, 273 buf, buf_len); 274 if (ret) { 275 ip_vs_nfct_expect_related(skb, ct, n_cp, 276 IPPROTO_TCP, 0, 0); 277 if (skb->ip_summed == CHECKSUM_COMPLETE) 278 skb->ip_summed = CHECKSUM_UNNECESSARY; 279 /* csum is updated */ 280 ret = 1; 281 } 282 } 283 284 /* 285 * Not setting 'diff' is intentional, otherwise the sequence 286 * would be adjusted twice. 287 */ 288 289 net = skb_net(skb); 290 cp->app_data = NULL; 291 ip_vs_tcp_conn_listen(net, n_cp); 292 ip_vs_conn_put(n_cp); 293 return ret; 294 } 295 return 1; 296 } 297 298 299 /* 300 * Look at incoming ftp packets to catch the PASV/PORT command 301 * (outside-to-inside). 302 * 303 * The incoming packet having the PORT command should be something like 304 * "PORT xxx,xxx,xxx,xxx,ppp,ppp\n". 305 * xxx,xxx,xxx,xxx is the client address, ppp,ppp is the client port number. 306 * In this case, we create a connection entry using the client address and 307 * port, so that the active ftp data connection from the server can reach 308 * the client. 309 */ 310 static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, 311 struct sk_buff *skb, int *diff) 312 { 313 struct iphdr *iph; 314 struct tcphdr *th; 315 char *data, *data_start, *data_limit; 316 char *start, *end; 317 union nf_inet_addr to; 318 __be16 port; 319 struct ip_vs_conn *n_cp; 320 struct net *net; 321 322 #ifdef CONFIG_IP_VS_IPV6 323 /* This application helper doesn't work with IPv6 yet, 324 * so turn this into a no-op for IPv6 packets 325 */ 326 if (cp->af == AF_INET6) 327 return 1; 328 #endif 329 330 /* no diff required for incoming packets */ 331 *diff = 0; 332 333 /* Only useful for established sessions */ 334 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 335 return 1; 336 337 /* Linear packets are much easier to deal with. */ 338 if (!skb_make_writable(skb, skb->len)) 339 return 0; 340 341 /* 342 * Detecting whether it is passive 343 */ 344 iph = ip_hdr(skb); 345 th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 346 347 /* Since there may be OPTIONS in the TCP packet and the HLEN is 348 the length of the header in 32-bit multiples, it is accurate 349 to calculate data address by th+HLEN*4 */ 350 data = data_start = (char *)th + (th->doff << 2); 351 data_limit = skb_tail_pointer(skb); 352 353 while (data <= data_limit - 6) { 354 if (strnicmp(data, "PASV\r\n", 6) == 0) { 355 /* Passive mode on */ 356 IP_VS_DBG(7, "got PASV at %td of %td\n", 357 data - data_start, 358 data_limit - data_start); 359 cp->app_data = &ip_vs_ftp_pasv; 360 return 1; 361 } 362 data++; 363 } 364 365 /* 366 * To support virtual FTP server, the scenerio is as follows: 367 * FTP client ----> Load Balancer ----> FTP server 368 * First detect the port number in the application data, 369 * then create a new connection entry for the coming data 370 * connection. 371 */ 372 if (ip_vs_ftp_get_addrport(data_start, data_limit, 373 CLIENT_STRING, sizeof(CLIENT_STRING)-1, 374 ' ', '\r', &to.ip, &port, 375 &start, &end) != 1) 376 return 1; 377 378 IP_VS_DBG(7, "PORT %pI4:%d detected\n", &to.ip, ntohs(port)); 379 380 /* Passive mode off */ 381 cp->app_data = NULL; 382 383 /* 384 * Now update or create a connection entry for it 385 */ 386 IP_VS_DBG(7, "protocol %s %pI4:%d %pI4:%d\n", 387 ip_vs_proto_name(iph->protocol), 388 &to.ip, ntohs(port), &cp->vaddr.ip, 0); 389 390 { 391 struct ip_vs_conn_param p; 392 ip_vs_conn_fill_param(ip_vs_conn_net(cp), AF_INET, 393 iph->protocol, &to, port, &cp->vaddr, 394 htons(ntohs(cp->vport)-1), &p); 395 n_cp = ip_vs_conn_in_get(&p); 396 if (!n_cp) { 397 n_cp = ip_vs_conn_new(&p, &cp->daddr, 398 htons(ntohs(cp->dport)-1), 399 IP_VS_CONN_F_NFCT, cp->dest, 400 skb->mark); 401 if (!n_cp) 402 return 0; 403 404 /* add its controller */ 405 ip_vs_control_add(n_cp, cp); 406 } 407 } 408 409 /* 410 * Move tunnel to listen state 411 */ 412 net = skb_net(skb); 413 ip_vs_tcp_conn_listen(net, n_cp); 414 ip_vs_conn_put(n_cp); 415 416 return 1; 417 } 418 419 420 static struct ip_vs_app ip_vs_ftp = { 421 .name = "ftp", 422 .type = IP_VS_APP_TYPE_FTP, 423 .protocol = IPPROTO_TCP, 424 .module = THIS_MODULE, 425 .incs_list = LIST_HEAD_INIT(ip_vs_ftp.incs_list), 426 .init_conn = ip_vs_ftp_init_conn, 427 .done_conn = ip_vs_ftp_done_conn, 428 .bind_conn = NULL, 429 .unbind_conn = NULL, 430 .pkt_out = ip_vs_ftp_out, 431 .pkt_in = ip_vs_ftp_in, 432 }; 433 434 /* 435 * per netns ip_vs_ftp initialization 436 */ 437 static int __net_init __ip_vs_ftp_init(struct net *net) 438 { 439 int i, ret; 440 struct ip_vs_app *app; 441 struct netns_ipvs *ipvs = net_ipvs(net); 442 443 if (!ipvs) 444 return -ENOENT; 445 446 app = register_ip_vs_app(net, &ip_vs_ftp); 447 if (IS_ERR(app)) 448 return PTR_ERR(app); 449 450 for (i = 0; i < ports_count; i++) { 451 if (!ports[i]) 452 continue; 453 ret = register_ip_vs_app_inc(net, app, app->protocol, ports[i]); 454 if (ret) 455 goto err_unreg; 456 pr_info("%s: loaded support on port[%d] = %d\n", 457 app->name, i, ports[i]); 458 } 459 return 0; 460 461 err_unreg: 462 unregister_ip_vs_app(net, &ip_vs_ftp); 463 return ret; 464 } 465 /* 466 * netns exit 467 */ 468 static void __ip_vs_ftp_exit(struct net *net) 469 { 470 unregister_ip_vs_app(net, &ip_vs_ftp); 471 } 472 473 static struct pernet_operations ip_vs_ftp_ops = { 474 .init = __ip_vs_ftp_init, 475 .exit = __ip_vs_ftp_exit, 476 }; 477 478 static int __init ip_vs_ftp_init(void) 479 { 480 int rv; 481 482 rv = register_pernet_subsys(&ip_vs_ftp_ops); 483 return rv; 484 } 485 486 /* 487 * ip_vs_ftp finish. 488 */ 489 static void __exit ip_vs_ftp_exit(void) 490 { 491 unregister_pernet_subsys(&ip_vs_ftp_ops); 492 } 493 494 495 module_init(ip_vs_ftp_init); 496 module_exit(ip_vs_ftp_exit); 497 MODULE_LICENSE("GPL"); 498