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 (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 */ 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 185 *diff = 0; 186 187 #ifdef CONFIG_IP_VS_IPV6 188 /* This application helper doesn't work with IPv6 yet, 189 * so turn this into a no-op for IPv6 packets 190 */ 191 if (cp->af == AF_INET6) 192 return 1; 193 #endif 194 195 /* Only useful for established sessions */ 196 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 197 return 1; 198 199 /* Linear packets are much easier to deal with. */ 200 if (!skb_make_writable(skb, skb->len)) 201 return 0; 202 203 if (cp->app_data == &ip_vs_ftp_pasv) { 204 iph = ip_hdr(skb); 205 th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 206 data = (char *)th + (th->doff << 2); 207 data_limit = skb_tail_pointer(skb); 208 209 if (ip_vs_ftp_get_addrport(data, data_limit, 210 SERVER_STRING, 211 sizeof(SERVER_STRING)-1, 212 '(', ')', 213 &from.ip, &port, 214 &start, &end) != 1) 215 return 1; 216 217 IP_VS_DBG(7, "PASV response (%pI4:%d) -> %pI4:%d detected\n", 218 &from.ip, ntohs(port), &cp->caddr.ip, 0); 219 220 /* 221 * Now update or create an connection entry for it 222 */ 223 { 224 struct ip_vs_conn_param p; 225 ip_vs_conn_fill_param(cp->ipvs, AF_INET, 226 iph->protocol, &from, port, 227 &cp->caddr, 0, &p); 228 n_cp = ip_vs_conn_out_get(&p); 229 } 230 if (!n_cp) { 231 struct ip_vs_conn_param p; 232 ip_vs_conn_fill_param(cp->ipvs, 233 AF_INET, IPPROTO_TCP, &cp->caddr, 234 0, &cp->vaddr, port, &p); 235 /* As above, this is ipv4 only */ 236 n_cp = ip_vs_conn_new(&p, AF_INET, &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 && (ct->status & IPS_NAT_MASK)) { 264 bool mangled; 265 266 /* If mangling fails this function will return 0 267 * which will cause the packet to be dropped. 268 * Mangling can only fail under memory pressure, 269 * hopefully it will succeed on the retransmitted 270 * packet. 271 */ 272 mangled = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, 273 iph->ihl * 4, 274 start - data, 275 end - start, 276 buf, buf_len); 277 if (mangled) { 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 cp->app_data = NULL; 293 ip_vs_tcp_conn_listen(n_cp); 294 ip_vs_conn_put(n_cp); 295 return ret; 296 } 297 return 1; 298 } 299 300 301 /* 302 * Look at incoming ftp packets to catch the PASV/PORT command 303 * (outside-to-inside). 304 * 305 * The incoming packet having the PORT command should be something like 306 * "PORT xxx,xxx,xxx,xxx,ppp,ppp\n". 307 * xxx,xxx,xxx,xxx is the client address, ppp,ppp is the client port number. 308 * In this case, we create a connection entry using the client address and 309 * port, so that the active ftp data connection from the server can reach 310 * the client. 311 */ 312 static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, 313 struct sk_buff *skb, int *diff) 314 { 315 struct iphdr *iph; 316 struct tcphdr *th; 317 char *data, *data_start, *data_limit; 318 char *start, *end; 319 union nf_inet_addr to; 320 __be16 port; 321 struct ip_vs_conn *n_cp; 322 323 /* no diff required for incoming packets */ 324 *diff = 0; 325 326 #ifdef CONFIG_IP_VS_IPV6 327 /* This application helper doesn't work with IPv6 yet, 328 * so turn this into a no-op for IPv6 packets 329 */ 330 if (cp->af == AF_INET6) 331 return 1; 332 #endif 333 334 /* Only useful for established sessions */ 335 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 336 return 1; 337 338 /* Linear packets are much easier to deal with. */ 339 if (!skb_make_writable(skb, skb->len)) 340 return 0; 341 342 /* 343 * Detecting whether it is passive 344 */ 345 iph = ip_hdr(skb); 346 th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]); 347 348 /* Since there may be OPTIONS in the TCP packet and the HLEN is 349 the length of the header in 32-bit multiples, it is accurate 350 to calculate data address by th+HLEN*4 */ 351 data = data_start = (char *)th + (th->doff << 2); 352 data_limit = skb_tail_pointer(skb); 353 354 while (data <= data_limit - 6) { 355 if (strncasecmp(data, "PASV\r\n", 6) == 0) { 356 /* Passive mode on */ 357 IP_VS_DBG(7, "got PASV at %td of %td\n", 358 data - data_start, 359 data_limit - data_start); 360 cp->app_data = &ip_vs_ftp_pasv; 361 return 1; 362 } 363 data++; 364 } 365 366 /* 367 * To support virtual FTP server, the scenerio is as follows: 368 * FTP client ----> Load Balancer ----> FTP server 369 * First detect the port number in the application data, 370 * then create a new connection entry for the coming data 371 * connection. 372 */ 373 if (ip_vs_ftp_get_addrport(data_start, data_limit, 374 CLIENT_STRING, sizeof(CLIENT_STRING)-1, 375 ' ', '\r', &to.ip, &port, 376 &start, &end) != 1) 377 return 1; 378 379 IP_VS_DBG(7, "PORT %pI4:%d detected\n", &to.ip, ntohs(port)); 380 381 /* Passive mode off */ 382 cp->app_data = NULL; 383 384 /* 385 * Now update or create a connection entry for it 386 */ 387 IP_VS_DBG(7, "protocol %s %pI4:%d %pI4:%d\n", 388 ip_vs_proto_name(iph->protocol), 389 &to.ip, ntohs(port), &cp->vaddr.ip, 0); 390 391 { 392 struct ip_vs_conn_param p; 393 ip_vs_conn_fill_param(cp->ipvs, AF_INET, 394 iph->protocol, &to, port, &cp->vaddr, 395 htons(ntohs(cp->vport)-1), &p); 396 n_cp = ip_vs_conn_in_get(&p); 397 if (!n_cp) { 398 /* This is ipv4 only */ 399 n_cp = ip_vs_conn_new(&p, AF_INET, &cp->daddr, 400 htons(ntohs(cp->dport)-1), 401 IP_VS_CONN_F_NFCT, cp->dest, 402 skb->mark); 403 if (!n_cp) 404 return 0; 405 406 /* add its controller */ 407 ip_vs_control_add(n_cp, cp); 408 } 409 } 410 411 /* 412 * Move tunnel to listen state 413 */ 414 ip_vs_tcp_conn_listen(n_cp); 415 ip_vs_conn_put(n_cp); 416 417 return 1; 418 } 419 420 421 static struct ip_vs_app ip_vs_ftp = { 422 .name = "ftp", 423 .type = IP_VS_APP_TYPE_FTP, 424 .protocol = IPPROTO_TCP, 425 .module = THIS_MODULE, 426 .incs_list = LIST_HEAD_INIT(ip_vs_ftp.incs_list), 427 .init_conn = ip_vs_ftp_init_conn, 428 .done_conn = ip_vs_ftp_done_conn, 429 .bind_conn = NULL, 430 .unbind_conn = NULL, 431 .pkt_out = ip_vs_ftp_out, 432 .pkt_in = ip_vs_ftp_in, 433 }; 434 435 /* 436 * per netns ip_vs_ftp initialization 437 */ 438 static int __net_init __ip_vs_ftp_init(struct net *net) 439 { 440 int i, ret; 441 struct ip_vs_app *app; 442 struct netns_ipvs *ipvs = net_ipvs(net); 443 444 if (!ipvs) 445 return -ENOENT; 446 447 app = register_ip_vs_app(ipvs, &ip_vs_ftp); 448 if (IS_ERR(app)) 449 return PTR_ERR(app); 450 451 for (i = 0; i < ports_count; i++) { 452 if (!ports[i]) 453 continue; 454 ret = register_ip_vs_app_inc(ipvs, app, app->protocol, ports[i]); 455 if (ret) 456 goto err_unreg; 457 pr_info("%s: loaded support on port[%d] = %d\n", 458 app->name, i, ports[i]); 459 } 460 return 0; 461 462 err_unreg: 463 unregister_ip_vs_app(ipvs, &ip_vs_ftp); 464 return ret; 465 } 466 /* 467 * netns exit 468 */ 469 static void __ip_vs_ftp_exit(struct net *net) 470 { 471 struct netns_ipvs *ipvs = net_ipvs(net); 472 473 if (!ipvs) 474 return; 475 476 unregister_ip_vs_app(ipvs, &ip_vs_ftp); 477 } 478 479 static struct pernet_operations ip_vs_ftp_ops = { 480 .init = __ip_vs_ftp_init, 481 .exit = __ip_vs_ftp_exit, 482 }; 483 484 static int __init ip_vs_ftp_init(void) 485 { 486 /* rcu_barrier() is called by netns on error */ 487 return register_pernet_subsys(&ip_vs_ftp_ops); 488 } 489 490 /* 491 * ip_vs_ftp finish. 492 */ 493 static void __exit ip_vs_ftp_exit(void) 494 { 495 unregister_pernet_subsys(&ip_vs_ftp_ops); 496 /* rcu_barrier() is called by netns */ 497 } 498 499 500 module_init(ip_vs_ftp_init); 501 module_exit(ip_vs_ftp_exit); 502 MODULE_LICENSE("GPL"); 503