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