1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * ip_vs_ftp.c: IPVS ftp application module 4 * 5 * Authors: Wensong Zhang <wensong@linuxvirtualserver.org> 6 * 7 * Changes: 8 * 9 * Most code here is taken from ip_masq_ftp.c in kernel 2.2. The difference 10 * is that ip_vs_ftp module handles the reverse direction to ip_masq_ftp. 11 * 12 * IP_MASQ_FTP ftp masquerading module 13 * 14 * Version: @(#)ip_masq_ftp.c 0.04 02/05/96 15 * 16 * Author: Wouter Gadeyne 17 */ 18 19 #define KMSG_COMPONENT "IPVS" 20 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 21 22 #include <linux/module.h> 23 #include <linux/moduleparam.h> 24 #include <linux/kernel.h> 25 #include <linux/skbuff.h> 26 #include <linux/ctype.h> 27 #include <linux/inet.h> 28 #include <linux/in.h> 29 #include <linux/ip.h> 30 #include <linux/netfilter.h> 31 #include <net/netfilter/nf_conntrack.h> 32 #include <net/netfilter/nf_conntrack_expect.h> 33 #include <net/netfilter/nf_nat.h> 34 #include <net/netfilter/nf_nat_helper.h> 35 #include <linux/gfp.h> 36 #include <net/protocol.h> 37 #include <net/tcp.h> 38 #include <asm/unaligned.h> 39 40 #include <net/ip_vs.h> 41 42 43 #define SERVER_STRING_PASV "227 " 44 #define CLIENT_STRING_PORT "PORT" 45 #define SERVER_STRING_EPSV "229 " 46 #define CLIENT_STRING_EPRT "EPRT" 47 48 enum { 49 IP_VS_FTP_ACTIVE = 0, 50 IP_VS_FTP_PORT = 0, 51 IP_VS_FTP_PASV, 52 IP_VS_FTP_EPRT, 53 IP_VS_FTP_EPSV, 54 }; 55 56 /* 57 * List of ports (up to IP_VS_APP_MAX_PORTS) to be handled by helper 58 * First port is set to the default port. 59 */ 60 static unsigned int ports_count = 1; 61 static unsigned short ports[IP_VS_APP_MAX_PORTS] = {21, 0}; 62 module_param_array(ports, ushort, &ports_count, 0444); 63 MODULE_PARM_DESC(ports, "Ports to monitor for FTP control commands"); 64 65 66 static char *ip_vs_ftp_data_ptr(struct sk_buff *skb, struct ip_vs_iphdr *ipvsh) 67 { 68 struct tcphdr *th = (struct tcphdr *)((char *)skb->data + ipvsh->len); 69 70 if ((th->doff << 2) < sizeof(struct tcphdr)) 71 return NULL; 72 73 return (char *)th + (th->doff << 2); 74 } 75 76 static int 77 ip_vs_ftp_init_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) 78 { 79 /* We use connection tracking for the command connection */ 80 cp->flags |= IP_VS_CONN_F_NFCT; 81 return 0; 82 } 83 84 85 static int 86 ip_vs_ftp_done_conn(struct ip_vs_app *app, struct ip_vs_conn *cp) 87 { 88 return 0; 89 } 90 91 92 /* Get <addr,port> from the string "xxx.xxx.xxx.xxx,ppp,ppp", started 93 * with the "pattern". <addr,port> is in network order. 94 * Parse extended format depending on ext. In this case addr can be pre-set. 95 */ 96 static int ip_vs_ftp_get_addrport(char *data, char *data_limit, 97 const char *pattern, size_t plen, 98 char skip, bool ext, int mode, 99 union nf_inet_addr *addr, __be16 *port, 100 __u16 af, char **start, char **end) 101 { 102 char *s, c; 103 unsigned char p[6]; 104 char edelim; 105 __u16 hport; 106 int i = 0; 107 108 if (data_limit - data < plen) { 109 /* check if there is partial match */ 110 if (strncasecmp(data, pattern, data_limit - data) == 0) 111 return -1; 112 else 113 return 0; 114 } 115 116 if (strncasecmp(data, pattern, plen) != 0) { 117 return 0; 118 } 119 s = data + plen; 120 if (skip) { 121 bool found = false; 122 123 for (;; s++) { 124 if (s == data_limit) 125 return -1; 126 if (!found) { 127 /* "(" is optional for non-extended format, 128 * so catch the start of IPv4 address 129 */ 130 if (!ext && isdigit(*s)) 131 break; 132 if (*s == skip) 133 found = true; 134 } else if (*s != skip) { 135 break; 136 } 137 } 138 } 139 /* Old IPv4-only format? */ 140 if (!ext) { 141 p[0] = 0; 142 for (data = s; ; data++) { 143 if (data == data_limit) 144 return -1; 145 c = *data; 146 if (isdigit(c)) { 147 p[i] = p[i]*10 + c - ''; 148 } else if (c == ',' && i < 5) { 149 i++; 150 p[i] = 0; 151 } else { 152 /* unexpected character or terminator */ 153 break; 154 } 155 } 156 157 if (i != 5) 158 return -1; 159 160 *start = s; 161 *end = data; 162 addr->ip = get_unaligned((__be32 *) p); 163 *port = get_unaligned((__be16 *) (p + 4)); 164 return 1; 165 } 166 if (s == data_limit) 167 return -1; 168 *start = s; 169 edelim = *s++; 170 if (edelim < 33 || edelim > 126) 171 return -1; 172 if (s == data_limit) 173 return -1; 174 if (*s == edelim) { 175 /* Address family is usually missing for EPSV response */ 176 if (mode != IP_VS_FTP_EPSV) 177 return -1; 178 s++; 179 if (s == data_limit) 180 return -1; 181 /* Then address should be missing too */ 182 if (*s != edelim) 183 return -1; 184 /* Caller can pre-set addr, if needed */ 185 s++; 186 } else { 187 const char *ep; 188 189 /* We allow address only from same family */ 190 if (af == AF_INET6 && *s != '2') 191 return -1; 192 if (af == AF_INET && *s != '1') 193 return -1; 194 s++; 195 if (s == data_limit) 196 return -1; 197 if (*s != edelim) 198 return -1; 199 s++; 200 if (s == data_limit) 201 return -1; 202 if (af == AF_INET6) { 203 if (in6_pton(s, data_limit - s, (u8 *)addr, edelim, 204 &ep) <= 0) 205 return -1; 206 } else { 207 if (in4_pton(s, data_limit - s, (u8 *)addr, edelim, 208 &ep) <= 0) 209 return -1; 210 } 211 s = (char *) ep; 212 if (s == data_limit) 213 return -1; 214 if (*s != edelim) 215 return -1; 216 s++; 217 } 218 for (hport = 0; ; s++) 219 { 220 if (s == data_limit) 221 return -1; 222 if (!isdigit(*s)) 223 break; 224 hport = hport * 10 + *s - ''; 225 } 226 if (s == data_limit || !hport || *s != edelim) 227 return -1; 228 s++; 229 *end = s; 230 *port = htons(hport); 231 return 1; 232 } 233 234 /* Look at outgoing ftp packets to catch the response to a PASV/EPSV command 235 * from the server (inside-to-outside). 236 * When we see one, we build a connection entry with the client address, 237 * client port 0 (unknown at the moment), the server address and the 238 * server port. Mark the current connection entry as a control channel 239 * of the new entry. All this work is just to make the data connection 240 * can be scheduled to the right server later. 241 * 242 * The outgoing packet should be something like 243 * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)". 244 * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number. 245 * The extended format for EPSV response provides usually only port: 246 * "229 Entering Extended Passive Mode (|||ppp|)" 247 */ 248 static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, 249 struct sk_buff *skb, int *diff, 250 struct ip_vs_iphdr *ipvsh) 251 { 252 char *data, *data_limit; 253 char *start, *end; 254 union nf_inet_addr from; 255 __be16 port; 256 struct ip_vs_conn *n_cp; 257 char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */ 258 unsigned int buf_len; 259 int ret = 0; 260 enum ip_conntrack_info ctinfo; 261 struct nf_conn *ct; 262 263 *diff = 0; 264 265 /* Only useful for established sessions */ 266 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 267 return 1; 268 269 /* Linear packets are much easier to deal with. */ 270 if (skb_ensure_writable(skb, skb->len)) 271 return 0; 272 273 if (cp->app_data == (void *) IP_VS_FTP_PASV) { 274 data = ip_vs_ftp_data_ptr(skb, ipvsh); 275 data_limit = skb_tail_pointer(skb); 276 277 if (!data || data >= data_limit) 278 return 1; 279 280 if (ip_vs_ftp_get_addrport(data, data_limit, 281 SERVER_STRING_PASV, 282 sizeof(SERVER_STRING_PASV)-1, 283 '(', false, IP_VS_FTP_PASV, 284 &from, &port, cp->af, 285 &start, &end) != 1) 286 return 1; 287 288 IP_VS_DBG(7, "PASV response (%pI4:%u) -> %pI4:%u detected\n", 289 &from.ip, ntohs(port), &cp->caddr.ip, 0); 290 } else if (cp->app_data == (void *) IP_VS_FTP_EPSV) { 291 data = ip_vs_ftp_data_ptr(skb, ipvsh); 292 data_limit = skb_tail_pointer(skb); 293 294 if (!data || data >= data_limit) 295 return 1; 296 297 /* Usually, data address is not specified but 298 * we support different address, so pre-set it. 299 */ 300 from = cp->daddr; 301 if (ip_vs_ftp_get_addrport(data, data_limit, 302 SERVER_STRING_EPSV, 303 sizeof(SERVER_STRING_EPSV)-1, 304 '(', true, IP_VS_FTP_EPSV, 305 &from, &port, cp->af, 306 &start, &end) != 1) 307 return 1; 308 309 IP_VS_DBG_BUF(7, "EPSV response (%s:%u) -> %s:%u detected\n", 310 IP_VS_DBG_ADDR(cp->af, &from), ntohs(port), 311 IP_VS_DBG_ADDR(cp->af, &cp->caddr), 0); 312 } else { 313 return 1; 314 } 315 316 /* Now update or create a connection entry for it */ 317 { 318 struct ip_vs_conn_param p; 319 320 ip_vs_conn_fill_param(cp->ipvs, cp->af, 321 ipvsh->protocol, &from, port, 322 &cp->caddr, 0, &p); 323 n_cp = ip_vs_conn_out_get(&p); 324 } 325 if (!n_cp) { 326 struct ip_vs_conn_param p; 327 328 ip_vs_conn_fill_param(cp->ipvs, 329 cp->af, ipvsh->protocol, &cp->caddr, 330 0, &cp->vaddr, port, &p); 331 n_cp = ip_vs_conn_new(&p, cp->af, &from, port, 332 IP_VS_CONN_F_NO_CPORT | 333 IP_VS_CONN_F_NFCT, 334 cp->dest, skb->mark); 335 if (!n_cp) 336 return 0; 337 338 /* add its controller */ 339 ip_vs_control_add(n_cp, cp); 340 } 341 342 /* Replace the old passive address with the new one */ 343 if (cp->app_data == (void *) IP_VS_FTP_PASV) { 344 from.ip = n_cp->vaddr.ip; 345 port = n_cp->vport; 346 snprintf(buf, sizeof(buf), "%u,%u,%u,%u,%u,%u", 347 ((unsigned char *)&from.ip)[0], 348 ((unsigned char *)&from.ip)[1], 349 ((unsigned char *)&from.ip)[2], 350 ((unsigned char *)&from.ip)[3], 351 ntohs(port) >> 8, 352 ntohs(port) & 0xFF); 353 } else if (cp->app_data == (void *) IP_VS_FTP_EPSV) { 354 from = n_cp->vaddr; 355 port = n_cp->vport; 356 /* Only port, client will use VIP for the data connection */ 357 snprintf(buf, sizeof(buf), "|||%u|", 358 ntohs(port)); 359 } else { 360 *buf = 0; 361 } 362 buf_len = strlen(buf); 363 364 ct = nf_ct_get(skb, &ctinfo); 365 if (ct) { 366 bool mangled; 367 368 /* If mangling fails this function will return 0 369 * which will cause the packet to be dropped. 370 * Mangling can only fail under memory pressure, 371 * hopefully it will succeed on the retransmitted 372 * packet. 373 */ 374 mangled = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, 375 ipvsh->len, 376 start - data, 377 end - start, 378 buf, buf_len); 379 if (mangled) { 380 ip_vs_nfct_expect_related(skb, ct, n_cp, 381 ipvsh->protocol, 0, 0); 382 if (skb->ip_summed == CHECKSUM_COMPLETE) 383 skb->ip_summed = CHECKSUM_UNNECESSARY; 384 /* csum is updated */ 385 ret = 1; 386 } 387 } 388 389 /* Not setting 'diff' is intentional, otherwise the sequence 390 * would be adjusted twice. 391 */ 392 393 cp->app_data = (void *) IP_VS_FTP_ACTIVE; 394 ip_vs_tcp_conn_listen(n_cp); 395 ip_vs_conn_put(n_cp); 396 return ret; 397 } 398 399 400 /* Look at incoming ftp packets to catch the PASV/PORT/EPRT/EPSV command 401 * (outside-to-inside). 402 * 403 * The incoming packet having the PORT command should be something like 404 * "PORT xxx,xxx,xxx,xxx,ppp,ppp\n". 405 * xxx,xxx,xxx,xxx is the client address, ppp,ppp is the client port number. 406 * In this case, we create a connection entry using the client address and 407 * port, so that the active ftp data connection from the server can reach 408 * the client. 409 * Extended format: 410 * "EPSV\r\n" when client requests server address from same family 411 * "EPSV 1\r\n" when client requests IPv4 server address 412 * "EPSV 2\r\n" when client requests IPv6 server address 413 * "EPSV ALL\r\n" - not supported 414 * EPRT with specified delimiter (ASCII 33..126), "|" by default: 415 * "EPRT |1|IPv4ADDR|PORT|\r\n" when client provides IPv4 addrport 416 * "EPRT |2|IPv6ADDR|PORT|\r\n" when client provides IPv6 addrport 417 */ 418 static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp, 419 struct sk_buff *skb, int *diff, 420 struct ip_vs_iphdr *ipvsh) 421 { 422 char *data, *data_start, *data_limit; 423 char *start, *end; 424 union nf_inet_addr to; 425 __be16 port; 426 struct ip_vs_conn *n_cp; 427 428 /* no diff required for incoming packets */ 429 *diff = 0; 430 431 /* Only useful for established sessions */ 432 if (cp->state != IP_VS_TCP_S_ESTABLISHED) 433 return 1; 434 435 /* Linear packets are much easier to deal with. */ 436 if (skb_ensure_writable(skb, skb->len)) 437 return 0; 438 439 data = data_start = ip_vs_ftp_data_ptr(skb, ipvsh); 440 data_limit = skb_tail_pointer(skb); 441 if (!data || data >= data_limit) 442 return 1; 443 444 while (data <= data_limit - 6) { 445 if (cp->af == AF_INET && 446 strncasecmp(data, "PASV\r\n", 6) == 0) { 447 /* Passive mode on */ 448 IP_VS_DBG(7, "got PASV at %td of %td\n", 449 data - data_start, 450 data_limit - data_start); 451 cp->app_data = (void *) IP_VS_FTP_PASV; 452 return 1; 453 } 454 455 /* EPSV or EPSV<space><net-prt> */ 456 if (strncasecmp(data, "EPSV", 4) == 0 && 457 (data[4] == ' ' || data[4] == '\r')) { 458 if (data[4] == ' ') { 459 char proto = data[5]; 460 461 if (data > data_limit - 7 || data[6] != '\r') 462 return 1; 463 464 #ifdef CONFIG_IP_VS_IPV6 465 if (cp->af == AF_INET6 && proto == '2') { 466 } else 467 #endif 468 if (cp->af == AF_INET && proto == '1') { 469 } else { 470 return 1; 471 } 472 } 473 /* Extended Passive mode on */ 474 IP_VS_DBG(7, "got EPSV at %td of %td\n", 475 data - data_start, 476 data_limit - data_start); 477 cp->app_data = (void *) IP_VS_FTP_EPSV; 478 return 1; 479 } 480 481 data++; 482 } 483 484 /* 485 * To support virtual FTP server, the scenerio is as follows: 486 * FTP client ----> Load Balancer ----> FTP server 487 * First detect the port number in the application data, 488 * then create a new connection entry for the coming data 489 * connection. 490 */ 491 if (cp->af == AF_INET && 492 ip_vs_ftp_get_addrport(data_start, data_limit, 493 CLIENT_STRING_PORT, 494 sizeof(CLIENT_STRING_PORT)-1, 495 ' ', false, IP_VS_FTP_PORT, 496 &to, &port, cp->af, 497 &start, &end) == 1) { 498 499 IP_VS_DBG(7, "PORT %pI4:%u detected\n", &to.ip, ntohs(port)); 500 501 /* Now update or create a connection entry for it */ 502 IP_VS_DBG(7, "protocol %s %pI4:%u %pI4:%u\n", 503 ip_vs_proto_name(ipvsh->protocol), 504 &to.ip, ntohs(port), &cp->vaddr.ip, 505 ntohs(cp->vport)-1); 506 } else if (ip_vs_ftp_get_addrport(data_start, data_limit, 507 CLIENT_STRING_EPRT, 508 sizeof(CLIENT_STRING_EPRT)-1, 509 ' ', true, IP_VS_FTP_EPRT, 510 &to, &port, cp->af, 511 &start, &end) == 1) { 512 513 IP_VS_DBG_BUF(7, "EPRT %s:%u detected\n", 514 IP_VS_DBG_ADDR(cp->af, &to), ntohs(port)); 515 516 /* Now update or create a connection entry for it */ 517 IP_VS_DBG_BUF(7, "protocol %s %s:%u %s:%u\n", 518 ip_vs_proto_name(ipvsh->protocol), 519 IP_VS_DBG_ADDR(cp->af, &to), ntohs(port), 520 IP_VS_DBG_ADDR(cp->af, &cp->vaddr), 521 ntohs(cp->vport)-1); 522 } else { 523 return 1; 524 } 525 526 /* Passive mode off */ 527 cp->app_data = (void *) IP_VS_FTP_ACTIVE; 528 529 { 530 struct ip_vs_conn_param p; 531 ip_vs_conn_fill_param(cp->ipvs, cp->af, 532 ipvsh->protocol, &to, port, &cp->vaddr, 533 htons(ntohs(cp->vport)-1), &p); 534 n_cp = ip_vs_conn_in_get(&p); 535 if (!n_cp) { 536 n_cp = ip_vs_conn_new(&p, cp->af, &cp->daddr, 537 htons(ntohs(cp->dport)-1), 538 IP_VS_CONN_F_NFCT, cp->dest, 539 skb->mark); 540 if (!n_cp) 541 return 0; 542 543 /* add its controller */ 544 ip_vs_control_add(n_cp, cp); 545 } 546 } 547 548 /* 549 * Move tunnel to listen state 550 */ 551 ip_vs_tcp_conn_listen(n_cp); 552 ip_vs_conn_put(n_cp); 553 554 return 1; 555 } 556 557 558 static struct ip_vs_app ip_vs_ftp = { 559 .name = "ftp", 560 .type = IP_VS_APP_TYPE_FTP, 561 .protocol = IPPROTO_TCP, 562 .module = THIS_MODULE, 563 .incs_list = LIST_HEAD_INIT(ip_vs_ftp.incs_list), 564 .init_conn = ip_vs_ftp_init_conn, 565 .done_conn = ip_vs_ftp_done_conn, 566 .bind_conn = NULL, 567 .unbind_conn = NULL, 568 .pkt_out = ip_vs_ftp_out, 569 .pkt_in = ip_vs_ftp_in, 570 }; 571 572 /* 573 * per netns ip_vs_ftp initialization 574 */ 575 static int __net_init __ip_vs_ftp_init(struct net *net) 576 { 577 int i, ret; 578 struct ip_vs_app *app; 579 struct netns_ipvs *ipvs = net_ipvs(net); 580 581 if (!ipvs) 582 return -ENOENT; 583 584 app = register_ip_vs_app(ipvs, &ip_vs_ftp); 585 if (IS_ERR(app)) 586 return PTR_ERR(app); 587 588 for (i = 0; i < ports_count; i++) { 589 if (!ports[i]) 590 continue; 591 ret = register_ip_vs_app_inc(ipvs, app, app->protocol, ports[i]); 592 if (ret) 593 goto err_unreg; 594 } 595 return 0; 596 597 err_unreg: 598 unregister_ip_vs_app(ipvs, &ip_vs_ftp); 599 return ret; 600 } 601 /* 602 * netns exit 603 */ 604 static void __ip_vs_ftp_exit(struct net *net) 605 { 606 struct netns_ipvs *ipvs = net_ipvs(net); 607 608 if (!ipvs) 609 return; 610 611 unregister_ip_vs_app(ipvs, &ip_vs_ftp); 612 } 613 614 static struct pernet_operations ip_vs_ftp_ops = { 615 .init = __ip_vs_ftp_init, 616 .exit = __ip_vs_ftp_exit, 617 }; 618 619 static int __init ip_vs_ftp_init(void) 620 { 621 /* rcu_barrier() is called by netns on error */ 622 return register_pernet_subsys(&ip_vs_ftp_ops); 623 } 624 625 /* 626 * ip_vs_ftp finish. 627 */ 628 static void __exit ip_vs_ftp_exit(void) 629 { 630 unregister_pernet_subsys(&ip_vs_ftp_ops); 631 /* rcu_barrier() is called by netns */ 632 } 633 634 635 module_init(ip_vs_ftp_init); 636 module_exit(ip_vs_ftp_exit); 637 MODULE_LICENSE("GPL"); 638 MODULE_DESCRIPTION("ipvs ftp helper"); 639
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.