1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
4  *
5  * Based on Rusty Russell's IPv4 NAT code. Development of IPv6 NAT
6  * funded by Astaro.
7  */
8 
9 #include <linux/module.h>
10 #include <linux/netfilter.h>
11 #include <linux/netfilter_ipv6.h>
12 #include <linux/netfilter_ipv6/ip6_tables.h>
13 #include <linux/ipv6.h>
14 #include <net/ipv6.h>
15 
16 #include <net/netfilter/nf_nat.h>
17 
18 static int __net_init ip6table_nat_table_init(struct net *net);
19 
20 static const struct xt_table nf_nat_ipv6_table = {
21 	.name		= "nat",
22 	.valid_hooks	= (1 << NF_INET_PRE_ROUTING) |
23 			  (1 << NF_INET_POST_ROUTING) |
24 			  (1 << NF_INET_LOCAL_OUT) |
25 			  (1 << NF_INET_LOCAL_IN),
26 	.me		= THIS_MODULE,
27 	.af		= NFPROTO_IPV6,
28 	.table_init	= ip6table_nat_table_init,
29 };
30 
31 static unsigned int ip6table_nat_do_chain(void *priv,
32 					  struct sk_buff *skb,
33 					  const struct nf_hook_state *state)
34 {
35 	return ip6t_do_table(skb, state, state->net->ipv6.ip6table_nat);
36 }
37 
38 static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
39 	{
40 		.hook		= ip6table_nat_do_chain,
41 		.pf		= NFPROTO_IPV6,
42 		.hooknum	= NF_INET_PRE_ROUTING,
43 		.priority	= NF_IP6_PRI_NAT_DST,
44 	},
45 	{
46 		.hook		= ip6table_nat_do_chain,
47 		.pf		= NFPROTO_IPV6,
48 		.hooknum	= NF_INET_POST_ROUTING,
49 		.priority	= NF_IP6_PRI_NAT_SRC,
50 	},
51 	{
52 		.hook		= ip6table_nat_do_chain,
53 		.pf		= NFPROTO_IPV6,
54 		.hooknum	= NF_INET_LOCAL_OUT,
55 		.priority	= NF_IP6_PRI_NAT_DST,
56 	},
57 	{
58 		.hook		= ip6table_nat_do_chain,
59 		.pf		= NFPROTO_IPV6,
60 		.hooknum	= NF_INET_LOCAL_IN,
61 		.priority	= NF_IP6_PRI_NAT_SRC,
62 	},
63 };
64 
65 static int ip6t_nat_register_lookups(struct net *net)
66 {
67 	int i, ret;
68 
69 	for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++) {
70 		ret = nf_nat_ipv6_register_fn(net, &nf_nat_ipv6_ops[i]);
71 		if (ret) {
72 			while (i)
73 				nf_nat_ipv6_unregister_fn(net, &nf_nat_ipv6_ops[--i]);
74 
75 			return ret;
76 		}
77 	}
78 
79 	return 0;
80 }
81 
82 static void ip6t_nat_unregister_lookups(struct net *net)
83 {
84 	int i;
85 
86 	for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++)
87 		nf_nat_ipv6_unregister_fn(net, &nf_nat_ipv6_ops[i]);
88 }
89 
90 static int __net_init ip6table_nat_table_init(struct net *net)
91 {
92 	struct ip6t_replace *repl;
93 	int ret;
94 
95 	if (net->ipv6.ip6table_nat)
96 		return 0;
97 
98 	repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table);
99 	if (repl == NULL)
100 		return -ENOMEM;
101 	ret = ip6t_register_table(net, &nf_nat_ipv6_table, repl,
102 				  NULL, &net->ipv6.ip6table_nat);
103 	if (ret < 0) {
104 		kfree(repl);
105 		return ret;
106 	}
107 
108 	ret = ip6t_nat_register_lookups(net);
109 	if (ret < 0) {
110 		ip6t_unregister_table(net, net->ipv6.ip6table_nat, NULL);
111 		net->ipv6.ip6table_nat = NULL;
112 	}
113 	kfree(repl);
114 	return ret;
115 }
116 
117 static void __net_exit ip6table_nat_net_pre_exit(struct net *net)
118 {
119 	if (net->ipv6.ip6table_nat)
120 		ip6t_nat_unregister_lookups(net);
121 }
122 
123 static void __net_exit ip6table_nat_net_exit(struct net *net)
124 {
125 	if (!net->ipv6.ip6table_nat)
126 		return;
127 	ip6t_unregister_table_exit(net, net->ipv6.ip6table_nat);
128 	net->ipv6.ip6table_nat = NULL;
129 }
130 
131 static struct pernet_operations ip6table_nat_net_ops = {
132 	.pre_exit = ip6table_nat_net_pre_exit,
133 	.exit	= ip6table_nat_net_exit,
134 };
135 
136 static int __init ip6table_nat_init(void)
137 {
138 	int ret = register_pernet_subsys(&ip6table_nat_net_ops);
139 
140 	if (ret)
141 		return ret;
142 
143 	ret = ip6table_nat_table_init(&init_net);
144 	if (ret)
145 		unregister_pernet_subsys(&ip6table_nat_net_ops);
146 	return ret;
147 }
148 
149 static void __exit ip6table_nat_exit(void)
150 {
151 	unregister_pernet_subsys(&ip6table_nat_net_ops);
152 }
153 
154 module_init(ip6table_nat_init);
155 module_exit(ip6table_nat_exit);
156 
157 MODULE_LICENSE("GPL");
158