Commit | Line | Data |
---|---|---|
5b1158e9 JK |
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_nat.h> | |
23 | #include <net/netfilter/nf_nat_rule.h> | |
24 | #include <net/netfilter/nf_nat_protocol.h> | |
25 | #include <net/netfilter/nf_nat_core.h> | |
26 | #include <net/netfilter/nf_nat_helper.h> | |
27 | #include <linux/netfilter_ipv4/ip_tables.h> | |
28 | ||
29 | #if 0 | |
30 | #define DEBUGP printk | |
31 | #else | |
32 | #define DEBUGP(format, args...) | |
33 | #endif | |
34 | ||
5b1158e9 JK |
35 | #ifdef CONFIG_XFRM |
36 | static void nat_decode_session(struct sk_buff *skb, struct flowi *fl) | |
37 | { | |
38 | struct nf_conn *ct; | |
39 | struct nf_conntrack_tuple *t; | |
40 | enum ip_conntrack_info ctinfo; | |
41 | enum ip_conntrack_dir dir; | |
42 | unsigned long statusbit; | |
43 | ||
44 | ct = nf_ct_get(skb, &ctinfo); | |
45 | if (ct == NULL) | |
46 | return; | |
47 | dir = CTINFO2DIR(ctinfo); | |
48 | t = &ct->tuplehash[dir].tuple; | |
49 | ||
50 | if (dir == IP_CT_DIR_ORIGINAL) | |
51 | statusbit = IPS_DST_NAT; | |
52 | else | |
53 | statusbit = IPS_SRC_NAT; | |
54 | ||
55 | if (ct->status & statusbit) { | |
56 | fl->fl4_dst = t->dst.u3.ip; | |
57 | if (t->dst.protonum == IPPROTO_TCP || | |
58 | t->dst.protonum == IPPROTO_UDP) | |
59 | fl->fl_ip_dport = t->dst.u.tcp.port; | |
60 | } | |
61 | ||
62 | statusbit ^= IPS_NAT_MASK; | |
63 | ||
64 | if (ct->status & statusbit) { | |
65 | fl->fl4_src = t->src.u3.ip; | |
66 | if (t->dst.protonum == IPPROTO_TCP || | |
67 | t->dst.protonum == IPPROTO_UDP) | |
68 | fl->fl_ip_sport = t->src.u.tcp.port; | |
69 | } | |
70 | } | |
71 | #endif | |
72 | ||
73 | static unsigned int | |
74 | nf_nat_fn(unsigned int hooknum, | |
75 | struct sk_buff **pskb, | |
76 | const struct net_device *in, | |
77 | const struct net_device *out, | |
78 | int (*okfn)(struct sk_buff *)) | |
79 | { | |
80 | struct nf_conn *ct; | |
81 | enum ip_conntrack_info ctinfo; | |
82 | struct nf_conn_nat *nat; | |
83 | struct nf_nat_info *info; | |
84 | /* maniptype == SRC for postrouting. */ | |
85 | enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); | |
86 | ||
87 | /* We never see fragments: conntrack defrags on pre-routing | |
88 | and local-out, and nf_nat_out protects post-routing. */ | |
eddc9ec5 | 89 | NF_CT_ASSERT(!(ip_hdr(*pskb)->frag_off & htons(IP_MF | IP_OFFSET))); |
5b1158e9 JK |
90 | |
91 | ct = nf_ct_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 | |
e905a9ed YH |
98 | hash table yet). We must not let this through, in |
99 | case we're doing NAT to the same network. */ | |
eddc9ec5 | 100 | if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP) { |
5b1158e9 JK |
101 | struct icmphdr _hdr, *hp; |
102 | ||
c9bdd4b5 | 103 | hp = skb_header_pointer(*pskb, ip_hdrlen(*pskb), |
5b1158e9 JK |
104 | sizeof(_hdr), &_hdr); |
105 | if (hp != NULL && | |
106 | hp->type == ICMP_REDIRECT) | |
107 | return NF_DROP; | |
108 | } | |
109 | return NF_ACCEPT; | |
110 | } | |
111 | ||
112 | /* Don't try to NAT if this packet is not conntracked */ | |
113 | if (ct == &nf_conntrack_untracked) | |
114 | return NF_ACCEPT; | |
115 | ||
116 | nat = nfct_nat(ct); | |
117 | if (!nat) | |
ffed53d2 | 118 | return NF_ACCEPT; |
5b1158e9 JK |
119 | |
120 | switch (ctinfo) { | |
121 | case IP_CT_RELATED: | |
122 | case IP_CT_RELATED+IP_CT_IS_REPLY: | |
eddc9ec5 | 123 | if (ip_hdr(*pskb)->protocol == IPPROTO_ICMP) { |
5b1158e9 JK |
124 | if (!nf_nat_icmp_reply_translation(ct, ctinfo, |
125 | hooknum, pskb)) | |
126 | return NF_DROP; | |
127 | else | |
128 | return NF_ACCEPT; | |
129 | } | |
130 | /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ | |
131 | case IP_CT_NEW: | |
132 | info = &nat->info; | |
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, info, | |
e905a9ed | 142 | hooknum); |
5b1158e9 JK |
143 | else if (hooknum == NF_IP_LOCAL_IN) |
144 | /* LOCAL_IN hook doesn't have a chain! */ | |
145 | ret = alloc_null_binding(ct, info, hooknum); | |
146 | else | |
147 | ret = nf_nat_rule_find(pskb, hooknum, in, out, | |
148 | ct, info); | |
149 | ||
150 | if (ret != NF_ACCEPT) { | |
151 | return ret; | |
152 | } | |
153 | } else | |
154 | DEBUGP("Already setup manip %s for ct %p\n", | |
155 | maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST", | |
156 | ct); | |
157 | break; | |
158 | ||
159 | default: | |
160 | /* ESTABLISHED */ | |
161 | NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || | |
162 | ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY)); | |
163 | info = &nat->info; | |
164 | } | |
165 | ||
166 | NF_CT_ASSERT(info); | |
167 | return nf_nat_packet(ct, ctinfo, hooknum, pskb); | |
168 | } | |
169 | ||
170 | static unsigned int | |
171 | nf_nat_in(unsigned int hooknum, | |
e905a9ed YH |
172 | struct sk_buff **pskb, |
173 | const struct net_device *in, | |
174 | const struct net_device *out, | |
175 | int (*okfn)(struct sk_buff *)) | |
5b1158e9 JK |
176 | { |
177 | unsigned int ret; | |
eddc9ec5 | 178 | __be32 daddr = ip_hdr(*pskb)->daddr; |
5b1158e9 JK |
179 | |
180 | ret = nf_nat_fn(hooknum, pskb, in, out, okfn); | |
181 | if (ret != NF_DROP && ret != NF_STOLEN && | |
eddc9ec5 | 182 | daddr != ip_hdr(*pskb)->daddr) { |
5b1158e9 JK |
183 | dst_release((*pskb)->dst); |
184 | (*pskb)->dst = NULL; | |
185 | } | |
186 | return ret; | |
187 | } | |
188 | ||
189 | static unsigned int | |
190 | nf_nat_out(unsigned int hooknum, | |
191 | struct sk_buff **pskb, | |
192 | const struct net_device *in, | |
193 | const struct net_device *out, | |
194 | int (*okfn)(struct sk_buff *)) | |
195 | { | |
196 | #ifdef CONFIG_XFRM | |
197 | struct nf_conn *ct; | |
198 | enum ip_conntrack_info ctinfo; | |
199 | #endif | |
200 | unsigned int ret; | |
201 | ||
202 | /* root is playing with raw sockets. */ | |
203 | if ((*pskb)->len < sizeof(struct iphdr) || | |
c9bdd4b5 | 204 | ip_hdrlen(*pskb) < sizeof(struct iphdr)) |
5b1158e9 JK |
205 | return NF_ACCEPT; |
206 | ||
207 | ret = nf_nat_fn(hooknum, pskb, in, out, okfn); | |
208 | #ifdef CONFIG_XFRM | |
209 | if (ret != NF_DROP && ret != NF_STOLEN && | |
210 | (ct = nf_ct_get(*pskb, &ctinfo)) != NULL) { | |
211 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | |
212 | ||
213 | if (ct->tuplehash[dir].tuple.src.u3.ip != | |
214 | ct->tuplehash[!dir].tuple.dst.u3.ip | |
215 | || ct->tuplehash[dir].tuple.src.u.all != | |
216 | ct->tuplehash[!dir].tuple.dst.u.all | |
217 | ) | |
218 | return ip_xfrm_me_harder(pskb) == 0 ? ret : NF_DROP; | |
219 | } | |
220 | #endif | |
221 | return ret; | |
222 | } | |
223 | ||
224 | static unsigned int | |
225 | nf_nat_local_fn(unsigned int hooknum, | |
226 | struct sk_buff **pskb, | |
227 | const struct net_device *in, | |
228 | const struct net_device *out, | |
229 | int (*okfn)(struct sk_buff *)) | |
230 | { | |
231 | struct nf_conn *ct; | |
232 | enum ip_conntrack_info ctinfo; | |
233 | unsigned int ret; | |
234 | ||
235 | /* root is playing with raw sockets. */ | |
236 | if ((*pskb)->len < sizeof(struct iphdr) || | |
c9bdd4b5 | 237 | ip_hdrlen(*pskb) < sizeof(struct iphdr)) |
5b1158e9 JK |
238 | return NF_ACCEPT; |
239 | ||
240 | ret = nf_nat_fn(hooknum, pskb, in, out, okfn); | |
241 | if (ret != NF_DROP && ret != NF_STOLEN && | |
242 | (ct = nf_ct_get(*pskb, &ctinfo)) != NULL) { | |
243 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | |
244 | ||
245 | if (ct->tuplehash[dir].tuple.dst.u3.ip != | |
848c29fd | 246 | ct->tuplehash[!dir].tuple.src.u3.ip) { |
5b1158e9 JK |
247 | if (ip_route_me_harder(pskb, RTN_UNSPEC)) |
248 | ret = NF_DROP; | |
848c29fd PM |
249 | } |
250 | #ifdef CONFIG_XFRM | |
251 | else if (ct->tuplehash[dir].tuple.dst.u.all != | |
252 | ct->tuplehash[!dir].tuple.src.u.all) | |
253 | if (ip_xfrm_me_harder(pskb)) | |
254 | ret = NF_DROP; | |
255 | #endif | |
5b1158e9 JK |
256 | } |
257 | return ret; | |
258 | } | |
259 | ||
260 | static unsigned int | |
261 | nf_nat_adjust(unsigned int hooknum, | |
262 | struct sk_buff **pskb, | |
263 | const struct net_device *in, | |
264 | const struct net_device *out, | |
265 | int (*okfn)(struct sk_buff *)) | |
266 | { | |
267 | struct nf_conn *ct; | |
268 | enum ip_conntrack_info ctinfo; | |
269 | ||
270 | ct = nf_ct_get(*pskb, &ctinfo); | |
271 | if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { | |
e905a9ed YH |
272 | DEBUGP("nf_nat_standalone: adjusting sequence number\n"); |
273 | if (!nf_nat_seq_adjust(pskb, ct, ctinfo)) | |
274 | return NF_DROP; | |
5b1158e9 JK |
275 | } |
276 | return NF_ACCEPT; | |
277 | } | |
278 | ||
279 | /* We must be after connection tracking and before packet filtering. */ | |
280 | ||
281 | static struct nf_hook_ops nf_nat_ops[] = { | |
282 | /* Before packet filtering, change destination */ | |
283 | { | |
284 | .hook = nf_nat_in, | |
285 | .owner = THIS_MODULE, | |
286 | .pf = PF_INET, | |
287 | .hooknum = NF_IP_PRE_ROUTING, | |
288 | .priority = NF_IP_PRI_NAT_DST, | |
289 | }, | |
290 | /* After packet filtering, change source */ | |
291 | { | |
292 | .hook = nf_nat_out, | |
293 | .owner = THIS_MODULE, | |
294 | .pf = PF_INET, | |
295 | .hooknum = NF_IP_POST_ROUTING, | |
296 | .priority = NF_IP_PRI_NAT_SRC, | |
297 | }, | |
298 | /* After conntrack, adjust sequence number */ | |
299 | { | |
300 | .hook = nf_nat_adjust, | |
301 | .owner = THIS_MODULE, | |
302 | .pf = PF_INET, | |
303 | .hooknum = NF_IP_POST_ROUTING, | |
304 | .priority = NF_IP_PRI_NAT_SEQ_ADJUST, | |
305 | }, | |
306 | /* Before packet filtering, change destination */ | |
307 | { | |
308 | .hook = nf_nat_local_fn, | |
309 | .owner = THIS_MODULE, | |
310 | .pf = PF_INET, | |
311 | .hooknum = NF_IP_LOCAL_OUT, | |
312 | .priority = NF_IP_PRI_NAT_DST, | |
313 | }, | |
314 | /* After packet filtering, change source */ | |
315 | { | |
316 | .hook = nf_nat_fn, | |
317 | .owner = THIS_MODULE, | |
318 | .pf = PF_INET, | |
319 | .hooknum = NF_IP_LOCAL_IN, | |
320 | .priority = NF_IP_PRI_NAT_SRC, | |
321 | }, | |
322 | /* After conntrack, adjust sequence number */ | |
323 | { | |
324 | .hook = nf_nat_adjust, | |
325 | .owner = THIS_MODULE, | |
326 | .pf = PF_INET, | |
327 | .hooknum = NF_IP_LOCAL_IN, | |
328 | .priority = NF_IP_PRI_NAT_SEQ_ADJUST, | |
329 | }, | |
330 | }; | |
331 | ||
332 | static int __init nf_nat_standalone_init(void) | |
333 | { | |
334 | int size, ret = 0; | |
335 | ||
336 | need_conntrack(); | |
337 | ||
338 | size = ALIGN(sizeof(struct nf_conn), __alignof__(struct nf_conn_nat)) + | |
339 | sizeof(struct nf_conn_nat); | |
340 | ret = nf_conntrack_register_cache(NF_CT_F_NAT, "nf_nat:base", size); | |
341 | if (ret < 0) { | |
342 | printk(KERN_ERR "nf_nat_init: Unable to create slab cache\n"); | |
343 | return ret; | |
344 | } | |
345 | ||
346 | size = ALIGN(size, __alignof__(struct nf_conn_help)) + | |
347 | sizeof(struct nf_conn_help); | |
348 | ret = nf_conntrack_register_cache(NF_CT_F_NAT|NF_CT_F_HELP, | |
349 | "nf_nat:help", size); | |
350 | if (ret < 0) { | |
351 | printk(KERN_ERR "nf_nat_init: Unable to create slab cache\n"); | |
352 | goto cleanup_register_cache; | |
353 | } | |
354 | #ifdef CONFIG_XFRM | |
355 | BUG_ON(ip_nat_decode_session != NULL); | |
356 | ip_nat_decode_session = nat_decode_session; | |
357 | #endif | |
358 | ret = nf_nat_rule_init(); | |
359 | if (ret < 0) { | |
360 | printk("nf_nat_init: can't setup rules.\n"); | |
361 | goto cleanup_decode_session; | |
362 | } | |
363 | ret = nf_register_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops)); | |
364 | if (ret < 0) { | |
365 | printk("nf_nat_init: can't register hooks.\n"); | |
366 | goto cleanup_rule_init; | |
367 | } | |
368 | nf_nat_module_is_loaded = 1; | |
369 | return ret; | |
370 | ||
371 | cleanup_rule_init: | |
372 | nf_nat_rule_cleanup(); | |
373 | cleanup_decode_session: | |
374 | #ifdef CONFIG_XFRM | |
375 | ip_nat_decode_session = NULL; | |
376 | synchronize_net(); | |
377 | #endif | |
378 | nf_conntrack_unregister_cache(NF_CT_F_NAT|NF_CT_F_HELP); | |
379 | cleanup_register_cache: | |
380 | nf_conntrack_unregister_cache(NF_CT_F_NAT); | |
381 | return ret; | |
382 | } | |
383 | ||
384 | static void __exit nf_nat_standalone_fini(void) | |
385 | { | |
386 | nf_unregister_hooks(nf_nat_ops, ARRAY_SIZE(nf_nat_ops)); | |
387 | nf_nat_rule_cleanup(); | |
388 | nf_nat_module_is_loaded = 0; | |
389 | #ifdef CONFIG_XFRM | |
390 | ip_nat_decode_session = NULL; | |
391 | synchronize_net(); | |
392 | #endif | |
393 | /* Conntrack caches are unregistered in nf_conntrack_cleanup */ | |
394 | } | |
395 | ||
396 | module_init(nf_nat_standalone_init); | |
397 | module_exit(nf_nat_standalone_fini); | |
398 | ||
399 | MODULE_LICENSE("GPL"); | |
400 | MODULE_ALIAS("ip_nat"); |