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_checksum.h>
19 #include <net/ip6_route.h>
20 #include <net/ipv6.h>
21 
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>
27 
28 static const struct nf_nat_l3proto nf_nat_l3proto_ipv6;
29 
30 #ifdef CONFIG_XFRM
nf_nat_ipv6_decode_session(struct sk_buff * skb,const struct nf_conn * ct,enum ip_conntrack_dir dir,unsigned long statusbit,struct flowi * fl)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,
35 				       struct flowi *fl)
36 {
37 	const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple;
38 	struct flowi6 *fl6 = &fl->u.ip6;
39 
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;
48 	}
49 
50 	statusbit ^= IPS_NAT_MASK;
51 
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;
60 	}
61 }
62 #endif
63 
nf_nat_ipv6_in_range(const struct nf_conntrack_tuple * t,const struct nf_nat_range2 * range)64 static bool nf_nat_ipv6_in_range(const struct nf_conntrack_tuple *t,
65 				 const struct nf_nat_range2 *range)
66 {
67 	return ipv6_addr_cmp(&t->src.u3.in6, &range->min_addr.in6) >= 0 &&
68 	       ipv6_addr_cmp(&t->src.u3.in6, &range->max_addr.in6) <= 0;
69 }
70 
nf_nat_ipv6_secure_port(const struct nf_conntrack_tuple * t,__be16 dport)71 static u32 nf_nat_ipv6_secure_port(const struct nf_conntrack_tuple *t,
72 				   __be16 dport)
73 {
74 	return secure_ipv6_port_ephemeral(t->src.u3.ip6, t->dst.u3.ip6, dport);
75 }
76 
nf_nat_ipv6_manip_pkt(struct sk_buff * skb,unsigned int iphdroff,const struct nf_nat_l4proto * l4proto,const struct nf_conntrack_tuple * target,enum nf_nat_manip_type maniptype)77 static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb,
78 				  unsigned int iphdroff,
79 				  const struct nf_nat_l4proto *l4proto,
80 				  const struct nf_conntrack_tuple *target,
81 				  enum nf_nat_manip_type maniptype)
82 {
83 	struct ipv6hdr *ipv6h;
84 	__be16 frag_off;
85 	int hdroff;
86 	u8 nexthdr;
87 
88 	if (!skb_make_writable(skb, iphdroff + sizeof(*ipv6h)))
89 		return false;
90 
91 	ipv6h = (void *)skb->data + iphdroff;
92 	nexthdr = ipv6h->nexthdr;
93 	hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h),
94 				  &nexthdr, &frag_off);
95 	if (hdroff < 0)
96 		goto manip_addr;
97 
98 	if ((frag_off & htons(~0x7)) == 0 &&
99 	    !l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv6, iphdroff, hdroff,
100 				target, maniptype))
101 		return false;
102 
103 	/* must reload, offset might have changed */
104 	ipv6h = (void *)skb->data + iphdroff;
105 
106 manip_addr:
107 	if (maniptype == NF_NAT_MANIP_SRC)
108 		ipv6h->saddr = target->src.u3.in6;
109 	else
110 		ipv6h->daddr = target->dst.u3.in6;
111 
112 	return true;
113 }
114 
nf_nat_ipv6_csum_update(struct sk_buff * skb,unsigned int iphdroff,__sum16 * check,const struct nf_conntrack_tuple * t,enum nf_nat_manip_type maniptype)115 static void nf_nat_ipv6_csum_update(struct sk_buff *skb,
116 				    unsigned int iphdroff, __sum16 *check,
117 				    const struct nf_conntrack_tuple *t,
118 				    enum nf_nat_manip_type maniptype)
119 {
120 	const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff);
121 	const struct in6_addr *oldip, *newip;
122 
123 	if (maniptype == NF_NAT_MANIP_SRC) {
124 		oldip = &ipv6h->saddr;
125 		newip = &t->src.u3.in6;
126 	} else {
127 		oldip = &ipv6h->daddr;
128 		newip = &t->dst.u3.in6;
129 	}
130 	inet_proto_csum_replace16(check, skb, oldip->s6_addr32,
131 				  newip->s6_addr32, true);
132 }
133 
nf_nat_ipv6_csum_recalc(struct sk_buff * skb,u8 proto,void * data,__sum16 * check,int datalen,int oldlen)134 static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
135 				    u8 proto, void *data, __sum16 *check,
136 				    int datalen, int oldlen)
137 {
138 	if (skb->ip_summed != CHECKSUM_PARTIAL) {
139 		const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
140 
141 		skb->ip_summed = CHECKSUM_PARTIAL;
142 		skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
143 			(data - (void *)skb->data);
144 		skb->csum_offset = (void *)check - data;
145 		*check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
146 					  datalen, proto, 0);
147 	} else
148 		inet_proto_csum_replace2(check, skb,
149 					 htons(oldlen), htons(datalen), true);
150 }
151 
152 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
nf_nat_ipv6_nlattr_to_range(struct nlattr * tb[],struct nf_nat_range2 * range)153 static int nf_nat_ipv6_nlattr_to_range(struct nlattr *tb[],
154 				       struct nf_nat_range2 *range)
155 {
156 	if (tb[CTA_NAT_V6_MINIP]) {
157 		nla_memcpy(&range->min_addr.ip6, tb[CTA_NAT_V6_MINIP],
158 			   sizeof(struct in6_addr));
159 		range->flags |= NF_NAT_RANGE_MAP_IPS;
160 	}
161 
162 	if (tb[CTA_NAT_V6_MAXIP])
163 		nla_memcpy(&range->max_addr.ip6, tb[CTA_NAT_V6_MAXIP],
164 			   sizeof(struct in6_addr));
165 	else
166 		range->max_addr = range->min_addr;
167 
168 	return 0;
169 }
170 #endif
171 
172 static const struct nf_nat_l3proto nf_nat_l3proto_ipv6 = {
173 	.l3proto		= NFPROTO_IPV6,
174 	.secure_port		= nf_nat_ipv6_secure_port,
175 	.in_range		= nf_nat_ipv6_in_range,
176 	.manip_pkt		= nf_nat_ipv6_manip_pkt,
177 	.csum_update		= nf_nat_ipv6_csum_update,
178 	.csum_recalc		= nf_nat_ipv6_csum_recalc,
179 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
180 	.nlattr_to_range	= nf_nat_ipv6_nlattr_to_range,
181 #endif
182 #ifdef CONFIG_XFRM
183 	.decode_session	= nf_nat_ipv6_decode_session,
184 #endif
185 };
186 
nf_nat_icmpv6_reply_translation(struct sk_buff * skb,struct nf_conn * ct,enum ip_conntrack_info ctinfo,unsigned int hooknum,unsigned int hdrlen)187 int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
188 				    struct nf_conn *ct,
189 				    enum ip_conntrack_info ctinfo,
190 				    unsigned int hooknum,
191 				    unsigned int hdrlen)
192 {
193 	struct {
194 		struct icmp6hdr	icmp6;
195 		struct ipv6hdr	ip6;
196 	} *inside;
197 	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
198 	enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
199 	const struct nf_nat_l4proto *l4proto;
200 	struct nf_conntrack_tuple target;
201 	unsigned long statusbit;
202 
203 	WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
204 
205 	if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
206 		return 0;
207 	if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6))
208 		return 0;
209 
210 	inside = (void *)skb->data + hdrlen;
211 	if (inside->icmp6.icmp6_type == NDISC_REDIRECT) {
212 		if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
213 			return 0;
214 		if (ct->status & IPS_NAT_MASK)
215 			return 0;
216 	}
217 
218 	if (manip == NF_NAT_MANIP_SRC)
219 		statusbit = IPS_SRC_NAT;
220 	else
221 		statusbit = IPS_DST_NAT;
222 
223 	/* Invert if this is reply direction */
224 	if (dir == IP_CT_DIR_REPLY)
225 		statusbit ^= IPS_NAT_MASK;
226 
227 	if (!(ct->status & statusbit))
228 		return 1;
229 
230 	l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, inside->ip6.nexthdr);
231 	if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6),
232 				   l4proto, &ct->tuplehash[!dir].tuple, !manip))
233 		return 0;
234 
235 	if (skb->ip_summed != CHECKSUM_PARTIAL) {
236 		struct ipv6hdr *ipv6h = ipv6_hdr(skb);
237 		inside = (void *)skb->data + hdrlen;
238 		inside->icmp6.icmp6_cksum = 0;
239 		inside->icmp6.icmp6_cksum =
240 			csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
241 					skb->len - hdrlen, IPPROTO_ICMPV6,
242 					skb_checksum(skb, hdrlen,
243 						     skb->len - hdrlen, 0));
244 	}
245 
246 	nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
247 	l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, IPPROTO_ICMPV6);
248 	if (!nf_nat_ipv6_manip_pkt(skb, 0, l4proto, &target, manip))
249 		return 0;
250 
251 	return 1;
252 }
253 EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
254 
255 static unsigned int
nf_nat_ipv6_fn(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)256 nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
257 	       const struct nf_hook_state *state)
258 {
259 	struct nf_conn *ct;
260 	enum ip_conntrack_info ctinfo;
261 	__be16 frag_off;
262 	int hdrlen;
263 	u8 nexthdr;
264 
265 	ct = nf_ct_get(skb, &ctinfo);
266 	/* Can't track?  It's not due to stress, or conntrack would
267 	 * have dropped it.  Hence it's the user's responsibilty to
268 	 * packet filter it out, or implement conntrack/NAT for that
269 	 * protocol. 8) --RR
270 	 */
271 	if (!ct)
272 		return NF_ACCEPT;
273 
274 	if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
275 		nexthdr = ipv6_hdr(skb)->nexthdr;
276 		hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
277 					  &nexthdr, &frag_off);
278 
279 		if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
280 			if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
281 							     state->hook,
282 							     hdrlen))
283 				return NF_DROP;
284 			else
285 				return NF_ACCEPT;
286 		}
287 	}
288 
289 	return nf_nat_inet_fn(priv, skb, state);
290 }
291 
292 static unsigned int
nf_nat_ipv6_in(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)293 nf_nat_ipv6_in(void *priv, struct sk_buff *skb,
294 	       const struct nf_hook_state *state)
295 {
296 	unsigned int ret;
297 	struct in6_addr daddr = ipv6_hdr(skb)->daddr;
298 
299 	ret = nf_nat_ipv6_fn(priv, skb, state);
300 	if (ret != NF_DROP && ret != NF_STOLEN &&
301 	    ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
302 		skb_dst_drop(skb);
303 
304 	return ret;
305 }
306 
307 static unsigned int
nf_nat_ipv6_out(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)308 nf_nat_ipv6_out(void *priv, struct sk_buff *skb,
309 		const struct nf_hook_state *state)
310 {
311 #ifdef CONFIG_XFRM
312 	const struct nf_conn *ct;
313 	enum ip_conntrack_info ctinfo;
314 	int err;
315 #endif
316 	unsigned int ret;
317 
318 	ret = nf_nat_ipv6_fn(priv, skb, state);
319 #ifdef CONFIG_XFRM
320 	if (ret != NF_DROP && ret != NF_STOLEN &&
321 	    !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
322 	    (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
323 		enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
324 
325 		if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
326 				      &ct->tuplehash[!dir].tuple.dst.u3) ||
327 		    (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
328 		     ct->tuplehash[dir].tuple.src.u.all !=
329 		     ct->tuplehash[!dir].tuple.dst.u.all)) {
330 			err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
331 			if (err < 0)
332 				ret = NF_DROP_ERR(err);
333 		}
334 	}
335 #endif
336 	return ret;
337 }
338 
339 static unsigned int
nf_nat_ipv6_local_fn(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)340 nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb,
341 		     const struct nf_hook_state *state)
342 {
343 	const struct nf_conn *ct;
344 	enum ip_conntrack_info ctinfo;
345 	unsigned int ret;
346 	int err;
347 
348 	ret = nf_nat_ipv6_fn(priv, skb, state);
349 	if (ret != NF_DROP && ret != NF_STOLEN &&
350 	    (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
351 		enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
352 
353 		if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
354 				      &ct->tuplehash[!dir].tuple.src.u3)) {
355 			err = ip6_route_me_harder(state->net, skb);
356 			if (err < 0)
357 				ret = NF_DROP_ERR(err);
358 		}
359 #ifdef CONFIG_XFRM
360 		else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
361 			 ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
362 			 ct->tuplehash[dir].tuple.dst.u.all !=
363 			 ct->tuplehash[!dir].tuple.src.u.all) {
364 			err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
365 			if (err < 0)
366 				ret = NF_DROP_ERR(err);
367 		}
368 #endif
369 	}
370 	return ret;
371 }
372 
373 static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
374 	/* Before packet filtering, change destination */
375 	{
376 		.hook		= nf_nat_ipv6_in,
377 		.pf		= NFPROTO_IPV6,
378 		.hooknum	= NF_INET_PRE_ROUTING,
379 		.priority	= NF_IP6_PRI_NAT_DST,
380 	},
381 	/* After packet filtering, change source */
382 	{
383 		.hook		= nf_nat_ipv6_out,
384 		.pf		= NFPROTO_IPV6,
385 		.hooknum	= NF_INET_POST_ROUTING,
386 		.priority	= NF_IP6_PRI_NAT_SRC,
387 	},
388 	/* Before packet filtering, change destination */
389 	{
390 		.hook		= nf_nat_ipv6_local_fn,
391 		.pf		= NFPROTO_IPV6,
392 		.hooknum	= NF_INET_LOCAL_OUT,
393 		.priority	= NF_IP6_PRI_NAT_DST,
394 	},
395 	/* After packet filtering, change source */
396 	{
397 		.hook		= nf_nat_ipv6_fn,
398 		.pf		= NFPROTO_IPV6,
399 		.hooknum	= NF_INET_LOCAL_IN,
400 		.priority	= NF_IP6_PRI_NAT_SRC,
401 	},
402 };
403 
nf_nat_l3proto_ipv6_register_fn(struct net * net,const struct nf_hook_ops * ops)404 int nf_nat_l3proto_ipv6_register_fn(struct net *net, const struct nf_hook_ops *ops)
405 {
406 	return nf_nat_register_fn(net, ops, nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
407 }
408 EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv6_register_fn);
409 
nf_nat_l3proto_ipv6_unregister_fn(struct net * net,const struct nf_hook_ops * ops)410 void nf_nat_l3proto_ipv6_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
411 {
412 	nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
413 }
414 EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv6_unregister_fn);
415 
nf_nat_l3proto_ipv6_init(void)416 static int __init nf_nat_l3proto_ipv6_init(void)
417 {
418 	int err;
419 
420 	err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
421 	if (err < 0)
422 		goto err1;
423 	err = nf_nat_l3proto_register(&nf_nat_l3proto_ipv6);
424 	if (err < 0)
425 		goto err2;
426 	return err;
427 
428 err2:
429 	nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
430 err1:
431 	return err;
432 }
433 
nf_nat_l3proto_ipv6_exit(void)434 static void __exit nf_nat_l3proto_ipv6_exit(void)
435 {
436 	nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv6);
437 	nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
438 }
439 
440 MODULE_LICENSE("GPL");
441 MODULE_ALIAS("nf-nat-" __stringify(AF_INET6));
442 
443 module_init(nf_nat_l3proto_ipv6_init);
444 module_exit(nf_nat_l3proto_ipv6_exit);
445