]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - net/ipv4/netfilter/nf_nat_standalone.c
dc316b9f9b1d2fc4a6447dfa8424ed7c31308c65
[linux-2.6-omap-h63xx.git] / net / ipv4 / netfilter / nf_nat_standalone.c
1 /* (C) 1999-2001 Paul `Rusty' Russell
2  * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 #include <linux/types.h>
9 #include <linux/icmp.h>
10 #include <linux/ip.h>
11 #include <linux/netfilter.h>
12 #include <linux/netfilter_ipv4.h>
13 #include <linux/module.h>
14 #include <linux/skbuff.h>
15 #include <linux/proc_fs.h>
16 #include <net/ip.h>
17 #include <net/checksum.h>
18 #include <linux/spinlock.h>
19
20 #include <net/netfilter/nf_conntrack.h>
21 #include <net/netfilter/nf_conntrack_core.h>
22 #include <net/netfilter/nf_conntrack_extend.h>
23 #include <net/netfilter/nf_nat.h>
24 #include <net/netfilter/nf_nat_rule.h>
25 #include <net/netfilter/nf_nat_protocol.h>
26 #include <net/netfilter/nf_nat_core.h>
27 #include <net/netfilter/nf_nat_helper.h>
28 #include <linux/netfilter_ipv4/ip_tables.h>
29
30 #ifdef CONFIG_XFRM
31 static void nat_decode_session(struct sk_buff *skb, struct flowi *fl)
32 {
33         const struct nf_conn *ct;
34         const struct nf_conntrack_tuple *t;
35         enum ip_conntrack_info ctinfo;
36         enum ip_conntrack_dir dir;
37         unsigned long statusbit;
38
39         ct = nf_ct_get(skb, &ctinfo);
40         if (ct == NULL)
41                 return;
42         dir = CTINFO2DIR(ctinfo);
43         t = &ct->tuplehash[dir].tuple;
44
45         if (dir == IP_CT_DIR_ORIGINAL)
46                 statusbit = IPS_DST_NAT;
47         else
48                 statusbit = IPS_SRC_NAT;
49
50         if (ct->status & statusbit) {
51                 fl->fl4_dst = t->dst.u3.ip;
52                 if (t->dst.protonum == IPPROTO_TCP ||
53                     t->dst.protonum == IPPROTO_UDP ||
54                     t->dst.protonum == IPPROTO_UDPLITE)
55                         fl->fl_ip_dport = t->dst.u.tcp.port;
56         }
57
58         statusbit ^= IPS_NAT_MASK;
59
60         if (ct->status & statusbit) {
61                 fl->fl4_src = t->src.u3.ip;
62                 if (t->dst.protonum == IPPROTO_TCP ||
63                     t->dst.protonum == IPPROTO_UDP ||
64                     t->dst.protonum == IPPROTO_UDPLITE)
65                         fl->fl_ip_sport = t->src.u.tcp.port;
66         }
67 }
68 #endif
69
70 static unsigned int
71 nf_nat_fn(unsigned int hooknum,
72           struct sk_buff *skb,
73           const struct net_device *in,
74           const struct net_device *out,
75           int (*okfn)(struct sk_buff *))
76 {
77         struct nf_conn *ct;
78         enum ip_conntrack_info ctinfo;
79         struct nf_conn_nat *nat;
80         /* maniptype == SRC for postrouting. */
81         enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
82
83         /* We never see fragments: conntrack defrags on pre-routing
84            and local-out, and nf_nat_out protects post-routing. */
85         NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)));
86
87         ct = nf_ct_get(skb, &ctinfo);
88         /* Can't track?  It's not due to stress, or conntrack would
89            have dropped it.  Hence it's the user's responsibilty to
90            packet filter it out, or implement conntrack/NAT for that
91            protocol. 8) --RR */
92         if (!ct) {
93                 /* Exception: ICMP redirect to new connection (not in
94                    hash table yet).  We must not let this through, in
95                    case we're doing NAT to the same network. */
96                 if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
97                         struct icmphdr _hdr, *hp;
98
99                         hp = skb_header_pointer(skb, ip_hdrlen(skb),
100                                                 sizeof(_hdr), &_hdr);
101                         if (hp != NULL &&
102                             hp->type == ICMP_REDIRECT)
103                                 return NF_DROP;
104                 }
105                 return NF_ACCEPT;
106         }
107
108         /* Don't try to NAT if this packet is not conntracked */
109         if (ct == &nf_conntrack_untracked)
110                 return NF_ACCEPT;
111
112         nat = nfct_nat(ct);
113         if (!nat) {
114                 nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
115                 if (nat == NULL) {
116                         pr_debug("failed to add NAT extension\n");
117                         return NF_ACCEPT;
118                 }
119         }
120
121         switch (ctinfo) {
122         case IP_CT_RELATED:
123         case IP_CT_RELATED+IP_CT_IS_REPLY:
124                 if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
125                         if (!nf_nat_icmp_reply_translation(ct, ctinfo,
126                                                            hooknum, skb))
127                                 return NF_DROP;
128                         else
129                                 return NF_ACCEPT;
130                 }
131                 /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
132         case IP_CT_NEW:
133
134                 /* Seen it before?  This can happen for loopback, retrans,
135                    or local packets.. */
136                 if (!nf_nat_initialized(ct, maniptype)) {
137                         unsigned int ret;
138
139                         if (unlikely(nf_ct_is_confirmed(ct)))
140                                 /* NAT module was loaded late */
141                                 ret = alloc_null_binding_confirmed(ct, hooknum);
142                         else if (hooknum == NF_INET_LOCAL_IN)
143                                 /* LOCAL_IN hook doesn't have a chain!  */
144                                 ret = alloc_null_binding(ct, hooknum);
145                         else
146                                 ret = nf_nat_rule_find(skb, hooknum, in, out,
147                                                        ct);
148
149                         if (ret != NF_ACCEPT) {
150                                 return ret;
151                         }
152                 } else
153                         pr_debug("Already setup manip %s for ct %p\n",
154                                  maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",
155                                  ct);
156                 break;
157
158         default:
159                 /* ESTABLISHED */
160                 NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
161                              ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));
162         }
163
164         return nf_nat_packet(ct, ctinfo, hooknum, skb);
165 }
166
167 static unsigned int
168 nf_nat_in(unsigned int hooknum,
169           struct sk_buff *skb,
170           const struct net_device *in,
171           const struct net_device *out,
172           int (*okfn)(struct sk_buff *))
173 {
174         unsigned int ret;
175         __be32 daddr = ip_hdr(skb)->daddr;
176
177         ret = nf_nat_fn(hooknum, skb, in, out, okfn);
178         if (ret != NF_DROP && ret != NF_STOLEN &&
179             daddr != ip_hdr(skb)->daddr) {
180                 dst_release(skb->dst);
181                 skb->dst = NULL;
182         }
183         return ret;
184 }
185
186 static unsigned int
187 nf_nat_out(unsigned int hooknum,
188            struct sk_buff *skb,
189            const struct net_device *in,
190            const struct net_device *out,
191            int (*okfn)(struct sk_buff *))
192 {
193 #ifdef CONFIG_XFRM
194         const struct nf_conn *ct;
195         enum ip_conntrack_info ctinfo;
196 #endif
197         unsigned int ret;
198
199         /* root is playing with raw sockets. */
200         if (skb->len < sizeof(struct iphdr) ||
201             ip_hdrlen(skb) < sizeof(struct iphdr))
202                 return NF_ACCEPT;
203
204         ret = nf_nat_fn(hooknum, skb, in, out, okfn);
205 #ifdef CONFIG_XFRM
206         if (ret != NF_DROP && ret != NF_STOLEN &&
207             (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
208                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
209
210                 if (ct->tuplehash[dir].tuple.src.u3.ip !=
211                     ct->tuplehash[!dir].tuple.dst.u3.ip
212                     || ct->tuplehash[dir].tuple.src.u.all !=
213                        ct->tuplehash[!dir].tuple.dst.u.all
214                     )
215                         return ip_xfrm_me_harder(skb) == 0 ? ret : NF_DROP;
216         }
217 #endif
218         return ret;
219 }
220
221 static unsigned int
222 nf_nat_local_fn(unsigned int hooknum,
223                 struct sk_buff *skb,
224                 const struct net_device *in,
225                 const struct net_device *out,
226                 int (*okfn)(struct sk_buff *))
227 {
228         const struct nf_conn *ct;
229         enum ip_conntrack_info ctinfo;
230         unsigned int ret;
231
232         /* root is playing with raw sockets. */
233         if (skb->len < sizeof(struct iphdr) ||
234             ip_hdrlen(skb) < sizeof(struct iphdr))
235                 return NF_ACCEPT;
236
237         ret = nf_nat_fn(hooknum, skb, in, out, okfn);
238         if (ret != NF_DROP && ret != NF_STOLEN &&
239             (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
240                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
241
242                 if (ct->tuplehash[dir].tuple.dst.u3.ip !=
243                     ct->tuplehash[!dir].tuple.src.u3.ip) {
244                         if (ip_route_me_harder(skb, RTN_UNSPEC))
245                                 ret = NF_DROP;
246                 }
247 #ifdef CONFIG_XFRM
248                 else if (ct->tuplehash[dir].tuple.dst.u.all !=
249                          ct->tuplehash[!dir].tuple.src.u.all)
250                         if (ip_xfrm_me_harder(skb))
251                                 ret = NF_DROP;
252 #endif
253         }
254         return ret;
255 }
256
257 static unsigned int
258 nf_nat_adjust(unsigned int hooknum,
259               struct sk_buff *skb,
260               const struct net_device *in,
261               const struct net_device *out,
262               int (*okfn)(struct sk_buff *))
263 {
264         struct nf_conn *ct;
265         enum ip_conntrack_info ctinfo;
266
267         ct = nf_ct_get(skb, &ctinfo);
268         if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) {
269                 pr_debug("nf_nat_standalone: adjusting sequence number\n");
270                 if (!nf_nat_seq_adjust(skb, ct, ctinfo))
271                         return NF_DROP;
272         }
273         return NF_ACCEPT;
274 }
275
276 /* We must be after connection tracking and before packet filtering. */
277
278 static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
279         /* Before packet filtering, change destination */
280         {
281                 .hook           = nf_nat_in,
282                 .owner          = THIS_MODULE,
283                 .pf             = PF_INET,
284                 .hooknum        = NF_INET_PRE_ROUTING,
285                 .priority       = NF_IP_PRI_NAT_DST,
286         },
287         /* After packet filtering, change source */
288         {
289                 .hook           = nf_nat_out,
290                 .owner          = THIS_MODULE,
291                 .pf             = PF_INET,
292                 .hooknum        = NF_INET_POST_ROUTING,
293                 .priority       = NF_IP_PRI_NAT_SRC,
294         },
295         /* After conntrack, adjust sequence number */
296         {
297                 .hook           = nf_nat_adjust,
298                 .owner          = THIS_MODULE,
299                 .pf             = PF_INET,
300                 .hooknum        = NF_INET_POST_ROUTING,
301                 .priority       = NF_IP_PRI_NAT_SEQ_ADJUST,
302         },
303         /* Before packet filtering, change destination */
304         {
305                 .hook           = nf_nat_local_fn,
306                 .owner          = THIS_MODULE,
307                 .pf             = PF_INET,
308                 .hooknum        = NF_INET_LOCAL_OUT,
309                 .priority       = NF_IP_PRI_NAT_DST,
310         },
311         /* After packet filtering, change source */
312         {
313                 .hook           = nf_nat_fn,
314                 .owner          = THIS_MODULE,
315                 .pf             = PF_INET,
316                 .hooknum        = NF_INET_LOCAL_IN,
317                 .priority       = NF_IP_PRI_NAT_SRC,
318         },
319         /* After conntrack, adjust sequence number */
320         {
321                 .hook           = nf_nat_adjust,
322                 .owner          = THIS_MODULE,
323                 .pf             = PF_INET,
324                 .hooknum        = NF_INET_LOCAL_IN,
325                 .priority       = NF_IP_PRI_NAT_SEQ_ADJUST,
326         },
327 };
328
329 static int __init nf_nat_standalone_init(void)
330 {
331         int ret = 0;
332
333         need_ipv4_conntrack();
334
335 #ifdef CONFIG_XFRM
336         BUG_ON(ip_nat_decode_session != NULL);
337         rcu_assign_pointer(ip_nat_decode_session, nat_decode_session);
338 #endif
339         ret = nf_nat_rule_init();
340         if (ret < 0) {
341                 printk("nf_nat_init: can't setup rules.\n");
342                 goto cleanup_decode_session;
343         }
344         ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
345         if (ret < 0) {
346                 printk("nf_nat_init: can't register hooks.\n");
347                 goto cleanup_rule_init;
348         }
349         return ret;
350
351  cleanup_rule_init:
352         nf_nat_rule_cleanup();
353  cleanup_decode_session:
354 #ifdef CONFIG_XFRM
355         rcu_assign_pointer(ip_nat_decode_session, NULL);
356         synchronize_net();
357 #endif
358         return ret;
359 }
360
361 static void __exit nf_nat_standalone_fini(void)
362 {
363         nf_unregister_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
364         nf_nat_rule_cleanup();
365 #ifdef CONFIG_XFRM
366         rcu_assign_pointer(ip_nat_decode_session, NULL);
367         synchronize_net();
368 #endif
369         /* Conntrack caches are unregistered in nf_conntrack_cleanup */
370 }
371
372 module_init(nf_nat_standalone_init);
373 module_exit(nf_nat_standalone_fini);
374
375 MODULE_LICENSE("GPL");
376 MODULE_ALIAS("ip_nat");