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