155a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik/* FTP extension for TCP NAT alteration. */ 255a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik/* (C) 1999-2001 Paul `Rusty' Russell 455a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> 555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik * 655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik * This program is free software; you can redistribute it and/or modify 755a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik * it under the terms of the GNU General Public License version 2 as 855a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik * published by the Free Software Foundation. 955a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik */ 1055a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 1155a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik#include <linux/module.h> 1255a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik#include <linux/moduleparam.h> 13d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy#include <linux/inet.h> 1455a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik#include <linux/tcp.h> 1555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik#include <linux/netfilter_ipv4.h> 1655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik#include <net/netfilter/nf_nat.h> 1755a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik#include <net/netfilter/nf_nat_helper.h> 1855a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik#include <net/netfilter/nf_conntrack_helper.h> 1955a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik#include <net/netfilter/nf_conntrack_expect.h> 2055a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik#include <linux/netfilter/nf_conntrack_ftp.h> 2155a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 2255a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef KadlecsikMODULE_LICENSE("GPL"); 2355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef KadlecsikMODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>"); 2455a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef KadlecsikMODULE_DESCRIPTION("ftp NAT helper"); 2555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef KadlecsikMODULE_ALIAS("ip_nat_ftp"); 2655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 2755a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik/* FIXME: Time out? --RR */ 2855a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 29d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardystatic int nf_nat_ftp_fmt_cmd(struct nf_conn *ct, enum nf_ct_ftp_type type, 30c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches char *buffer, size_t buflen, 31d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy union nf_inet_addr *addr, u16 port) 3255a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik{ 33c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches switch (type) { 34c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches case NF_CT_FTP_PORT: 35c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches case NF_CT_FTP_PASV: 36c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches return snprintf(buffer, buflen, "%u,%u,%u,%u,%u,%u", 37d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy ((unsigned char *)&addr->ip)[0], 38d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy ((unsigned char *)&addr->ip)[1], 39d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy ((unsigned char *)&addr->ip)[2], 40d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy ((unsigned char *)&addr->ip)[3], 41c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches port >> 8, 42c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches port & 0xFF); 43c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches case NF_CT_FTP_EPRT: 44d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy if (nf_ct_l3num(ct) == NFPROTO_IPV4) 45d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy return snprintf(buffer, buflen, "|1|%pI4|%u|", 46d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy &addr->ip, port); 47d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy else 48d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy return snprintf(buffer, buflen, "|2|%pI6|%u|", 49d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy &addr->ip6, port); 50c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches case NF_CT_FTP_EPSV: 51c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches return snprintf(buffer, buflen, "|||%u|", port); 52c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches } 5355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 54c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches return 0; 5555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik} 5655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 5755a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik/* So, this packet has hit the connection tracking matching code. 5855a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik Mangle it, and change the expectation to match the new version. */ 593db05fea51cdb162cfa8f69e9cfb9e228919d2a9Herbert Xustatic unsigned int nf_nat_ftp(struct sk_buff *skb, 6055a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik enum ip_conntrack_info ctinfo, 6155a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik enum nf_ct_ftp_type type, 62051966c0c644a1c96092d4206e00704ade813c9aPatrick McHardy unsigned int protoff, 6355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik unsigned int matchoff, 6455a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik unsigned int matchlen, 6525b86e05467a2bf936b78695ef49039e3bbd1e0cPatrick McHardy struct nf_conntrack_expect *exp) 6655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik{ 67d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy union nf_inet_addr newaddr; 6855a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik u_int16_t port; 6955a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik int dir = CTINFO2DIR(ctinfo); 7055a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik struct nf_conn *ct = exp->master; 71d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy char buffer[sizeof("|1||65535|") + INET6_ADDRSTRLEN]; 72c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches unsigned int buflen; 7355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 740d53778e81ac7af266dac8a20cc328328c327112Patrick McHardy pr_debug("FTP_NAT: type %i, off %u len %u\n", type, matchoff, matchlen); 7555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 7655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik /* Connection will come from wherever this packet goes, hence !dir */ 77d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy newaddr = ct->tuplehash[!dir].tuple.dst.u3; 7855a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik exp->saved_proto.tcp.port = exp->tuple.dst.u.tcp.port; 7955a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik exp->dir = !dir; 8055a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 8155a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik /* When you see the packet, we need to NAT it the same as the 8255a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik * this one. */ 8355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik exp->expectfn = nf_nat_follow_master; 8455a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 8555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik /* Try to get same port: if not, try to change it. */ 8655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) { 875b92b61f3891517d18d0573ad2c939c81b59ecfePablo Neira Ayuso int ret; 885b92b61f3891517d18d0573ad2c939c81b59ecfePablo Neira Ayuso 8955a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik exp->tuple.dst.u.tcp.port = htons(port); 905b92b61f3891517d18d0573ad2c939c81b59ecfePablo Neira Ayuso ret = nf_ct_expect_related(exp); 915b92b61f3891517d18d0573ad2c939c81b59ecfePablo Neira Ayuso if (ret == 0) 925b92b61f3891517d18d0573ad2c939c81b59ecfePablo Neira Ayuso break; 935b92b61f3891517d18d0573ad2c939c81b59ecfePablo Neira Ayuso else if (ret != -EBUSY) { 945b92b61f3891517d18d0573ad2c939c81b59ecfePablo Neira Ayuso port = 0; 9555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik break; 965b92b61f3891517d18d0573ad2c939c81b59ecfePablo Neira Ayuso } 9755a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik } 9855a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 99b20ab9cc63ca4605aec154cf54faa8455749f3f6Pablo Neira Ayuso if (port == 0) { 100b20ab9cc63ca4605aec154cf54faa8455749f3f6Pablo Neira Ayuso nf_ct_helper_log(skb, ct, "all ports in use"); 10155a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik return NF_DROP; 102b20ab9cc63ca4605aec154cf54faa8455749f3f6Pablo Neira Ayuso } 10355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 104d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy buflen = nf_nat_ftp_fmt_cmd(ct, type, buffer, sizeof(buffer), 105d33cbeeb1a46a7dc82fe9f53e40a742ce0c67c79Patrick McHardy &newaddr, port); 106c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches if (!buflen) 107c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches goto out; 108c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches 109c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches pr_debug("calling nf_nat_mangle_tcp_packet\n"); 110c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches 111051966c0c644a1c96092d4206e00704ade813c9aPatrick McHardy if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo, protoff, matchoff, 112c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches matchlen, buffer, buflen)) 113c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches goto out; 114c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches 11555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik return NF_ACCEPT; 116c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches 117c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perchesout: 118b20ab9cc63ca4605aec154cf54faa8455749f3f6Pablo Neira Ayuso nf_ct_helper_log(skb, ct, "cannot mangle packet"); 119c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches nf_ct_unexpect_related(exp); 120c299bd53aa2616e6afc304b4f848186af3b3a881Joe Perches return NF_DROP; 12155a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik} 12255a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 12355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsikstatic void __exit nf_nat_ftp_fini(void) 12455a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik{ 125a9b3cd7f323b2e57593e7215362a7b02fc933e3aStephen Hemminger RCU_INIT_POINTER(nf_nat_ftp_hook, NULL); 12655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik synchronize_rcu(); 12755a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik} 12855a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 12955a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsikstatic int __init nf_nat_ftp_init(void) 13055a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik{ 131d1332e0ab84479d941de5cf4a69c71dfd385a25ePatrick McHardy BUG_ON(nf_nat_ftp_hook != NULL); 132a9b3cd7f323b2e57593e7215362a7b02fc933e3aStephen Hemminger RCU_INIT_POINTER(nf_nat_ftp_hook, nf_nat_ftp); 13355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik return 0; 13455a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik} 13555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 13655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik/* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ 13755a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsikstatic int warn_set(const char *val, struct kernel_param *kp) 13855a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik{ 13955a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik printk(KERN_INFO KBUILD_MODNAME 14055a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); 14155a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik return 0; 14255a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik} 14355a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsikmodule_param_call(ports, warn_set, NULL, NULL, 0); 14455a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsik 14555a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsikmodule_init(nf_nat_ftp_init); 14655a733247d6d2883d9bb77825fafac3dfca13fc2Jozsef Kadlecsikmodule_exit(nf_nat_ftp_fini); 147