ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / net / ipv4 / netfilter / ip_nat_ftp.c
1 /* FTP extension for TCP NAT alteration. */
2
3 /* (C) 1999-2001 Paul `Rusty' Russell
4  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
5  *
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.
9  */
10
11 #include <linux/module.h>
12 #include <linux/netfilter_ipv4.h>
13 #include <linux/ip.h>
14 #include <linux/tcp.h>
15 #include <net/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>
21
22 MODULE_LICENSE("GPL");
23 MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
24 MODULE_DESCRIPTION("ftp NAT helper");
25
26 #if 0
27 #define DEBUGP printk
28 #else
29 #define DEBUGP(format, args...)
30 #endif
31
32 #define MAX_PORTS 8
33 static int ports[MAX_PORTS];
34 static int ports_c;
35
36 #ifdef MODULE_PARM
37 MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
38 #endif
39
40 DECLARE_LOCK_EXTERN(ip_ftp_lock);
41
42 /* FIXME: Time out? --RR */
43
44 static unsigned int
45 ftp_nat_expected(struct sk_buff **pskb,
46                  unsigned int hooknum,
47                  struct ip_conntrack *ct,
48                  struct ip_nat_info *info)
49 {
50         struct ip_nat_multi_range mr;
51         u_int32_t newdstip, newsrcip, newip;
52         struct ip_ct_ftp_expect *exp_ftp_info;
53
54         struct ip_conntrack *master = master_ct(ct);
55         
56         IP_NF_ASSERT(info);
57         IP_NF_ASSERT(master);
58
59         IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
60
61         DEBUGP("nat_expected: We have a connection!\n");
62         exp_ftp_info = &ct->master->help.exp_ftp_info;
63
64         LOCK_BH(&ip_ftp_lock);
65
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));
73         } else {
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));
79         }
80         UNLOCK_BH(&ip_ftp_lock);
81
82         if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
83                 newip = newsrcip;
84         else
85                 newip = newdstip;
86
87         DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
88
89         mr.rangesize = 1;
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;
93
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) } });
101         }
102         return ip_nat_setup_info(ct, &mr, hooknum);
103 }
104
105 static int
106 mangle_rfc959_packet(struct sk_buff **pskb,
107                      u_int32_t newip,
108                      u_int16_t port,
109                      unsigned int matchoff,
110                      unsigned int matchlen,
111                      struct ip_conntrack *ct,
112                      enum ip_conntrack_info ctinfo)
113 {
114         char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
115
116         MUST_BE_LOCKED(&ip_ftp_lock);
117
118         sprintf(buffer, "%u,%u,%u,%u,%u,%u",
119                 NIPQUAD(newip), port>>8, port&0xFF);
120
121         DEBUGP("calling ip_nat_mangle_tcp_packet\n");
122
123         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 
124                                         matchlen, buffer, strlen(buffer));
125 }
126
127 /* |1|132.235.1.2|6275| */
128 static int
129 mangle_eprt_packet(struct sk_buff **pskb,
130                    u_int32_t newip,
131                    u_int16_t port,
132                    unsigned int matchoff,
133                    unsigned int matchlen,
134                    struct ip_conntrack *ct,
135                    enum ip_conntrack_info ctinfo)
136 {
137         char buffer[sizeof("|1|255.255.255.255|65535|")];
138
139         MUST_BE_LOCKED(&ip_ftp_lock);
140
141         sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
142
143         DEBUGP("calling ip_nat_mangle_tcp_packet\n");
144
145         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 
146                                         matchlen, buffer, strlen(buffer));
147 }
148
149 /* |1|132.235.1.2|6275| */
150 static int
151 mangle_epsv_packet(struct sk_buff **pskb,
152                    u_int32_t newip,
153                    u_int16_t port,
154                    unsigned int matchoff,
155                    unsigned int matchlen,
156                    struct ip_conntrack *ct,
157                    enum ip_conntrack_info ctinfo)
158 {
159         char buffer[sizeof("|||65535|")];
160
161         MUST_BE_LOCKED(&ip_ftp_lock);
162
163         sprintf(buffer, "|||%u|", port);
164
165         DEBUGP("calling ip_nat_mangle_tcp_packet\n");
166
167         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 
168                                         matchlen, buffer, strlen(buffer));
169 }
170
171 static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
172                      unsigned int,
173                      unsigned int,
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
180 };
181
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)
187 {
188         u_int32_t newip;
189         struct iphdr *iph = (*pskb)->nh.iph;
190         struct tcphdr *tcph = (void *)iph + iph->ihl*4;
191         u_int16_t port;
192         struct ip_conntrack_tuple newtuple;
193
194         MUST_BE_LOCKED(&ip_ftp_lock);
195         DEBUGP("FTP_NAT: seq %u + %u in %u\n",
196                expect->seq, ct_ftp_info->len,
197                ntohl(tcph->seq));
198
199         /* Change address inside packet to match way we're mapping
200            this connection. */
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
204                    is */
205                 newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
206                 /* Expect something from client->server */
207                 newtuple.src.ip = 
208                         ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
209                 newtuple.dst.ip = 
210                         ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
211         } else {
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 */
215                 newtuple.src.ip = 
216                         ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
217                 newtuple.dst.ip = 
218                         ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
219         }
220         newtuple.dst.protonum = IPPROTO_TCP;
221         newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
222
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);
226
227                 if (ip_conntrack_change_expect(expect, &newtuple) == 0)
228                         break;
229         }
230         if (port == 0)
231                 return 0;
232
233         if (!mangle[ct_ftp_info->ftptype](pskb, newip, port,
234                                           expect->seq - ntohl(tcph->seq),
235                                           ct_ftp_info->len, ct, ctinfo))
236                 return 0;
237
238         return 1;
239 }
240
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)
247 {
248         struct iphdr *iph = (*pskb)->nh.iph;
249         struct tcphdr *tcph = (void *)iph + iph->ihl*4;
250         unsigned int datalen;
251         int dir;
252         struct ip_ct_ftp_expect *ct_ftp_info;
253
254         if (!exp)
255                 DEBUGP("ip_nat_ftp: no exp!!");
256
257         ct_ftp_info = &exp->help.exp_ftp_info;
258
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" : "???");
269                 return NF_ACCEPT;
270         }
271
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,
276                     ntohl(tcph->seq),
277                     ntohl(tcph->seq) + datalen)) {
278                 if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) {
279                         UNLOCK_BH(&ip_ftp_lock);
280                         return NF_DROP;
281                 }
282         } else {
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,
288                                ntohl(tcph->seq),
289                                ntohl(tcph->seq) + datalen);
290                 }
291                 UNLOCK_BH(&ip_ftp_lock);
292                 return NF_DROP;
293         }
294         UNLOCK_BH(&ip_ftp_lock);
295
296         return NF_ACCEPT;
297 }
298
299 static struct ip_nat_helper ftp[MAX_PORTS];
300 static char ftp_names[MAX_PORTS][10];
301
302 /* Not __exit: called from init() */
303 static void fini(void)
304 {
305         int i;
306
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]);
310         }
311 }
312
313 static int __init init(void)
314 {
315         int i, ret = 0;
316         char *tmpname;
317
318         if (ports[0] == 0)
319                 ports[0] = FTP_PORT;
320
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;
326                 ftp[i].help = help;
327                 ftp[i].me = THIS_MODULE;
328                 ftp[i].flags = 0;
329                 ftp[i].expect = ftp_nat_expected;
330
331                 tmpname = &ftp_names[i][0];
332                 if (ports[i] == FTP_PORT)
333                         sprintf(tmpname, "ftp");
334                 else
335                         sprintf(tmpname, "ftp-%d", i);
336                 ftp[i].name = tmpname;
337
338                 DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
339                                 ports[i]);
340                 ret = ip_nat_helper_register(&ftp[i]);
341
342                 if (ret) {
343                         printk("ip_nat_ftp: error registering "
344                                "helper for port %d\n", ports[i]);
345                         fini();
346                         return ret;
347                 }
348                 ports_c++;
349         }
350
351         return ret;
352 }
353
354 NEEDS_CONNTRACK(ftp);
355
356 module_init(init);
357 module_exit(fini);