linux/net/netfilter/ipvs/ip_vs_ftp.c
<<
>>
Prefs
   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
  54enum {
  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 */
  66static unsigned int ports_count = 1;
  67static unsigned short ports[IP_VS_APP_MAX_PORTS] = {21, 0};
  68module_param_array(ports, ushort, &ports_count, 0444);
  69MODULE_PARM_DESC(ports, "Ports to monitor for FTP control commands");
  70
  71
  72static 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
  82static int
  83ip_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
  91static int
  92ip_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 */
 102static 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 */
 254static 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 */
 424static 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
 564static 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 */
 581static 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
 605err_unreg:
 606        unregister_ip_vs_app(ipvs, &ip_vs_ftp);
 607        return ret;
 608}
 609/*
 610 *      netns exit
 611 */
 612static 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
 622static struct pernet_operations ip_vs_ftp_ops = {
 623        .init = __ip_vs_ftp_init,
 624        .exit = __ip_vs_ftp_exit,
 625};
 626
 627static 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 */
 636static 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
 643module_init(ip_vs_ftp_init);
 644module_exit(ip_vs_ftp_exit);
 645MODULE_LICENSE("GPL");
 646