linux-old/net/ipv4/ip_masq_ftp.c
<<
>>
Prefs
   1/*
   2 *              IP_MASQ_FTP ftp masquerading module
   3 *
   4 *
   5 * Version:     @(#)ip_masq_ftp.c 0.04   02/05/96
   6 *
   7 * Author:      Wouter Gadeyne
   8 *              
   9 *
  10 * Fixes:
  11 *      Wouter Gadeyne          :       Fixed masquerading support of ftp PORT commands
  12 *      Juan Jose Ciarlante     :       Code moved and adapted from ip_fw.c
  13 *      Keith Owens             :       Add keep alive for ftp control channel
  14 *      Nigel Metheringham      :       Added multiple port support
  15 *      Juan Jose Ciarlante     :       Use control_add() for ftp control chan
  16 *      Juan Jose Ciarlante     :       Litl bits for 2.1
  17 *      Juan Jose Ciarlante     :       use ip_masq_listen() 
  18 *      Juan Jose Ciarlante     :       use private app_data for own flag(s)
  19 *
  20 *
  21 *
  22 *      This program is free software; you can redistribute it and/or
  23 *      modify it under the terms of the GNU General Public License
  24 *      as published by the Free Software Foundation; either version
  25 *      2 of the License, or (at your option) any later version.
  26 *      
  27 * Multiple Port Support
  28 *      The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
  29 *      with the port numbers being defined at module load time.  The module
  30 *      uses the symbol "ports" to define a list of monitored ports, which can
  31 *      be specified on the insmod command line as
  32 *              ports=x1,x2,x3...
  33 *      where x[n] are integer port numbers.  This option can be put into
  34 *      /etc/conf.modules (or /etc/modules.conf depending on your config)
  35 *      where modload will pick it up should you use modload to load your
  36 *      modules.
  37 *      
  38 */
  39
  40#include <linux/config.h>
  41#include <linux/module.h>
  42#include <asm/system.h>
  43#include <linux/types.h>
  44#include <linux/kernel.h>
  45#include <linux/skbuff.h>
  46#include <linux/in.h>
  47#include <linux/ip.h>
  48#include <linux/init.h>
  49#include <net/protocol.h>
  50#include <net/tcp.h>
  51
  52/* #define IP_MASQ_NDEBUG */
  53#include <net/ip_masq.h>
  54
  55
  56/* 
  57 * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
  58 * First port is set to the default port.
  59 */
  60static int ports[MAX_MASQ_APP_PORTS] = {21}; /* I rely on the trailing items being set to zero */
  61struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
  62
  63/*
  64 *      Debug level
  65 */
  66#ifdef CONFIG_IP_MASQ_DEBUG
  67static int debug=0;
  68MODULE_PARM(debug, "i");
  69#endif
  70
  71MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_MASQ_APP_PORTS) "i");
  72
  73/*      Dummy variable */
  74static int masq_ftp_pasv;
  75
  76static int
  77masq_ftp_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
  78{
  79        MOD_INC_USE_COUNT;
  80        return 0;
  81}
  82
  83static int
  84masq_ftp_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
  85{
  86        MOD_DEC_USE_COUNT;
  87        return 0;
  88}
  89
  90int
  91masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
  92{
  93        struct sk_buff *skb;
  94        struct iphdr *iph;
  95        struct tcphdr *th;
  96        char *p, *data, *data_limit;
  97        unsigned char p1,p2,p3,p4,p5,p6;
  98        __u32 from;
  99        __u16 port;
 100        struct ip_masq *n_ms;
 101        char buf[24];           /* xxx.xxx.xxx.xxx,ppp,ppp\000 */
 102        unsigned buf_len;
 103        int diff;
 104
 105        skb = *skb_p;
 106        iph = skb->nh.iph;
 107        th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
 108        data = (char *)&th[1];
 109
 110        data_limit = skb->h.raw + skb->len - 18;
 111        if (skb->len >= 6 && (memcmp(data, "PASV\r\n", 6) == 0 || memcmp(data, "pasv\r\n", 6) == 0))
 112                ms->app_data = &masq_ftp_pasv;
 113
 114        while (data < data_limit)
 115        {
 116                if (memcmp(data,"PORT ",5) && memcmp(data,"port ",5))
 117                {
 118                        data ++;
 119                        continue;
 120                }
 121                p = data+5;
 122                p1 = simple_strtoul(data+5,&data,10);
 123                if (*data!=',')
 124                        continue;
 125                p2 = simple_strtoul(data+1,&data,10);
 126                if (*data!=',')
 127                        continue;
 128                p3 = simple_strtoul(data+1,&data,10);
 129                if (*data!=',')
 130                        continue;
 131                p4 = simple_strtoul(data+1,&data,10);
 132                if (*data!=',')
 133                        continue;
 134                p5 = simple_strtoul(data+1,&data,10);
 135                if (*data!=',')
 136                        continue;
 137                p6 = simple_strtoul(data+1,&data,10);
 138                if (*data!='\r' && *data!='\n')
 139                        continue;
 140
 141                from = (p1<<24) | (p2<<16) | (p3<<8) | p4;
 142                port = (p5<<8) | p6;
 143
 144                IP_MASQ_DEBUG(1-debug, "PORT %X:%X detected\n",from,port);
 145
 146                /*
 147                 * Now update or create an masquerade entry for it
 148                 */
 149
 150                IP_MASQ_DEBUG(1-debug, "protocol %d %lX:%X %X:%X\n", iph->protocol, htonl(from), htons(port), iph->daddr, 0);
 151
 152                n_ms = ip_masq_out_get(iph->protocol,
 153                                         htonl(from), htons(port),
 154                                         iph->daddr, 0);
 155                if (!n_ms) {
 156                        n_ms = ip_masq_new(IPPROTO_TCP,
 157                                           maddr, 0,
 158                                           htonl(from), htons(port),
 159                                           iph->daddr, 0,
 160                                           IP_MASQ_F_NO_DPORT);
 161
 162                        if (n_ms==NULL)
 163                                return 0;
 164                        ip_masq_control_add(n_ms, ms);
 165                }
 166
 167                /*
 168                 * Replace the old PORT with the new one
 169                 */
 170                from = ntohl(n_ms->maddr);
 171                port = ntohs(n_ms->mport);
 172                sprintf(buf,"%d,%d,%d,%d,%d,%d",
 173                        from>>24&255,from>>16&255,from>>8&255,from&255,
 174                        port>>8&255,port&255);
 175                buf_len = strlen(buf);
 176
 177                IP_MASQ_DEBUG(1-debug, "new PORT %X:%X\n",from,port);
 178
 179                /*
 180                 * Calculate required delta-offset to keep TCP happy
 181                 */
 182                
 183                diff = buf_len - (data-p);
 184                
 185                /*
 186                 *      No shift.
 187                 */
 188                
 189                if (diff==0) {
 190                        /*
 191                         * simple case, just replace the old PORT cmd
 192                         */
 193                        memcpy(p,buf,buf_len);
 194                } else {
 195
 196                        *skb_p = ip_masq_skb_replace(skb, GFP_ATOMIC, p, data-p, buf, buf_len);
 197                }
 198                /*
 199                 *      Move tunnel to listen state
 200                 */
 201                ip_masq_listen(n_ms);
 202                ip_masq_put(n_ms);
 203
 204                return diff;
 205
 206        }
 207        return 0;
 208
 209}
 210
 211/*
 212 * Look at incoming ftp packets to catch the response to a PASV command.  When
 213 * we see one we build a masquerading entry for the client address, client port
 214 * 0 (unknown at the moment), the server address and the server port.  Mark the
 215 * current masquerade entry as a control channel and point the new entry at the
 216 * control entry.  All this work just for ftp keepalive across masquerading.
 217 *
 218 * The incoming packet should be something like
 219 * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)".
 220 * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number.
 221 * ncftp 2.3.0 cheats by skipping the leading number then going 22 bytes into
 222 * the data so we do the same.  If it's good enough for ncftp then it's good
 223 * enough for me.
 224 *
 225 * In this case, the client is the source machine being masqueraded, the server
 226 * is the destination for ftp requests.  It all depends on your point of view ...
 227 */
 228
 229int
 230masq_ftp_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, __u32 maddr)
 231{
 232        struct sk_buff *skb;
 233        struct iphdr *iph;
 234        struct tcphdr *th;
 235        char *data, *data_limit;
 236        unsigned char p1,p2,p3,p4,p5,p6;
 237        __u32 to;
 238        __u16 port;
 239        struct ip_masq *n_ms;
 240
 241        if (ms->app_data != &masq_ftp_pasv)
 242                return 0;       /* quick exit if no outstanding PASV */
 243
 244        skb = *skb_p;
 245        iph = skb->nh.iph;
 246        th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
 247        data = (char *)&th[1];
 248        data_limit = skb->h.raw + skb->len;
 249
 250        while (data < data_limit && *data != ' ')
 251                ++data; 
 252        while (data < data_limit && *data == ' ')
 253                ++data; 
 254        data += 22;
 255        if (data >= data_limit || *data != '(')
 256                return 0;
 257        p1 = simple_strtoul(data+1, &data, 10);
 258        if (data >= data_limit || *data != ',')
 259                return 0;
 260        p2 = simple_strtoul(data+1, &data, 10);
 261        if (data >= data_limit || *data != ',')
 262                return 0;
 263        p3 = simple_strtoul(data+1, &data, 10);
 264        if (data >= data_limit || *data != ',')
 265                return 0;
 266        p4 = simple_strtoul(data+1, &data, 10);
 267        if (data >= data_limit || *data != ',')
 268                return 0;
 269        p5 = simple_strtoul(data+1, &data, 10);
 270        if (data >= data_limit || *data != ',')
 271                return 0;
 272        p6 = simple_strtoul(data+1, &data, 10);
 273        if (data >= data_limit || *data != ')')
 274                return 0;
 275
 276        to = (p1<<24) | (p2<<16) | (p3<<8) | p4;
 277        port = (p5<<8) | p6;
 278
 279        /*
 280         * Now update or create an masquerade entry for it
 281         */
 282        IP_MASQ_DEBUG(1-debug, "PASV response %lX:%X %X:%X detected\n", ntohl(ms->saddr), 0, to, port);
 283
 284        n_ms = ip_masq_out_get(iph->protocol,
 285                                 ms->saddr, 0,
 286                                 htonl(to), htons(port));
 287        if (!n_ms) {
 288                n_ms = ip_masq_new(IPPROTO_TCP,
 289                                        maddr, 0,
 290                                        ms->saddr, 0,
 291                                        htonl(to), htons(port),
 292                                        IP_MASQ_F_NO_SPORT);
 293
 294                if (n_ms==NULL)
 295                        return 0;
 296                ip_masq_control_add(n_ms, ms);
 297        }
 298
 299#if 0   /* v0.12 state processing */
 300
 301        /*
 302         * keep for a bit longer than tcp_fin, client may not issue open
 303         * to server port before tcp_fin_timeout.
 304         */
 305        n_ms->timeout = ip_masq_expire->tcp_fin_timeout*3;
 306#endif
 307        ms->app_data = NULL;
 308        ip_masq_put(n_ms);
 309
 310        return 0;       /* no diff required for incoming packets, thank goodness */
 311}
 312
 313struct ip_masq_app ip_masq_ftp = {
 314        NULL,                   /* next */
 315        "ftp",                  /* name */
 316        0,                      /* type */
 317        0,                      /* n_attach */
 318        masq_ftp_init_1,        /* ip_masq_init_1 */
 319        masq_ftp_done_1,        /* ip_masq_done_1 */
 320        masq_ftp_out,           /* pkt_out */
 321        masq_ftp_in,            /* pkt_in */
 322};
 323
 324/*
 325 *      ip_masq_ftp initialization
 326 */
 327
 328__initfunc(int ip_masq_ftp_init(void))
 329{
 330        int i, j;
 331
 332        for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
 333                if (ports[i]) {
 334                        if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
 335                                                            GFP_KERNEL)) == NULL)
 336                                return -ENOMEM;
 337                        memcpy(masq_incarnations[i], &ip_masq_ftp, sizeof(struct ip_masq_app));
 338                        if ((j = register_ip_masq_app(masq_incarnations[i], 
 339                                                      IPPROTO_TCP, 
 340                                                      ports[i]))) {
 341                                return j;
 342                        }
 343                        IP_MASQ_DEBUG(1-debug, "Ftp: loaded support on port[%d] = %d\n",
 344                               i, ports[i]);
 345                } else {
 346                        /* To be safe, force the incarnation table entry to NULL */
 347                        masq_incarnations[i] = NULL;
 348                }
 349        }
 350        return 0;
 351}
 352
 353/*
 354 *      ip_masq_ftp fin.
 355 */
 356
 357int ip_masq_ftp_done(void)
 358{
 359        int i, j, k;
 360
 361        k=0;
 362        for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
 363                if (masq_incarnations[i]) {
 364                        if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
 365                                k = j;
 366                        } else {
 367                                kfree(masq_incarnations[i]);
 368                                masq_incarnations[i] = NULL;
 369                                IP_MASQ_DEBUG(1-debug, "Ftp: unloaded support on port[%d] = %d\n",
 370                                       i, ports[i]);
 371                        }
 372                }
 373        }
 374        return k;
 375}
 376
 377#ifdef MODULE
 378EXPORT_NO_SYMBOLS;
 379
 380int init_module(void)
 381{
 382        if (ip_masq_ftp_init() != 0)
 383                return -EIO;
 384        return 0;
 385}
 386
 387void cleanup_module(void)
 388{
 389        if (ip_masq_ftp_done() != 0)
 390                printk(KERN_INFO "ip_masq_ftp: can't remove module");
 391}
 392
 393#endif /* MODULE */
 394
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.