1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * ip_vs_ftp.c: IPVS ftp application module
5 * Authors: Wensong Zhang <wensong@linuxvirtualserver.org>
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.
12 * IP_MASQ_FTP ftp masquerading module
14 * Version: @(#)ip_masq_ftp.c 0.04 02/05/96
16 * Author: Wouter Gadeyne
19 #define KMSG_COMPONENT "IPVS"
20 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
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>
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>
38 #include <asm/unaligned.h>
40 #include <net/ip_vs.h>
43 #define SERVER_STRING_PASV "227 "
44 #define CLIENT_STRING_PORT "PORT"
45 #define SERVER_STRING_EPSV "229 "
46 #define CLIENT_STRING_EPRT "EPRT"
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.
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");
66 static char *ip_vs_ftp_data_ptr(struct sk_buff
*skb
, struct ip_vs_iphdr
*ipvsh
)
68 struct tcphdr
*th
= (struct tcphdr
*)((char *)skb
->data
+ ipvsh
->len
);
70 if ((th
->doff
<< 2) < sizeof(struct tcphdr
))
73 return (char *)th
+ (th
->doff
<< 2);
77 ip_vs_ftp_init_conn(struct ip_vs_app
*app
, struct ip_vs_conn
*cp
)
79 /* We use connection tracking for the command connection */
80 cp
->flags
|= IP_VS_CONN_F_NFCT
;
86 ip_vs_ftp_done_conn(struct ip_vs_app
*app
, struct ip_vs_conn
*cp
)
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.
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
)
108 if (data_limit
- data
< plen
) {
109 /* check if there is partial match */
110 if (strncasecmp(data
, pattern
, data_limit
- data
) == 0)
116 if (strncasecmp(data
, pattern
, plen
) != 0) {
127 /* "(" is optional for non-extended format,
128 * so catch the start of IPv4 address
130 if (!ext
&& isdigit(*s
))
134 } else if (*s
!= skip
) {
139 /* Old IPv4-only format? */
142 for (data
= s
; ; data
++) {
143 if (data
== data_limit
)
147 p
[i
] = p
[i
]*10 + c
- '0';
148 } else if (c
== ',' && i
< 5) {
152 /* unexpected character or terminator */
162 addr
->ip
= get_unaligned((__be32
*) p
);
163 *port
= get_unaligned((__be16
*) (p
+ 4));
170 if (edelim
< 33 || edelim
> 126)
175 /* Address family is usually missing for EPSV response */
176 if (mode
!= IP_VS_FTP_EPSV
)
181 /* Then address should be missing too */
184 /* Caller can pre-set addr, if needed */
189 /* We allow address only from same family */
190 if (af
== AF_INET6
&& *s
!= '2')
192 if (af
== AF_INET
&& *s
!= '1')
202 if (af
== AF_INET6
) {
203 if (in6_pton(s
, data_limit
- s
, (u8
*)addr
, edelim
,
207 if (in4_pton(s
, data_limit
- s
, (u8
*)addr
, edelim
,
218 for (hport
= 0; ; s
++)
224 hport
= hport
* 10 + *s
- '0';
226 if (s
== data_limit
|| !hport
|| *s
!= edelim
)
230 *port
= htons(hport
);
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.
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|)"
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
)
252 char *data
, *data_limit
;
254 union nf_inet_addr from
;
256 struct ip_vs_conn
*n_cp
;
257 char buf
[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */
258 unsigned int buf_len
;
260 enum ip_conntrack_info ctinfo
;
265 /* Only useful for established sessions */
266 if (cp
->state
!= IP_VS_TCP_S_ESTABLISHED
)
269 /* Linear packets are much easier to deal with. */
270 if (!skb_make_writable(skb
, skb
->len
))
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
);
277 if (!data
|| data
>= data_limit
)
280 if (ip_vs_ftp_get_addrport(data
, data_limit
,
282 sizeof(SERVER_STRING_PASV
)-1,
283 '(', false, IP_VS_FTP_PASV
,
284 &from
, &port
, cp
->af
,
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
);
294 if (!data
|| data
>= data_limit
)
297 /* Usually, data address is not specified but
298 * we support different address, so pre-set it.
301 if (ip_vs_ftp_get_addrport(data
, data_limit
,
303 sizeof(SERVER_STRING_EPSV
)-1,
304 '(', true, IP_VS_FTP_EPSV
,
305 &from
, &port
, cp
->af
,
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);
316 /* Now update or create a connection entry for it */
318 struct ip_vs_conn_param p
;
320 ip_vs_conn_fill_param(cp
->ipvs
, cp
->af
,
321 ipvsh
->protocol
, &from
, port
,
323 n_cp
= ip_vs_conn_out_get(&p
);
326 struct ip_vs_conn_param p
;
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
|
334 cp
->dest
, skb
->mark
);
338 /* add its controller */
339 ip_vs_control_add(n_cp
, cp
);
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
;
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],
353 } else if (cp
->app_data
== (void *) IP_VS_FTP_EPSV
) {
356 /* Only port, client will use VIP for the data connection */
357 snprintf(buf
, sizeof(buf
), "|||%u|",
362 buf_len
= strlen(buf
);
364 ct
= nf_ct_get(skb
, &ctinfo
);
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
374 mangled
= nf_nat_mangle_tcp_packet(skb
, ct
, ctinfo
,
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 */
389 /* Not setting 'diff' is intentional, otherwise the sequence
390 * would be adjusted twice.
393 cp
->app_data
= (void *) IP_VS_FTP_ACTIVE
;
394 ip_vs_tcp_conn_listen(n_cp
);
395 ip_vs_conn_put(n_cp
);
400 /* Look at incoming ftp packets to catch the PASV/PORT/EPRT/EPSV command
401 * (outside-to-inside).
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
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
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
)
422 char *data
, *data_start
, *data_limit
;
424 union nf_inet_addr to
;
426 struct ip_vs_conn
*n_cp
;
428 /* no diff required for incoming packets */
431 /* Only useful for established sessions */
432 if (cp
->state
!= IP_VS_TCP_S_ESTABLISHED
)
435 /* Linear packets are much easier to deal with. */
436 if (!skb_make_writable(skb
, skb
->len
))
439 data
= data_start
= ip_vs_ftp_data_ptr(skb
, ipvsh
);
440 data_limit
= skb_tail_pointer(skb
);
441 if (!data
|| data
>= data_limit
)
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",
450 data_limit
- data_start
);
451 cp
->app_data
= (void *) IP_VS_FTP_PASV
;
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];
461 if (data
> data_limit
- 7 || data
[6] != '\r')
464 #ifdef CONFIG_IP_VS_IPV6
465 if (cp
->af
== AF_INET6
&& proto
== '2') {
468 if (cp
->af
== AF_INET
&& proto
== '1') {
473 /* Extended Passive mode on */
474 IP_VS_DBG(7, "got EPSV at %td of %td\n",
476 data_limit
- data_start
);
477 cp
->app_data
= (void *) IP_VS_FTP_EPSV
;
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
491 if (cp
->af
== AF_INET
&&
492 ip_vs_ftp_get_addrport(data_start
, data_limit
,
494 sizeof(CLIENT_STRING_PORT
)-1,
495 ' ', false, IP_VS_FTP_PORT
,
497 &start
, &end
) == 1) {
499 IP_VS_DBG(7, "PORT %pI4:%u detected\n", &to
.ip
, ntohs(port
));
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
,
506 } else if (ip_vs_ftp_get_addrport(data_start
, data_limit
,
508 sizeof(CLIENT_STRING_EPRT
)-1,
509 ' ', true, IP_VS_FTP_EPRT
,
511 &start
, &end
) == 1) {
513 IP_VS_DBG_BUF(7, "EPRT %s:%u detected\n",
514 IP_VS_DBG_ADDR(cp
->af
, &to
), ntohs(port
));
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
),
526 /* Passive mode off */
527 cp
->app_data
= (void *) IP_VS_FTP_ACTIVE
;
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
);
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
,
543 /* add its controller */
544 ip_vs_control_add(n_cp
, cp
);
549 * Move tunnel to listen state
551 ip_vs_tcp_conn_listen(n_cp
);
552 ip_vs_conn_put(n_cp
);
558 static struct ip_vs_app ip_vs_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
,
568 .pkt_out
= ip_vs_ftp_out
,
569 .pkt_in
= ip_vs_ftp_in
,
573 * per netns ip_vs_ftp initialization
575 static int __net_init
__ip_vs_ftp_init(struct net
*net
)
578 struct ip_vs_app
*app
;
579 struct netns_ipvs
*ipvs
= net_ipvs(net
);
584 app
= register_ip_vs_app(ipvs
, &ip_vs_ftp
);
588 for (i
= 0; i
< ports_count
; i
++) {
591 ret
= register_ip_vs_app_inc(ipvs
, app
, app
->protocol
, ports
[i
]);
594 pr_info("%s: loaded support on port[%d] = %u\n",
595 app
->name
, i
, ports
[i
]);
600 unregister_ip_vs_app(ipvs
, &ip_vs_ftp
);
606 static void __ip_vs_ftp_exit(struct net
*net
)
608 struct netns_ipvs
*ipvs
= net_ipvs(net
);
613 unregister_ip_vs_app(ipvs
, &ip_vs_ftp
);
616 static struct pernet_operations ip_vs_ftp_ops
= {
617 .init
= __ip_vs_ftp_init
,
618 .exit
= __ip_vs_ftp_exit
,
621 static int __init
ip_vs_ftp_init(void)
623 /* rcu_barrier() is called by netns on error */
624 return register_pernet_subsys(&ip_vs_ftp_ops
);
630 static void __exit
ip_vs_ftp_exit(void)
632 unregister_pernet_subsys(&ip_vs_ftp_ops
);
633 /* rcu_barrier() is called by netns */
637 module_init(ip_vs_ftp_init
);
638 module_exit(ip_vs_ftp_exit
);
639 MODULE_LICENSE("GPL");