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