]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
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/ip.h> | |
31 | #include <net/checksum.h> | |
32 | #include <linux/spinlock.h> | |
33 | ||
e45b1be8 PM |
34 | #define ASSERT_READ_LOCK(x) |
35 | #define ASSERT_WRITE_LOCK(x) | |
1da177e4 LT |
36 | |
37 | #include <linux/netfilter_ipv4/ip_nat.h> | |
38 | #include <linux/netfilter_ipv4/ip_nat_rule.h> | |
39 | #include <linux/netfilter_ipv4/ip_nat_protocol.h> | |
40 | #include <linux/netfilter_ipv4/ip_nat_core.h> | |
41 | #include <linux/netfilter_ipv4/ip_nat_helper.h> | |
42 | #include <linux/netfilter_ipv4/ip_tables.h> | |
43 | #include <linux/netfilter_ipv4/ip_conntrack_core.h> | |
44 | #include <linux/netfilter_ipv4/listhelp.h> | |
45 | ||
46 | #if 0 | |
47 | #define DEBUGP printk | |
48 | #else | |
49 | #define DEBUGP(format, args...) | |
50 | #endif | |
51 | ||
52 | #define HOOKNAME(hooknum) ((hooknum) == NF_IP_POST_ROUTING ? "POST_ROUTING" \ | |
53 | : ((hooknum) == NF_IP_PRE_ROUTING ? "PRE_ROUTING" \ | |
54 | : ((hooknum) == NF_IP_LOCAL_OUT ? "LOCAL_OUT" \ | |
55 | : ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN" \ | |
56 | : "*ERROR*"))) | |
57 | ||
58 | static unsigned int | |
59 | ip_nat_fn(unsigned int hooknum, | |
60 | struct sk_buff **pskb, | |
61 | const struct net_device *in, | |
62 | const struct net_device *out, | |
63 | int (*okfn)(struct sk_buff *)) | |
64 | { | |
65 | struct ip_conntrack *ct; | |
66 | enum ip_conntrack_info ctinfo; | |
67 | struct ip_nat_info *info; | |
68 | /* maniptype == SRC for postrouting. */ | |
69 | enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum); | |
70 | ||
71 | /* We never see fragments: conntrack defrags on pre-routing | |
72 | and local-out, and ip_nat_out protects post-routing. */ | |
73 | IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off | |
74 | & htons(IP_MF|IP_OFFSET))); | |
75 | ||
1da177e4 LT |
76 | /* If we had a hardware checksum before, it's now invalid */ |
77 | if ((*pskb)->ip_summed == CHECKSUM_HW) | |
78 | if (skb_checksum_help(*pskb, (out == NULL))) | |
79 | return NF_DROP; | |
80 | ||
81 | ct = ip_conntrack_get(*pskb, &ctinfo); | |
82 | /* Can't track? It's not due to stress, or conntrack would | |
83 | have dropped it. Hence it's the user's responsibilty to | |
84 | packet filter it out, or implement conntrack/NAT for that | |
85 | protocol. 8) --RR */ | |
86 | if (!ct) { | |
87 | /* Exception: ICMP redirect to new connection (not in | |
88 | hash table yet). We must not let this through, in | |
89 | case we're doing NAT to the same network. */ | |
90 | if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { | |
91 | struct icmphdr _hdr, *hp; | |
92 | ||
93 | hp = skb_header_pointer(*pskb, | |
94 | (*pskb)->nh.iph->ihl*4, | |
95 | sizeof(_hdr), &_hdr); | |
96 | if (hp != NULL && | |
97 | hp->type == ICMP_REDIRECT) | |
98 | return NF_DROP; | |
99 | } | |
100 | return NF_ACCEPT; | |
101 | } | |
102 | ||
8b83bc77 HW |
103 | /* Don't try to NAT if this packet is not conntracked */ |
104 | if (ct == &ip_conntrack_untracked) | |
105 | return NF_ACCEPT; | |
106 | ||
1da177e4 LT |
107 | switch (ctinfo) { |
108 | case IP_CT_RELATED: | |
109 | case IP_CT_RELATED+IP_CT_IS_REPLY: | |
110 | if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { | |
111 | if (!icmp_reply_translation(pskb, ct, maniptype, | |
112 | CTINFO2DIR(ctinfo))) | |
113 | return NF_DROP; | |
114 | else | |
115 | return NF_ACCEPT; | |
116 | } | |
117 | /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ | |
118 | case IP_CT_NEW: | |
119 | info = &ct->nat.info; | |
120 | ||
121 | /* Seen it before? This can happen for loopback, retrans, | |
122 | or local packets.. */ | |
123 | if (!ip_nat_initialized(ct, maniptype)) { | |
124 | unsigned int ret; | |
125 | ||
126 | /* LOCAL_IN hook doesn't have a chain! */ | |
127 | if (hooknum == NF_IP_LOCAL_IN) | |
128 | ret = alloc_null_binding(ct, info, hooknum); | |
129 | else | |
130 | ret = ip_nat_rule_find(pskb, hooknum, | |
131 | in, out, ct, | |
132 | info); | |
133 | ||
134 | if (ret != NF_ACCEPT) { | |
135 | return ret; | |
136 | } | |
137 | } else | |
138 | DEBUGP("Already setup manip %s for ct %p\n", | |
139 | maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST", | |
140 | ct); | |
141 | break; | |
142 | ||
143 | default: | |
144 | /* ESTABLISHED */ | |
145 | IP_NF_ASSERT(ctinfo == IP_CT_ESTABLISHED | |
146 | || ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY)); | |
147 | info = &ct->nat.info; | |
148 | } | |
149 | ||
150 | IP_NF_ASSERT(info); | |
151 | return nat_packet(ct, ctinfo, hooknum, pskb); | |
152 | } | |
153 | ||
154 | static unsigned int | |
155 | ip_nat_in(unsigned int hooknum, | |
156 | struct sk_buff **pskb, | |
157 | const struct net_device *in, | |
158 | const struct net_device *out, | |
159 | int (*okfn)(struct sk_buff *)) | |
160 | { | |
161 | u_int32_t saddr, daddr; | |
162 | unsigned int ret; | |
163 | ||
164 | saddr = (*pskb)->nh.iph->saddr; | |
165 | daddr = (*pskb)->nh.iph->daddr; | |
166 | ||
167 | ret = ip_nat_fn(hooknum, pskb, in, out, okfn); | |
168 | if (ret != NF_DROP && ret != NF_STOLEN | |
169 | && ((*pskb)->nh.iph->saddr != saddr | |
170 | || (*pskb)->nh.iph->daddr != daddr)) { | |
171 | dst_release((*pskb)->dst); | |
172 | (*pskb)->dst = NULL; | |
173 | } | |
174 | return ret; | |
175 | } | |
176 | ||
177 | static unsigned int | |
178 | ip_nat_out(unsigned int hooknum, | |
179 | struct sk_buff **pskb, | |
180 | const struct net_device *in, | |
181 | const struct net_device *out, | |
182 | int (*okfn)(struct sk_buff *)) | |
183 | { | |
184 | /* root is playing with raw sockets. */ | |
185 | if ((*pskb)->len < sizeof(struct iphdr) | |
186 | || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) | |
187 | return NF_ACCEPT; | |
188 | ||
189 | /* We can hit fragment here; forwarded packets get | |
190 | defragmented by connection tracking coming in, then | |
191 | fragmented (grr) by the forward code. | |
192 | ||
193 | In future: If we have nfct != NULL, AND we have NAT | |
194 | initialized, AND there is no helper, then we can do full | |
195 | NAPT on the head, and IP-address-only NAT on the rest. | |
196 | ||
197 | I'm starting to have nightmares about fragments. */ | |
198 | ||
199 | if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) { | |
200 | *pskb = ip_ct_gather_frags(*pskb, IP_DEFRAG_NAT_OUT); | |
201 | ||
202 | if (!*pskb) | |
203 | return NF_STOLEN; | |
204 | } | |
205 | ||
206 | return ip_nat_fn(hooknum, pskb, in, out, okfn); | |
207 | } | |
208 | ||
209 | static unsigned int | |
210 | ip_nat_local_fn(unsigned int hooknum, | |
211 | struct sk_buff **pskb, | |
212 | const struct net_device *in, | |
213 | const struct net_device *out, | |
214 | int (*okfn)(struct sk_buff *)) | |
215 | { | |
216 | u_int32_t saddr, daddr; | |
217 | unsigned int ret; | |
218 | ||
219 | /* root is playing with raw sockets. */ | |
220 | if ((*pskb)->len < sizeof(struct iphdr) | |
221 | || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr)) | |
222 | return NF_ACCEPT; | |
223 | ||
224 | saddr = (*pskb)->nh.iph->saddr; | |
225 | daddr = (*pskb)->nh.iph->daddr; | |
226 | ||
227 | ret = ip_nat_fn(hooknum, pskb, in, out, okfn); | |
228 | if (ret != NF_DROP && ret != NF_STOLEN | |
229 | && ((*pskb)->nh.iph->saddr != saddr | |
230 | || (*pskb)->nh.iph->daddr != daddr)) | |
231 | return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP; | |
232 | return ret; | |
233 | } | |
234 | ||
e281e3ac PM |
235 | static unsigned int |
236 | ip_nat_adjust(unsigned int hooknum, | |
237 | struct sk_buff **pskb, | |
238 | const struct net_device *in, | |
239 | const struct net_device *out, | |
240 | int (*okfn)(struct sk_buff *)) | |
241 | { | |
242 | struct ip_conntrack *ct; | |
243 | enum ip_conntrack_info ctinfo; | |
244 | ||
245 | ct = ip_conntrack_get(*pskb, &ctinfo); | |
246 | if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { | |
247 | DEBUGP("ip_nat_standalone: adjusting sequence number\n"); | |
248 | if (!ip_nat_seq_adjust(pskb, ct, ctinfo)) | |
249 | return NF_DROP; | |
250 | } | |
251 | return NF_ACCEPT; | |
252 | } | |
253 | ||
1da177e4 LT |
254 | /* We must be after connection tracking and before packet filtering. */ |
255 | ||
256 | /* Before packet filtering, change destination */ | |
257 | static struct nf_hook_ops ip_nat_in_ops = { | |
258 | .hook = ip_nat_in, | |
259 | .owner = THIS_MODULE, | |
260 | .pf = PF_INET, | |
261 | .hooknum = NF_IP_PRE_ROUTING, | |
262 | .priority = NF_IP_PRI_NAT_DST, | |
263 | }; | |
264 | ||
265 | /* After packet filtering, change source */ | |
266 | static struct nf_hook_ops ip_nat_out_ops = { | |
267 | .hook = ip_nat_out, | |
268 | .owner = THIS_MODULE, | |
269 | .pf = PF_INET, | |
270 | .hooknum = NF_IP_POST_ROUTING, | |
271 | .priority = NF_IP_PRI_NAT_SRC, | |
272 | }; | |
273 | ||
e281e3ac PM |
274 | /* After conntrack, adjust sequence number */ |
275 | static struct nf_hook_ops ip_nat_adjust_out_ops = { | |
276 | .hook = ip_nat_adjust, | |
277 | .owner = THIS_MODULE, | |
278 | .pf = PF_INET, | |
279 | .hooknum = NF_IP_POST_ROUTING, | |
280 | .priority = NF_IP_PRI_NAT_SEQ_ADJUST, | |
281 | }; | |
282 | ||
1da177e4 LT |
283 | /* Before packet filtering, change destination */ |
284 | static struct nf_hook_ops ip_nat_local_out_ops = { | |
285 | .hook = ip_nat_local_fn, | |
286 | .owner = THIS_MODULE, | |
287 | .pf = PF_INET, | |
288 | .hooknum = NF_IP_LOCAL_OUT, | |
289 | .priority = NF_IP_PRI_NAT_DST, | |
290 | }; | |
291 | ||
292 | /* After packet filtering, change source for reply packets of LOCAL_OUT DNAT */ | |
293 | static struct nf_hook_ops ip_nat_local_in_ops = { | |
294 | .hook = ip_nat_fn, | |
295 | .owner = THIS_MODULE, | |
296 | .pf = PF_INET, | |
297 | .hooknum = NF_IP_LOCAL_IN, | |
298 | .priority = NF_IP_PRI_NAT_SRC, | |
299 | }; | |
300 | ||
e281e3ac PM |
301 | /* After conntrack, adjust sequence number */ |
302 | static struct nf_hook_ops ip_nat_adjust_in_ops = { | |
303 | .hook = ip_nat_adjust, | |
304 | .owner = THIS_MODULE, | |
305 | .pf = PF_INET, | |
306 | .hooknum = NF_IP_LOCAL_IN, | |
307 | .priority = NF_IP_PRI_NAT_SEQ_ADJUST, | |
308 | }; | |
309 | ||
310 | ||
1da177e4 LT |
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 | } | |
e281e3ac PM |
339 | ret = nf_register_hook(&ip_nat_adjust_in_ops); |
340 | if (ret < 0) { | |
341 | printk("ip_nat_init: can't register adjust in hook.\n"); | |
342 | goto cleanup_outops; | |
343 | } | |
344 | ret = nf_register_hook(&ip_nat_adjust_out_ops); | |
345 | if (ret < 0) { | |
346 | printk("ip_nat_init: can't register adjust out hook.\n"); | |
347 | goto cleanup_adjustin_ops; | |
348 | } | |
1da177e4 LT |
349 | ret = nf_register_hook(&ip_nat_local_out_ops); |
350 | if (ret < 0) { | |
351 | printk("ip_nat_init: can't register local out hook.\n"); | |
e281e3ac | 352 | goto cleanup_adjustout_ops;; |
1da177e4 LT |
353 | } |
354 | ret = nf_register_hook(&ip_nat_local_in_ops); | |
355 | if (ret < 0) { | |
356 | printk("ip_nat_init: can't register local in hook.\n"); | |
357 | goto cleanup_localoutops; | |
358 | } | |
359 | return ret; | |
360 | ||
361 | cleanup: | |
362 | nf_unregister_hook(&ip_nat_local_in_ops); | |
363 | cleanup_localoutops: | |
364 | nf_unregister_hook(&ip_nat_local_out_ops); | |
e281e3ac PM |
365 | cleanup_adjustout_ops: |
366 | nf_unregister_hook(&ip_nat_adjust_out_ops); | |
367 | cleanup_adjustin_ops: | |
368 | nf_unregister_hook(&ip_nat_adjust_in_ops); | |
1da177e4 LT |
369 | cleanup_outops: |
370 | nf_unregister_hook(&ip_nat_out_ops); | |
371 | cleanup_inops: | |
372 | nf_unregister_hook(&ip_nat_in_ops); | |
373 | cleanup_nat: | |
374 | ip_nat_cleanup(); | |
375 | cleanup_rule_init: | |
376 | ip_nat_rule_cleanup(); | |
377 | cleanup_nothing: | |
1da177e4 LT |
378 | return ret; |
379 | } | |
380 | ||
381 | static int __init init(void) | |
382 | { | |
383 | return init_or_cleanup(1); | |
384 | } | |
385 | ||
386 | static void __exit fini(void) | |
387 | { | |
388 | init_or_cleanup(0); | |
389 | } | |
390 | ||
391 | module_init(init); | |
392 | module_exit(fini); | |
393 | ||
394 | EXPORT_SYMBOL(ip_nat_setup_info); | |
395 | EXPORT_SYMBOL(ip_nat_protocol_register); | |
396 | EXPORT_SYMBOL(ip_nat_protocol_unregister); | |
397 | EXPORT_SYMBOL(ip_nat_cheat_check); | |
398 | EXPORT_SYMBOL(ip_nat_mangle_tcp_packet); | |
399 | EXPORT_SYMBOL(ip_nat_mangle_udp_packet); | |
400 | EXPORT_SYMBOL(ip_nat_used_tuple); | |
401 | EXPORT_SYMBOL(ip_nat_follow_master); | |
402 | MODULE_LICENSE("GPL"); |