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>
16 #include <linux/netfilter_ipv4/ip_nat.h>
17 #include <linux/netfilter_ipv4/ip_nat_helper.h>
18 #include <linux/netfilter_ipv4/ip_nat_rule.h>
19 #include <linux/netfilter_ipv4/ip_conntrack_ftp.h>
20 #include <linux/netfilter_ipv4/ip_conntrack_helper.h>
22 MODULE_LICENSE("GPL");
23 MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
24 MODULE_DESCRIPTION("ftp NAT helper");
29 #define DEBUGP(format, args...)
33 static int ports[MAX_PORTS];
37 MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
40 DECLARE_LOCK_EXTERN(ip_ftp_lock);
42 /* FIXME: Time out? --RR */
45 ftp_nat_expected(struct sk_buff **pskb,
47 struct ip_conntrack *ct,
48 struct ip_nat_info *info)
50 struct ip_nat_multi_range mr;
51 u_int32_t newdstip, newsrcip, newip;
52 struct ip_ct_ftp_expect *exp_ftp_info;
54 struct ip_conntrack *master = master_ct(ct);
59 IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
61 DEBUGP("nat_expected: We have a connection!\n");
62 exp_ftp_info = &ct->master->help.exp_ftp_info;
64 LOCK_BH(&ip_ftp_lock);
66 if (exp_ftp_info->ftptype == IP_CT_FTP_PORT
67 || exp_ftp_info->ftptype == IP_CT_FTP_EPRT) {
68 /* PORT command: make connection go to the client. */
69 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
70 newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
71 DEBUGP("nat_expected: PORT cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
72 NIPQUAD(newsrcip), NIPQUAD(newdstip));
74 /* PASV command: make the connection go to the server */
75 newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
76 newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
77 DEBUGP("nat_expected: PASV cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
78 NIPQUAD(newsrcip), NIPQUAD(newdstip));
80 UNLOCK_BH(&ip_ftp_lock);
82 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
87 DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
90 /* We don't want to manip the per-protocol, just the IPs... */
91 mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
92 mr.range[0].min_ip = mr.range[0].max_ip = newip;
94 /* ... unless we're doing a MANIP_DST, in which case, make
95 sure we map to the correct port */
96 if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
97 mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
98 mr.range[0].min = mr.range[0].max
99 = ((union ip_conntrack_manip_proto)
100 { .tcp = { htons(exp_ftp_info->port) } });
102 return ip_nat_setup_info(ct, &mr, hooknum);
106 mangle_rfc959_packet(struct sk_buff **pskb,
109 unsigned int matchoff,
110 unsigned int matchlen,
111 struct ip_conntrack *ct,
112 enum ip_conntrack_info ctinfo)
114 char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
116 MUST_BE_LOCKED(&ip_ftp_lock);
118 sprintf(buffer, "%u,%u,%u,%u,%u,%u",
119 NIPQUAD(newip), port>>8, port&0xFF);
121 DEBUGP("calling ip_nat_mangle_tcp_packet\n");
123 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
124 matchlen, buffer, strlen(buffer));
127 /* |1|132.235.1.2|6275| */
129 mangle_eprt_packet(struct sk_buff **pskb,
132 unsigned int matchoff,
133 unsigned int matchlen,
134 struct ip_conntrack *ct,
135 enum ip_conntrack_info ctinfo)
137 char buffer[sizeof("|1|255.255.255.255|65535|")];
139 MUST_BE_LOCKED(&ip_ftp_lock);
141 sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
143 DEBUGP("calling ip_nat_mangle_tcp_packet\n");
145 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
146 matchlen, buffer, strlen(buffer));
149 /* |1|132.235.1.2|6275| */
151 mangle_epsv_packet(struct sk_buff **pskb,
154 unsigned int matchoff,
155 unsigned int matchlen,
156 struct ip_conntrack *ct,
157 enum ip_conntrack_info ctinfo)
159 char buffer[sizeof("|||65535|")];
161 MUST_BE_LOCKED(&ip_ftp_lock);
163 sprintf(buffer, "|||%u|", port);
165 DEBUGP("calling ip_nat_mangle_tcp_packet\n");
167 return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff,
168 matchlen, buffer, strlen(buffer));
171 static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
174 struct ip_conntrack *,
175 enum ip_conntrack_info)
176 = { [IP_CT_FTP_PORT] = mangle_rfc959_packet,
177 [IP_CT_FTP_PASV] = mangle_rfc959_packet,
178 [IP_CT_FTP_EPRT] = mangle_eprt_packet,
179 [IP_CT_FTP_EPSV] = mangle_epsv_packet
182 static int ftp_data_fixup(const struct ip_ct_ftp_expect *ct_ftp_info,
183 struct ip_conntrack *ct,
184 struct sk_buff **pskb,
185 enum ip_conntrack_info ctinfo,
186 struct ip_conntrack_expect *expect)
189 struct iphdr *iph = (*pskb)->nh.iph;
190 struct tcphdr *tcph = (void *)iph + iph->ihl*4;
192 struct ip_conntrack_tuple newtuple;
194 MUST_BE_LOCKED(&ip_ftp_lock);
195 DEBUGP("FTP_NAT: seq %u + %u in %u\n",
196 expect->seq, ct_ftp_info->len,
199 /* Change address inside packet to match way we're mapping
201 if (ct_ftp_info->ftptype == IP_CT_FTP_PASV
202 || ct_ftp_info->ftptype == IP_CT_FTP_EPSV) {
203 /* PASV/EPSV response: must be where client thinks server
205 newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
206 /* Expect something from client->server */
208 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
210 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
212 /* PORT command: must be where server thinks client is */
213 newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
214 /* Expect something from server->client */
216 ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
218 ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
220 newtuple.dst.protonum = IPPROTO_TCP;
221 newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
223 /* Try to get same port: if not, try to change it. */
224 for (port = ct_ftp_info->port; port != 0; port++) {
225 newtuple.dst.u.tcp.port = htons(port);
227 if (ip_conntrack_change_expect(expect, &newtuple) == 0)
233 if (!mangle[ct_ftp_info->ftptype](pskb, newip, port,
234 expect->seq - ntohl(tcph->seq),
235 ct_ftp_info->len, ct, ctinfo))
241 static unsigned int help(struct ip_conntrack *ct,
242 struct ip_conntrack_expect *exp,
243 struct ip_nat_info *info,
244 enum ip_conntrack_info ctinfo,
245 unsigned int hooknum,
246 struct sk_buff **pskb)
248 struct iphdr *iph = (*pskb)->nh.iph;
249 struct tcphdr *tcph = (void *)iph + iph->ihl*4;
250 unsigned int datalen;
252 struct ip_ct_ftp_expect *ct_ftp_info;
255 DEBUGP("ip_nat_ftp: no exp!!");
257 ct_ftp_info = &exp->help.exp_ftp_info;
259 /* Only mangle things once: original direction in POST_ROUTING
260 and reply direction on PRE_ROUTING. */
261 dir = CTINFO2DIR(ctinfo);
262 if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
263 || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
264 DEBUGP("nat_ftp: Not touching dir %s at hook %s\n",
265 dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
266 hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
267 : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
268 : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
272 datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
273 LOCK_BH(&ip_ftp_lock);
274 /* If it's in the right range... */
275 if (between(exp->seq + ct_ftp_info->len,
277 ntohl(tcph->seq) + datalen)) {
278 if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) {
279 UNLOCK_BH(&ip_ftp_lock);
283 /* Half a match? This means a partial retransmisison.
284 It's a cracker being funky. */
285 if (net_ratelimit()) {
286 printk("FTP_NAT: partial packet %u/%u in %u/%u\n",
287 exp->seq, ct_ftp_info->len,
289 ntohl(tcph->seq) + datalen);
291 UNLOCK_BH(&ip_ftp_lock);
294 UNLOCK_BH(&ip_ftp_lock);
299 static struct ip_nat_helper ftp[MAX_PORTS];
300 static char ftp_names[MAX_PORTS][10];
302 /* Not __exit: called from init() */
303 static void fini(void)
307 for (i = 0; i < ports_c; i++) {
308 DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]);
309 ip_nat_helper_unregister(&ftp[i]);
313 static int __init init(void)
321 for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
322 ftp[i].tuple.dst.protonum = IPPROTO_TCP;
323 ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
324 ftp[i].mask.dst.protonum = 0xFFFF;
325 ftp[i].mask.src.u.tcp.port = 0xFFFF;
327 ftp[i].me = THIS_MODULE;
329 ftp[i].expect = ftp_nat_expected;
331 tmpname = &ftp_names[i][0];
332 if (ports[i] == FTP_PORT)
333 sprintf(tmpname, "ftp");
335 sprintf(tmpname, "ftp-%d", i);
336 ftp[i].name = tmpname;
338 DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
340 ret = ip_nat_helper_register(&ftp[i]);
343 printk("ip_nat_ftp: error registering "
344 "helper for port %d\n", ports[i]);
354 NEEDS_CONNTRACK(ftp);