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))
50 nat = nf_ct_nat_ext_add(ct);
56 case IP_CT_RELATED + IP_CT_IS_REPLY:
57 nexthdr = ipv6_hdr(skb)->nexthdr;
58 hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
61 if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
62 if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
71 if (nf_nat_initialized(ct, maniptype))
74 nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
76 ret = nft_do_chain(&pkt, ops);
79 if (!nf_nat_initialized(ct, maniptype)) {
80 ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
88 return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
91 static unsigned int nf_nat_ipv6_prerouting(const struct nf_hook_ops *ops,
93 const struct net_device *in,
94 const struct net_device *out,
95 int (*okfn)(struct sk_buff *))
97 struct in6_addr daddr = ipv6_hdr(skb)->daddr;
100 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
101 if (ret != NF_DROP && ret != NF_STOLEN &&
102 ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
108 static unsigned int nf_nat_ipv6_postrouting(const struct nf_hook_ops *ops,
110 const struct net_device *in,
111 const struct net_device *out,
112 int (*okfn)(struct sk_buff *))
114 enum ip_conntrack_info ctinfo __maybe_unused;
115 const struct nf_conn *ct __maybe_unused;
118 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
120 if (ret != NF_DROP && ret != NF_STOLEN &&
121 !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
122 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
123 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
125 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
126 &ct->tuplehash[!dir].tuple.dst.u3) ||
127 (ct->tuplehash[dir].tuple.src.u.all !=
128 ct->tuplehash[!dir].tuple.dst.u.all))
129 if (nf_xfrm_me_harder(skb, AF_INET6) < 0)
136 static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops,
138 const struct net_device *in,
139 const struct net_device *out,
140 int (*okfn)(struct sk_buff *))
142 enum ip_conntrack_info ctinfo;
143 const struct nf_conn *ct;
146 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
147 if (ret != NF_DROP && ret != NF_STOLEN &&
148 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
149 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
151 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
152 &ct->tuplehash[!dir].tuple.src.u3)) {
153 if (ip6_route_me_harder(skb))
157 else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
158 ct->tuplehash[dir].tuple.dst.u.all !=
159 ct->tuplehash[!dir].tuple.src.u.all)
160 if (nf_xfrm_me_harder(skb, AF_INET6))
167 static const struct nf_chain_type nft_chain_nat_ipv6 = {
169 .type = NFT_CHAIN_T_NAT,
170 .family = NFPROTO_IPV6,
171 .owner = THIS_MODULE,
172 .hook_mask = (1 << NF_INET_PRE_ROUTING) |
173 (1 << NF_INET_POST_ROUTING) |
174 (1 << NF_INET_LOCAL_OUT) |
175 (1 << NF_INET_LOCAL_IN),
177 [NF_INET_PRE_ROUTING] = nf_nat_ipv6_prerouting,
178 [NF_INET_POST_ROUTING] = nf_nat_ipv6_postrouting,
179 [NF_INET_LOCAL_OUT] = nf_nat_ipv6_output,
180 [NF_INET_LOCAL_IN] = nf_nat_ipv6_fn,
184 static int __init nft_chain_nat_ipv6_init(void)
188 err = nft_register_chain_type(&nft_chain_nat_ipv6);
195 static void __exit nft_chain_nat_ipv6_exit(void)
197 nft_unregister_chain_type(&nft_chain_nat_ipv6);
200 module_init(nft_chain_nat_ipv6_init);
201 module_exit(nft_chain_nat_ipv6_exit);
203 MODULE_LICENSE("GPL");
204 MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
205 MODULE_ALIAS_NFT_CHAIN(AF_INET6, "nat");