Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* (C) 1999-2001 Paul `Rusty' Russell |
2 | * (C) 2002-2004 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 | ||
9 | /* Everything about the rules for NAT. */ | |
10 | #include <linux/types.h> | |
11 | #include <linux/ip.h> | |
12 | #include <linux/netfilter.h> | |
13 | #include <linux/netfilter_ipv4.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/kmod.h> | |
16 | #include <linux/skbuff.h> | |
17 | #include <linux/proc_fs.h> | |
18 | #include <net/checksum.h> | |
19 | #include <net/route.h> | |
20 | #include <linux/bitops.h> | |
21 | ||
e45b1be8 PM |
22 | #define ASSERT_READ_LOCK(x) |
23 | #define ASSERT_WRITE_LOCK(x) | |
1da177e4 LT |
24 | |
25 | #include <linux/netfilter_ipv4/ip_tables.h> | |
26 | #include <linux/netfilter_ipv4/ip_nat.h> | |
27 | #include <linux/netfilter_ipv4/ip_nat_core.h> | |
28 | #include <linux/netfilter_ipv4/ip_nat_rule.h> | |
29 | #include <linux/netfilter_ipv4/listhelp.h> | |
30 | ||
31 | #if 0 | |
32 | #define DEBUGP printk | |
33 | #else | |
34 | #define DEBUGP(format, args...) | |
35 | #endif | |
36 | ||
37 | #define NAT_VALID_HOOKS ((1<<NF_IP_PRE_ROUTING) | (1<<NF_IP_POST_ROUTING) | (1<<NF_IP_LOCAL_OUT)) | |
38 | ||
39 | static struct | |
40 | { | |
41 | struct ipt_replace repl; | |
42 | struct ipt_standard entries[3]; | |
43 | struct ipt_error term; | |
44 | } nat_initial_table __initdata | |
45 | = { { "nat", NAT_VALID_HOOKS, 4, | |
46 | sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error), | |
47 | { [NF_IP_PRE_ROUTING] = 0, | |
48 | [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard), | |
49 | [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 }, | |
50 | { [NF_IP_PRE_ROUTING] = 0, | |
51 | [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard), | |
52 | [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2 }, | |
53 | 0, NULL, { } }, | |
54 | { | |
55 | /* PRE_ROUTING */ | |
56 | { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, | |
57 | 0, | |
58 | sizeof(struct ipt_entry), | |
59 | sizeof(struct ipt_standard), | |
60 | 0, { 0, 0 }, { } }, | |
61 | { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, | |
62 | -NF_ACCEPT - 1 } }, | |
63 | /* POST_ROUTING */ | |
64 | { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, | |
65 | 0, | |
66 | sizeof(struct ipt_entry), | |
67 | sizeof(struct ipt_standard), | |
68 | 0, { 0, 0 }, { } }, | |
69 | { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, | |
70 | -NF_ACCEPT - 1 } }, | |
71 | /* LOCAL_OUT */ | |
72 | { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, | |
73 | 0, | |
74 | sizeof(struct ipt_entry), | |
75 | sizeof(struct ipt_standard), | |
76 | 0, { 0, 0 }, { } }, | |
77 | { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } }, | |
78 | -NF_ACCEPT - 1 } } | |
79 | }, | |
80 | /* ERROR */ | |
81 | { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 }, | |
82 | 0, | |
83 | sizeof(struct ipt_entry), | |
84 | sizeof(struct ipt_error), | |
85 | 0, { 0, 0 }, { } }, | |
86 | { { { { IPT_ALIGN(sizeof(struct ipt_error_target)), IPT_ERROR_TARGET } }, | |
87 | { } }, | |
88 | "ERROR" | |
89 | } | |
90 | } | |
91 | }; | |
92 | ||
93 | static struct ipt_table nat_table = { | |
94 | .name = "nat", | |
95 | .valid_hooks = NAT_VALID_HOOKS, | |
96 | .lock = RW_LOCK_UNLOCKED, | |
97 | .me = THIS_MODULE, | |
2e4e6a17 | 98 | .af = AF_INET, |
1da177e4 LT |
99 | }; |
100 | ||
101 | /* Source NAT */ | |
102 | static unsigned int ipt_snat_target(struct sk_buff **pskb, | |
103 | const struct net_device *in, | |
104 | const struct net_device *out, | |
105 | unsigned int hooknum, | |
c4986734 | 106 | const struct ipt_target *target, |
fe1cb108 | 107 | const void *targinfo) |
1da177e4 LT |
108 | { |
109 | struct ip_conntrack *ct; | |
110 | enum ip_conntrack_info ctinfo; | |
111 | const struct ip_nat_multi_range_compat *mr = targinfo; | |
112 | ||
113 | IP_NF_ASSERT(hooknum == NF_IP_POST_ROUTING); | |
114 | ||
115 | ct = ip_conntrack_get(*pskb, &ctinfo); | |
116 | ||
117 | /* Connection must be valid and new. */ | |
118 | IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED | |
119 | || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)); | |
120 | IP_NF_ASSERT(out); | |
121 | ||
122 | return ip_nat_setup_info(ct, &mr->range[0], hooknum); | |
123 | } | |
124 | ||
125 | /* Before 2.6.11 we did implicit source NAT if required. Warn about change. */ | |
126 | static void warn_if_extra_mangle(u32 dstip, u32 srcip) | |
127 | { | |
128 | static int warned = 0; | |
129 | struct flowi fl = { .nl_u = { .ip4_u = { .daddr = dstip } } }; | |
130 | struct rtable *rt; | |
131 | ||
132 | if (ip_route_output_key(&rt, &fl) != 0) | |
133 | return; | |
134 | ||
135 | if (rt->rt_src != srcip && !warned) { | |
136 | printk("NAT: no longer support implicit source local NAT\n"); | |
137 | printk("NAT: packet src %u.%u.%u.%u -> dst %u.%u.%u.%u\n", | |
138 | NIPQUAD(srcip), NIPQUAD(dstip)); | |
139 | warned = 1; | |
140 | } | |
141 | ip_rt_put(rt); | |
142 | } | |
143 | ||
144 | static unsigned int ipt_dnat_target(struct sk_buff **pskb, | |
145 | const struct net_device *in, | |
146 | const struct net_device *out, | |
147 | unsigned int hooknum, | |
c4986734 | 148 | const struct ipt_target *target, |
fe1cb108 | 149 | const void *targinfo) |
1da177e4 LT |
150 | { |
151 | struct ip_conntrack *ct; | |
152 | enum ip_conntrack_info ctinfo; | |
153 | const struct ip_nat_multi_range_compat *mr = targinfo; | |
154 | ||
155 | IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING | |
156 | || hooknum == NF_IP_LOCAL_OUT); | |
157 | ||
158 | ct = ip_conntrack_get(*pskb, &ctinfo); | |
159 | ||
160 | /* Connection must be valid and new. */ | |
161 | IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); | |
162 | ||
163 | if (hooknum == NF_IP_LOCAL_OUT | |
164 | && mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) | |
165 | warn_if_extra_mangle((*pskb)->nh.iph->daddr, | |
166 | mr->range[0].min_ip); | |
167 | ||
168 | return ip_nat_setup_info(ct, &mr->range[0], hooknum); | |
169 | } | |
170 | ||
171 | static int ipt_snat_checkentry(const char *tablename, | |
2e4e6a17 | 172 | const void *entry, |
c4986734 | 173 | const struct ipt_target *target, |
1da177e4 LT |
174 | void *targinfo, |
175 | unsigned int targinfosize, | |
176 | unsigned int hook_mask) | |
177 | { | |
178 | struct ip_nat_multi_range_compat *mr = targinfo; | |
179 | ||
180 | /* Must be a valid range */ | |
181 | if (mr->rangesize != 1) { | |
182 | printk("SNAT: multiple ranges no longer supported\n"); | |
183 | return 0; | |
184 | } | |
1da177e4 LT |
185 | return 1; |
186 | } | |
187 | ||
188 | static int ipt_dnat_checkentry(const char *tablename, | |
2e4e6a17 | 189 | const void *entry, |
c4986734 | 190 | const struct ipt_target *target, |
1da177e4 LT |
191 | void *targinfo, |
192 | unsigned int targinfosize, | |
193 | unsigned int hook_mask) | |
194 | { | |
195 | struct ip_nat_multi_range_compat *mr = targinfo; | |
196 | ||
197 | /* Must be a valid range */ | |
198 | if (mr->rangesize != 1) { | |
199 | printk("DNAT: multiple ranges no longer supported\n"); | |
200 | return 0; | |
201 | } | |
1da177e4 LT |
202 | return 1; |
203 | } | |
204 | ||
205 | inline unsigned int | |
206 | alloc_null_binding(struct ip_conntrack *conntrack, | |
207 | struct ip_nat_info *info, | |
208 | unsigned int hooknum) | |
209 | { | |
210 | /* Force range to this IP; let proto decide mapping for | |
211 | per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). | |
212 | Use reply in case it's already been mangled (eg local packet). | |
213 | */ | |
214 | u_int32_t ip | |
215 | = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC | |
216 | ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip | |
217 | : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); | |
218 | struct ip_nat_range range | |
219 | = { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } }; | |
220 | ||
221 | DEBUGP("Allocating NULL binding for %p (%u.%u.%u.%u)\n", conntrack, | |
222 | NIPQUAD(ip)); | |
223 | return ip_nat_setup_info(conntrack, &range, hooknum); | |
224 | } | |
225 | ||
03486a4f PM |
226 | unsigned int |
227 | alloc_null_binding_confirmed(struct ip_conntrack *conntrack, | |
228 | struct ip_nat_info *info, | |
229 | unsigned int hooknum) | |
230 | { | |
231 | u_int32_t ip | |
232 | = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC | |
233 | ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip | |
234 | : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); | |
235 | u_int16_t all | |
236 | = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC | |
237 | ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all | |
238 | : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.all); | |
239 | struct ip_nat_range range | |
240 | = { IP_NAT_RANGE_MAP_IPS, ip, ip, { all }, { all } }; | |
241 | ||
242 | DEBUGP("Allocating NULL binding for confirmed %p (%u.%u.%u.%u)\n", | |
243 | conntrack, NIPQUAD(ip)); | |
244 | return ip_nat_setup_info(conntrack, &range, hooknum); | |
245 | } | |
246 | ||
1da177e4 LT |
247 | int ip_nat_rule_find(struct sk_buff **pskb, |
248 | unsigned int hooknum, | |
249 | const struct net_device *in, | |
250 | const struct net_device *out, | |
251 | struct ip_conntrack *ct, | |
252 | struct ip_nat_info *info) | |
253 | { | |
254 | int ret; | |
255 | ||
fe1cb108 | 256 | ret = ipt_do_table(pskb, hooknum, in, out, &nat_table); |
1da177e4 LT |
257 | |
258 | if (ret == NF_ACCEPT) { | |
259 | if (!ip_nat_initialized(ct, HOOK2MANIP(hooknum))) | |
260 | /* NUL mapping */ | |
261 | ret = alloc_null_binding(ct, info, hooknum); | |
262 | } | |
263 | return ret; | |
264 | } | |
265 | ||
266 | static struct ipt_target ipt_snat_reg = { | |
267 | .name = "SNAT", | |
268 | .target = ipt_snat_target, | |
1d5cd909 PM |
269 | .targetsize = sizeof(struct ip_nat_multi_range_compat), |
270 | .table = "nat", | |
271 | .hooks = 1 << NF_IP_POST_ROUTING, | |
1da177e4 LT |
272 | .checkentry = ipt_snat_checkentry, |
273 | }; | |
274 | ||
275 | static struct ipt_target ipt_dnat_reg = { | |
276 | .name = "DNAT", | |
277 | .target = ipt_dnat_target, | |
1d5cd909 PM |
278 | .targetsize = sizeof(struct ip_nat_multi_range_compat), |
279 | .table = "nat", | |
19910d1a | 280 | .hooks = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT), |
1da177e4 LT |
281 | .checkentry = ipt_dnat_checkentry, |
282 | }; | |
283 | ||
284 | int __init ip_nat_rule_init(void) | |
285 | { | |
286 | int ret; | |
287 | ||
288 | ret = ipt_register_table(&nat_table, &nat_initial_table.repl); | |
289 | if (ret != 0) | |
290 | return ret; | |
291 | ret = ipt_register_target(&ipt_snat_reg); | |
292 | if (ret != 0) | |
293 | goto unregister_table; | |
294 | ||
295 | ret = ipt_register_target(&ipt_dnat_reg); | |
296 | if (ret != 0) | |
297 | goto unregister_snat; | |
298 | ||
299 | return ret; | |
300 | ||
301 | unregister_snat: | |
302 | ipt_unregister_target(&ipt_snat_reg); | |
303 | unregister_table: | |
304 | ipt_unregister_table(&nat_table); | |
305 | ||
306 | return ret; | |
307 | } | |
308 | ||
309 | void ip_nat_rule_cleanup(void) | |
310 | { | |
311 | ipt_unregister_target(&ipt_dnat_reg); | |
312 | ipt_unregister_target(&ipt_snat_reg); | |
313 | ipt_unregister_table(&nat_table); | |
314 | } |