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