blob: b759ffa1098d6637a6539599d245ea89a58523f5 [file] [log] [blame]
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -08001/* (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>
Yasuyuki Kozakai2d59e5c2007-07-07 22:24:28 -070022#include <net/netfilter/nf_conntrack_extend.h>
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080023#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
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080030#ifdef CONFIG_XFRM
31static void nat_decode_session(struct sk_buff *skb, struct flowi *fl)
32{
Jan Engelhardt72b72942008-04-14 11:15:42 +020033 const struct nf_conn *ct;
34 const struct nf_conntrack_tuple *t;
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080035 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 ||
Patrick McHardy6185f872008-03-20 15:15:51 +010053 t->dst.protonum == IPPROTO_UDP ||
Patrick McHardy4910a082008-03-20 15:15:57 +010054 t->dst.protonum == IPPROTO_UDPLITE ||
55 t->dst.protonum == IPPROTO_DCCP)
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080056 fl->fl_ip_dport = t->dst.u.tcp.port;
57 }
58
59 statusbit ^= IPS_NAT_MASK;
60
61 if (ct->status & statusbit) {
62 fl->fl4_src = t->src.u3.ip;
63 if (t->dst.protonum == IPPROTO_TCP ||
Patrick McHardy6185f872008-03-20 15:15:51 +010064 t->dst.protonum == IPPROTO_UDP ||
Patrick McHardy4910a082008-03-20 15:15:57 +010065 t->dst.protonum == IPPROTO_UDPLITE ||
66 t->dst.protonum == IPPROTO_DCCP)
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080067 fl->fl_ip_sport = t->src.u.tcp.port;
68 }
69}
70#endif
71
72static unsigned int
73nf_nat_fn(unsigned int hooknum,
Herbert Xu3db05fe2007-10-15 00:53:15 -070074 struct sk_buff *skb,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080075 const struct net_device *in,
76 const struct net_device *out,
77 int (*okfn)(struct sk_buff *))
78{
79 struct nf_conn *ct;
80 enum ip_conntrack_info ctinfo;
81 struct nf_conn_nat *nat;
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080082 /* maniptype == SRC for postrouting. */
83 enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
84
85 /* We never see fragments: conntrack defrags on pre-routing
86 and local-out, and nf_nat_out protects post-routing. */
Herbert Xu3db05fe2007-10-15 00:53:15 -070087 NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)));
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080088
Herbert Xu3db05fe2007-10-15 00:53:15 -070089 ct = nf_ct_get(skb, &ctinfo);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080090 /* Can't track? It's not due to stress, or conntrack would
91 have dropped it. Hence it's the user's responsibilty to
92 packet filter it out, or implement conntrack/NAT for that
93 protocol. 8) --RR */
94 if (!ct) {
95 /* Exception: ICMP redirect to new connection (not in
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +090096 hash table yet). We must not let this through, in
97 case we're doing NAT to the same network. */
Herbert Xu3db05fe2007-10-15 00:53:15 -070098 if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -080099 struct icmphdr _hdr, *hp;
100
Herbert Xu3db05fe2007-10-15 00:53:15 -0700101 hp = skb_header_pointer(skb, ip_hdrlen(skb),
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800102 sizeof(_hdr), &_hdr);
103 if (hp != NULL &&
104 hp->type == ICMP_REDIRECT)
105 return NF_DROP;
106 }
107 return NF_ACCEPT;
108 }
109
110 /* Don't try to NAT if this packet is not conntracked */
111 if (ct == &nf_conntrack_untracked)
112 return NF_ACCEPT;
113
114 nat = nfct_nat(ct);
Yasuyuki Kozakai2d59e5c2007-07-07 22:24:28 -0700115 if (!nat) {
116 nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
117 if (nat == NULL) {
Patrick McHardy0d537782007-07-07 22:39:38 -0700118 pr_debug("failed to add NAT extension\n");
Yasuyuki Kozakai2d59e5c2007-07-07 22:24:28 -0700119 return NF_ACCEPT;
120 }
121 }
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800122
123 switch (ctinfo) {
124 case IP_CT_RELATED:
125 case IP_CT_RELATED+IP_CT_IS_REPLY:
Herbert Xu3db05fe2007-10-15 00:53:15 -0700126 if (ip_hdr(skb)->protocol == IPPROTO_ICMP) {
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800127 if (!nf_nat_icmp_reply_translation(ct, ctinfo,
Herbert Xu3db05fe2007-10-15 00:53:15 -0700128 hooknum, skb))
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800129 return NF_DROP;
130 else
131 return NF_ACCEPT;
132 }
133 /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
134 case IP_CT_NEW:
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800135
136 /* Seen it before? This can happen for loopback, retrans,
137 or local packets.. */
138 if (!nf_nat_initialized(ct, maniptype)) {
139 unsigned int ret;
140
141 if (unlikely(nf_ct_is_confirmed(ct)))
142 /* NAT module was loaded late */
Yasuyuki Kozakaiba4c7cb2007-05-10 14:14:45 -0700143 ret = alloc_null_binding_confirmed(ct, hooknum);
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800144 else if (hooknum == NF_INET_LOCAL_IN)
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800145 /* LOCAL_IN hook doesn't have a chain! */
Yasuyuki Kozakaiba4c7cb2007-05-10 14:14:45 -0700146 ret = alloc_null_binding(ct, hooknum);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800147 else
Herbert Xu3db05fe2007-10-15 00:53:15 -0700148 ret = nf_nat_rule_find(skb, hooknum, in, out,
Yasuyuki Kozakaiba4c7cb2007-05-10 14:14:45 -0700149 ct);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800150
151 if (ret != NF_ACCEPT) {
152 return ret;
153 }
154 } else
Patrick McHardy0d537782007-07-07 22:39:38 -0700155 pr_debug("Already setup manip %s for ct %p\n",
156 maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",
157 ct);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800158 break;
159
160 default:
161 /* ESTABLISHED */
162 NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
163 ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800164 }
165
Herbert Xu3db05fe2007-10-15 00:53:15 -0700166 return nf_nat_packet(ct, ctinfo, hooknum, skb);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800167}
168
169static unsigned int
170nf_nat_in(unsigned int hooknum,
Herbert Xu3db05fe2007-10-15 00:53:15 -0700171 struct sk_buff *skb,
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900172 const struct net_device *in,
173 const struct net_device *out,
174 int (*okfn)(struct sk_buff *))
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800175{
176 unsigned int ret;
Herbert Xu3db05fe2007-10-15 00:53:15 -0700177 __be32 daddr = ip_hdr(skb)->daddr;
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800178
Herbert Xu3db05fe2007-10-15 00:53:15 -0700179 ret = nf_nat_fn(hooknum, skb, in, out, okfn);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800180 if (ret != NF_DROP && ret != NF_STOLEN &&
Herbert Xu3db05fe2007-10-15 00:53:15 -0700181 daddr != ip_hdr(skb)->daddr) {
182 dst_release(skb->dst);
183 skb->dst = NULL;
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800184 }
185 return ret;
186}
187
188static unsigned int
189nf_nat_out(unsigned int hooknum,
Herbert Xu3db05fe2007-10-15 00:53:15 -0700190 struct sk_buff *skb,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800191 const struct net_device *in,
192 const struct net_device *out,
193 int (*okfn)(struct sk_buff *))
194{
195#ifdef CONFIG_XFRM
Jan Engelhardt72b72942008-04-14 11:15:42 +0200196 const struct nf_conn *ct;
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800197 enum ip_conntrack_info ctinfo;
198#endif
199 unsigned int ret;
200
201 /* root is playing with raw sockets. */
Herbert Xu3db05fe2007-10-15 00:53:15 -0700202 if (skb->len < sizeof(struct iphdr) ||
203 ip_hdrlen(skb) < sizeof(struct iphdr))
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800204 return NF_ACCEPT;
205
Herbert Xu3db05fe2007-10-15 00:53:15 -0700206 ret = nf_nat_fn(hooknum, skb, in, out, okfn);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800207#ifdef CONFIG_XFRM
208 if (ret != NF_DROP && ret != NF_STOLEN &&
Herbert Xu3db05fe2007-10-15 00:53:15 -0700209 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800210 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
211
212 if (ct->tuplehash[dir].tuple.src.u3.ip !=
213 ct->tuplehash[!dir].tuple.dst.u3.ip
214 || ct->tuplehash[dir].tuple.src.u.all !=
215 ct->tuplehash[!dir].tuple.dst.u.all
216 )
Herbert Xu3db05fe2007-10-15 00:53:15 -0700217 return ip_xfrm_me_harder(skb) == 0 ? ret : NF_DROP;
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800218 }
219#endif
220 return ret;
221}
222
223static unsigned int
224nf_nat_local_fn(unsigned int hooknum,
Herbert Xu3db05fe2007-10-15 00:53:15 -0700225 struct sk_buff *skb,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800226 const struct net_device *in,
227 const struct net_device *out,
228 int (*okfn)(struct sk_buff *))
229{
Jan Engelhardt72b72942008-04-14 11:15:42 +0200230 const struct nf_conn *ct;
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800231 enum ip_conntrack_info ctinfo;
232 unsigned int ret;
233
234 /* root is playing with raw sockets. */
Herbert Xu3db05fe2007-10-15 00:53:15 -0700235 if (skb->len < sizeof(struct iphdr) ||
236 ip_hdrlen(skb) < sizeof(struct iphdr))
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800237 return NF_ACCEPT;
238
Herbert Xu3db05fe2007-10-15 00:53:15 -0700239 ret = nf_nat_fn(hooknum, skb, in, out, okfn);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800240 if (ret != NF_DROP && ret != NF_STOLEN &&
Herbert Xu3db05fe2007-10-15 00:53:15 -0700241 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800242 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
243
244 if (ct->tuplehash[dir].tuple.dst.u3.ip !=
Patrick McHardy848c29f2007-03-22 12:30:29 -0700245 ct->tuplehash[!dir].tuple.src.u3.ip) {
Herbert Xu3db05fe2007-10-15 00:53:15 -0700246 if (ip_route_me_harder(skb, RTN_UNSPEC))
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800247 ret = NF_DROP;
Patrick McHardy848c29f2007-03-22 12:30:29 -0700248 }
249#ifdef CONFIG_XFRM
250 else if (ct->tuplehash[dir].tuple.dst.u.all !=
251 ct->tuplehash[!dir].tuple.src.u.all)
Herbert Xu3db05fe2007-10-15 00:53:15 -0700252 if (ip_xfrm_me_harder(skb))
Patrick McHardy848c29f2007-03-22 12:30:29 -0700253 ret = NF_DROP;
254#endif
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800255 }
256 return ret;
257}
258
259static unsigned int
260nf_nat_adjust(unsigned int hooknum,
Herbert Xu3db05fe2007-10-15 00:53:15 -0700261 struct sk_buff *skb,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800262 const struct net_device *in,
263 const struct net_device *out,
264 int (*okfn)(struct sk_buff *))
265{
266 struct nf_conn *ct;
267 enum ip_conntrack_info ctinfo;
268
Herbert Xu3db05fe2007-10-15 00:53:15 -0700269 ct = nf_ct_get(skb, &ctinfo);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800270 if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) {
Patrick McHardy0d537782007-07-07 22:39:38 -0700271 pr_debug("nf_nat_standalone: adjusting sequence number\n");
Herbert Xu3db05fe2007-10-15 00:53:15 -0700272 if (!nf_nat_seq_adjust(skb, ct, ctinfo))
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900273 return NF_DROP;
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800274 }
275 return NF_ACCEPT;
276}
277
278/* We must be after connection tracking and before packet filtering. */
279
Patrick McHardy19994142007-12-05 01:23:00 -0800280static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800281 /* Before packet filtering, change destination */
282 {
283 .hook = nf_nat_in,
284 .owner = THIS_MODULE,
285 .pf = PF_INET,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800286 .hooknum = NF_INET_PRE_ROUTING,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800287 .priority = NF_IP_PRI_NAT_DST,
288 },
289 /* After packet filtering, change source */
290 {
291 .hook = nf_nat_out,
292 .owner = THIS_MODULE,
293 .pf = PF_INET,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800294 .hooknum = NF_INET_POST_ROUTING,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800295 .priority = NF_IP_PRI_NAT_SRC,
296 },
297 /* After conntrack, adjust sequence number */
298 {
299 .hook = nf_nat_adjust,
300 .owner = THIS_MODULE,
301 .pf = PF_INET,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800302 .hooknum = NF_INET_POST_ROUTING,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800303 .priority = NF_IP_PRI_NAT_SEQ_ADJUST,
304 },
305 /* Before packet filtering, change destination */
306 {
307 .hook = nf_nat_local_fn,
308 .owner = THIS_MODULE,
309 .pf = PF_INET,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800310 .hooknum = NF_INET_LOCAL_OUT,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800311 .priority = NF_IP_PRI_NAT_DST,
312 },
313 /* After packet filtering, change source */
314 {
315 .hook = nf_nat_fn,
316 .owner = THIS_MODULE,
317 .pf = PF_INET,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800318 .hooknum = NF_INET_LOCAL_IN,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800319 .priority = NF_IP_PRI_NAT_SRC,
320 },
321 /* After conntrack, adjust sequence number */
322 {
323 .hook = nf_nat_adjust,
324 .owner = THIS_MODULE,
325 .pf = PF_INET,
Patrick McHardy6e23ae22007-11-19 18:53:30 -0800326 .hooknum = NF_INET_LOCAL_IN,
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800327 .priority = NF_IP_PRI_NAT_SEQ_ADJUST,
328 },
329};
330
331static int __init nf_nat_standalone_init(void)
332{
Yasuyuki Kozakai2d59e5c2007-07-07 22:24:28 -0700333 int ret = 0;
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800334
Patrick McHardy591e6202007-08-07 18:12:01 -0700335 need_ipv4_conntrack();
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800336
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800337#ifdef CONFIG_XFRM
338 BUG_ON(ip_nat_decode_session != NULL);
Patrick McHardy051578c2007-12-17 22:42:51 -0800339 rcu_assign_pointer(ip_nat_decode_session, nat_decode_session);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800340#endif
341 ret = nf_nat_rule_init();
342 if (ret < 0) {
343 printk("nf_nat_init: can't setup rules.\n");
344 goto cleanup_decode_session;
345 }
346 ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
347 if (ret < 0) {
348 printk("nf_nat_init: can't register hooks.\n");
349 goto cleanup_rule_init;
350 }
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800351 return ret;
352
353 cleanup_rule_init:
354 nf_nat_rule_cleanup();
355 cleanup_decode_session:
356#ifdef CONFIG_XFRM
Patrick McHardy051578c2007-12-17 22:42:51 -0800357 rcu_assign_pointer(ip_nat_decode_session, NULL);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800358 synchronize_net();
359#endif
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800360 return ret;
361}
362
363static void __exit nf_nat_standalone_fini(void)
364{
365 nf_unregister_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops));
366 nf_nat_rule_cleanup();
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800367#ifdef CONFIG_XFRM
Patrick McHardy051578c2007-12-17 22:42:51 -0800368 rcu_assign_pointer(ip_nat_decode_session, NULL);
Jozsef Kadlecsik5b1158e2006-12-02 22:07:13 -0800369 synchronize_net();
370#endif
371 /* Conntrack caches are unregistered in nf_conntrack_cleanup */
372}
373
374module_init(nf_nat_standalone_init);
375module_exit(nf_nat_standalone_fini);
376
377MODULE_LICENSE("GPL");
378MODULE_ALIAS("ip_nat");