2 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
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.
8 * Development of IPv6 NAT funded by Astaro.
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_checksum.h>
19 #include <net/ip6_route.h>
22 #include <net/netfilter/nf_conntrack_core.h>
23 #include <net/netfilter/nf_conntrack.h>
24 #include <net/netfilter/nf_nat_core.h>
25 #include <net/netfilter/nf_nat_l3proto.h>
26 #include <net/netfilter/nf_nat_l4proto.h>
28 static const struct nf_nat_l3proto nf_nat_l3proto_ipv6
;
31 static void nf_nat_ipv6_decode_session(struct sk_buff
*skb
,
32 const struct nf_conn
*ct
,
33 enum ip_conntrack_dir dir
,
34 unsigned long statusbit
,
37 const struct nf_conntrack_tuple
*t
= &ct
->tuplehash
[dir
].tuple
;
38 struct flowi6
*fl6
= &fl
->u
.ip6
;
40 if (ct
->status
& statusbit
) {
41 fl6
->daddr
= t
->dst
.u3
.in6
;
42 if (t
->dst
.protonum
== IPPROTO_TCP
||
43 t
->dst
.protonum
== IPPROTO_UDP
||
44 t
->dst
.protonum
== IPPROTO_UDPLITE
||
45 t
->dst
.protonum
== IPPROTO_DCCP
||
46 t
->dst
.protonum
== IPPROTO_SCTP
)
47 fl6
->fl6_dport
= t
->dst
.u
.all
;
50 statusbit
^= IPS_NAT_MASK
;
52 if (ct
->status
& statusbit
) {
53 fl6
->saddr
= t
->src
.u3
.in6
;
54 if (t
->dst
.protonum
== IPPROTO_TCP
||
55 t
->dst
.protonum
== IPPROTO_UDP
||
56 t
->dst
.protonum
== IPPROTO_UDPLITE
||
57 t
->dst
.protonum
== IPPROTO_DCCP
||
58 t
->dst
.protonum
== IPPROTO_SCTP
)
59 fl6
->fl6_sport
= t
->src
.u
.all
;
64 static bool nf_nat_ipv6_manip_pkt(struct sk_buff
*skb
,
65 unsigned int iphdroff
,
66 const struct nf_conntrack_tuple
*target
,
67 enum nf_nat_manip_type maniptype
)
69 struct ipv6hdr
*ipv6h
;
74 if (!skb_make_writable(skb
, iphdroff
+ sizeof(*ipv6h
)))
77 ipv6h
= (void *)skb
->data
+ iphdroff
;
78 nexthdr
= ipv6h
->nexthdr
;
79 hdroff
= ipv6_skip_exthdr(skb
, iphdroff
+ sizeof(*ipv6h
),
84 if ((frag_off
& htons(~0x7)) == 0 &&
85 !nf_nat_l4proto_manip_pkt(skb
, &nf_nat_l3proto_ipv6
, iphdroff
, hdroff
,
89 /* must reload, offset might have changed */
90 ipv6h
= (void *)skb
->data
+ iphdroff
;
93 if (maniptype
== NF_NAT_MANIP_SRC
)
94 ipv6h
->saddr
= target
->src
.u3
.in6
;
96 ipv6h
->daddr
= target
->dst
.u3
.in6
;
101 static void nf_nat_ipv6_csum_update(struct sk_buff
*skb
,
102 unsigned int iphdroff
, __sum16
*check
,
103 const struct nf_conntrack_tuple
*t
,
104 enum nf_nat_manip_type maniptype
)
106 const struct ipv6hdr
*ipv6h
= (struct ipv6hdr
*)(skb
->data
+ iphdroff
);
107 const struct in6_addr
*oldip
, *newip
;
109 if (maniptype
== NF_NAT_MANIP_SRC
) {
110 oldip
= &ipv6h
->saddr
;
111 newip
= &t
->src
.u3
.in6
;
113 oldip
= &ipv6h
->daddr
;
114 newip
= &t
->dst
.u3
.in6
;
116 inet_proto_csum_replace16(check
, skb
, oldip
->s6_addr32
,
117 newip
->s6_addr32
, true);
120 static void nf_nat_ipv6_csum_recalc(struct sk_buff
*skb
,
121 u8 proto
, void *data
, __sum16
*check
,
122 int datalen
, int oldlen
)
124 if (skb
->ip_summed
!= CHECKSUM_PARTIAL
) {
125 const struct ipv6hdr
*ipv6h
= ipv6_hdr(skb
);
127 skb
->ip_summed
= CHECKSUM_PARTIAL
;
128 skb
->csum_start
= skb_headroom(skb
) + skb_network_offset(skb
) +
129 (data
- (void *)skb
->data
);
130 skb
->csum_offset
= (void *)check
- data
;
131 *check
= ~csum_ipv6_magic(&ipv6h
->saddr
, &ipv6h
->daddr
,
134 inet_proto_csum_replace2(check
, skb
,
135 htons(oldlen
), htons(datalen
), true);
138 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
139 static int nf_nat_ipv6_nlattr_to_range(struct nlattr
*tb
[],
140 struct nf_nat_range2
*range
)
142 if (tb
[CTA_NAT_V6_MINIP
]) {
143 nla_memcpy(&range
->min_addr
.ip6
, tb
[CTA_NAT_V6_MINIP
],
144 sizeof(struct in6_addr
));
145 range
->flags
|= NF_NAT_RANGE_MAP_IPS
;
148 if (tb
[CTA_NAT_V6_MAXIP
])
149 nla_memcpy(&range
->max_addr
.ip6
, tb
[CTA_NAT_V6_MAXIP
],
150 sizeof(struct in6_addr
));
152 range
->max_addr
= range
->min_addr
;
158 static const struct nf_nat_l3proto nf_nat_l3proto_ipv6
= {
159 .l3proto
= NFPROTO_IPV6
,
160 .manip_pkt
= nf_nat_ipv6_manip_pkt
,
161 .csum_update
= nf_nat_ipv6_csum_update
,
162 .csum_recalc
= nf_nat_ipv6_csum_recalc
,
163 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
164 .nlattr_to_range
= nf_nat_ipv6_nlattr_to_range
,
167 .decode_session
= nf_nat_ipv6_decode_session
,
171 int nf_nat_icmpv6_reply_translation(struct sk_buff
*skb
,
173 enum ip_conntrack_info ctinfo
,
174 unsigned int hooknum
,
178 struct icmp6hdr icmp6
;
181 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
182 enum nf_nat_manip_type manip
= HOOK2MANIP(hooknum
);
183 struct nf_conntrack_tuple target
;
184 unsigned long statusbit
;
186 WARN_ON(ctinfo
!= IP_CT_RELATED
&& ctinfo
!= IP_CT_RELATED_REPLY
);
188 if (!skb_make_writable(skb
, hdrlen
+ sizeof(*inside
)))
190 if (nf_ip6_checksum(skb
, hooknum
, hdrlen
, IPPROTO_ICMPV6
))
193 inside
= (void *)skb
->data
+ hdrlen
;
194 if (inside
->icmp6
.icmp6_type
== NDISC_REDIRECT
) {
195 if ((ct
->status
& IPS_NAT_DONE_MASK
) != IPS_NAT_DONE_MASK
)
197 if (ct
->status
& IPS_NAT_MASK
)
201 if (manip
== NF_NAT_MANIP_SRC
)
202 statusbit
= IPS_SRC_NAT
;
204 statusbit
= IPS_DST_NAT
;
206 /* Invert if this is reply direction */
207 if (dir
== IP_CT_DIR_REPLY
)
208 statusbit
^= IPS_NAT_MASK
;
210 if (!(ct
->status
& statusbit
))
213 if (!nf_nat_ipv6_manip_pkt(skb
, hdrlen
+ sizeof(inside
->icmp6
),
214 &ct
->tuplehash
[!dir
].tuple
, !manip
))
217 if (skb
->ip_summed
!= CHECKSUM_PARTIAL
) {
218 struct ipv6hdr
*ipv6h
= ipv6_hdr(skb
);
219 inside
= (void *)skb
->data
+ hdrlen
;
220 inside
->icmp6
.icmp6_cksum
= 0;
221 inside
->icmp6
.icmp6_cksum
=
222 csum_ipv6_magic(&ipv6h
->saddr
, &ipv6h
->daddr
,
223 skb
->len
- hdrlen
, IPPROTO_ICMPV6
,
224 skb_checksum(skb
, hdrlen
,
225 skb
->len
- hdrlen
, 0));
228 nf_ct_invert_tuplepr(&target
, &ct
->tuplehash
[!dir
].tuple
);
229 target
.dst
.protonum
= IPPROTO_ICMPV6
;
230 if (!nf_nat_ipv6_manip_pkt(skb
, 0, &target
, manip
))
235 EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation
);
238 nf_nat_ipv6_fn(void *priv
, struct sk_buff
*skb
,
239 const struct nf_hook_state
*state
)
242 enum ip_conntrack_info ctinfo
;
247 ct
= nf_ct_get(skb
, &ctinfo
);
248 /* Can't track? It's not due to stress, or conntrack would
249 * have dropped it. Hence it's the user's responsibilty to
250 * packet filter it out, or implement conntrack/NAT for that
256 if (ctinfo
== IP_CT_RELATED
|| ctinfo
== IP_CT_RELATED_REPLY
) {
257 nexthdr
= ipv6_hdr(skb
)->nexthdr
;
258 hdrlen
= ipv6_skip_exthdr(skb
, sizeof(struct ipv6hdr
),
259 &nexthdr
, &frag_off
);
261 if (hdrlen
>= 0 && nexthdr
== IPPROTO_ICMPV6
) {
262 if (!nf_nat_icmpv6_reply_translation(skb
, ct
, ctinfo
,
271 return nf_nat_inet_fn(priv
, skb
, state
);
275 nf_nat_ipv6_in(void *priv
, struct sk_buff
*skb
,
276 const struct nf_hook_state
*state
)
279 struct in6_addr daddr
= ipv6_hdr(skb
)->daddr
;
281 ret
= nf_nat_ipv6_fn(priv
, skb
, state
);
282 if (ret
!= NF_DROP
&& ret
!= NF_STOLEN
&&
283 ipv6_addr_cmp(&daddr
, &ipv6_hdr(skb
)->daddr
))
290 nf_nat_ipv6_out(void *priv
, struct sk_buff
*skb
,
291 const struct nf_hook_state
*state
)
294 const struct nf_conn
*ct
;
295 enum ip_conntrack_info ctinfo
;
300 ret
= nf_nat_ipv6_fn(priv
, skb
, state
);
302 if (ret
!= NF_DROP
&& ret
!= NF_STOLEN
&&
303 !(IP6CB(skb
)->flags
& IP6SKB_XFRM_TRANSFORMED
) &&
304 (ct
= nf_ct_get(skb
, &ctinfo
)) != NULL
) {
305 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
307 if (!nf_inet_addr_cmp(&ct
->tuplehash
[dir
].tuple
.src
.u3
,
308 &ct
->tuplehash
[!dir
].tuple
.dst
.u3
) ||
309 (ct
->tuplehash
[dir
].tuple
.dst
.protonum
!= IPPROTO_ICMPV6
&&
310 ct
->tuplehash
[dir
].tuple
.src
.u
.all
!=
311 ct
->tuplehash
[!dir
].tuple
.dst
.u
.all
)) {
312 err
= nf_xfrm_me_harder(state
->net
, skb
, AF_INET6
);
314 ret
= NF_DROP_ERR(err
);
322 nf_nat_ipv6_local_fn(void *priv
, struct sk_buff
*skb
,
323 const struct nf_hook_state
*state
)
325 const struct nf_conn
*ct
;
326 enum ip_conntrack_info ctinfo
;
330 ret
= nf_nat_ipv6_fn(priv
, skb
, state
);
331 if (ret
!= NF_DROP
&& ret
!= NF_STOLEN
&&
332 (ct
= nf_ct_get(skb
, &ctinfo
)) != NULL
) {
333 enum ip_conntrack_dir dir
= CTINFO2DIR(ctinfo
);
335 if (!nf_inet_addr_cmp(&ct
->tuplehash
[dir
].tuple
.dst
.u3
,
336 &ct
->tuplehash
[!dir
].tuple
.src
.u3
)) {
337 err
= ip6_route_me_harder(state
->net
, skb
);
339 ret
= NF_DROP_ERR(err
);
342 else if (!(IP6CB(skb
)->flags
& IP6SKB_XFRM_TRANSFORMED
) &&
343 ct
->tuplehash
[dir
].tuple
.dst
.protonum
!= IPPROTO_ICMPV6
&&
344 ct
->tuplehash
[dir
].tuple
.dst
.u
.all
!=
345 ct
->tuplehash
[!dir
].tuple
.src
.u
.all
) {
346 err
= nf_xfrm_me_harder(state
->net
, skb
, AF_INET6
);
348 ret
= NF_DROP_ERR(err
);
355 static const struct nf_hook_ops nf_nat_ipv6_ops
[] = {
356 /* Before packet filtering, change destination */
358 .hook
= nf_nat_ipv6_in
,
360 .hooknum
= NF_INET_PRE_ROUTING
,
361 .priority
= NF_IP6_PRI_NAT_DST
,
363 /* After packet filtering, change source */
365 .hook
= nf_nat_ipv6_out
,
367 .hooknum
= NF_INET_POST_ROUTING
,
368 .priority
= NF_IP6_PRI_NAT_SRC
,
370 /* Before packet filtering, change destination */
372 .hook
= nf_nat_ipv6_local_fn
,
374 .hooknum
= NF_INET_LOCAL_OUT
,
375 .priority
= NF_IP6_PRI_NAT_DST
,
377 /* After packet filtering, change source */
379 .hook
= nf_nat_ipv6_fn
,
381 .hooknum
= NF_INET_LOCAL_IN
,
382 .priority
= NF_IP6_PRI_NAT_SRC
,
386 int nf_nat_l3proto_ipv6_register_fn(struct net
*net
, const struct nf_hook_ops
*ops
)
388 return nf_nat_register_fn(net
, ops
, nf_nat_ipv6_ops
, ARRAY_SIZE(nf_nat_ipv6_ops
));
390 EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv6_register_fn
);
392 void nf_nat_l3proto_ipv6_unregister_fn(struct net
*net
, const struct nf_hook_ops
*ops
)
394 nf_nat_unregister_fn(net
, ops
, ARRAY_SIZE(nf_nat_ipv6_ops
));
396 EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv6_unregister_fn
);
398 static int __init
nf_nat_l3proto_ipv6_init(void)
400 return nf_nat_l3proto_register(&nf_nat_l3proto_ipv6
);
403 static void __exit
nf_nat_l3proto_ipv6_exit(void)
405 nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv6
);
408 MODULE_LICENSE("GPL");
409 MODULE_ALIAS("nf-nat-" __stringify(AF_INET6
));
411 module_init(nf_nat_l3proto_ipv6_init
);
412 module_exit(nf_nat_l3proto_ipv6_exit
);