patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / net / ipv4 / netfilter / ip_nat_standalone.c
1 /* This file contains all the functions required for the standalone
2    ip_nat module.
3
4    These are not required by the compatibility layer.
5 */
6
7 /* (C) 1999-2001 Paul `Rusty' Russell
8  * (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  */
14
15 /*
16  * 23 Apr 2001: Harald Welte <laforge@gnumonks.org>
17  *      - new API and handling of conntrack/nat helpers
18  *      - now capable of multiple expectations for one master
19  * */
20
21 #include <linux/config.h>
22 #include <linux/types.h>
23 #include <linux/icmp.h>
24 #include <linux/ip.h>
25 #include <linux/netfilter.h>
26 #include <linux/netfilter_ipv4.h>
27 #include <linux/module.h>
28 #include <linux/skbuff.h>
29 #include <linux/proc_fs.h>
30 #include <net/checksum.h>
31 #include <linux/spinlock.h>
32
33 #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
34 #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
35
36 #include <linux/netfilter_ipv4/ip_nat.h>
37 #include <linux/netfilter_ipv4/ip_nat_rule.h>
38 #include <linux/netfilter_ipv4/ip_nat_protocol.h>
39 #include <linux/netfilter_ipv4/ip_nat_core.h>
40 #include <linux/netfilter_ipv4/ip_nat_helper.h>
41 #include <linux/netfilter_ipv4/ip_tables.h>
42 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
43 #include <linux/netfilter_ipv4/listhelp.h>
44
45 #if 0
46 #define DEBUGP printk
47 #else
48 #define DEBUGP(format, args...)
49 #endif
50
51 #define HOOKNAME(hooknum) ((hooknum) == NF_IP_POST_ROUTING ? "POST_ROUTING"  \
52                            : ((hooknum) == NF_IP_PRE_ROUTING ? "PRE_ROUTING" \
53                               : ((hooknum) == NF_IP_LOCAL_OUT ? "LOCAL_OUT"  \
54                                  : ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN"  \
55                                     : "*ERROR*")))
56
57 static inline int call_expect(struct ip_conntrack *master,
58                               struct sk_buff **pskb,
59                               unsigned int hooknum,
60                               struct ip_conntrack *ct,
61                               struct ip_nat_info *info)
62 {
63         return master->nat.info.helper->expect(pskb, hooknum, ct, info);
64 }
65
66 static unsigned int
67 ip_nat_fn(unsigned int hooknum,
68           struct sk_buff **pskb,
69           const struct net_device *in,
70           const struct net_device *out,
71           int (*okfn)(struct sk_buff *))
72 {
73         struct ip_conntrack *ct;
74         enum ip_conntrack_info ctinfo;
75         struct ip_nat_info *info;
76         /* maniptype == SRC for postrouting. */
77         enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum);
78
79         /* We never see fragments: conntrack defrags on pre-routing
80            and local-out, and ip_nat_out protects post-routing. */
81         IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off
82                        & htons(IP_MF|IP_OFFSET)));
83
84         (*pskb)->nfcache |= NFC_UNKNOWN;
85
86         /* If we had a hardware checksum before, it's now invalid */
87         if ((*pskb)->ip_summed == CHECKSUM_HW)
88                 if (skb_checksum_help(pskb, (out == NULL)))
89                         return NF_DROP;
90
91         ct = ip_conntrack_get(*pskb, &ctinfo);
92         /* Can't track?  It's not due to stress, or conntrack would
93            have dropped it.  Hence it's the user's responsibilty to
94            packet filter it out, or implement conntrack/NAT for that
95            protocol. 8) --RR */
96         if (!ct) {
97                 /* Exception: ICMP redirect to new connection (not in
98                    hash table yet).  We must not let this through, in
99                    case we're doing NAT to the same network. */
100                 if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
101                         struct icmphdr hdr;
102
103                         if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,
104                                           &hdr, sizeof(hdr)) == 0
105                             && hdr.type == ICMP_REDIRECT)
106                                 return NF_DROP;
107                 }
108                 return NF_ACCEPT;
109         }
110
111         switch (ctinfo) {
112         case IP_CT_RELATED:
113         case IP_CT_RELATED+IP_CT_IS_REPLY:
114                 if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
115                         if (!icmp_reply_translation(pskb, ct, hooknum,
116                                                     CTINFO2DIR(ctinfo)))
117                                 return NF_DROP;
118                         else
119                                 return NF_ACCEPT;
120                 }
121                 /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
122         case IP_CT_NEW:
123                 info = &ct->nat.info;
124
125                 WRITE_LOCK(&ip_nat_lock);
126                 /* Seen it before?  This can happen for loopback, retrans,
127                    or local packets.. */
128                 if (!(info->initialized & (1 << maniptype))
129 #ifndef CONFIG_IP_NF_NAT_LOCAL
130                     /* If this session has already been confirmed we must not
131                      * touch it again even if there is no mapping set up.
132                      * Can only happen on local->local traffic with
133                      * CONFIG_IP_NF_NAT_LOCAL disabled.
134                      */
135                     && !(ct->status & IPS_CONFIRMED)
136 #endif
137                     ) {
138                         unsigned int ret;
139
140                         if (ct->master
141                             && master_ct(ct)->nat.info.helper
142                             && master_ct(ct)->nat.info.helper->expect) {
143                                 ret = call_expect(master_ct(ct), pskb, 
144                                                   hooknum, ct, info);
145                         } else {
146 #ifdef CONFIG_IP_NF_NAT_LOCAL
147                                 /* LOCAL_IN hook doesn't have a chain!  */
148                                 if (hooknum == NF_IP_LOCAL_IN)
149                                         ret = alloc_null_binding(ct, info,
150                                                                  hooknum);
151                                 else
152 #endif
153                                 ret = ip_nat_rule_find(pskb, hooknum, in, out,
154                                                        ct, info);
155                         }
156
157                         if (ret != NF_ACCEPT) {
158                                 WRITE_UNLOCK(&ip_nat_lock);
159                                 return ret;
160                         }
161                 } else
162                         DEBUGP("Already setup manip %s for ct %p\n",
163                                maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",
164                                ct);
165                 WRITE_UNLOCK(&ip_nat_lock);
166                 break;
167
168         default:
169                 /* ESTABLISHED */
170                 IP_NF_ASSERT(ctinfo == IP_CT_ESTABLISHED
171                              || ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));
172                 info = &ct->nat.info;
173         }
174
175         IP_NF_ASSERT(info);
176         return do_bindings(ct, ctinfo, info, hooknum, pskb);
177 }
178
179 static unsigned int
180 ip_nat_out(unsigned int hooknum,
181            struct sk_buff **pskb,
182            const struct net_device *in,
183            const struct net_device *out,
184            int (*okfn)(struct sk_buff *))
185 {
186         /* root is playing with raw sockets. */
187         if ((*pskb)->len < sizeof(struct iphdr)
188             || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
189                 return NF_ACCEPT;
190
191         /* We can hit fragment here; forwarded packets get
192            defragmented by connection tracking coming in, then
193            fragmented (grr) by the forward code.
194
195            In future: If we have nfct != NULL, AND we have NAT
196            initialized, AND there is no helper, then we can do full
197            NAPT on the head, and IP-address-only NAT on the rest.
198
199            I'm starting to have nightmares about fragments.  */
200
201         if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
202                 *pskb = ip_ct_gather_frags(*pskb);
203
204                 if (!*pskb)
205                         return NF_STOLEN;
206         }
207
208         return ip_nat_fn(hooknum, pskb, in, out, okfn);
209 }
210
211 #ifdef CONFIG_IP_NF_NAT_LOCAL
212 static unsigned int
213 ip_nat_local_fn(unsigned int hooknum,
214                 struct sk_buff **pskb,
215                 const struct net_device *in,
216                 const struct net_device *out,
217                 int (*okfn)(struct sk_buff *))
218 {
219         u_int32_t saddr, daddr;
220         unsigned int ret;
221
222         /* root is playing with raw sockets. */
223         if ((*pskb)->len < sizeof(struct iphdr)
224             || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
225                 return NF_ACCEPT;
226
227         saddr = (*pskb)->nh.iph->saddr;
228         daddr = (*pskb)->nh.iph->daddr;
229
230         ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
231         if (ret != NF_DROP && ret != NF_STOLEN
232             && ((*pskb)->nh.iph->saddr != saddr
233                 || (*pskb)->nh.iph->daddr != daddr))
234                 return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
235         return ret;
236 }
237 #endif
238
239 /* We must be after connection tracking and before packet filtering. */
240
241 /* Before packet filtering, change destination */
242 static struct nf_hook_ops ip_nat_in_ops = {
243         .hook           = ip_nat_fn,
244         .owner          = THIS_MODULE,
245         .pf             = PF_INET,
246         .hooknum        = NF_IP_PRE_ROUTING,
247         .priority       = NF_IP_PRI_NAT_DST,
248 };
249
250 /* After packet filtering, change source */
251 static struct nf_hook_ops ip_nat_out_ops = {
252         .hook           = ip_nat_out,
253         .owner          = THIS_MODULE,
254         .pf             = PF_INET,
255         .hooknum        = NF_IP_POST_ROUTING,
256         .priority       = NF_IP_PRI_NAT_SRC,
257 };
258
259 #ifdef CONFIG_IP_NF_NAT_LOCAL
260 /* Before packet filtering, change destination */
261 static struct nf_hook_ops ip_nat_local_out_ops = {
262         .hook           = ip_nat_local_fn,
263         .owner          = THIS_MODULE,
264         .pf             = PF_INET,
265         .hooknum        = NF_IP_LOCAL_OUT,
266         .priority       = NF_IP_PRI_NAT_DST,
267 };
268
269 /* After packet filtering, change source for reply packets of LOCAL_OUT DNAT */
270 static struct nf_hook_ops ip_nat_local_in_ops = {
271         .hook           = ip_nat_fn,
272         .owner          = THIS_MODULE,
273         .pf             = PF_INET,
274         .hooknum        = NF_IP_LOCAL_IN,
275         .priority       = NF_IP_PRI_NAT_SRC,
276 };
277 #endif
278
279 /* Protocol registration. */
280 int ip_nat_protocol_register(struct ip_nat_protocol *proto)
281 {
282         int ret = 0;
283         struct list_head *i;
284
285         WRITE_LOCK(&ip_nat_lock);
286         list_for_each(i, &protos) {
287                 if (((struct ip_nat_protocol *)i)->protonum
288                     == proto->protonum) {
289                         ret = -EBUSY;
290                         goto out;
291                 }
292         }
293
294         list_prepend(&protos, proto);
295  out:
296         WRITE_UNLOCK(&ip_nat_lock);
297         return ret;
298 }
299
300 /* Noone stores the protocol anywhere; simply delete it. */
301 void ip_nat_protocol_unregister(struct ip_nat_protocol *proto)
302 {
303         WRITE_LOCK(&ip_nat_lock);
304         LIST_DELETE(&protos, proto);
305         WRITE_UNLOCK(&ip_nat_lock);
306
307         /* Someone could be still looking at the proto in a bh. */
308         synchronize_net();
309 }
310
311 static int init_or_cleanup(int init)
312 {
313         int ret = 0;
314
315         need_ip_conntrack();
316
317         if (!init) goto cleanup;
318
319         ret = ip_nat_rule_init();
320         if (ret < 0) {
321                 printk("ip_nat_init: can't setup rules.\n");
322                 goto cleanup_nothing;
323         }
324         ret = ip_nat_init();
325         if (ret < 0) {
326                 printk("ip_nat_init: can't setup rules.\n");
327                 goto cleanup_rule_init;
328         }
329         ret = nf_register_hook(&ip_nat_in_ops);
330         if (ret < 0) {
331                 printk("ip_nat_init: can't register in hook.\n");
332                 goto cleanup_nat;
333         }
334         ret = nf_register_hook(&ip_nat_out_ops);
335         if (ret < 0) {
336                 printk("ip_nat_init: can't register out hook.\n");
337                 goto cleanup_inops;
338         }
339 #ifdef CONFIG_IP_NF_NAT_LOCAL
340         ret = nf_register_hook(&ip_nat_local_out_ops);
341         if (ret < 0) {
342                 printk("ip_nat_init: can't register local out hook.\n");
343                 goto cleanup_outops;
344         }
345         ret = nf_register_hook(&ip_nat_local_in_ops);
346         if (ret < 0) {
347                 printk("ip_nat_init: can't register local in hook.\n");
348                 goto cleanup_localoutops;
349         }
350 #endif
351         return ret;
352
353  cleanup:
354 #ifdef CONFIG_IP_NF_NAT_LOCAL
355         nf_unregister_hook(&ip_nat_local_in_ops);
356  cleanup_localoutops:
357         nf_unregister_hook(&ip_nat_local_out_ops);
358  cleanup_outops:
359 #endif
360         nf_unregister_hook(&ip_nat_out_ops);
361  cleanup_inops:
362         nf_unregister_hook(&ip_nat_in_ops);
363  cleanup_nat:
364         ip_nat_cleanup();
365  cleanup_rule_init:
366         ip_nat_rule_cleanup();
367  cleanup_nothing:
368         MUST_BE_READ_WRITE_UNLOCKED(&ip_nat_lock);
369         return ret;
370 }
371
372 static int __init init(void)
373 {
374         return init_or_cleanup(1);
375 }
376
377 static void __exit fini(void)
378 {
379         init_or_cleanup(0);
380 }
381
382 module_init(init);
383 module_exit(fini);
384
385 EXPORT_SYMBOL(ip_nat_setup_info);
386 EXPORT_SYMBOL(ip_nat_protocol_register);
387 EXPORT_SYMBOL(ip_nat_protocol_unregister);
388 EXPORT_SYMBOL(ip_nat_helper_register);
389 EXPORT_SYMBOL(ip_nat_helper_unregister);
390 EXPORT_SYMBOL(ip_nat_cheat_check);
391 EXPORT_SYMBOL(ip_nat_mangle_tcp_packet);
392 EXPORT_SYMBOL(ip_nat_mangle_udp_packet);
393 EXPORT_SYMBOL(ip_nat_used_tuple);
394 MODULE_LICENSE("GPL");