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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 13 #include <linux/module.h> 14 #include <linux/moduleparam.h> 15 #include <linux/inet.h> 16 #include <linux/tcp.h> 17 #include <linux/netfilter_ipv4.h> 18 #include <net/netfilter/nf_nat.h> 19 #include <net/netfilter/nf_nat_helper.h> 20 #include <net/netfilter/nf_conntrack_helper.h> 21 #include <net/netfilter/nf_conntrack_expect.h> 22 #include <linux/netfilter/nf_conntrack_ftp.h> 23 24 MODULE_LICENSE("GPL"); 25 MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>"); 26 MODULE_DESCRIPTION("ftp NAT helper"); 27 MODULE_ALIAS("ip_nat_ftp"); 28 29 /* FIXME: Time out? --RR */ 30 31 static int nf_nat_ftp_fmt_cmd(struct nf_conn *ct, enum nf_ct_ftp_type type, 32 char *buffer, size_t buflen, 33 union nf_inet_addr *addr, u16 port) 34 { 35 switch (type) { 36 case NF_CT_FTP_PORT: 37 case NF_CT_FTP_PASV: 38 return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u", 39 ((unsigned char *)&addr->ip)[0], 40 ((unsigned char *)&addr->ip)[1], 41 ((unsigned char *)&addr->ip)[2], 42 ((unsigned char *)&addr->ip)[3], 43 port >> 8, 44 port & 0xFF); 45 case NF_CT_FTP_EPRT: 46 if (nf_ct_l3num(ct) == NFPROTO_IPV4) 47 return snprintf(buffer, buflen, "|1|%pI4|%u|", 48 &addr->ip, port); 49 else 50 return snprintf(buffer, buflen, "|2|%pI6|%u|", 51 &addr->ip6, port); 52 case NF_CT_FTP_EPSV: 53 return snprintf(buffer, buflen, "|||%u|", port); 54 } 55 56 return 0; 57 } 58 59 /* So, this packet has hit the connection tracking matching code. 60 Mangle it, and change the expectation to match the new version. */ 61 static unsigned int nf_nat_ftp(struct sk_buff *skb, 62 enum ip_conntrack_info ctinfo, 63 enum nf_ct_ftp_type type, 64 unsigned int protoff, 65 unsigned int matchoff, 66 unsigned int matchlen, 67 struct nf_conntrack_expect *exp) 68 { 69 union nf_inet_addr newaddr; 70 u_int16_t port; 71 int dir = CTINFO2DIR(ctinfo); 72 struct nf_conn *ct = exp->master; 73 char buffer[sizeof("|1||65535|") + INET6_ADDRSTRLEN]; 74 unsigned int buflen; 75 76 pr_debug("type %i, off %u len %u\n", type, matchoff, matchlen); 77 78 /* Connection will come from wherever this packet goes, hence !dir */ 79 newaddr = ct->tuplehash[!dir].tuple.dst.u3; 80 exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; 81 exp->dir = !dir; 82 83 /* When you see the packet, we need to NAT it the same as the 84 * this one. */ 85 exp->expectfn = nf_nat_follow_master; 86 87 /* Try to get same port: if not, try to change it. */ 88 for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { 89 int ret; 90 91 exp->tuple.dst.u.tcp.port = htons(port); 92 ret = nf_ct_expect_related(exp); 93 if (ret == 0) 94 break; 95 else if (ret != -EBUSY) { 96 port = 0; 97 break; 98 } 99 } 100 101 if (port == 0) { 102 nf_ct_helper_log(skb, ct, "all ports in use"); 103 return NF_DROP; 104 } 105 106 buflen = nf_nat_ftp_fmt_cmd(ct, type, buffer, sizeof(buffer), 107 &newaddr, port); 108 if (!buflen) 109 goto out; 110 111 pr_debug("calling nf_nat_mangle_tcp_packet\n"); 112 113 if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff, 114 matchlen, buffer, buflen)) 115 goto out; 116 117 return NF_ACCEPT; 118 119 out: 120 nf_ct_helper_log(skb, ct, "cannot mangle packet"); 121 nf_ct_unexpect_related(exp); 122 return NF_DROP; 123 } 124 125 static void __exit nf_nat_ftp_fini(void) 126 { 127 RCU_INIT_POINTER(nf_nat_ftp_hook, NULL); 128 synchronize_rcu(); 129 } 130 131 static int __init nf_nat_ftp_init(void) 132 { 133 BUG_ON(nf_nat_ftp_hook != NULL); 134 RCU_INIT_POINTER(nf_nat_ftp_hook, nf_nat_ftp); 135 return 0; 136 } 137 138 /* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ 139 static int warn_set(const char *val, const struct kernel_param *kp) 140 { 141 pr_info("kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); 142 return 0; 143 } 144 module_param_call(ports, warn_set, NULL, NULL, 0); 145 146 module_init(nf_nat_ftp_init); 147 module_exit(nf_nat_ftp_fini); 148