diff options
author | Patrick McHardy <kaber@trash.net> | 2012-08-26 13:14:12 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2012-08-29 21:00:17 -0400 |
commit | 58a317f1061c894d2344c0b6a18ab4a64b69b815 (patch) | |
tree | 0e8c4dd2a74cff86cedca36de278e3eb42f939bd | |
parent | 2cf545e835aae92173ef0b1f4af385e9c40f21e8 (diff) |
netfilter: ipv6: add IPv6 NAT support
Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r-- | include/linux/netfilter/nfnetlink_conntrack.h | 2 | ||||
-rw-r--r-- | include/net/netfilter/nf_nat_l3proto.h | 5 | ||||
-rw-r--r-- | include/net/netfilter/nf_nat_l4proto.h | 1 | ||||
-rw-r--r-- | include/net/netns/ipv6.h | 1 | ||||
-rw-r--r-- | net/core/secure_seq.c | 1 | ||||
-rw-r--r-- | net/ipv6/netfilter/Kconfig | 12 | ||||
-rw-r--r-- | net/ipv6/netfilter/Makefile | 4 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6table_nat.c | 321 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 37 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | 287 | ||||
-rw-r--r-- | net/ipv6/netfilter/nf_nat_proto_icmpv6.c | 90 | ||||
-rw-r--r-- | net/netfilter/nf_nat_core.c | 2 | ||||
-rw-r--r-- | net/netfilter/xt_nat.c | 3 |
13 files changed, 764 insertions, 2 deletions
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h index 68920eab287c..43bfe3e1685b 100644 --- a/include/linux/netfilter/nfnetlink_conntrack.h +++ b/include/linux/netfilter/nfnetlink_conntrack.h | |||
@@ -147,6 +147,8 @@ enum ctattr_nat { | |||
147 | CTA_NAT_V4_MAXIP, | 147 | CTA_NAT_V4_MAXIP, |
148 | #define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP | 148 | #define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP |
149 | CTA_NAT_PROTO, | 149 | CTA_NAT_PROTO, |
150 | CTA_NAT_V6_MINIP, | ||
151 | CTA_NAT_V6_MAXIP, | ||
150 | __CTA_NAT_MAX | 152 | __CTA_NAT_MAX |
151 | }; | 153 | }; |
152 | #define CTA_NAT_MAX (__CTA_NAT_MAX - 1) | 154 | #define CTA_NAT_MAX (__CTA_NAT_MAX - 1) |
diff --git a/include/net/netfilter/nf_nat_l3proto.h b/include/net/netfilter/nf_nat_l3proto.h index beed96961fa7..bd3b97e02c82 100644 --- a/include/net/netfilter/nf_nat_l3proto.h +++ b/include/net/netfilter/nf_nat_l3proto.h | |||
@@ -43,5 +43,10 @@ extern int nf_nat_icmp_reply_translation(struct sk_buff *skb, | |||
43 | struct nf_conn *ct, | 43 | struct nf_conn *ct, |
44 | enum ip_conntrack_info ctinfo, | 44 | enum ip_conntrack_info ctinfo, |
45 | unsigned int hooknum); | 45 | unsigned int hooknum); |
46 | extern int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, | ||
47 | struct nf_conn *ct, | ||
48 | enum ip_conntrack_info ctinfo, | ||
49 | unsigned int hooknum, | ||
50 | unsigned int hdrlen); | ||
46 | 51 | ||
47 | #endif /* _NF_NAT_L3PROTO_H */ | 52 | #endif /* _NF_NAT_L3PROTO_H */ |
diff --git a/include/net/netfilter/nf_nat_l4proto.h b/include/net/netfilter/nf_nat_l4proto.h index 1f0a4f018fcf..24feb68d1bcc 100644 --- a/include/net/netfilter/nf_nat_l4proto.h +++ b/include/net/netfilter/nf_nat_l4proto.h | |||
@@ -51,6 +51,7 @@ extern const struct nf_nat_l4proto *__nf_nat_l4proto_find(u8 l3proto, u8 l4proto | |||
51 | extern const struct nf_nat_l4proto nf_nat_l4proto_tcp; | 51 | extern const struct nf_nat_l4proto nf_nat_l4proto_tcp; |
52 | extern const struct nf_nat_l4proto nf_nat_l4proto_udp; | 52 | extern const struct nf_nat_l4proto nf_nat_l4proto_udp; |
53 | extern const struct nf_nat_l4proto nf_nat_l4proto_icmp; | 53 | extern const struct nf_nat_l4proto nf_nat_l4proto_icmp; |
54 | extern const struct nf_nat_l4proto nf_nat_l4proto_icmpv6; | ||
54 | extern const struct nf_nat_l4proto nf_nat_l4proto_unknown; | 55 | extern const struct nf_nat_l4proto nf_nat_l4proto_unknown; |
55 | 56 | ||
56 | extern bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, | 57 | extern bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, |
diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index df0a5456a3fd..0318104a9458 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h | |||
@@ -42,6 +42,7 @@ struct netns_ipv6 { | |||
42 | #ifdef CONFIG_SECURITY | 42 | #ifdef CONFIG_SECURITY |
43 | struct xt_table *ip6table_security; | 43 | struct xt_table *ip6table_security; |
44 | #endif | 44 | #endif |
45 | struct xt_table *ip6table_nat; | ||
45 | #endif | 46 | #endif |
46 | struct rt6_info *ip6_null_entry; | 47 | struct rt6_info *ip6_null_entry; |
47 | struct rt6_statistics *rt6_stats; | 48 | struct rt6_statistics *rt6_stats; |
diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index 99b2596531bb..e61a8bb7fce7 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c | |||
@@ -76,6 +76,7 @@ u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, | |||
76 | 76 | ||
77 | return hash[0]; | 77 | return hash[0]; |
78 | } | 78 | } |
79 | EXPORT_SYMBOL(secure_ipv6_port_ephemeral); | ||
79 | #endif | 80 | #endif |
80 | 81 | ||
81 | #ifdef CONFIG_INET | 82 | #ifdef CONFIG_INET |
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 10135342799e..b27e0ad4b738 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig | |||
@@ -25,6 +25,18 @@ config NF_CONNTRACK_IPV6 | |||
25 | 25 | ||
26 | To compile it as a module, choose M here. If unsure, say N. | 26 | To compile it as a module, choose M here. If unsure, say N. |
27 | 27 | ||
28 | config NF_NAT_IPV6 | ||
29 | tristate "IPv6 NAT" | ||
30 | depends on NF_CONNTRACK_IPV6 | ||
31 | depends on NETFILTER_ADVANCED | ||
32 | select NF_NAT | ||
33 | help | ||
34 | The IPv6 NAT option allows masquerading, port forwarding and other | ||
35 | forms of full Network Address Port Translation. It is controlled by | ||
36 | the `nat' table in ip6tables, see the man page for ip6tables(8). | ||
37 | |||
38 | To compile it as a module, choose M here. If unsure, say N. | ||
39 | |||
28 | config IP6_NF_IPTABLES | 40 | config IP6_NF_IPTABLES |
29 | tristate "IP6 tables support (required for filtering)" | 41 | tristate "IP6 tables support (required for filtering)" |
30 | depends on INET && IPV6 | 42 | depends on INET && IPV6 |
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 534d3f216f7b..76779376da4c 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile | |||
@@ -8,6 +8,7 @@ obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o | |||
8 | obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o | 8 | obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o |
9 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o | 9 | obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o |
10 | obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o | 10 | obj-$(CONFIG_IP6_NF_SECURITY) += ip6table_security.o |
11 | obj-$(CONFIG_NF_NAT_IPV6) += ip6table_nat.o | ||
11 | 12 | ||
12 | # objects for l3 independent conntrack | 13 | # objects for l3 independent conntrack |
13 | nf_conntrack_ipv6-y := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o | 14 | nf_conntrack_ipv6-y := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o |
@@ -15,6 +16,9 @@ nf_conntrack_ipv6-y := nf_conntrack_l3proto_ipv6.o nf_conntrack_proto_icmpv6.o | |||
15 | # l3 independent conntrack | 16 | # l3 independent conntrack |
16 | obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o nf_defrag_ipv6.o | 17 | obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o nf_defrag_ipv6.o |
17 | 18 | ||
19 | nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o nf_nat_proto_icmpv6.o | ||
20 | obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o | ||
21 | |||
18 | # defrag | 22 | # defrag |
19 | nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o | 23 | nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o |
20 | obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o | 24 | obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o |
diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c new file mode 100644 index 000000000000..e418bd6350a4 --- /dev/null +++ b/net/ipv6/netfilter/ip6table_nat.c | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Patrick McHardy <kaber@trash.net> | ||
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 | * Based on Rusty Russell's IPv4 NAT code. Development of IPv6 NAT | ||
9 | * funded by Astaro. | ||
10 | */ | ||
11 | |||
12 | #include <linux/module.h> | ||
13 | #include <linux/netfilter.h> | ||
14 | #include <linux/netfilter_ipv6.h> | ||
15 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
16 | #include <linux/ipv6.h> | ||
17 | #include <net/ipv6.h> | ||
18 | |||
19 | #include <net/netfilter/nf_nat.h> | ||
20 | #include <net/netfilter/nf_nat_core.h> | ||
21 | #include <net/netfilter/nf_nat_l3proto.h> | ||
22 | |||
23 | static const struct xt_table nf_nat_ipv6_table = { | ||
24 | .name = "nat", | ||
25 | .valid_hooks = (1 << NF_INET_PRE_ROUTING) | | ||
26 | (1 << NF_INET_POST_ROUTING) | | ||
27 | (1 << NF_INET_LOCAL_OUT) | | ||
28 | (1 << NF_INET_LOCAL_IN), | ||
29 | .me = THIS_MODULE, | ||
30 | .af = NFPROTO_IPV6, | ||
31 | }; | ||
32 | |||
33 | static unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) | ||
34 | { | ||
35 | /* Force range to this IP; let proto decide mapping for | ||
36 | * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). | ||
37 | */ | ||
38 | struct nf_nat_range range; | ||
39 | |||
40 | range.flags = 0; | ||
41 | pr_debug("Allocating NULL binding for %p (%pI6)\n", ct, | ||
42 | HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ? | ||
43 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6 : | ||
44 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6); | ||
45 | |||
46 | return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); | ||
47 | } | ||
48 | |||
49 | static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum, | ||
50 | const struct net_device *in, | ||
51 | const struct net_device *out, | ||
52 | struct nf_conn *ct) | ||
53 | { | ||
54 | struct net *net = nf_ct_net(ct); | ||
55 | unsigned int ret; | ||
56 | |||
57 | ret = ip6t_do_table(skb, hooknum, in, out, net->ipv6.ip6table_nat); | ||
58 | if (ret == NF_ACCEPT) { | ||
59 | if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum))) | ||
60 | ret = alloc_null_binding(ct, hooknum); | ||
61 | } | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | static unsigned int | ||
66 | nf_nat_ipv6_fn(unsigned int hooknum, | ||
67 | struct sk_buff *skb, | ||
68 | const struct net_device *in, | ||
69 | const struct net_device *out, | ||
70 | int (*okfn)(struct sk_buff *)) | ||
71 | { | ||
72 | struct nf_conn *ct; | ||
73 | enum ip_conntrack_info ctinfo; | ||
74 | struct nf_conn_nat *nat; | ||
75 | enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); | ||
76 | __be16 frag_off; | ||
77 | int hdrlen; | ||
78 | u8 nexthdr; | ||
79 | |||
80 | ct = nf_ct_get(skb, &ctinfo); | ||
81 | /* Can't track? It's not due to stress, or conntrack would | ||
82 | * have dropped it. Hence it's the user's responsibilty to | ||
83 | * packet filter it out, or implement conntrack/NAT for that | ||
84 | * protocol. 8) --RR | ||
85 | */ | ||
86 | if (!ct) | ||
87 | return NF_ACCEPT; | ||
88 | |||
89 | /* Don't try to NAT if this packet is not conntracked */ | ||
90 | if (nf_ct_is_untracked(ct)) | ||
91 | return NF_ACCEPT; | ||
92 | |||
93 | nat = nfct_nat(ct); | ||
94 | if (!nat) { | ||
95 | /* NAT module was loaded late. */ | ||
96 | if (nf_ct_is_confirmed(ct)) | ||
97 | return NF_ACCEPT; | ||
98 | nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); | ||
99 | if (nat == NULL) { | ||
100 | pr_debug("failed to add NAT extension\n"); | ||
101 | return NF_ACCEPT; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | switch (ctinfo) { | ||
106 | case IP_CT_RELATED: | ||
107 | case IP_CT_RELATED_REPLY: | ||
108 | nexthdr = ipv6_hdr(skb)->nexthdr; | ||
109 | hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), | ||
110 | &nexthdr, &frag_off); | ||
111 | |||
112 | if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { | ||
113 | if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo, | ||
114 | hooknum, hdrlen)) | ||
115 | return NF_DROP; | ||
116 | else | ||
117 | return NF_ACCEPT; | ||
118 | } | ||
119 | /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */ | ||
120 | case IP_CT_NEW: | ||
121 | /* Seen it before? This can happen for loopback, retrans, | ||
122 | * or local packets. | ||
123 | */ | ||
124 | if (!nf_nat_initialized(ct, maniptype)) { | ||
125 | unsigned int ret; | ||
126 | |||
127 | ret = nf_nat_rule_find(skb, hooknum, in, out, ct); | ||
128 | if (ret != NF_ACCEPT) | ||
129 | return ret; | ||
130 | } else | ||
131 | pr_debug("Already setup manip %s for ct %p\n", | ||
132 | maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", | ||
133 | ct); | ||
134 | break; | ||
135 | |||
136 | default: | ||
137 | /* ESTABLISHED */ | ||
138 | NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || | ||
139 | ctinfo == IP_CT_ESTABLISHED_REPLY); | ||
140 | } | ||
141 | |||
142 | return nf_nat_packet(ct, ctinfo, hooknum, skb); | ||
143 | } | ||
144 | |||
145 | static unsigned int | ||
146 | nf_nat_ipv6_in(unsigned int hooknum, | ||
147 | struct sk_buff *skb, | ||
148 | const struct net_device *in, | ||
149 | const struct net_device *out, | ||
150 | int (*okfn)(struct sk_buff *)) | ||
151 | { | ||
152 | unsigned int ret; | ||
153 | struct in6_addr daddr = ipv6_hdr(skb)->daddr; | ||
154 | |||
155 | ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn); | ||
156 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
157 | ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr)) | ||
158 | skb_dst_drop(skb); | ||
159 | |||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | static unsigned int | ||
164 | nf_nat_ipv6_out(unsigned int hooknum, | ||
165 | struct sk_buff *skb, | ||
166 | const struct net_device *in, | ||
167 | const struct net_device *out, | ||
168 | int (*okfn)(struct sk_buff *)) | ||
169 | { | ||
170 | #ifdef CONFIG_XFRM | ||
171 | const struct nf_conn *ct; | ||
172 | enum ip_conntrack_info ctinfo; | ||
173 | #endif | ||
174 | unsigned int ret; | ||
175 | |||
176 | /* root is playing with raw sockets. */ | ||
177 | if (skb->len < sizeof(struct ipv6hdr)) | ||
178 | return NF_ACCEPT; | ||
179 | |||
180 | ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn); | ||
181 | #ifdef CONFIG_XFRM | ||
182 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
183 | !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && | ||
184 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
185 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
186 | |||
187 | if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, | ||
188 | &ct->tuplehash[!dir].tuple.dst.u3) || | ||
189 | (ct->tuplehash[dir].tuple.src.u.all != | ||
190 | ct->tuplehash[!dir].tuple.dst.u.all)) | ||
191 | if (nf_xfrm_me_harder(skb, AF_INET6) < 0) | ||
192 | ret = NF_DROP; | ||
193 | } | ||
194 | #endif | ||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | static unsigned int | ||
199 | nf_nat_ipv6_local_fn(unsigned int hooknum, | ||
200 | struct sk_buff *skb, | ||
201 | const struct net_device *in, | ||
202 | const struct net_device *out, | ||
203 | int (*okfn)(struct sk_buff *)) | ||
204 | { | ||
205 | const struct nf_conn *ct; | ||
206 | enum ip_conntrack_info ctinfo; | ||
207 | unsigned int ret; | ||
208 | |||
209 | /* root is playing with raw sockets. */ | ||
210 | if (skb->len < sizeof(struct ipv6hdr)) | ||
211 | return NF_ACCEPT; | ||
212 | |||
213 | ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn); | ||
214 | if (ret != NF_DROP && ret != NF_STOLEN && | ||
215 | (ct = nf_ct_get(skb, &ctinfo)) != NULL) { | ||
216 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
217 | |||
218 | if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, | ||
219 | &ct->tuplehash[!dir].tuple.src.u3)) { | ||
220 | if (ip6_route_me_harder(skb)) | ||
221 | ret = NF_DROP; | ||
222 | } | ||
223 | #ifdef CONFIG_XFRM | ||
224 | else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && | ||
225 | ct->tuplehash[dir].tuple.dst.u.all != | ||
226 | ct->tuplehash[!dir].tuple.src.u.all) | ||
227 | if (nf_xfrm_me_harder(skb, AF_INET6)) | ||
228 | ret = NF_DROP; | ||
229 | #endif | ||
230 | } | ||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = { | ||
235 | /* Before packet filtering, change destination */ | ||
236 | { | ||
237 | .hook = nf_nat_ipv6_in, | ||
238 | .owner = THIS_MODULE, | ||
239 | .pf = NFPROTO_IPV6, | ||
240 | .hooknum = NF_INET_PRE_ROUTING, | ||
241 | .priority = NF_IP6_PRI_NAT_DST, | ||
242 | }, | ||
243 | /* After packet filtering, change source */ | ||
244 | { | ||
245 | .hook = nf_nat_ipv6_out, | ||
246 | .owner = THIS_MODULE, | ||
247 | .pf = NFPROTO_IPV6, | ||
248 | .hooknum = NF_INET_POST_ROUTING, | ||
249 | .priority = NF_IP6_PRI_NAT_SRC, | ||
250 | }, | ||
251 | /* Before packet filtering, change destination */ | ||
252 | { | ||
253 | .hook = nf_nat_ipv6_local_fn, | ||
254 | .owner = THIS_MODULE, | ||
255 | .pf = NFPROTO_IPV6, | ||
256 | .hooknum = NF_INET_LOCAL_OUT, | ||
257 | .priority = NF_IP6_PRI_NAT_DST, | ||
258 | }, | ||
259 | /* After packet filtering, change source */ | ||
260 | { | ||
261 | .hook = nf_nat_ipv6_fn, | ||
262 | .owner = THIS_MODULE, | ||
263 | .pf = NFPROTO_IPV6, | ||
264 | .hooknum = NF_INET_LOCAL_IN, | ||
265 | .priority = NF_IP6_PRI_NAT_SRC, | ||
266 | }, | ||
267 | }; | ||
268 | |||
269 | static int __net_init ip6table_nat_net_init(struct net *net) | ||
270 | { | ||
271 | struct ip6t_replace *repl; | ||
272 | |||
273 | repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table); | ||
274 | if (repl == NULL) | ||
275 | return -ENOMEM; | ||
276 | net->ipv6.ip6table_nat = ip6t_register_table(net, &nf_nat_ipv6_table, repl); | ||
277 | kfree(repl); | ||
278 | if (IS_ERR(net->ipv6.ip6table_nat)) | ||
279 | return PTR_ERR(net->ipv6.ip6table_nat); | ||
280 | return 0; | ||
281 | } | ||
282 | |||
283 | static void __net_exit ip6table_nat_net_exit(struct net *net) | ||
284 | { | ||
285 | ip6t_unregister_table(net, net->ipv6.ip6table_nat); | ||
286 | } | ||
287 | |||
288 | static struct pernet_operations ip6table_nat_net_ops = { | ||
289 | .init = ip6table_nat_net_init, | ||
290 | .exit = ip6table_nat_net_exit, | ||
291 | }; | ||
292 | |||
293 | static int __init ip6table_nat_init(void) | ||
294 | { | ||
295 | int err; | ||
296 | |||
297 | err = register_pernet_subsys(&ip6table_nat_net_ops); | ||
298 | if (err < 0) | ||
299 | goto err1; | ||
300 | |||
301 | err = nf_register_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops)); | ||
302 | if (err < 0) | ||
303 | goto err2; | ||
304 | return 0; | ||
305 | |||
306 | err2: | ||
307 | unregister_pernet_subsys(&ip6table_nat_net_ops); | ||
308 | err1: | ||
309 | return err; | ||
310 | } | ||
311 | |||
312 | static void __exit ip6table_nat_exit(void) | ||
313 | { | ||
314 | nf_unregister_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops)); | ||
315 | unregister_pernet_subsys(&ip6table_nat_net_ops); | ||
316 | } | ||
317 | |||
318 | module_init(ip6table_nat_init); | ||
319 | module_exit(ip6table_nat_exit); | ||
320 | |||
321 | MODULE_LICENSE("GPL"); | ||
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index dcf6010f68d9..8860d23e61cf 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <net/netfilter/nf_conntrack_core.h> | 28 | #include <net/netfilter/nf_conntrack_core.h> |
29 | #include <net/netfilter/nf_conntrack_zones.h> | 29 | #include <net/netfilter/nf_conntrack_zones.h> |
30 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> | 30 | #include <net/netfilter/ipv6/nf_conntrack_ipv6.h> |
31 | #include <net/netfilter/nf_nat_helper.h> | ||
31 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> | 32 | #include <net/netfilter/ipv6/nf_defrag_ipv6.h> |
32 | #include <net/netfilter/nf_log.h> | 33 | #include <net/netfilter/nf_log.h> |
33 | 34 | ||
@@ -142,6 +143,36 @@ static unsigned int ipv6_confirm(unsigned int hooknum, | |||
142 | const struct net_device *out, | 143 | const struct net_device *out, |
143 | int (*okfn)(struct sk_buff *)) | 144 | int (*okfn)(struct sk_buff *)) |
144 | { | 145 | { |
146 | struct nf_conn *ct; | ||
147 | enum ip_conntrack_info ctinfo; | ||
148 | unsigned char pnum = ipv6_hdr(skb)->nexthdr; | ||
149 | int protoff; | ||
150 | __be16 frag_off; | ||
151 | |||
152 | ct = nf_ct_get(skb, &ctinfo); | ||
153 | if (!ct || ctinfo == IP_CT_RELATED_REPLY) | ||
154 | goto out; | ||
155 | |||
156 | protoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &pnum, | ||
157 | &frag_off); | ||
158 | if (protoff < 0 || (frag_off & htons(~0x7)) != 0) { | ||
159 | pr_debug("proto header not found\n"); | ||
160 | goto out; | ||
161 | } | ||
162 | |||
163 | /* adjust seqs for loopback traffic only in outgoing direction */ | ||
164 | if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) && | ||
165 | !nf_is_loopback_packet(skb)) { | ||
166 | typeof(nf_nat_seq_adjust_hook) seq_adjust; | ||
167 | |||
168 | seq_adjust = rcu_dereference(nf_nat_seq_adjust_hook); | ||
169 | if (!seq_adjust || | ||
170 | !seq_adjust(skb, ct, ctinfo, protoff)) { | ||
171 | NF_CT_STAT_INC_ATOMIC(nf_ct_net(ct), drop); | ||
172 | return NF_DROP; | ||
173 | } | ||
174 | } | ||
175 | out: | ||
145 | /* We've seen it coming out the other side: confirm it */ | 176 | /* We've seen it coming out the other side: confirm it */ |
146 | return nf_conntrack_confirm(skb); | 177 | return nf_conntrack_confirm(skb); |
147 | } | 178 | } |
@@ -170,12 +201,14 @@ static unsigned int __ipv6_conntrack_in(struct net *net, | |||
170 | } | 201 | } |
171 | 202 | ||
172 | /* Conntrack helpers need the entire reassembled packet in the | 203 | /* Conntrack helpers need the entire reassembled packet in the |
173 | * POST_ROUTING hook. | 204 | * POST_ROUTING hook. In case of unconfirmed connections NAT |
205 | * might reassign a helper, so the entire packet is also | ||
206 | * required. | ||
174 | */ | 207 | */ |
175 | ct = nf_ct_get(reasm, &ctinfo); | 208 | ct = nf_ct_get(reasm, &ctinfo); |
176 | if (ct != NULL && !nf_ct_is_untracked(ct)) { | 209 | if (ct != NULL && !nf_ct_is_untracked(ct)) { |
177 | help = nfct_help(ct); | 210 | help = nfct_help(ct); |
178 | if (help && help->helper) { | 211 | if ((help && help->helper) || !nf_ct_is_confirmed(ct)) { |
179 | nf_conntrack_get_reasm(skb); | 212 | nf_conntrack_get_reasm(skb); |
180 | NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm, | 213 | NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm, |
181 | (struct net_device *)in, | 214 | (struct net_device *)in, |
diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c new file mode 100644 index 000000000000..81a2d1c3da8e --- /dev/null +++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | |||
@@ -0,0 +1,287 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Patrick McHardy <kaber@trash.net> | ||
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 | * Development of IPv6 NAT funded by Astaro. | ||
9 | */ | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/skbuff.h> | ||
13 | #include <linux/ipv6.h> | ||
14 | #include <linux/netfilter.h> | ||
15 | #include <linux/netfilter_ipv6.h> | ||
16 | #include <net/secure_seq.h> | ||
17 | #include <net/checksum.h> | ||
18 | #include <net/ip6_route.h> | ||
19 | #include <net/ipv6.h> | ||
20 | |||
21 | #include <net/netfilter/nf_conntrack_core.h> | ||
22 | #include <net/netfilter/nf_conntrack.h> | ||
23 | #include <net/netfilter/nf_nat_core.h> | ||
24 | #include <net/netfilter/nf_nat_l3proto.h> | ||
25 | #include <net/netfilter/nf_nat_l4proto.h> | ||
26 | |||
27 | static const struct nf_nat_l3proto nf_nat_l3proto_ipv6; | ||
28 | |||
29 | #ifdef CONFIG_XFRM | ||
30 | static void nf_nat_ipv6_decode_session(struct sk_buff *skb, | ||
31 | const struct nf_conn *ct, | ||
32 | enum ip_conntrack_dir dir, | ||
33 | unsigned long statusbit, | ||
34 | struct flowi *fl) | ||
35 | { | ||
36 | const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple; | ||
37 | struct flowi6 *fl6 = &fl->u.ip6; | ||
38 | |||
39 | if (ct->status & statusbit) { | ||
40 | fl6->daddr = t->dst.u3.in6; | ||
41 | if (t->dst.protonum == IPPROTO_TCP || | ||
42 | t->dst.protonum == IPPROTO_UDP || | ||
43 | t->dst.protonum == IPPROTO_UDPLITE || | ||
44 | t->dst.protonum == IPPROTO_DCCP || | ||
45 | t->dst.protonum == IPPROTO_SCTP) | ||
46 | fl6->fl6_dport = t->dst.u.all; | ||
47 | } | ||
48 | |||
49 | statusbit ^= IPS_NAT_MASK; | ||
50 | |||
51 | if (ct->status & statusbit) { | ||
52 | fl6->saddr = t->src.u3.in6; | ||
53 | if (t->dst.protonum == IPPROTO_TCP || | ||
54 | t->dst.protonum == IPPROTO_UDP || | ||
55 | t->dst.protonum == IPPROTO_UDPLITE || | ||
56 | t->dst.protonum == IPPROTO_DCCP || | ||
57 | t->dst.protonum == IPPROTO_SCTP) | ||
58 | fl6->fl6_sport = t->src.u.all; | ||
59 | } | ||
60 | } | ||
61 | #endif | ||
62 | |||
63 | static bool nf_nat_ipv6_in_range(const struct nf_conntrack_tuple *t, | ||
64 | const struct nf_nat_range *range) | ||
65 | { | ||
66 | return ipv6_addr_cmp(&t->src.u3.in6, &range->min_addr.in6) >= 0 && | ||
67 | ipv6_addr_cmp(&t->src.u3.in6, &range->max_addr.in6) <= 0; | ||
68 | } | ||
69 | |||
70 | static u32 nf_nat_ipv6_secure_port(const struct nf_conntrack_tuple *t, | ||
71 | __be16 dport) | ||
72 | { | ||
73 | return secure_ipv6_port_ephemeral(t->src.u3.ip6, t->dst.u3.ip6, dport); | ||
74 | } | ||
75 | |||
76 | static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb, | ||
77 | unsigned int iphdroff, | ||
78 | const struct nf_nat_l4proto *l4proto, | ||
79 | const struct nf_conntrack_tuple *target, | ||
80 | enum nf_nat_manip_type maniptype) | ||
81 | { | ||
82 | struct ipv6hdr *ipv6h; | ||
83 | __be16 frag_off; | ||
84 | int hdroff; | ||
85 | u8 nexthdr; | ||
86 | |||
87 | if (!skb_make_writable(skb, iphdroff + sizeof(*ipv6h))) | ||
88 | return false; | ||
89 | |||
90 | ipv6h = (void *)skb->data + iphdroff; | ||
91 | nexthdr = ipv6h->nexthdr; | ||
92 | hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h), | ||
93 | &nexthdr, &frag_off); | ||
94 | if (hdroff < 0) | ||
95 | goto manip_addr; | ||
96 | |||
97 | if ((frag_off & htons(~0x7)) == 0 && | ||
98 | !l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv6, iphdroff, hdroff, | ||
99 | target, maniptype)) | ||
100 | return false; | ||
101 | manip_addr: | ||
102 | if (maniptype == NF_NAT_MANIP_SRC) | ||
103 | ipv6h->saddr = target->src.u3.in6; | ||
104 | else | ||
105 | ipv6h->daddr = target->dst.u3.in6; | ||
106 | |||
107 | return true; | ||
108 | } | ||
109 | |||
110 | static void nf_nat_ipv6_csum_update(struct sk_buff *skb, | ||
111 | unsigned int iphdroff, __sum16 *check, | ||
112 | const struct nf_conntrack_tuple *t, | ||
113 | enum nf_nat_manip_type maniptype) | ||
114 | { | ||
115 | const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff); | ||
116 | const struct in6_addr *oldip, *newip; | ||
117 | |||
118 | if (maniptype == NF_NAT_MANIP_SRC) { | ||
119 | oldip = &ipv6h->saddr; | ||
120 | newip = &t->src.u3.in6; | ||
121 | } else { | ||
122 | oldip = &ipv6h->daddr; | ||
123 | newip = &t->dst.u3.in6; | ||
124 | } | ||
125 | inet_proto_csum_replace16(check, skb, oldip->s6_addr32, | ||
126 | newip->s6_addr32, 1); | ||
127 | } | ||
128 | |||
129 | static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb, | ||
130 | u8 proto, void *data, __sum16 *check, | ||
131 | int datalen, int oldlen) | ||
132 | { | ||
133 | const struct ipv6hdr *ipv6h = ipv6_hdr(skb); | ||
134 | struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); | ||
135 | |||
136 | if (skb->ip_summed != CHECKSUM_PARTIAL) { | ||
137 | if (!(rt->rt6i_flags & RTF_LOCAL) && | ||
138 | (!skb->dev || skb->dev->features & NETIF_F_V6_CSUM)) { | ||
139 | skb->ip_summed = CHECKSUM_PARTIAL; | ||
140 | skb->csum_start = skb_headroom(skb) + | ||
141 | skb_network_offset(skb) + | ||
142 | (data - (void *)skb->data); | ||
143 | skb->csum_offset = (void *)check - data; | ||
144 | *check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, | ||
145 | datalen, proto, 0); | ||
146 | } else { | ||
147 | *check = 0; | ||
148 | *check = csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, | ||
149 | datalen, proto, | ||
150 | csum_partial(data, datalen, | ||
151 | 0)); | ||
152 | if (proto == IPPROTO_UDP && !*check) | ||
153 | *check = CSUM_MANGLED_0; | ||
154 | } | ||
155 | } else | ||
156 | inet_proto_csum_replace2(check, skb, | ||
157 | htons(oldlen), htons(datalen), 1); | ||
158 | } | ||
159 | |||
160 | static int nf_nat_ipv6_nlattr_to_range(struct nlattr *tb[], | ||
161 | struct nf_nat_range *range) | ||
162 | { | ||
163 | if (tb[CTA_NAT_V6_MINIP]) { | ||
164 | nla_memcpy(&range->min_addr.ip6, tb[CTA_NAT_V6_MINIP], | ||
165 | sizeof(struct in6_addr)); | ||
166 | range->flags |= NF_NAT_RANGE_MAP_IPS; | ||
167 | } | ||
168 | |||
169 | if (tb[CTA_NAT_V6_MAXIP]) | ||
170 | nla_memcpy(&range->max_addr.ip6, tb[CTA_NAT_V6_MAXIP], | ||
171 | sizeof(struct in6_addr)); | ||
172 | else | ||
173 | range->max_addr = range->min_addr; | ||
174 | |||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static const struct nf_nat_l3proto nf_nat_l3proto_ipv6 = { | ||
179 | .l3proto = NFPROTO_IPV6, | ||
180 | .secure_port = nf_nat_ipv6_secure_port, | ||
181 | .in_range = nf_nat_ipv6_in_range, | ||
182 | .manip_pkt = nf_nat_ipv6_manip_pkt, | ||
183 | .csum_update = nf_nat_ipv6_csum_update, | ||
184 | .csum_recalc = nf_nat_ipv6_csum_recalc, | ||
185 | .nlattr_to_range = nf_nat_ipv6_nlattr_to_range, | ||
186 | #ifdef CONFIG_XFRM | ||
187 | .decode_session = nf_nat_ipv6_decode_session, | ||
188 | #endif | ||
189 | }; | ||
190 | |||
191 | int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, | ||
192 | struct nf_conn *ct, | ||
193 | enum ip_conntrack_info ctinfo, | ||
194 | unsigned int hooknum, | ||
195 | unsigned int hdrlen) | ||
196 | { | ||
197 | struct { | ||
198 | struct icmp6hdr icmp6; | ||
199 | struct ipv6hdr ip6; | ||
200 | } *inside; | ||
201 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
202 | enum nf_nat_manip_type manip = HOOK2MANIP(hooknum); | ||
203 | const struct nf_nat_l4proto *l4proto; | ||
204 | struct nf_conntrack_tuple target; | ||
205 | unsigned long statusbit; | ||
206 | |||
207 | NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY); | ||
208 | |||
209 | if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) | ||
210 | return 0; | ||
211 | if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6)) | ||
212 | return 0; | ||
213 | |||
214 | inside = (void *)skb->data + hdrlen; | ||
215 | if (inside->icmp6.icmp6_type == NDISC_REDIRECT) { | ||
216 | if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK) | ||
217 | return 0; | ||
218 | if (ct->status & IPS_NAT_MASK) | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | if (manip == NF_NAT_MANIP_SRC) | ||
223 | statusbit = IPS_SRC_NAT; | ||
224 | else | ||
225 | statusbit = IPS_DST_NAT; | ||
226 | |||
227 | /* Invert if this is reply direction */ | ||
228 | if (dir == IP_CT_DIR_REPLY) | ||
229 | statusbit ^= IPS_NAT_MASK; | ||
230 | |||
231 | if (!(ct->status & statusbit)) | ||
232 | return 1; | ||
233 | |||
234 | l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, inside->ip6.nexthdr); | ||
235 | if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6), | ||
236 | l4proto, &ct->tuplehash[!dir].tuple, !manip)) | ||
237 | return 0; | ||
238 | |||
239 | if (skb->ip_summed != CHECKSUM_PARTIAL) { | ||
240 | struct ipv6hdr *ipv6h = ipv6_hdr(skb); | ||
241 | inside = (void *)skb->data + hdrlen; | ||
242 | inside->icmp6.icmp6_cksum = 0; | ||
243 | inside->icmp6.icmp6_cksum = | ||
244 | csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, | ||
245 | skb->len - hdrlen, IPPROTO_ICMPV6, | ||
246 | csum_partial(&inside->icmp6, | ||
247 | skb->len - hdrlen, 0)); | ||
248 | } | ||
249 | |||
250 | nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple); | ||
251 | l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, IPPROTO_ICMPV6); | ||
252 | if (!nf_nat_ipv6_manip_pkt(skb, 0, l4proto, &target, manip)) | ||
253 | return 0; | ||
254 | |||
255 | return 1; | ||
256 | } | ||
257 | EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation); | ||
258 | |||
259 | static int __init nf_nat_l3proto_ipv6_init(void) | ||
260 | { | ||
261 | int err; | ||
262 | |||
263 | err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6); | ||
264 | if (err < 0) | ||
265 | goto err1; | ||
266 | err = nf_nat_l3proto_register(&nf_nat_l3proto_ipv6); | ||
267 | if (err < 0) | ||
268 | goto err2; | ||
269 | return err; | ||
270 | |||
271 | err2: | ||
272 | nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6); | ||
273 | err1: | ||
274 | return err; | ||
275 | } | ||
276 | |||
277 | static void __exit nf_nat_l3proto_ipv6_exit(void) | ||
278 | { | ||
279 | nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv6); | ||
280 | nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6); | ||
281 | } | ||
282 | |||
283 | MODULE_LICENSE("GPL"); | ||
284 | MODULE_ALIAS("nf-nat-" __stringify(AF_INET6)); | ||
285 | |||
286 | module_init(nf_nat_l3proto_ipv6_init); | ||
287 | module_exit(nf_nat_l3proto_ipv6_exit); | ||
diff --git a/net/ipv6/netfilter/nf_nat_proto_icmpv6.c b/net/ipv6/netfilter/nf_nat_proto_icmpv6.c new file mode 100644 index 000000000000..5d6da784305b --- /dev/null +++ b/net/ipv6/netfilter/nf_nat_proto_icmpv6.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Patrick Mchardy <kaber@trash.net> | ||
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 | * Based on Rusty Russell's IPv4 ICMP NAT code. Development of IPv6 | ||
9 | * NAT funded by Astaro. | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/icmpv6.h> | ||
15 | |||
16 | #include <linux/netfilter.h> | ||
17 | #include <net/netfilter/nf_nat.h> | ||
18 | #include <net/netfilter/nf_nat_core.h> | ||
19 | #include <net/netfilter/nf_nat_l3proto.h> | ||
20 | #include <net/netfilter/nf_nat_l4proto.h> | ||
21 | |||
22 | static bool | ||
23 | icmpv6_in_range(const struct nf_conntrack_tuple *tuple, | ||
24 | enum nf_nat_manip_type maniptype, | ||
25 | const union nf_conntrack_man_proto *min, | ||
26 | const union nf_conntrack_man_proto *max) | ||
27 | { | ||
28 | return ntohs(tuple->src.u.icmp.id) >= ntohs(min->icmp.id) && | ||
29 | ntohs(tuple->src.u.icmp.id) <= ntohs(max->icmp.id); | ||
30 | } | ||
31 | |||
32 | static void | ||
33 | icmpv6_unique_tuple(const struct nf_nat_l3proto *l3proto, | ||
34 | struct nf_conntrack_tuple *tuple, | ||
35 | const struct nf_nat_range *range, | ||
36 | enum nf_nat_manip_type maniptype, | ||
37 | const struct nf_conn *ct) | ||
38 | { | ||
39 | static u16 id; | ||
40 | unsigned int range_size; | ||
41 | unsigned int i; | ||
42 | |||
43 | range_size = ntohs(range->max_proto.icmp.id) - | ||
44 | ntohs(range->min_proto.icmp.id) + 1; | ||
45 | |||
46 | if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) | ||
47 | range_size = 0xffff; | ||
48 | |||
49 | for (i = 0; ; ++id) { | ||
50 | tuple->src.u.icmp.id = htons(ntohs(range->min_proto.icmp.id) + | ||
51 | (id % range_size)); | ||
52 | if (++i == range_size || !nf_nat_used_tuple(tuple, ct)) | ||
53 | return; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | static bool | ||
58 | icmpv6_manip_pkt(struct sk_buff *skb, | ||
59 | const struct nf_nat_l3proto *l3proto, | ||
60 | unsigned int iphdroff, unsigned int hdroff, | ||
61 | const struct nf_conntrack_tuple *tuple, | ||
62 | enum nf_nat_manip_type maniptype) | ||
63 | { | ||
64 | struct icmp6hdr *hdr; | ||
65 | |||
66 | if (!skb_make_writable(skb, hdroff + sizeof(*hdr))) | ||
67 | return false; | ||
68 | |||
69 | hdr = (struct icmp6hdr *)(skb->data + hdroff); | ||
70 | l3proto->csum_update(skb, iphdroff, &hdr->icmp6_cksum, | ||
71 | tuple, maniptype); | ||
72 | if (hdr->icmp6_code == ICMPV6_ECHO_REQUEST || | ||
73 | hdr->icmp6_code == ICMPV6_ECHO_REPLY) { | ||
74 | inet_proto_csum_replace2(&hdr->icmp6_cksum, skb, | ||
75 | hdr->icmp6_identifier, | ||
76 | tuple->src.u.icmp.id, 0); | ||
77 | hdr->icmp6_identifier = tuple->src.u.icmp.id; | ||
78 | } | ||
79 | return true; | ||
80 | } | ||
81 | |||
82 | const struct nf_nat_l4proto nf_nat_l4proto_icmpv6 = { | ||
83 | .l4proto = IPPROTO_ICMPV6, | ||
84 | .manip_pkt = icmpv6_manip_pkt, | ||
85 | .in_range = icmpv6_in_range, | ||
86 | .unique_tuple = icmpv6_unique_tuple, | ||
87 | #if defined(CONFIG_NF_CT_NETLINK) || defined(CONFIG_NF_CT_NETLINK_MODULE) | ||
88 | .nlattr_to_range = nf_nat_l4proto_nlattr_to_range, | ||
89 | #endif | ||
90 | }; | ||
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index c577b753fb9a..29d445235199 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c | |||
@@ -696,6 +696,8 @@ static int nfnetlink_parse_nat_proto(struct nlattr *attr, | |||
696 | static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { | 696 | static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = { |
697 | [CTA_NAT_V4_MINIP] = { .type = NLA_U32 }, | 697 | [CTA_NAT_V4_MINIP] = { .type = NLA_U32 }, |
698 | [CTA_NAT_V4_MAXIP] = { .type = NLA_U32 }, | 698 | [CTA_NAT_V4_MAXIP] = { .type = NLA_U32 }, |
699 | [CTA_NAT_V6_MINIP] = { .len = sizeof(struct in6_addr) }, | ||
700 | [CTA_NAT_V6_MAXIP] = { .len = sizeof(struct in6_addr) }, | ||
699 | [CTA_NAT_PROTO] = { .type = NLA_NESTED }, | 701 | [CTA_NAT_PROTO] = { .type = NLA_NESTED }, |
700 | }; | 702 | }; |
701 | 703 | ||
diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c index 7521368a6034..81aafa8e4fef 100644 --- a/net/netfilter/xt_nat.c +++ b/net/netfilter/xt_nat.c | |||
@@ -163,5 +163,8 @@ module_init(xt_nat_init); | |||
163 | module_exit(xt_nat_exit); | 163 | module_exit(xt_nat_exit); |
164 | 164 | ||
165 | MODULE_LICENSE("GPL"); | 165 | MODULE_LICENSE("GPL"); |
166 | MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); | ||
166 | MODULE_ALIAS("ipt_SNAT"); | 167 | MODULE_ALIAS("ipt_SNAT"); |
167 | MODULE_ALIAS("ipt_DNAT"); | 168 | MODULE_ALIAS("ipt_DNAT"); |
169 | MODULE_ALIAS("ip6t_SNAT"); | ||
170 | MODULE_ALIAS("ip6t_DNAT"); | ||