aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2014-09-10 15:46:32 -0400
committerDavid S. Miller <davem@davemloft.net>2014-09-10 15:46:32 -0400
commit0aac383353693edcd8fc5a82a692bb7e5f60d2fd (patch)
tree53750f41a82c12fb468d360b635bcbd65958ccc9 /net/ipv6
parentb167a37c7bbc6f7589f439ba7d9a49af5ad37ff5 (diff)
parent9ba1f726bec090399eb9bb9157eb32dedc8e8c45 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
Pablo Neira Ayuso says: ==================== nf-next pull request The following patchset contains Netfilter/IPVS updates for your net-next tree. Regarding nf_tables, most updates focus on consolidating the NAT infrastructure and adding support for masquerading. More specifically, they are: 1) use __u8 instead of u_int8_t in arptables header, from Mike Frysinger. 2) Add support to match by skb->pkttype to the meta expression, from Ana Rey. 3) Add support to match by cpu to the meta expression, also from Ana Rey. 4) A smatch warning about IPSET_ATTR_MARKMASK validation, patch from Vytas Dauksa. 5) Fix netnet and netportnet hash types the range support for IPv4, from Sergey Popovich. 6) Fix missing-field-initializer warnings resolved, from Mark Rustad. 7) Dan Carperter reported possible integer overflows in ipset, from Jozsef Kadlecsick. 8) Filter out accounting objects in nfacct by type, so you can selectively reset quotas, from Alexey Perevalov. 9) Move specific NAT IPv4 functions to the core so x_tables and nf_tables can share the same NAT IPv4 engine. 10) Use the new NAT IPv4 functions from nft_chain_nat_ipv4. 11) Move specific NAT IPv6 functions to the core so x_tables and nf_tables can share the same NAT IPv4 engine. 12) Use the new NAT IPv6 functions from nft_chain_nat_ipv6. 13) Refactor code to add nft_delrule(), which can be reused in the enhancement of the NFT_MSG_DELTABLE to remove a table and its content, from Arturo Borrero. 14) Add a helper function to unregister chain hooks, from Arturo Borrero. 15) A cleanup to rename to nft_delrule_by_chain for consistency with the new nft_*() functions, also from Arturo. 16) Add support to match devgroup to the meta expression, from Ana Rey. 17) Reduce stack usage for IPVS socket option, from Julian Anastasov. 18) Remove unnecessary textsearch state initialization in xt_string, from Bojan Prtvar. 19) Add several helper functions to nf_tables, more work to prepare the enhancement of NFT_MSG_DELTABLE, again from Arturo Borrero. 20) Enhance NFT_MSG_DELTABLE to delete a table and its content, from Arturo Borrero. 21) Support NAT flags in the nat expression to indicate the flavour, eg. random fully, from Arturo. 22) Add missing audit code to ebtables when replacing tables, from Nicolas Dichtel. 23) Generalize the IPv4 masquerading code to allow its re-use from nf_tables, from Arturo. 24) Generalize the IPv6 masquerading code, also from Arturo. 25) Add the new masq expression to support IPv4/IPv6 masquerading from nf_tables, also from Arturo. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/netfilter/Kconfig13
-rw-r--r--net/ipv6/netfilter/Makefile2
-rw-r--r--net/ipv6/netfilter/ip6t_MASQUERADE.c76
-rw-r--r--net/ipv6/netfilter/ip6table_nat.c233
-rw-r--r--net/ipv6/netfilter/nf_nat_l3proto_ipv6.c199
-rw-r--r--net/ipv6/netfilter/nf_nat_masquerade_ipv6.c120
-rw-r--r--net/ipv6/netfilter/nft_chain_nat_ipv6.c165
-rw-r--r--net/ipv6/netfilter/nft_masq_ipv6.c89
8 files changed, 499 insertions, 398 deletions
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index 2812816aabdc..a8f25306a46a 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -258,8 +258,21 @@ config IP6_NF_NAT
258 258
259if IP6_NF_NAT 259if IP6_NF_NAT
260 260
261config NF_NAT_MASQUERADE_IPV6
262 tristate "IPv6 masquerade support"
263 help
264 This is the kernel functionality to provide NAT in the masquerade
265 flavour (automatic source address selection) for IPv6.
266
267config NFT_MASQ_IPV6
268 tristate "IPv6 masquerade support for nf_tables"
269 depends on NF_TABLES_IPV6
270 depends on NFT_MASQ
271 select NF_NAT_MASQUERADE_IPV6
272
261config IP6_NF_TARGET_MASQUERADE 273config IP6_NF_TARGET_MASQUERADE
262 tristate "MASQUERADE target support" 274 tristate "MASQUERADE target support"
275 select NF_NAT_MASQUERADE_IPV6
263 help 276 help
264 Masquerading is a special case of NAT: all outgoing connections are 277 Masquerading is a special case of NAT: all outgoing connections are
265 changed to seem to come from a particular interface's address, and 278 changed to seem to come from a particular interface's address, and
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index c3d3286db4bb..0f7e5b3f328d 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_NF_CONNTRACK_IPV6) += nf_conntrack_ipv6.o
18 18
19nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o nf_nat_proto_icmpv6.o 19nf_nat_ipv6-y := nf_nat_l3proto_ipv6.o nf_nat_proto_icmpv6.o
20obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o 20obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o
21obj-$(CONFIG_NF_NAT_MASQUERADE_IPV6) += nf_nat_masquerade_ipv6.o
21 22
22# defrag 23# defrag
23nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o 24nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o
@@ -31,6 +32,7 @@ obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o
31obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o 32obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
32obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o 33obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o
33obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o 34obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o
35obj-$(CONFIG_NFT_MASQ_IPV6) += nft_masq_ipv6.o
34 36
35# matches 37# matches
36obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o 38obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
diff --git a/net/ipv6/netfilter/ip6t_MASQUERADE.c b/net/ipv6/netfilter/ip6t_MASQUERADE.c
index 3e4e92d5e157..7f9f45d829d2 100644
--- a/net/ipv6/netfilter/ip6t_MASQUERADE.c
+++ b/net/ipv6/netfilter/ip6t_MASQUERADE.c
@@ -19,33 +19,12 @@
19#include <net/netfilter/nf_nat.h> 19#include <net/netfilter/nf_nat.h>
20#include <net/addrconf.h> 20#include <net/addrconf.h>
21#include <net/ipv6.h> 21#include <net/ipv6.h>
22#include <net/netfilter/ipv6/nf_nat_masquerade.h>
22 23
23static unsigned int 24static unsigned int
24masquerade_tg6(struct sk_buff *skb, const struct xt_action_param *par) 25masquerade_tg6(struct sk_buff *skb, const struct xt_action_param *par)
25{ 26{
26 const struct nf_nat_range *range = par->targinfo; 27 return nf_nat_masquerade_ipv6(skb, par->targinfo, par->out);
27 enum ip_conntrack_info ctinfo;
28 struct in6_addr src;
29 struct nf_conn *ct;
30 struct nf_nat_range newrange;
31
32 ct = nf_ct_get(skb, &ctinfo);
33 NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
34 ctinfo == IP_CT_RELATED_REPLY));
35
36 if (ipv6_dev_get_saddr(dev_net(par->out), par->out,
37 &ipv6_hdr(skb)->daddr, 0, &src) < 0)
38 return NF_DROP;
39
40 nfct_nat(ct)->masq_index = par->out->ifindex;
41
42 newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
43 newrange.min_addr.in6 = src;
44 newrange.max_addr.in6 = src;
45 newrange.min_proto = range->min_proto;
46 newrange.max_proto = range->max_proto;
47
48 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC);
49} 28}
50 29
51static int masquerade_tg6_checkentry(const struct xt_tgchk_param *par) 30static int masquerade_tg6_checkentry(const struct xt_tgchk_param *par)
@@ -57,48 +36,6 @@ static int masquerade_tg6_checkentry(const struct xt_tgchk_param *par)
57 return 0; 36 return 0;
58} 37}
59 38
60static int device_cmp(struct nf_conn *ct, void *ifindex)
61{
62 const struct nf_conn_nat *nat = nfct_nat(ct);
63
64 if (!nat)
65 return 0;
66 if (nf_ct_l3num(ct) != NFPROTO_IPV6)
67 return 0;
68 return nat->masq_index == (int)(long)ifindex;
69}
70
71static int masq_device_event(struct notifier_block *this,
72 unsigned long event, void *ptr)
73{
74 const struct net_device *dev = netdev_notifier_info_to_dev(ptr);
75 struct net *net = dev_net(dev);
76
77 if (event == NETDEV_DOWN)
78 nf_ct_iterate_cleanup(net, device_cmp,
79 (void *)(long)dev->ifindex, 0, 0);
80
81 return NOTIFY_DONE;
82}
83
84static struct notifier_block masq_dev_notifier = {
85 .notifier_call = masq_device_event,
86};
87
88static int masq_inet_event(struct notifier_block *this,
89 unsigned long event, void *ptr)
90{
91 struct inet6_ifaddr *ifa = ptr;
92 struct netdev_notifier_info info;
93
94 netdev_notifier_info_init(&info, ifa->idev->dev);
95 return masq_device_event(this, event, &info);
96}
97
98static struct notifier_block masq_inet_notifier = {
99 .notifier_call = masq_inet_event,
100};
101
102static struct xt_target masquerade_tg6_reg __read_mostly = { 39static struct xt_target masquerade_tg6_reg __read_mostly = {
103 .name = "MASQUERADE", 40 .name = "MASQUERADE",
104 .family = NFPROTO_IPV6, 41 .family = NFPROTO_IPV6,
@@ -115,17 +52,14 @@ static int __init masquerade_tg6_init(void)
115 int err; 52 int err;
116 53
117 err = xt_register_target(&masquerade_tg6_reg); 54 err = xt_register_target(&masquerade_tg6_reg);
118 if (err == 0) { 55 if (err == 0)
119 register_netdevice_notifier(&masq_dev_notifier); 56 nf_nat_masquerade_ipv6_register_notifier();
120 register_inet6addr_notifier(&masq_inet_notifier);
121 }
122 57
123 return err; 58 return err;
124} 59}
125static void __exit masquerade_tg6_exit(void) 60static void __exit masquerade_tg6_exit(void)
126{ 61{
127 unregister_inet6addr_notifier(&masq_inet_notifier); 62 nf_nat_masquerade_ipv6_unregister_notifier();
128 unregister_netdevice_notifier(&masq_dev_notifier);
129 xt_unregister_target(&masquerade_tg6_reg); 63 xt_unregister_target(&masquerade_tg6_reg);
130} 64}
131 65
diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c
index 387d8b8fc18d..b0634ac996b7 100644
--- a/net/ipv6/netfilter/ip6table_nat.c
+++ b/net/ipv6/netfilter/ip6table_nat.c
@@ -30,222 +30,57 @@ static const struct xt_table nf_nat_ipv6_table = {
30 .af = NFPROTO_IPV6, 30 .af = NFPROTO_IPV6,
31}; 31};
32 32
33static unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) 33static unsigned int ip6table_nat_do_chain(const struct nf_hook_ops *ops,
34{ 34 struct sk_buff *skb,
35 /* Force range to this IP; let proto decide mapping for 35 const struct net_device *in,
36 * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). 36 const struct net_device *out,
37 */ 37 struct nf_conn *ct)
38 struct nf_nat_range range;
39
40 range.flags = 0;
41 pr_debug("Allocating NULL binding for %p (%pI6)\n", ct,
42 HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ?
43 &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6 :
44 &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6);
45
46 return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
47}
48
49static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum,
50 const struct net_device *in,
51 const struct net_device *out,
52 struct nf_conn *ct)
53{ 38{
54 struct net *net = nf_ct_net(ct); 39 struct net *net = nf_ct_net(ct);
55 unsigned int ret;
56 40
57 ret = ip6t_do_table(skb, hooknum, in, out, net->ipv6.ip6table_nat); 41 return ip6t_do_table(skb, ops->hooknum, in, out, net->ipv6.ip6table_nat);
58 if (ret == NF_ACCEPT) {
59 if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum)))
60 ret = alloc_null_binding(ct, hooknum);
61 }
62 return ret;
63} 42}
64 43
65static unsigned int 44static unsigned int ip6table_nat_fn(const struct nf_hook_ops *ops,
66nf_nat_ipv6_fn(const struct nf_hook_ops *ops, 45 struct sk_buff *skb,
67 struct sk_buff *skb, 46 const struct net_device *in,
68 const struct net_device *in, 47 const struct net_device *out,
69 const struct net_device *out, 48 int (*okfn)(struct sk_buff *))
70 int (*okfn)(struct sk_buff *))
71{ 49{
72 struct nf_conn *ct; 50 return nf_nat_ipv6_fn(ops, skb, in, out, ip6table_nat_do_chain);
73 enum ip_conntrack_info ctinfo;
74 struct nf_conn_nat *nat;
75 enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
76 __be16 frag_off;
77 int hdrlen;
78 u8 nexthdr;
79
80 ct = nf_ct_get(skb, &ctinfo);
81 /* Can't track? It's not due to stress, or conntrack would
82 * have dropped it. Hence it's the user's responsibilty to
83 * packet filter it out, or implement conntrack/NAT for that
84 * protocol. 8) --RR
85 */
86 if (!ct)
87 return NF_ACCEPT;
88
89 /* Don't try to NAT if this packet is not conntracked */
90 if (nf_ct_is_untracked(ct))
91 return NF_ACCEPT;
92
93 nat = nf_ct_nat_ext_add(ct);
94 if (nat == NULL)
95 return NF_ACCEPT;
96
97 switch (ctinfo) {
98 case IP_CT_RELATED:
99 case IP_CT_RELATED_REPLY:
100 nexthdr = ipv6_hdr(skb)->nexthdr;
101 hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
102 &nexthdr, &frag_off);
103
104 if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
105 if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
106 ops->hooknum,
107 hdrlen))
108 return NF_DROP;
109 else
110 return NF_ACCEPT;
111 }
112 /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
113 case IP_CT_NEW:
114 /* Seen it before? This can happen for loopback, retrans,
115 * or local packets.
116 */
117 if (!nf_nat_initialized(ct, maniptype)) {
118 unsigned int ret;
119
120 ret = nf_nat_rule_find(skb, ops->hooknum, in, out, ct);
121 if (ret != NF_ACCEPT)
122 return ret;
123 } else {
124 pr_debug("Already setup manip %s for ct %p\n",
125 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
126 ct);
127 if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
128 goto oif_changed;
129 }
130 break;
131
132 default:
133 /* ESTABLISHED */
134 NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
135 ctinfo == IP_CT_ESTABLISHED_REPLY);
136 if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
137 goto oif_changed;
138 }
139
140 return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
141
142oif_changed:
143 nf_ct_kill_acct(ct, ctinfo, skb);
144 return NF_DROP;
145} 51}
146 52
147static unsigned int 53static unsigned int ip6table_nat_in(const struct nf_hook_ops *ops,
148nf_nat_ipv6_in(const struct nf_hook_ops *ops, 54 struct sk_buff *skb,
149 struct sk_buff *skb, 55 const struct net_device *in,
150 const struct net_device *in, 56 const struct net_device *out,
151 const struct net_device *out, 57 int (*okfn)(struct sk_buff *))
152 int (*okfn)(struct sk_buff *))
153{ 58{
154 unsigned int ret; 59 return nf_nat_ipv6_in(ops, skb, in, out, ip6table_nat_do_chain);
155 struct in6_addr daddr = ipv6_hdr(skb)->daddr;
156
157 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
158 if (ret != NF_DROP && ret != NF_STOLEN &&
159 ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
160 skb_dst_drop(skb);
161
162 return ret;
163} 60}
164 61
165static unsigned int 62static unsigned int ip6table_nat_out(const struct nf_hook_ops *ops,
166nf_nat_ipv6_out(const struct nf_hook_ops *ops, 63 struct sk_buff *skb,
167 struct sk_buff *skb, 64 const struct net_device *in,
168 const struct net_device *in, 65 const struct net_device *out,
169 const struct net_device *out, 66 int (*okfn)(struct sk_buff *))
170 int (*okfn)(struct sk_buff *))
171{ 67{
172#ifdef CONFIG_XFRM 68 return nf_nat_ipv6_out(ops, skb, in, out, ip6table_nat_do_chain);
173 const struct nf_conn *ct;
174 enum ip_conntrack_info ctinfo;
175 int err;
176#endif
177 unsigned int ret;
178
179 /* root is playing with raw sockets. */
180 if (skb->len < sizeof(struct ipv6hdr))
181 return NF_ACCEPT;
182
183 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
184#ifdef CONFIG_XFRM
185 if (ret != NF_DROP && ret != NF_STOLEN &&
186 !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
187 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
188 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
189
190 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
191 &ct->tuplehash[!dir].tuple.dst.u3) ||
192 (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
193 ct->tuplehash[dir].tuple.src.u.all !=
194 ct->tuplehash[!dir].tuple.dst.u.all)) {
195 err = nf_xfrm_me_harder(skb, AF_INET6);
196 if (err < 0)
197 ret = NF_DROP_ERR(err);
198 }
199 }
200#endif
201 return ret;
202} 69}
203 70
204static unsigned int 71static unsigned int ip6table_nat_local_fn(const struct nf_hook_ops *ops,
205nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, 72 struct sk_buff *skb,
206 struct sk_buff *skb, 73 const struct net_device *in,
207 const struct net_device *in, 74 const struct net_device *out,
208 const struct net_device *out, 75 int (*okfn)(struct sk_buff *))
209 int (*okfn)(struct sk_buff *))
210{ 76{
211 const struct nf_conn *ct; 77 return nf_nat_ipv6_local_fn(ops, skb, in, out, ip6table_nat_do_chain);
212 enum ip_conntrack_info ctinfo;
213 unsigned int ret;
214 int err;
215
216 /* root is playing with raw sockets. */
217 if (skb->len < sizeof(struct ipv6hdr))
218 return NF_ACCEPT;
219
220 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
221 if (ret != NF_DROP && ret != NF_STOLEN &&
222 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
223 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
224
225 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
226 &ct->tuplehash[!dir].tuple.src.u3)) {
227 err = ip6_route_me_harder(skb);
228 if (err < 0)
229 ret = NF_DROP_ERR(err);
230 }
231#ifdef CONFIG_XFRM
232 else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
233 ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
234 ct->tuplehash[dir].tuple.dst.u.all !=
235 ct->tuplehash[!dir].tuple.src.u.all) {
236 err = nf_xfrm_me_harder(skb, AF_INET6);
237 if (err < 0)
238 ret = NF_DROP_ERR(err);
239 }
240#endif
241 }
242 return ret;
243} 78}
244 79
245static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = { 80static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
246 /* Before packet filtering, change destination */ 81 /* Before packet filtering, change destination */
247 { 82 {
248 .hook = nf_nat_ipv6_in, 83 .hook = ip6table_nat_in,
249 .owner = THIS_MODULE, 84 .owner = THIS_MODULE,
250 .pf = NFPROTO_IPV6, 85 .pf = NFPROTO_IPV6,
251 .hooknum = NF_INET_PRE_ROUTING, 86 .hooknum = NF_INET_PRE_ROUTING,
@@ -253,7 +88,7 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
253 }, 88 },
254 /* After packet filtering, change source */ 89 /* After packet filtering, change source */
255 { 90 {
256 .hook = nf_nat_ipv6_out, 91 .hook = ip6table_nat_out,
257 .owner = THIS_MODULE, 92 .owner = THIS_MODULE,
258 .pf = NFPROTO_IPV6, 93 .pf = NFPROTO_IPV6,
259 .hooknum = NF_INET_POST_ROUTING, 94 .hooknum = NF_INET_POST_ROUTING,
@@ -261,7 +96,7 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
261 }, 96 },
262 /* Before packet filtering, change destination */ 97 /* Before packet filtering, change destination */
263 { 98 {
264 .hook = nf_nat_ipv6_local_fn, 99 .hook = ip6table_nat_local_fn,
265 .owner = THIS_MODULE, 100 .owner = THIS_MODULE,
266 .pf = NFPROTO_IPV6, 101 .pf = NFPROTO_IPV6,
267 .hooknum = NF_INET_LOCAL_OUT, 102 .hooknum = NF_INET_LOCAL_OUT,
@@ -269,7 +104,7 @@ static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
269 }, 104 },
270 /* After packet filtering, change source */ 105 /* After packet filtering, change source */
271 { 106 {
272 .hook = nf_nat_ipv6_fn, 107 .hook = ip6table_nat_fn,
273 .owner = THIS_MODULE, 108 .owner = THIS_MODULE,
274 .pf = NFPROTO_IPV6, 109 .pf = NFPROTO_IPV6,
275 .hooknum = NF_INET_LOCAL_IN, 110 .hooknum = NF_INET_LOCAL_IN,
diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
index fc8e49b2ff3e..c5812e1c1ffb 100644
--- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c
@@ -261,6 +261,205 @@ int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
261} 261}
262EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation); 262EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
263 263
264unsigned int
265nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
266 const struct net_device *in, const struct net_device *out,
267 unsigned int (*do_chain)(const struct nf_hook_ops *ops,
268 struct sk_buff *skb,
269 const struct net_device *in,
270 const struct net_device *out,
271 struct nf_conn *ct))
272{
273 struct nf_conn *ct;
274 enum ip_conntrack_info ctinfo;
275 struct nf_conn_nat *nat;
276 enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum);
277 __be16 frag_off;
278 int hdrlen;
279 u8 nexthdr;
280
281 ct = nf_ct_get(skb, &ctinfo);
282 /* Can't track? It's not due to stress, or conntrack would
283 * have dropped it. Hence it's the user's responsibilty to
284 * packet filter it out, or implement conntrack/NAT for that
285 * protocol. 8) --RR
286 */
287 if (!ct)
288 return NF_ACCEPT;
289
290 /* Don't try to NAT if this packet is not conntracked */
291 if (nf_ct_is_untracked(ct))
292 return NF_ACCEPT;
293
294 nat = nf_ct_nat_ext_add(ct);
295 if (nat == NULL)
296 return NF_ACCEPT;
297
298 switch (ctinfo) {
299 case IP_CT_RELATED:
300 case IP_CT_RELATED_REPLY:
301 nexthdr = ipv6_hdr(skb)->nexthdr;
302 hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
303 &nexthdr, &frag_off);
304
305 if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
306 if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
307 ops->hooknum,
308 hdrlen))
309 return NF_DROP;
310 else
311 return NF_ACCEPT;
312 }
313 /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
314 case IP_CT_NEW:
315 /* Seen it before? This can happen for loopback, retrans,
316 * or local packets.
317 */
318 if (!nf_nat_initialized(ct, maniptype)) {
319 unsigned int ret;
320
321 ret = do_chain(ops, skb, in, out, ct);
322 if (ret != NF_ACCEPT)
323 return ret;
324
325 if (nf_nat_initialized(ct, HOOK2MANIP(ops->hooknum)))
326 break;
327
328 ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
329 if (ret != NF_ACCEPT)
330 return ret;
331 } else {
332 pr_debug("Already setup manip %s for ct %p\n",
333 maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
334 ct);
335 if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
336 goto oif_changed;
337 }
338 break;
339
340 default:
341 /* ESTABLISHED */
342 NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
343 ctinfo == IP_CT_ESTABLISHED_REPLY);
344 if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out))
345 goto oif_changed;
346 }
347
348 return nf_nat_packet(ct, ctinfo, ops->hooknum, skb);
349
350oif_changed:
351 nf_ct_kill_acct(ct, ctinfo, skb);
352 return NF_DROP;
353}
354EXPORT_SYMBOL_GPL(nf_nat_ipv6_fn);
355
356unsigned int
357nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb,
358 const struct net_device *in, const struct net_device *out,
359 unsigned int (*do_chain)(const struct nf_hook_ops *ops,
360 struct sk_buff *skb,
361 const struct net_device *in,
362 const struct net_device *out,
363 struct nf_conn *ct))
364{
365 unsigned int ret;
366 struct in6_addr daddr = ipv6_hdr(skb)->daddr;
367
368 ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain);
369 if (ret != NF_DROP && ret != NF_STOLEN &&
370 ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
371 skb_dst_drop(skb);
372
373 return ret;
374}
375EXPORT_SYMBOL_GPL(nf_nat_ipv6_in);
376
377unsigned int
378nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb,
379 const struct net_device *in, const struct net_device *out,
380 unsigned int (*do_chain)(const struct nf_hook_ops *ops,
381 struct sk_buff *skb,
382 const struct net_device *in,
383 const struct net_device *out,
384 struct nf_conn *ct))
385{
386#ifdef CONFIG_XFRM
387 const struct nf_conn *ct;
388 enum ip_conntrack_info ctinfo;
389 int err;
390#endif
391 unsigned int ret;
392
393 /* root is playing with raw sockets. */
394 if (skb->len < sizeof(struct ipv6hdr))
395 return NF_ACCEPT;
396
397 ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain);
398#ifdef CONFIG_XFRM
399 if (ret != NF_DROP && ret != NF_STOLEN &&
400 !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
401 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
402 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
403
404 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
405 &ct->tuplehash[!dir].tuple.dst.u3) ||
406 (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
407 ct->tuplehash[dir].tuple.src.u.all !=
408 ct->tuplehash[!dir].tuple.dst.u.all)) {
409 err = nf_xfrm_me_harder(skb, AF_INET6);
410 if (err < 0)
411 ret = NF_DROP_ERR(err);
412 }
413 }
414#endif
415 return ret;
416}
417EXPORT_SYMBOL_GPL(nf_nat_ipv6_out);
418
419unsigned int
420nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb,
421 const struct net_device *in, const struct net_device *out,
422 unsigned int (*do_chain)(const struct nf_hook_ops *ops,
423 struct sk_buff *skb,
424 const struct net_device *in,
425 const struct net_device *out,
426 struct nf_conn *ct))
427{
428 const struct nf_conn *ct;
429 enum ip_conntrack_info ctinfo;
430 unsigned int ret;
431 int err;
432
433 /* root is playing with raw sockets. */
434 if (skb->len < sizeof(struct ipv6hdr))
435 return NF_ACCEPT;
436
437 ret = nf_nat_ipv6_fn(ops, skb, in, out, do_chain);
438 if (ret != NF_DROP && ret != NF_STOLEN &&
439 (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
440 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
441
442 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
443 &ct->tuplehash[!dir].tuple.src.u3)) {
444 err = ip6_route_me_harder(skb);
445 if (err < 0)
446 ret = NF_DROP_ERR(err);
447 }
448#ifdef CONFIG_XFRM
449 else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
450 ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
451 ct->tuplehash[dir].tuple.dst.u.all !=
452 ct->tuplehash[!dir].tuple.src.u.all) {
453 err = nf_xfrm_me_harder(skb, AF_INET6);
454 if (err < 0)
455 ret = NF_DROP_ERR(err);
456 }
457#endif
458 }
459 return ret;
460}
461EXPORT_SYMBOL_GPL(nf_nat_ipv6_local_fn);
462
264static int __init nf_nat_l3proto_ipv6_init(void) 463static int __init nf_nat_l3proto_ipv6_init(void)
265{ 464{
266 int err; 465 int err;
diff --git a/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c b/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c
new file mode 100644
index 000000000000..7745609665cd
--- /dev/null
+++ b/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c
@@ -0,0 +1,120 @@
1/*
2 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * Based on Rusty Russell's IPv6 MASQUERADE target. Development of IPv6
9 * NAT funded by Astaro.
10 */
11
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/atomic.h>
15#include <linux/netdevice.h>
16#include <linux/ipv6.h>
17#include <linux/netfilter.h>
18#include <linux/netfilter_ipv6.h>
19#include <net/netfilter/nf_nat.h>
20#include <net/addrconf.h>
21#include <net/ipv6.h>
22#include <net/netfilter/ipv6/nf_nat_masquerade.h>
23
24unsigned int
25nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range *range,
26 const struct net_device *out)
27{
28 enum ip_conntrack_info ctinfo;
29 struct in6_addr src;
30 struct nf_conn *ct;
31 struct nf_nat_range newrange;
32
33 ct = nf_ct_get(skb, &ctinfo);
34 NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
35 ctinfo == IP_CT_RELATED_REPLY));
36
37 if (ipv6_dev_get_saddr(dev_net(out), out,
38 &ipv6_hdr(skb)->daddr, 0, &src) < 0)
39 return NF_DROP;
40
41 nfct_nat(ct)->masq_index = out->ifindex;
42
43 newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
44 newrange.min_addr.in6 = src;
45 newrange.max_addr.in6 = src;
46 newrange.min_proto = range->min_proto;
47 newrange.max_proto = range->max_proto;
48
49 return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_SRC);
50}
51EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6);
52
53static int device_cmp(struct nf_conn *ct, void *ifindex)
54{
55 const struct nf_conn_nat *nat = nfct_nat(ct);
56
57 if (!nat)
58 return 0;
59 if (nf_ct_l3num(ct) != NFPROTO_IPV6)
60 return 0;
61 return nat->masq_index == (int)(long)ifindex;
62}
63
64static int masq_device_event(struct notifier_block *this,
65 unsigned long event, void *ptr)
66{
67 const struct net_device *dev = netdev_notifier_info_to_dev(ptr);
68 struct net *net = dev_net(dev);
69
70 if (event == NETDEV_DOWN)
71 nf_ct_iterate_cleanup(net, device_cmp,
72 (void *)(long)dev->ifindex, 0, 0);
73
74 return NOTIFY_DONE;
75}
76
77static struct notifier_block masq_dev_notifier = {
78 .notifier_call = masq_device_event,
79};
80
81static int masq_inet_event(struct notifier_block *this,
82 unsigned long event, void *ptr)
83{
84 struct inet6_ifaddr *ifa = ptr;
85 struct netdev_notifier_info info;
86
87 netdev_notifier_info_init(&info, ifa->idev->dev);
88 return masq_device_event(this, event, &info);
89}
90
91static struct notifier_block masq_inet_notifier = {
92 .notifier_call = masq_inet_event,
93};
94
95static atomic_t masquerade_notifier_refcount = ATOMIC_INIT(0);
96
97void nf_nat_masquerade_ipv6_register_notifier(void)
98{
99 /* check if the notifier is already set */
100 if (atomic_inc_return(&masquerade_notifier_refcount) > 1)
101 return;
102
103 register_netdevice_notifier(&masq_dev_notifier);
104 register_inet6addr_notifier(&masq_inet_notifier);
105}
106EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_register_notifier);
107
108void nf_nat_masquerade_ipv6_unregister_notifier(void)
109{
110 /* check if the notifier still has clients */
111 if (atomic_dec_return(&masquerade_notifier_refcount) > 0)
112 return;
113
114 unregister_inet6addr_notifier(&masq_inet_notifier);
115 unregister_netdevice_notifier(&masq_dev_notifier);
116}
117EXPORT_SYMBOL_GPL(nf_nat_masquerade_ipv6_unregister_notifier);
118
119MODULE_LICENSE("GPL");
120MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c
index d189fcb437fe..1c4b75dd425b 100644
--- a/net/ipv6/netfilter/nft_chain_nat_ipv6.c
+++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c
@@ -24,144 +24,53 @@
24#include <net/netfilter/nf_nat_l3proto.h> 24#include <net/netfilter/nf_nat_l3proto.h>
25#include <net/ipv6.h> 25#include <net/ipv6.h>
26 26
27/* 27static unsigned int nft_nat_do_chain(const struct nf_hook_ops *ops,
28 * IPv6 NAT chains 28 struct sk_buff *skb,
29 */ 29 const struct net_device *in,
30 30 const struct net_device *out,
31static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, 31 struct nf_conn *ct)
32 struct sk_buff *skb,
33 const struct net_device *in,
34 const struct net_device *out,
35 int (*okfn)(struct sk_buff *))
36{ 32{
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);
41 __be16 frag_off;
42 int hdrlen;
43 u8 nexthdr;
44 struct nft_pktinfo pkt; 33 struct nft_pktinfo pkt;
45 unsigned int ret;
46
47 if (ct == NULL || nf_ct_is_untracked(ct))
48 return NF_ACCEPT;
49
50 nat = nf_ct_nat_ext_add(ct);
51 if (nat == NULL)
52 return NF_ACCEPT;
53
54 switch (ctinfo) {
55 case IP_CT_RELATED:
56 case IP_CT_RELATED + IP_CT_IS_REPLY:
57 nexthdr = ipv6_hdr(skb)->nexthdr;
58 hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
59 &nexthdr, &frag_off);
60
61 if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
62 if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
63 ops->hooknum,
64 hdrlen))
65 return NF_DROP;
66 else
67 return NF_ACCEPT;
68 }
69 /* Fall through */
70 case IP_CT_NEW:
71 if (nf_nat_initialized(ct, maniptype))
72 break;
73
74 nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
75 34
76 ret = nft_do_chain(&pkt, ops); 35 nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
77 if (ret != NF_ACCEPT)
78 return ret;
79 if (!nf_nat_initialized(ct, maniptype)) {
80 ret = nf_nat_alloc_null_binding(ct, ops->hooknum);
81 if (ret != NF_ACCEPT)
82 return ret;
83 }
84 default:
85 break;
86 }
87 36
88 return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); 37 return nft_do_chain(&pkt, ops);
89} 38}
90 39
91static unsigned int nf_nat_ipv6_prerouting(const struct nf_hook_ops *ops, 40static unsigned int nft_nat_ipv6_fn(const struct nf_hook_ops *ops,
92 struct sk_buff *skb, 41 struct sk_buff *skb,
93 const struct net_device *in, 42 const struct net_device *in,
94 const struct net_device *out, 43 const struct net_device *out,
95 int (*okfn)(struct sk_buff *)) 44 int (*okfn)(struct sk_buff *))
96{ 45{
97 struct in6_addr daddr = ipv6_hdr(skb)->daddr; 46 return nf_nat_ipv6_fn(ops, skb, in, out, nft_nat_do_chain);
98 unsigned int ret;
99
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))
103 skb_dst_drop(skb);
104
105 return ret;
106} 47}
107 48
108static unsigned int nf_nat_ipv6_postrouting(const struct nf_hook_ops *ops, 49static unsigned int nft_nat_ipv6_in(const struct nf_hook_ops *ops,
109 struct sk_buff *skb, 50 struct sk_buff *skb,
110 const struct net_device *in, 51 const struct net_device *in,
111 const struct net_device *out, 52 const struct net_device *out,
112 int (*okfn)(struct sk_buff *)) 53 int (*okfn)(struct sk_buff *))
113{ 54{
114 enum ip_conntrack_info ctinfo __maybe_unused; 55 return nf_nat_ipv6_in(ops, skb, in, out, nft_nat_do_chain);
115 const struct nf_conn *ct __maybe_unused;
116 unsigned int ret;
117
118 ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn);
119#ifdef CONFIG_XFRM
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);
124
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)
130 ret = NF_DROP;
131 }
132#endif
133 return ret;
134} 56}
135 57
136static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops, 58static unsigned int nft_nat_ipv6_out(const struct nf_hook_ops *ops,
137 struct sk_buff *skb, 59 struct sk_buff *skb,
138 const struct net_device *in, 60 const struct net_device *in,
139 const struct net_device *out, 61 const struct net_device *out,
140 int (*okfn)(struct sk_buff *)) 62 int (*okfn)(struct sk_buff *))
141{ 63{
142 enum ip_conntrack_info ctinfo; 64 return nf_nat_ipv6_out(ops, skb, in, out, nft_nat_do_chain);
143 const struct nf_conn *ct; 65}
144 unsigned int ret;
145
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);
150 66
151 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, 67static unsigned int nft_nat_ipv6_local_fn(const struct nf_hook_ops *ops,
152 &ct->tuplehash[!dir].tuple.src.u3)) { 68 struct sk_buff *skb,
153 if (ip6_route_me_harder(skb)) 69 const struct net_device *in,
154 ret = NF_DROP; 70 const struct net_device *out,
155 } 71 int (*okfn)(struct sk_buff *))
156#ifdef CONFIG_XFRM 72{
157 else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && 73 return nf_nat_ipv6_local_fn(ops, skb, in, out, nft_nat_do_chain);
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))
161 ret = NF_DROP;
162#endif
163 }
164 return ret;
165} 74}
166 75
167static const struct nf_chain_type nft_chain_nat_ipv6 = { 76static const struct nf_chain_type nft_chain_nat_ipv6 = {
@@ -174,10 +83,10 @@ static const struct nf_chain_type nft_chain_nat_ipv6 = {
174 (1 << NF_INET_LOCAL_OUT) | 83 (1 << NF_INET_LOCAL_OUT) |
175 (1 << NF_INET_LOCAL_IN), 84 (1 << NF_INET_LOCAL_IN),
176 .hooks = { 85 .hooks = {
177 [NF_INET_PRE_ROUTING] = nf_nat_ipv6_prerouting, 86 [NF_INET_PRE_ROUTING] = nft_nat_ipv6_in,
178 [NF_INET_POST_ROUTING] = nf_nat_ipv6_postrouting, 87 [NF_INET_POST_ROUTING] = nft_nat_ipv6_out,
179 [NF_INET_LOCAL_OUT] = nf_nat_ipv6_output, 88 [NF_INET_LOCAL_OUT] = nft_nat_ipv6_local_fn,
180 [NF_INET_LOCAL_IN] = nf_nat_ipv6_fn, 89 [NF_INET_LOCAL_IN] = nft_nat_ipv6_fn,
181 }, 90 },
182}; 91};
183 92
diff --git a/net/ipv6/netfilter/nft_masq_ipv6.c b/net/ipv6/netfilter/nft_masq_ipv6.c
new file mode 100644
index 000000000000..4e51334ef6b7
--- /dev/null
+++ b/net/ipv6/netfilter/nft_masq_ipv6.c
@@ -0,0 +1,89 @@
1/*
2 * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/kernel.h>
10#include <linux/init.h>
11#include <linux/module.h>
12#include <linux/netlink.h>
13#include <linux/netfilter.h>
14#include <linux/netfilter/nf_tables.h>
15#include <net/netfilter/nf_tables.h>
16#include <net/netfilter/nf_nat.h>
17#include <net/netfilter/nft_masq.h>
18#include <net/netfilter/ipv6/nf_nat_masquerade.h>
19
20static void nft_masq_ipv6_eval(const struct nft_expr *expr,
21 struct nft_data data[NFT_REG_MAX + 1],
22 const struct nft_pktinfo *pkt)
23{
24 struct nft_masq *priv = nft_expr_priv(expr);
25 struct nf_nat_range range;
26 unsigned int verdict;
27
28 range.flags = priv->flags;
29
30 verdict = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out);
31
32 data[NFT_REG_VERDICT].verdict = verdict;
33}
34
35static int nft_masq_ipv6_init(const struct nft_ctx *ctx,
36 const struct nft_expr *expr,
37 const struct nlattr * const tb[])
38{
39 int err;
40
41 err = nft_masq_init(ctx, expr, tb);
42 if (err < 0)
43 return err;
44
45 nf_nat_masquerade_ipv6_register_notifier();
46 return 0;
47}
48
49static void nft_masq_ipv6_destroy(const struct nft_ctx *ctx,
50 const struct nft_expr *expr)
51{
52 nf_nat_masquerade_ipv6_unregister_notifier();
53}
54
55static struct nft_expr_type nft_masq_ipv6_type;
56static const struct nft_expr_ops nft_masq_ipv6_ops = {
57 .type = &nft_masq_ipv6_type,
58 .size = NFT_EXPR_SIZE(sizeof(struct nft_masq)),
59 .eval = nft_masq_ipv6_eval,
60 .init = nft_masq_ipv6_init,
61 .destroy = nft_masq_ipv6_destroy,
62 .dump = nft_masq_dump,
63};
64
65static struct nft_expr_type nft_masq_ipv6_type __read_mostly = {
66 .family = NFPROTO_IPV6,
67 .name = "masq",
68 .ops = &nft_masq_ipv6_ops,
69 .policy = nft_masq_policy,
70 .maxattr = NFTA_MASQ_MAX,
71 .owner = THIS_MODULE,
72};
73
74static int __init nft_masq_ipv6_module_init(void)
75{
76 return nft_register_expr(&nft_masq_ipv6_type);
77}
78
79static void __exit nft_masq_ipv6_module_exit(void)
80{
81 nft_unregister_expr(&nft_masq_ipv6_type);
82}
83
84module_init(nft_masq_ipv6_module_init);
85module_exit(nft_masq_ipv6_module_exit);
86
87MODULE_LICENSE("GPL");
88MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");
89MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "masq");