linux/net/ipv4/netfilter/nf_nat_ftp.c
<<
>>
Prefs
   1/* FTP extension for TCP NAT alteration. */
   2
   3/* (C) 1999-2001 Paul `Rusty' Russell
   4 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/moduleparam.h>
  13#include <linux/ip.h>
  14#include <linux/tcp.h>
  15#include <linux/netfilter_ipv4.h>
  16#include <net/netfilter/nf_nat.h>
  17#include <net/netfilter/nf_nat_helper.h>
  18#include <net/netfilter/nf_nat_rule.h>
  19#include <net/netfilter/nf_conntrack_helper.h>
  20#include <net/netfilter/nf_conntrack_expect.h>
  21#include <linux/netfilter/nf_conntrack_ftp.h>
  22
  23MODULE_LICENSE("GPL");
  24MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
  25MODULE_DESCRIPTION("ftp NAT helper");
  26MODULE_ALIAS("ip_nat_ftp");
  27
  28/* FIXME: Time out? --RR */
  29
  30static int
  31mangle_rfc959_packet(struct sk_buff *skb,
  32                     __be32 newip,
  33                     u_int16_t port,
  34                     unsigned int matchoff,
  35                     unsigned int matchlen,
  36                     struct nf_conn *ct,
  37                     enum ip_conntrack_info ctinfo)
  38{
  39        char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
  40
  41        sprintf(buffer, "%u,%u,%u,%u,%u,%u",
  42                NIPQUAD(newip), port>>8, port&0xFF);
  43
  44        pr_debug("calling nf_nat_mangle_tcp_packet\n");
  45
  46        return nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
  47                                        matchlen, buffer, strlen(buffer));
  48}
  49
  50/* |1|132.235.1.2|6275| */
  51static int
  52mangle_eprt_packet(struct sk_buff *skb,
  53                   __be32 newip,
  54                   u_int16_t port,
  55                   unsigned int matchoff,
  56                   unsigned int matchlen,
  57                   struct nf_conn *ct,
  58                   enum ip_conntrack_info ctinfo)
  59{
  60        char buffer[sizeof("|1|255.255.255.255|65535|")];
  61
  62        sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
  63
  64        pr_debug("calling nf_nat_mangle_tcp_packet\n");
  65
  66        return nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
  67                                        matchlen, buffer, strlen(buffer));
  68}
  69
  70/* |1|132.235.1.2|6275| */
  71static int
  72mangle_epsv_packet(struct sk_buff *skb,
  73                   __be32 newip,
  74                   u_int16_t port,
  75                   unsigned int matchoff,
  76                   unsigned int matchlen,
  77                   struct nf_conn *ct,
  78                   enum ip_conntrack_info ctinfo)
  79{
  80        char buffer[sizeof("|||65535|")];
  81
  82        sprintf(buffer, "|||%u|", port);
  83
  84        pr_debug("calling nf_nat_mangle_tcp_packet\n");
  85
  86        return nf_nat_mangle_tcp_packet(skb, ct, ctinfo, matchoff,
  87                                        matchlen, buffer, strlen(buffer));
  88}
  89
  90static int (*mangle[])(struct sk_buff *, __be32, u_int16_t,
  91                       unsigned int, unsigned int, struct nf_conn *,
  92                       enum ip_conntrack_info)
  93= {
  94        [NF_CT_FTP_PORT] = mangle_rfc959_packet,
  95        [NF_CT_FTP_PASV] = mangle_rfc959_packet,
  96        [NF_CT_FTP_EPRT] = mangle_eprt_packet,
  97        [NF_CT_FTP_EPSV] = mangle_epsv_packet
  98};
  99
 100/* So, this packet has hit the connection tracking matching code.
 101   Mangle it, and change the expectation to match the new version. */
 102static unsigned int nf_nat_ftp(struct sk_buff *skb,
 103                               enum ip_conntrack_info ctinfo,
 104                               enum nf_ct_ftp_type type,
 105                               unsigned int matchoff,
 106                               unsigned int matchlen,
 107                               struct nf_conntrack_expect *exp)
 108{
 109        __be32 newip;
 110        u_int16_t port;
 111        int dir = CTINFO2DIR(ctinfo);
 112        struct nf_conn *ct = exp->master;
 113
 114        pr_debug("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen);
 115
 116        /* Connection will come from wherever this packet goes, hence !dir */
 117        newip = ct->tuplehash[!dir].tuple.dst.u3.ip;
 118        exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port;
 119        exp->dir = !dir;
 120
 121        /* When you see the packet, we need to NAT it the same as the
 122         * this one. */
 123        exp->expectfn = nf_nat_follow_master;
 124
 125        /* Try to get same port: if not, try to change it. */
 126        for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
 127                exp->tuple.dst.u.tcp.port = htons(port);
 128                if (nf_ct_expect_related(exp) == 0)
 129                        break;
 130        }
 131
 132        if (port == 0)
 133                return NF_DROP;
 134
 135        if (!mangle[type](skb, newip, port, matchoff, matchlen, ct, ctinfo)) {
 136                nf_ct_unexpect_related(exp);
 137                return NF_DROP;
 138        }
 139        return NF_ACCEPT;
 140}
 141
 142static void __exit nf_nat_ftp_fini(void)
 143{
 144        rcu_assign_pointer(nf_nat_ftp_hook, NULL);
 145        synchronize_rcu();
 146}
 147
 148static int __init nf_nat_ftp_init(void)
 149{
 150        BUG_ON(nf_nat_ftp_hook != NULL);
 151        rcu_assign_pointer(nf_nat_ftp_hook, nf_nat_ftp);
 152        return 0;
 153}
 154
 155/* Prior to 2.6.11, we had a ports param.  No longer, but don't break users. */
 156static int warn_set(const char *val, struct kernel_param *kp)
 157{
 158        printk(KERN_INFO KBUILD_MODNAME
 159               ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n");
 160        return 0;
 161}
 162module_param_call(ports, warn_set, NULL, NULL, 0);
 163
 164module_init(nf_nat_ftp_init);
 165module_exit(nf_nat_ftp_fini);
 166