1 /* FTP extension for TCP NAT alteration. */
3 /* (C) 1999-2001 Paul `Rusty' Russell
4 * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
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.
11 #include <linux/module.h>
12 #include <linux/netfilter_ipv4.h>
14 #include <linux/tcp.h>
15 #include <linux/moduleparam.h>
17 #include <linux/netfilter_ipv4/ip_nat.h>
18 #include <linux/netfilter_ipv4/ip_nat_helper.h>
19 #include <linux/netfilter_ipv4/ip_nat_rule.h>
20 #include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
21 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
23 MODULE_LICENSE("GPL");
24 MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
25 MODULE_DESCRIPTION("ftp NAT helper");
30 #define DEBUGP(format, args...)
34 static int ports[MAX_PORTS];
37 module_param_array(ports, int, &ports_c, 0400);
39 /* FIXME: Time out? --RR */
42 ftp_nat_expected(struct sk_buff **pskb,
44 struct ip_conntrack *ct,
45 struct ip_nat_info *info)
47 struct ip_nat_range range;
48 u_int32_t newdstip, newsrcip, newip;
49 struct ip_ct_ftp_expect *exp_ftp_info;
51 struct ip_conntrack *master = master_ct(ct);
56 IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
58 DEBUGP("nat_expected: We have a connection!\n");
59 exp_ftp_info = &ct->master->help.exp_ftp_info;
61 if (exp_ftp_info->ftptype == IP_CT_FTP_PORT
62 || exp_ftp_info->ftptype == IP_CT_FTP_EPRT) {
63 /* PORT command: make connection go to the client. */
64 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
65 newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
66 DEBUGP("nat_expected: PORT cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
67 NIPQUAD(newsrcip), NIPQUAD(newdstip));
69 /* PASV command: make the connection go to the server */
70 newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
71 newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
72 DEBUGP("nat_expected: PASV cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
73 NIPQUAD(newsrcip), NIPQUAD(newdstip));
76 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
81 DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
83 /* We don't want to manip the per-protocol, just the IPs... */
84 range.flags = IP_NAT_RANGE_MAP_IPS;
85 range.min_ip = range.max_ip = newip;
87 /* ... unless we're doing a MANIP_DST, in which case, make
88 sure we map to the correct port */
89 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
90 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
92 = ((union ip_conntrack_manip_proto)
93 { .tcp = { htons(exp_ftp_info->port) } });
95 return ip_nat_setup_info(ct, &range, hooknum);
99 mangle_rfc959_packet(struct sk_buff **pskb,
102 unsigned int matchoff,
103 unsigned int matchlen,
104 struct ip_conntrack *ct,
105 enum ip_conntrack_info ctinfo)
107 char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
109 sprintf(buffer, "%u,%u,%u,%u,%u,%u",
110 NIPQUAD(newip), port>>8, port&0xFF);
112 DEBUGP("calling ip_nat_mangle_tcp_packet\n");
114 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
115 matchlen, buffer, strlen(buffer));
118 /* |1|132.235.1.2|6275| */
120 mangle_eprt_packet(struct sk_buff **pskb,
123 unsigned int matchoff,
124 unsigned int matchlen,
125 struct ip_conntrack *ct,
126 enum ip_conntrack_info ctinfo)
128 char buffer[sizeof("|1|255.255.255.255|65535|")];
130 sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
132 DEBUGP("calling ip_nat_mangle_tcp_packet\n");
134 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
135 matchlen, buffer, strlen(buffer));
138 /* |1|132.235.1.2|6275| */
140 mangle_epsv_packet(struct sk_buff **pskb,
143 unsigned int matchoff,
144 unsigned int matchlen,
145 struct ip_conntrack *ct,
146 enum ip_conntrack_info ctinfo)
148 char buffer[sizeof("|||65535|")];
150 sprintf(buffer, "|||%u|", port);
152 DEBUGP("calling ip_nat_mangle_tcp_packet\n");
154 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
155 matchlen, buffer, strlen(buffer));
158 static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
161 struct ip_conntrack *,
162 enum ip_conntrack_info)
163 = { [IP_CT_FTP_PORT] = mangle_rfc959_packet,
164 [IP_CT_FTP_PASV] = mangle_rfc959_packet,
165 [IP_CT_FTP_EPRT] = mangle_eprt_packet,
166 [IP_CT_FTP_EPSV] = mangle_epsv_packet
169 static int ftp_data_fixup(const struct ip_ct_ftp_expect *exp_ftp_info,
170 struct ip_conntrack *ct,
171 struct sk_buff **pskb,
173 enum ip_conntrack_info ctinfo,
174 struct ip_conntrack_expect *expect)
178 struct ip_conntrack_tuple newtuple;
180 DEBUGP("FTP_NAT: seq %u + %u in %u\n",
181 expect->seq, exp_ftp_info->len, tcp_seq);
183 /* Change address inside packet to match way we're mapping
185 if (exp_ftp_info->ftptype == IP_CT_FTP_PASV
186 || exp_ftp_info->ftptype == IP_CT_FTP_EPSV) {
187 /* PASV/EPSV response: must be where client thinks server
189 newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
190 /* Expect something from client->server */
192 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
194 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
196 /* PORT command: must be where server thinks client is */
197 newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
198 /* Expect something from server->client */
200 ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
202 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
204 newtuple.dst.protonum = IPPROTO_TCP;
205 newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
207 /* Try to get same port: if not, try to change it. */
208 for (port = exp_ftp_info->port; port != 0; port++) {
209 newtuple.dst.u.tcp.port = htons(port);
211 if (ip_conntrack_change_expect(expect, &newtuple) == 0)
217 if (!mangle[exp_ftp_info->ftptype](pskb, newip, port,
218 expect->seq - tcp_seq,
219 exp_ftp_info->len, ct, ctinfo))
225 static unsigned int help(struct ip_conntrack *ct,
226 struct ip_conntrack_expect *exp,
227 struct ip_nat_info *info,
228 enum ip_conntrack_info ctinfo,
229 unsigned int hooknum,
230 struct sk_buff **pskb)
232 struct iphdr *iph = (*pskb)->nh.iph;
233 struct tcphdr _tcph, *tcph;
234 unsigned int datalen;
236 struct ip_ct_ftp_expect *exp_ftp_info;
239 DEBUGP("ip_nat_ftp: no exp!!");
241 exp_ftp_info = &exp->help.exp_ftp_info;
243 /* Only mangle things once: original direction in POST_ROUTING
244 and reply direction on PRE_ROUTING. */
245 dir = CTINFO2DIR(ctinfo);
246 if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
247 || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
248 DEBUGP("nat_ftp: Not touching dir %s at hook %s\n",
249 dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
250 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
251 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
252 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
256 /* We passed tcp tracking, plus ftp helper: this must succeed. */
257 tcph = skb_header_pointer(*pskb, iph->ihl * 4, sizeof(_tcph), &_tcph);
260 datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
261 /* If it's in the right range... */
262 if (between(exp->seq + exp_ftp_info->len,
264 ntohl(tcph->seq) + datalen)) {
265 if (!ftp_data_fixup(exp_ftp_info, ct, pskb, ntohl(tcph->seq),
269 /* Half a match? This means a partial retransmisison.
270 It's a cracker being funky. */
271 if (net_ratelimit()) {
272 printk("FTP_NAT: partial packet %u/%u in %u/%u\n",
273 exp->seq, exp_ftp_info->len,
275 ntohl(tcph->seq) + datalen);
282 static struct ip_nat_helper ftp[MAX_PORTS];
283 static char ftp_names[MAX_PORTS][10];
285 /* Not __exit: called from init() */
286 static void fini(void)
290 for (i = 0; i < ports_c; i++) {
291 DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]);
292 ip_nat_helper_unregister(&ftp[i]);
296 static int __init init(void)
302 ports[ports_c++] = FTP_PORT;
304 for (i = 0; i < ports_c; i++) {
305 ftp[i].tuple.dst.protonum = IPPROTO_TCP;
306 ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
307 ftp[i].mask.dst.protonum = 0xFFFF;
308 ftp[i].mask.src.u.tcp.port = 0xFFFF;
310 ftp[i].me = THIS_MODULE;
312 ftp[i].expect = ftp_nat_expected;
314 tmpname = &ftp_names[i][0];
315 if (ports[i] == FTP_PORT)
316 sprintf(tmpname, "ftp");
318 sprintf(tmpname, "ftp-%d", i);
319 ftp[i].name = tmpname;
321 DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
323 ret = ip_nat_helper_register(&ftp[i]);
326 printk("ip_nat_ftp: error registering "
327 "helper for port %d\n", ports[i]);
336 NEEDS_CONNTRACK(ftp);