- patches.fixes/patch-2.6.11-rc1: 2.6.11-rc1.
[linux-flexiantxendom0-3.2.10.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 <linux/moduleparam.h>
16 #include <net/tcp.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>
22
23 MODULE_LICENSE("GPL");
24 MODULE_AUTHOR("Rusty Russell <rusty@rustcorp.com.au>");
25 MODULE_DESCRIPTION("ftp NAT helper");
26
27 #if 0
28 #define DEBUGP printk
29 #else
30 #define DEBUGP(format, args...)
31 #endif
32
33 #define MAX_PORTS 8
34 static int ports[MAX_PORTS];
35 static int ports_c;
36
37 module_param_array(ports, int, &ports_c, 0400);
38
39 /* FIXME: Time out? --RR */
40
41 static unsigned int
42 ftp_nat_expected(struct sk_buff **pskb,
43                  unsigned int hooknum,
44                  struct ip_conntrack *ct,
45                  struct ip_nat_info *info)
46 {
47         struct ip_nat_range range;
48         u_int32_t newdstip, newsrcip, newip;
49         struct ip_ct_ftp_expect *exp_ftp_info;
50
51         struct ip_conntrack *master = master_ct(ct);
52         
53         IP_NF_ASSERT(info);
54         IP_NF_ASSERT(master);
55
56         IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
57
58         DEBUGP("nat_expected: We have a connection!\n");
59         exp_ftp_info = &ct->master->help.exp_ftp_info;
60
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));
68         } else {
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));
74         }
75
76         if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
77                 newip = newsrcip;
78         else
79                 newip = newdstip;
80
81         DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
82
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;
86
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;
91                 range.min = range.max
92                         = ((union ip_conntrack_manip_proto)
93                                 { .tcp = { htons(exp_ftp_info->port) } });
94         }
95         return ip_nat_setup_info(ct, &range, hooknum);
96 }
97
98 static int
99 mangle_rfc959_packet(struct sk_buff **pskb,
100                      u_int32_t newip,
101                      u_int16_t port,
102                      unsigned int matchoff,
103                      unsigned int matchlen,
104                      struct ip_conntrack *ct,
105                      enum ip_conntrack_info ctinfo)
106 {
107         char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
108
109         sprintf(buffer, "%u,%u,%u,%u,%u,%u",
110                 NIPQUAD(newip), port>>8, port&0xFF);
111
112         DEBUGP("calling ip_nat_mangle_tcp_packet\n");
113
114         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 
115                                         matchlen, buffer, strlen(buffer));
116 }
117
118 /* |1|132.235.1.2|6275| */
119 static int
120 mangle_eprt_packet(struct sk_buff **pskb,
121                    u_int32_t newip,
122                    u_int16_t port,
123                    unsigned int matchoff,
124                    unsigned int matchlen,
125                    struct ip_conntrack *ct,
126                    enum ip_conntrack_info ctinfo)
127 {
128         char buffer[sizeof("|1|255.255.255.255|65535|")];
129
130         sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
131
132         DEBUGP("calling ip_nat_mangle_tcp_packet\n");
133
134         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 
135                                         matchlen, buffer, strlen(buffer));
136 }
137
138 /* |1|132.235.1.2|6275| */
139 static int
140 mangle_epsv_packet(struct sk_buff **pskb,
141                    u_int32_t newip,
142                    u_int16_t port,
143                    unsigned int matchoff,
144                    unsigned int matchlen,
145                    struct ip_conntrack *ct,
146                    enum ip_conntrack_info ctinfo)
147 {
148         char buffer[sizeof("|||65535|")];
149
150         sprintf(buffer, "|||%u|", port);
151
152         DEBUGP("calling ip_nat_mangle_tcp_packet\n");
153
154         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 
155                                         matchlen, buffer, strlen(buffer));
156 }
157
158 static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
159                      unsigned int,
160                      unsigned int,
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
167 };
168
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,
172                           u32 tcp_seq,
173                           enum ip_conntrack_info ctinfo,
174                           struct ip_conntrack_expect *expect)
175 {
176         u_int32_t newip;
177         u_int16_t port;
178         struct ip_conntrack_tuple newtuple;
179
180         DEBUGP("FTP_NAT: seq %u + %u in %u\n",
181                expect->seq, exp_ftp_info->len, tcp_seq);
182
183         /* Change address inside packet to match way we're mapping
184            this connection. */
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
188                    is */
189                 newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
190                 /* Expect something from client->server */
191                 newtuple.src.ip = 
192                         ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
193                 newtuple.dst.ip = 
194                         ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
195         } else {
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 */
199                 newtuple.src.ip = 
200                         ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
201                 newtuple.dst.ip = 
202                         ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
203         }
204         newtuple.dst.protonum = IPPROTO_TCP;
205         newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
206
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);
210
211                 if (ip_conntrack_change_expect(expect, &newtuple) == 0)
212                         break;
213         }
214         if (port == 0)
215                 return 0;
216
217         if (!mangle[exp_ftp_info->ftptype](pskb, newip, port,
218                                           expect->seq - tcp_seq,
219                                           exp_ftp_info->len, ct, ctinfo))
220                 return 0;
221
222         return 1;
223 }
224
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)
231 {
232         struct iphdr *iph = (*pskb)->nh.iph;
233         struct tcphdr _tcph, *tcph;
234         unsigned int datalen;
235         int dir;
236         struct ip_ct_ftp_expect *exp_ftp_info;
237
238         if (!exp)
239                 DEBUGP("ip_nat_ftp: no exp!!");
240
241         exp_ftp_info = &exp->help.exp_ftp_info;
242
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" : "???");
253                 return NF_ACCEPT;
254         }
255
256         /* We passed tcp tracking, plus ftp helper: this must succeed. */
257         tcph = skb_header_pointer(*pskb, iph->ihl * 4, sizeof(_tcph), &_tcph);
258         BUG_ON(!tcph);
259
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,
263                     ntohl(tcph->seq),
264                     ntohl(tcph->seq) + datalen)) {
265                 if (!ftp_data_fixup(exp_ftp_info, ct, pskb, ntohl(tcph->seq),
266                                     ctinfo, exp))
267                         return NF_DROP;
268         } else {
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,
274                                ntohl(tcph->seq),
275                                ntohl(tcph->seq) + datalen);
276                 }
277                 return NF_DROP;
278         }
279         return NF_ACCEPT;
280 }
281
282 static struct ip_nat_helper ftp[MAX_PORTS];
283 static char ftp_names[MAX_PORTS][10];
284
285 /* Not __exit: called from init() */
286 static void fini(void)
287 {
288         int i;
289
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]);
293         }
294 }
295
296 static int __init init(void)
297 {
298         int i, ret = 0;
299         char *tmpname;
300
301         if (ports_c == 0)
302                 ports[ports_c++] = FTP_PORT;
303
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;
309                 ftp[i].help = help;
310                 ftp[i].me = THIS_MODULE;
311                 ftp[i].flags = 0;
312                 ftp[i].expect = ftp_nat_expected;
313
314                 tmpname = &ftp_names[i][0];
315                 if (ports[i] == FTP_PORT)
316                         sprintf(tmpname, "ftp");
317                 else
318                         sprintf(tmpname, "ftp-%d", i);
319                 ftp[i].name = tmpname;
320
321                 DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
322                                 ports[i]);
323                 ret = ip_nat_helper_register(&ftp[i]);
324
325                 if (ret) {
326                         printk("ip_nat_ftp: error registering "
327                                "helper for port %d\n", ports[i]);
328                         fini();
329                         return ret;
330                 }
331         }
332
333         return ret;
334 }
335
336 NEEDS_CONNTRACK(ftp);
337
338 module_init(init);
339 module_exit(fini);