VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[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 MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
37
38 DECLARE_LOCK_EXTERN(ip_ftp_lock);
39
40 /* FIXME: Time out? --RR */
41
42 static unsigned int
43 ftp_nat_expected(struct sk_buff **pskb,
44                  unsigned int hooknum,
45                  struct ip_conntrack *ct,
46                  struct ip_nat_info *info)
47 {
48         struct ip_nat_multi_range mr;
49         u_int32_t newdstip, newsrcip, newip;
50         struct ip_ct_ftp_expect *exp_ftp_info;
51
52         struct ip_conntrack *master = master_ct(ct);
53         
54         IP_NF_ASSERT(info);
55         IP_NF_ASSERT(master);
56
57         IP_NF_ASSERT(!(info->initialized & (1<<HOOK2MANIP(hooknum))));
58
59         DEBUGP("nat_expected: We have a connection!\n");
60         exp_ftp_info = &ct->master->help.exp_ftp_info;
61
62         LOCK_BH(&ip_ftp_lock);
63
64         if (exp_ftp_info->ftptype == IP_CT_FTP_PORT
65             || exp_ftp_info->ftptype == IP_CT_FTP_EPRT) {
66                 /* PORT command: make connection go to the client. */
67                 newdstip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
68                 newsrcip = master->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
69                 DEBUGP("nat_expected: PORT cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
70                        NIPQUAD(newsrcip), NIPQUAD(newdstip));
71         } else {
72                 /* PASV command: make the connection go to the server */
73                 newdstip = master->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
74                 newsrcip = master->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
75                 DEBUGP("nat_expected: PASV cmd. %u.%u.%u.%u->%u.%u.%u.%u\n",
76                        NIPQUAD(newsrcip), NIPQUAD(newdstip));
77         }
78         UNLOCK_BH(&ip_ftp_lock);
79
80         if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC)
81                 newip = newsrcip;
82         else
83                 newip = newdstip;
84
85         DEBUGP("nat_expected: IP to %u.%u.%u.%u\n", NIPQUAD(newip));
86
87         mr.rangesize = 1;
88         /* We don't want to manip the per-protocol, just the IPs... */
89         mr.range[0].flags = IP_NAT_RANGE_MAP_IPS;
90         mr.range[0].min_ip = mr.range[0].max_ip = newip;
91
92         /* ... unless we're doing a MANIP_DST, in which case, make
93            sure we map to the correct port */
94         if (HOOK2MANIP(hooknum) == IP_NAT_MANIP_DST) {
95                 mr.range[0].flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
96                 mr.range[0].min = mr.range[0].max
97                         = ((union ip_conntrack_manip_proto)
98                                 { .tcp = { htons(exp_ftp_info->port) } });
99         }
100         return ip_nat_setup_info(ct, &mr, hooknum);
101 }
102
103 static int
104 mangle_rfc959_packet(struct sk_buff **pskb,
105                      u_int32_t newip,
106                      u_int16_t port,
107                      unsigned int matchoff,
108                      unsigned int matchlen,
109                      struct ip_conntrack *ct,
110                      enum ip_conntrack_info ctinfo)
111 {
112         char buffer[sizeof("nnn,nnn,nnn,nnn,nnn,nnn")];
113
114         MUST_BE_LOCKED(&ip_ftp_lock);
115
116         sprintf(buffer, "%u,%u,%u,%u,%u,%u",
117                 NIPQUAD(newip), port>>8, port&0xFF);
118
119         DEBUGP("calling ip_nat_mangle_tcp_packet\n");
120
121         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 
122                                         matchlen, buffer, strlen(buffer));
123 }
124
125 /* |1|132.235.1.2|6275| */
126 static int
127 mangle_eprt_packet(struct sk_buff **pskb,
128                    u_int32_t newip,
129                    u_int16_t port,
130                    unsigned int matchoff,
131                    unsigned int matchlen,
132                    struct ip_conntrack *ct,
133                    enum ip_conntrack_info ctinfo)
134 {
135         char buffer[sizeof("|1|255.255.255.255|65535|")];
136
137         MUST_BE_LOCKED(&ip_ftp_lock);
138
139         sprintf(buffer, "|1|%u.%u.%u.%u|%u|", NIPQUAD(newip), port);
140
141         DEBUGP("calling ip_nat_mangle_tcp_packet\n");
142
143         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 
144                                         matchlen, buffer, strlen(buffer));
145 }
146
147 /* |1|132.235.1.2|6275| */
148 static int
149 mangle_epsv_packet(struct sk_buff **pskb,
150                    u_int32_t newip,
151                    u_int16_t port,
152                    unsigned int matchoff,
153                    unsigned int matchlen,
154                    struct ip_conntrack *ct,
155                    enum ip_conntrack_info ctinfo)
156 {
157         char buffer[sizeof("|||65535|")];
158
159         MUST_BE_LOCKED(&ip_ftp_lock);
160
161         sprintf(buffer, "|||%u|", port);
162
163         DEBUGP("calling ip_nat_mangle_tcp_packet\n");
164
165         return ip_nat_mangle_tcp_packet(pskb, ct, ctinfo, matchoff, 
166                                         matchlen, buffer, strlen(buffer));
167 }
168
169 static int (*mangle[])(struct sk_buff **, u_int32_t, u_int16_t,
170                      unsigned int,
171                      unsigned int,
172                      struct ip_conntrack *,
173                      enum ip_conntrack_info)
174 = { [IP_CT_FTP_PORT] = mangle_rfc959_packet,
175     [IP_CT_FTP_PASV] = mangle_rfc959_packet,
176     [IP_CT_FTP_EPRT] = mangle_eprt_packet,
177     [IP_CT_FTP_EPSV] = mangle_epsv_packet
178 };
179
180 static int ftp_data_fixup(const struct ip_ct_ftp_expect *ct_ftp_info,
181                           struct ip_conntrack *ct,
182                           struct sk_buff **pskb,
183                           enum ip_conntrack_info ctinfo,
184                           struct ip_conntrack_expect *expect)
185 {
186         u_int32_t newip;
187         struct iphdr *iph = (*pskb)->nh.iph;
188         struct tcphdr *tcph = (void *)iph + iph->ihl*4;
189         u_int16_t port;
190         struct ip_conntrack_tuple newtuple;
191
192         MUST_BE_LOCKED(&ip_ftp_lock);
193         DEBUGP("FTP_NAT: seq %u + %u in %u\n",
194                expect->seq, ct_ftp_info->len,
195                ntohl(tcph->seq));
196
197         /* Change address inside packet to match way we're mapping
198            this connection. */
199         if (ct_ftp_info->ftptype == IP_CT_FTP_PASV
200             || ct_ftp_info->ftptype == IP_CT_FTP_EPSV) {
201                 /* PASV/EPSV response: must be where client thinks server
202                    is */
203                 newip = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
204                 /* Expect something from client->server */
205                 newtuple.src.ip = 
206                         ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.ip;
207                 newtuple.dst.ip = 
208                         ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.ip;
209         } else {
210                 /* PORT command: must be where server thinks client is */
211                 newip = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
212                 /* Expect something from server->client */
213                 newtuple.src.ip = 
214                         ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip;
215                 newtuple.dst.ip = 
216                         ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip;
217         }
218         newtuple.dst.protonum = IPPROTO_TCP;
219         newtuple.src.u.tcp.port = expect->tuple.src.u.tcp.port;
220
221         /* Try to get same port: if not, try to change it. */
222         for (port = ct_ftp_info->port; port != 0; port++) {
223                 newtuple.dst.u.tcp.port = htons(port);
224
225                 if (ip_conntrack_change_expect(expect, &newtuple) == 0)
226                         break;
227         }
228         if (port == 0)
229                 return 0;
230
231         if (!mangle[ct_ftp_info->ftptype](pskb, newip, port,
232                                           expect->seq - ntohl(tcph->seq),
233                                           ct_ftp_info->len, ct, ctinfo))
234                 return 0;
235
236         return 1;
237 }
238
239 static unsigned int help(struct ip_conntrack *ct,
240                          struct ip_conntrack_expect *exp,
241                          struct ip_nat_info *info,
242                          enum ip_conntrack_info ctinfo,
243                          unsigned int hooknum,
244                          struct sk_buff **pskb)
245 {
246         struct iphdr *iph = (*pskb)->nh.iph;
247         struct tcphdr *tcph = (void *)iph + iph->ihl*4;
248         unsigned int datalen;
249         int dir;
250         struct ip_ct_ftp_expect *ct_ftp_info;
251
252         if (!exp)
253                 DEBUGP("ip_nat_ftp: no exp!!");
254
255         ct_ftp_info = &exp->help.exp_ftp_info;
256
257         /* Only mangle things once: original direction in POST_ROUTING
258            and reply direction on PRE_ROUTING. */
259         dir = CTINFO2DIR(ctinfo);
260         if (!((hooknum == NF_IP_POST_ROUTING && dir == IP_CT_DIR_ORIGINAL)
261               || (hooknum == NF_IP_PRE_ROUTING && dir == IP_CT_DIR_REPLY))) {
262                 DEBUGP("nat_ftp: Not touching dir %s at hook %s\n",
263                        dir == IP_CT_DIR_ORIGINAL ? "ORIG" : "REPLY",
264                        hooknum == NF_IP_POST_ROUTING ? "POSTROUTING"
265                        : hooknum == NF_IP_PRE_ROUTING ? "PREROUTING"
266                        : hooknum == NF_IP_LOCAL_OUT ? "OUTPUT" : "???");
267                 return NF_ACCEPT;
268         }
269
270         datalen = (*pskb)->len - iph->ihl * 4 - tcph->doff * 4;
271         LOCK_BH(&ip_ftp_lock);
272         /* If it's in the right range... */
273         if (between(exp->seq + ct_ftp_info->len,
274                     ntohl(tcph->seq),
275                     ntohl(tcph->seq) + datalen)) {
276                 if (!ftp_data_fixup(ct_ftp_info, ct, pskb, ctinfo, exp)) {
277                         UNLOCK_BH(&ip_ftp_lock);
278                         return NF_DROP;
279                 }
280         } else {
281                 /* Half a match?  This means a partial retransmisison.
282                    It's a cracker being funky. */
283                 if (net_ratelimit()) {
284                         printk("FTP_NAT: partial packet %u/%u in %u/%u\n",
285                                exp->seq, ct_ftp_info->len,
286                                ntohl(tcph->seq),
287                                ntohl(tcph->seq) + datalen);
288                 }
289                 UNLOCK_BH(&ip_ftp_lock);
290                 return NF_DROP;
291         }
292         UNLOCK_BH(&ip_ftp_lock);
293
294         return NF_ACCEPT;
295 }
296
297 static struct ip_nat_helper ftp[MAX_PORTS];
298 static char ftp_names[MAX_PORTS][10];
299
300 /* Not __exit: called from init() */
301 static void fini(void)
302 {
303         int i;
304
305         for (i = 0; i < ports_c; i++) {
306                 DEBUGP("ip_nat_ftp: unregistering port %d\n", ports[i]);
307                 ip_nat_helper_unregister(&ftp[i]);
308         }
309 }
310
311 static int __init init(void)
312 {
313         int i, ret = 0;
314         char *tmpname;
315
316         if (ports[0] == 0)
317                 ports[0] = FTP_PORT;
318
319         for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
320                 ftp[i].tuple.dst.protonum = IPPROTO_TCP;
321                 ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
322                 ftp[i].mask.dst.protonum = 0xFFFF;
323                 ftp[i].mask.src.u.tcp.port = 0xFFFF;
324                 ftp[i].help = help;
325                 ftp[i].me = THIS_MODULE;
326                 ftp[i].flags = 0;
327                 ftp[i].expect = ftp_nat_expected;
328
329                 tmpname = &ftp_names[i][0];
330                 if (ports[i] == FTP_PORT)
331                         sprintf(tmpname, "ftp");
332                 else
333                         sprintf(tmpname, "ftp-%d", i);
334                 ftp[i].name = tmpname;
335
336                 DEBUGP("ip_nat_ftp: Trying to register for port %d\n",
337                                 ports[i]);
338                 ret = ip_nat_helper_register(&ftp[i]);
339
340                 if (ret) {
341                         printk("ip_nat_ftp: error registering "
342                                "helper for port %d\n", ports[i]);
343                         fini();
344                         return ret;
345                 }
346                 ports_c++;
347         }
348
349         return ret;
350 }
351
352 NEEDS_CONNTRACK(ftp);
353
354 module_init(init);
355 module_exit(fini);