158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy/*
258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy *
458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy * This program is free software; you can redistribute it and/or modify
558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy * it under the terms of the GNU General Public License version 2 as
658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy * published by the Free Software Foundation.
758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy *
858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy * Development of IPv6 NAT funded by Astaro.
958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy */
1058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <linux/types.h>
1158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <linux/module.h>
1258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <linux/skbuff.h>
1358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <linux/ipv6.h>
1458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <linux/netfilter.h>
1558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <linux/netfilter_ipv6.h>
1658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <net/secure_seq.h>
1758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <net/checksum.h>
182c9693222952b08c224093a1263cb5aee02d10ffStephen Rothwell#include <net/ip6_checksum.h>
1958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <net/ip6_route.h>
2058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <net/ipv6.h>
2158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
2258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <net/netfilter/nf_conntrack_core.h>
2358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <net/netfilter/nf_conntrack.h>
2458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <net/netfilter/nf_nat_core.h>
2558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <net/netfilter/nf_nat_l3proto.h>
2658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#include <net/netfilter/nf_nat_l4proto.h>
2758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
2858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic const struct nf_nat_l3proto nf_nat_l3proto_ipv6;
2958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
3058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#ifdef CONFIG_XFRM
3158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic void nf_nat_ipv6_decode_session(struct sk_buff *skb,
3258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				       const struct nf_conn *ct,
3358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				       enum ip_conntrack_dir dir,
3458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				       unsigned long statusbit,
3558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				       struct flowi *fl)
3658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
3758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple;
3858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	struct flowi6 *fl6 = &fl->u.ip6;
3958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
4058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (ct->status & statusbit) {
4158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		fl6->daddr = t->dst.u3.in6;
4258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		if (t->dst.protonum == IPPROTO_TCP ||
4358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		    t->dst.protonum == IPPROTO_UDP ||
4458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		    t->dst.protonum == IPPROTO_UDPLITE ||
4558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		    t->dst.protonum == IPPROTO_DCCP ||
4658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		    t->dst.protonum == IPPROTO_SCTP)
4758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			fl6->fl6_dport = t->dst.u.all;
4858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	}
4958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
5058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	statusbit ^= IPS_NAT_MASK;
5158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
5258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (ct->status & statusbit) {
5358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		fl6->saddr = t->src.u3.in6;
5458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		if (t->dst.protonum == IPPROTO_TCP ||
5558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		    t->dst.protonum == IPPROTO_UDP ||
5658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		    t->dst.protonum == IPPROTO_UDPLITE ||
5758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		    t->dst.protonum == IPPROTO_DCCP ||
5858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		    t->dst.protonum == IPPROTO_SCTP)
5958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			fl6->fl6_sport = t->src.u.all;
6058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	}
6158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
6258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#endif
6358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
6458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic bool nf_nat_ipv6_in_range(const struct nf_conntrack_tuple *t,
6558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				 const struct nf_nat_range *range)
6658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
6758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	return ipv6_addr_cmp(&t->src.u3.in6, &range->min_addr.in6) >= 0 &&
6858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	       ipv6_addr_cmp(&t->src.u3.in6, &range->max_addr.in6) <= 0;
6958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
7058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
7158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic u32 nf_nat_ipv6_secure_port(const struct nf_conntrack_tuple *t,
7258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				   __be16 dport)
7358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
7458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	return secure_ipv6_port_ephemeral(t->src.u3.ip6, t->dst.u3.ip6, dport);
7558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
7658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
7758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb,
7858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				  unsigned int iphdroff,
7958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				  const struct nf_nat_l4proto *l4proto,
8058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				  const struct nf_conntrack_tuple *target,
8158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				  enum nf_nat_manip_type maniptype)
8258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
8358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	struct ipv6hdr *ipv6h;
8458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	__be16 frag_off;
8558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	int hdroff;
8658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	u8 nexthdr;
8758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
8858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (!skb_make_writable(skb, iphdroff + sizeof(*ipv6h)))
8958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		return false;
9058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
9158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	ipv6h = (void *)skb->data + iphdroff;
9258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	nexthdr = ipv6h->nexthdr;
9358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h),
9458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				  &nexthdr, &frag_off);
9558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (hdroff < 0)
9658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		goto manip_addr;
9758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
9858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if ((frag_off & htons(~0x7)) == 0 &&
9958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	    !l4proto->manip_pkt(skb, &nf_nat_l3proto_ipv6, iphdroff, hdroff,
10058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				target, maniptype))
10158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		return false;
10258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardymanip_addr:
10358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (maniptype == NF_NAT_MANIP_SRC)
10458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		ipv6h->saddr = target->src.u3.in6;
10558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	else
10658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		ipv6h->daddr = target->dst.u3.in6;
10758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
10858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	return true;
10958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
11058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
11158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic void nf_nat_ipv6_csum_update(struct sk_buff *skb,
11258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				    unsigned int iphdroff, __sum16 *check,
11358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				    const struct nf_conntrack_tuple *t,
11458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				    enum nf_nat_manip_type maniptype)
11558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
11658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff);
11758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	const struct in6_addr *oldip, *newip;
11858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
11958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (maniptype == NF_NAT_MANIP_SRC) {
12058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		oldip = &ipv6h->saddr;
12158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		newip = &t->src.u3.in6;
12258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	} else {
12358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		oldip = &ipv6h->daddr;
12458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		newip = &t->dst.u3.in6;
12558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	}
12658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	inet_proto_csum_replace16(check, skb, oldip->s6_addr32,
12758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				  newip->s6_addr32, 1);
12858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
12958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
13058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
13158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				    u8 proto, void *data, __sum16 *check,
13258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				    int datalen, int oldlen)
13358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
13458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
13558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
13658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
13758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (skb->ip_summed != CHECKSUM_PARTIAL) {
13858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		if (!(rt->rt6i_flags & RTF_LOCAL) &&
13958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		    (!skb->dev || skb->dev->features & NETIF_F_V6_CSUM)) {
14058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			skb->ip_summed = CHECKSUM_PARTIAL;
14158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			skb->csum_start = skb_headroom(skb) +
14258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy					  skb_network_offset(skb) +
14358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy					  (data - (void *)skb->data);
14458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			skb->csum_offset = (void *)check - data;
14558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			*check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
14658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy						  datalen, proto, 0);
14758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		} else {
14858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			*check = 0;
14958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			*check = csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
15058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy						 datalen, proto,
15158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy						 csum_partial(data, datalen,
15258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy							      0));
15358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			if (proto == IPPROTO_UDP && !*check)
15458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				*check = CSUM_MANGLED_0;
15558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		}
15658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	} else
15758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		inet_proto_csum_replace2(check, skb,
15858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy					 htons(oldlen), htons(datalen), 1);
15958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
16058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
16124de3d377539e384621c5b8f8f8d8d01852dddc8Duan Jiong#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
16258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic int nf_nat_ipv6_nlattr_to_range(struct nlattr *tb[],
16358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				       struct nf_nat_range *range)
16458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
16558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (tb[CTA_NAT_V6_MINIP]) {
16658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		nla_memcpy(&range->min_addr.ip6, tb[CTA_NAT_V6_MINIP],
16758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			   sizeof(struct in6_addr));
16858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		range->flags |= NF_NAT_RANGE_MAP_IPS;
16958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	}
17058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
17158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (tb[CTA_NAT_V6_MAXIP])
17258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		nla_memcpy(&range->max_addr.ip6, tb[CTA_NAT_V6_MAXIP],
17358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			   sizeof(struct in6_addr));
17458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	else
17558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		range->max_addr = range->min_addr;
17658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
17758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	return 0;
17858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
17924de3d377539e384621c5b8f8f8d8d01852dddc8Duan Jiong#endif
18058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
18158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic const struct nf_nat_l3proto nf_nat_l3proto_ipv6 = {
18258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	.l3proto		= NFPROTO_IPV6,
18358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	.secure_port		= nf_nat_ipv6_secure_port,
18458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	.in_range		= nf_nat_ipv6_in_range,
18558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	.manip_pkt		= nf_nat_ipv6_manip_pkt,
18658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	.csum_update		= nf_nat_ipv6_csum_update,
18758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	.csum_recalc		= nf_nat_ipv6_csum_recalc,
18824de3d377539e384621c5b8f8f8d8d01852dddc8Duan Jiong#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
18958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	.nlattr_to_range	= nf_nat_ipv6_nlattr_to_range,
19024de3d377539e384621c5b8f8f8d8d01852dddc8Duan Jiong#endif
19158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#ifdef CONFIG_XFRM
19258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	.decode_session	= nf_nat_ipv6_decode_session,
19358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy#endif
19458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy};
19558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
19658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardyint nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
19758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				    struct nf_conn *ct,
19858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				    enum ip_conntrack_info ctinfo,
19958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				    unsigned int hooknum,
20058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				    unsigned int hdrlen)
20158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
20258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	struct {
20358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		struct icmp6hdr	icmp6;
20458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		struct ipv6hdr	ip6;
20558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	} *inside;
20658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
20758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
20858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	const struct nf_nat_l4proto *l4proto;
20958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	struct nf_conntrack_tuple target;
21058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	unsigned long statusbit;
21158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
21258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY);
21358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
21458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
21558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		return 0;
21658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6))
21758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		return 0;
21858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
21958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	inside = (void *)skb->data + hdrlen;
22058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (inside->icmp6.icmp6_type == NDISC_REDIRECT) {
22158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
22258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			return 0;
22358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		if (ct->status & IPS_NAT_MASK)
22458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			return 0;
22558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	}
22658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
22758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (manip == NF_NAT_MANIP_SRC)
22858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		statusbit = IPS_SRC_NAT;
22958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	else
23058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		statusbit = IPS_DST_NAT;
23158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
23258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	/* Invert if this is reply direction */
23358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (dir == IP_CT_DIR_REPLY)
23458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		statusbit ^= IPS_NAT_MASK;
23558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
23658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (!(ct->status & statusbit))
23758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		return 1;
23858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
23958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, inside->ip6.nexthdr);
24058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6),
24158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy				   l4proto, &ct->tuplehash[!dir].tuple, !manip))
24258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		return 0;
24358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
24458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (skb->ip_summed != CHECKSUM_PARTIAL) {
24558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		struct ipv6hdr *ipv6h = ipv6_hdr(skb);
24658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		inside = (void *)skb->data + hdrlen;
24758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		inside->icmp6.icmp6_cksum = 0;
24858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		inside->icmp6.icmp6_cksum =
24958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy			csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
25058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy					skb->len - hdrlen, IPPROTO_ICMPV6,
25158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy					csum_partial(&inside->icmp6,
25258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy						     skb->len - hdrlen, 0));
25358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	}
25458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
25558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	nf_ct_invert_tuplepr(&target, &ct->tuplehash[!dir].tuple);
25658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	l4proto = __nf_nat_l4proto_find(NFPROTO_IPV6, IPPROTO_ICMPV6);
25758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (!nf_nat_ipv6_manip_pkt(skb, 0, l4proto, &target, manip))
25858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		return 0;
25958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
26058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	return 1;
26158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
26258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardyEXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
26358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
2642a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayusounsigned int
2652a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayusonf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
2662a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	       const struct net_device *in, const struct net_device *out,
2672a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	       unsigned int (*do_chain)(const struct nf_hook_ops *ops,
2682a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					struct sk_buff *skb,
2692a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					const struct net_device *in,
2702a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					const struct net_device *out,
2712a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					struct nf_conn *ct))
2722a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso{
2732a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	struct nf_conn *ct;
2742a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	enum ip_conntrack_info ctinfo;
2752a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	struct nf_conn_nat *nat;
2762a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
2772a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	__be16 frag_off;
2782a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	int hdrlen;
2792a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	u8 nexthdr;
2802a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
2812a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	ct = nf_ct_get(skb, &ctinfo);
2822a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	/* Can't track?  It's not due to stress, or conntrack would
2832a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	 * have dropped it.  Hence it's the user's responsibilty to
2842a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	 * packet filter it out, or implement conntrack/NAT for that
2852a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	 * protocol. 8) --RR
2862a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	 */
2872a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	if (!ct)
2882a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		return NF_ACCEPT;
2892a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
2902a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	/* Don't try to NAT if this packet is not conntracked */
2912a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	if (nf_ct_is_untracked(ct))
2922a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		return NF_ACCEPT;
2932a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
2942a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	nat = nf_ct_nat_ext_add(ct);
2952a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	if (nat == NULL)
2962a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		return NF_ACCEPT;
2972a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
2982a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	switch (ctinfo) {
2992a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	case IP_CT_RELATED:
3002a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	case IP_CT_RELATED_REPLY:
3012a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		nexthdr = ipv6_hdr(skb)->nexthdr;
3022a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
3032a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					  &nexthdr, &frag_off);
3042a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3052a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
3062a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
3072a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso							     ops->hooknum,
3082a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso							     hdrlen))
3092a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				return NF_DROP;
3102a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			else
3112a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				return NF_ACCEPT;
3122a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		}
3132a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		/* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
3142a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	case IP_CT_NEW:
3152a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		/* Seen it before?  This can happen for loopback, retrans,
3162a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		 * or local packets.
3172a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		 */
3182a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		if (!nf_nat_initialized(ct, maniptype)) {
3192a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			unsigned int ret;
3202a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3212a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			ret = do_chain(ops, skb, in, out, ct);
3222a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			if (ret != NF_ACCEPT)
3232a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				return ret;
3242a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3252a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			if (nf_nat_initialized(ct, HOOK2MANIP(ops->hooknum)))
3262a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				break;
3272a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3282a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
3292a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			if (ret != NF_ACCEPT)
3302a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				return ret;
3312a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		} else {
3322a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			pr_debug("Already setup manip %s for ct %p\n",
3332a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
3342a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				 ct);
3352a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
3362a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				goto oif_changed;
3372a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		}
3382a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		break;
3392a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3402a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	default:
3412a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		/* ESTABLISHED */
3422a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
3432a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			     ctinfo == IP_CT_ESTABLISHED_REPLY);
3442a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
3452a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			goto oif_changed;
3462a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	}
3472a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3482a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
3492a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3502a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayusooif_changed:
3512a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	nf_ct_kill_acct(ct, ctinfo, skb);
3522a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	return NF_DROP;
3532a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso}
3542a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira AyusoEXPORT_SYMBOL_GPL(nf_nat_ipv6_fn);
3552a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3562a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayusounsigned int
3572a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayusonf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
3582a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	       const struct net_device *in, const struct net_device *out,
3592a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	       unsigned int (*do_chain)(const struct nf_hook_ops *ops,
3602a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					struct sk_buff *skb,
3612a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					const struct net_device *in,
3622a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					const struct net_device *out,
3632a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					struct nf_conn *ct))
3642a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso{
3652a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	unsigned int ret;
3662a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	struct in6_addr daddr = ipv6_hdr(skb)->daddr;
3672a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3682a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain);
3692a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	if (ret != NF_DROP && ret != NF_STOLEN &&
3702a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	    ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
3712a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		skb_dst_drop(skb);
3722a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3732a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	return ret;
3742a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso}
3752a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira AyusoEXPORT_SYMBOL_GPL(nf_nat_ipv6_in);
3762a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3772a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayusounsigned int
3782a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayusonf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
3792a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		const struct net_device *in, const struct net_device *out,
3802a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		unsigned int (*do_chain)(const struct nf_hook_ops *ops,
3812a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					 struct sk_buff *skb,
3822a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					 const struct net_device *in,
3832a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					 const struct net_device *out,
3842a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					 struct nf_conn *ct))
3852a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso{
3862a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso#ifdef CONFIG_XFRM
3872a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	const struct nf_conn *ct;
3882a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	enum ip_conntrack_info ctinfo;
3892a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	int err;
3902a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso#endif
3912a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	unsigned int ret;
3922a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3932a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	/* root is playing with raw sockets. */
3942a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	if (skb->len < sizeof(struct ipv6hdr))
3952a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		return NF_ACCEPT;
3962a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
3972a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain);
3982a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso#ifdef CONFIG_XFRM
3992a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	if (ret != NF_DROP && ret != NF_STOLEN &&
4002a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	    !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
4012a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	    (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
4022a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
4032a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
4042a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
4052a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				      &ct->tuplehash[!dir].tuple.dst.u3) ||
4062a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		    (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
4072a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		     ct->tuplehash[dir].tuple.src.u.all !=
4082a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		     ct->tuplehash[!dir].tuple.dst.u.all)) {
4092a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			err = nf_xfrm_me_harder(skb, AF_INET6);
4102a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			if (err < 0)
4112a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				ret = NF_DROP_ERR(err);
4122a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		}
4132a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	}
4142a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso#endif
4152a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	return ret;
4162a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso}
4172a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira AyusoEXPORT_SYMBOL_GPL(nf_nat_ipv6_out);
4182a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
4192a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayusounsigned int
4202a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayusonf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
4212a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		     const struct net_device *in, const struct net_device *out,
4222a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		     unsigned int (*do_chain)(const struct nf_hook_ops *ops,
4232a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					      struct sk_buff *skb,
4242a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					      const struct net_device *in,
4252a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					      const struct net_device *out,
4262a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso					      struct nf_conn *ct))
4272a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso{
4282a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	const struct nf_conn *ct;
4292a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	enum ip_conntrack_info ctinfo;
4302a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	unsigned int ret;
4312a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	int err;
4322a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
4332a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	/* root is playing with raw sockets. */
4342a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	if (skb->len < sizeof(struct ipv6hdr))
4352a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		return NF_ACCEPT;
4362a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
4372a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain);
4382a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	if (ret != NF_DROP && ret != NF_STOLEN &&
4392a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	    (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
4402a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
4412a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
4422a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
4432a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				      &ct->tuplehash[!dir].tuple.src.u3)) {
4442a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			err = ip6_route_me_harder(skb);
4452a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			if (err < 0)
4462a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				ret = NF_DROP_ERR(err);
4472a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		}
4482a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso#ifdef CONFIG_XFRM
4492a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
4502a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			 ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
4512a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			 ct->tuplehash[dir].tuple.dst.u.all !=
4522a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			 ct->tuplehash[!dir].tuple.src.u.all) {
4532a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			err = nf_xfrm_me_harder(skb, AF_INET6);
4542a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso			if (err < 0)
4552a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso				ret = NF_DROP_ERR(err);
4562a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso		}
4572a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso#endif
4582a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	}
4592a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso	return ret;
4602a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso}
4612a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira AyusoEXPORT_SYMBOL_GPL(nf_nat_ipv6_local_fn);
4622a5538e9aa4929329813bee69922c9ae4990fcadPablo Neira Ayuso
46358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic int __init nf_nat_l3proto_ipv6_init(void)
46458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
46558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	int err;
46658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
46758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	err = nf_nat_l4proto_register(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
46858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (err < 0)
46958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		goto err1;
47058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	err = nf_nat_l3proto_register(&nf_nat_l3proto_ipv6);
47158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	if (err < 0)
47258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy		goto err2;
47358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	return err;
47458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
47558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardyerr2:
47658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
47758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardyerr1:
47858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	return err;
47958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
48058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
48158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardystatic void __exit nf_nat_l3proto_ipv6_exit(void)
48258a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy{
48358a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv6);
48458a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy	nf_nat_l4proto_unregister(NFPROTO_IPV6, &nf_nat_l4proto_icmpv6);
48558a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy}
48658a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
48758a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardyMODULE_LICENSE("GPL");
48858a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardyMODULE_ALIAS("nf-nat-" __stringify(AF_INET6));
48958a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardy
49058a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardymodule_init(nf_nat_l3proto_ipv6_init);
49158a317f1061c894d2344c0b6a18ab4a64b69b815Patrick McHardymodule_exit(nf_nat_l3proto_ipv6_exit);
492