2 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
3 * Copyright (c) 2012 Intel Corporation
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/list.h>
14 #include <linux/skbuff.h>
16 #include <linux/netfilter.h>
17 #include <linux/netfilter_ipv6.h>
18 #include <linux/netfilter/nf_tables.h>
19 #include <net/netfilter/nf_conntrack.h>
20 #include <net/netfilter/nf_nat.h>
21 #include <net/netfilter/nf_nat_core.h>
22 #include <net/netfilter/nf_tables.h>
23 #include <net/netfilter/nf_tables_ipv6.h>
24 #include <net/netfilter/nf_nat_l3proto.h>
31 static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops,
33 const struct net_device *in,
34 const struct net_device *out,
35 int (*okfn)(struct sk_buff *))
37 enum ip_conntrack_info ctinfo;
38 struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
39 struct nf_conn_nat *nat;
40 enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
44 struct nft_pktinfo pkt;
47 if (ct == NULL || nf_ct_is_untracked(ct))
52 /* Conntrack module was loaded late, can't add extension. */
53 if (nf_ct_is_confirmed(ct))
55 nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
62 case IP_CT_RELATED + IP_CT_IS_REPLY:
63 nexthdr = ipv6_hdr(skb)->nexthdr;
64 hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
67 if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
68 if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
77 if (nf_nat_initialized(ct, maniptype))
80 nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
82 ret = nft_do_chain_pktinfo(&pkt, ops);
85 if (!nf_nat_initialized(ct, maniptype)) {
86 ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
94 return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
97 static unsigned int nf_nat_ipv6_prerouting(const struct nf_hook_ops *ops,
99 const struct net_device *in,
100 const struct net_device *out,
101 int (*okfn)(struct sk_buff *))
103 struct in6_addr daddr = ipv6_hdr(skb)->daddr;
106 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
107 if (ret != NF_DROP && ret != NF_STOLEN &&
108 ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
114 static unsigned int nf_nat_ipv6_postrouting(const struct nf_hook_ops *ops,
116 const struct net_device *in,
117 const struct net_device *out,
118 int (*okfn)(struct sk_buff *))
120 enum ip_conntrack_info ctinfo __maybe_unused;
121 const struct nf_conn *ct __maybe_unused;
124 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
126 if (ret != NF_DROP && ret != NF_STOLEN &&
127 !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
128 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
129 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
131 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
132 &ct->tuplehash[!dir].tuple.dst.u3) ||
133 (ct->tuplehash[dir].tuple.src.u.all !=
134 ct->tuplehash[!dir].tuple.dst.u.all))
135 if (nf_xfrm_me_harder(skb, AF_INET6) < 0)
142 static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops,
144 const struct net_device *in,
145 const struct net_device *out,
146 int (*okfn)(struct sk_buff *))
148 enum ip_conntrack_info ctinfo;
149 const struct nf_conn *ct;
152 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
153 if (ret != NF_DROP && ret != NF_STOLEN &&
154 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
155 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
157 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
158 &ct->tuplehash[!dir].tuple.src.u3)) {
159 if (ip6_route_me_harder(skb))
163 else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
164 ct->tuplehash[dir].tuple.dst.u.all !=
165 ct->tuplehash[!dir].tuple.src.u.all)
166 if (nf_xfrm_me_harder(skb, AF_INET6))
173 static struct nf_chain_type nft_chain_nat_ipv6 = {
174 .family = NFPROTO_IPV6,
176 .type = NFT_CHAIN_T_NAT,
177 .hook_mask = (1 << NF_INET_PRE_ROUTING) |
178 (1 << NF_INET_POST_ROUTING) |
179 (1 << NF_INET_LOCAL_OUT) |
180 (1 << NF_INET_LOCAL_IN),
182 [NF_INET_PRE_ROUTING] = nf_nat_ipv6_prerouting,
183 [NF_INET_POST_ROUTING] = nf_nat_ipv6_postrouting,
184 [NF_INET_LOCAL_OUT] = nf_nat_ipv6_output,
185 [NF_INET_LOCAL_IN] = nf_nat_ipv6_fn,
190 static int __init nft_chain_nat_ipv6_init(void)
194 err = nft_register_chain_type(&nft_chain_nat_ipv6);
201 static void __exit nft_chain_nat_ipv6_exit(void)
203 nft_unregister_chain_type(&nft_chain_nat_ipv6);
206 module_init(nft_chain_nat_ipv6_init);
207 module_exit(nft_chain_nat_ipv6_exit);
209 MODULE_LICENSE("GPL");
210 MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
211 MODULE_ALIAS_NFT_CHAIN(AF_INET6, "nat");