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 | struct ip6table_nat_pernet { |
19 | struct nf_hook_ops *nf_nat_ops; |
20 | }; |
21 | |
22 | static unsigned int ip6table_nat_net_id __read_mostly; |
23 | |
24 | static const struct xt_table nf_nat_ipv6_table = { |
25 | .name = "nat" , |
26 | .valid_hooks = (1 << NF_INET_PRE_ROUTING) | |
27 | (1 << NF_INET_POST_ROUTING) | |
28 | (1 << NF_INET_LOCAL_OUT) | |
29 | (1 << NF_INET_LOCAL_IN), |
30 | .me = THIS_MODULE, |
31 | .af = NFPROTO_IPV6, |
32 | }; |
33 | |
34 | static const struct nf_hook_ops nf_nat_ipv6_ops[] = { |
35 | { |
36 | .hook = ip6t_do_table, |
37 | .pf = NFPROTO_IPV6, |
38 | .hooknum = NF_INET_PRE_ROUTING, |
39 | .priority = NF_IP6_PRI_NAT_DST, |
40 | }, |
41 | { |
42 | .hook = ip6t_do_table, |
43 | .pf = NFPROTO_IPV6, |
44 | .hooknum = NF_INET_POST_ROUTING, |
45 | .priority = NF_IP6_PRI_NAT_SRC, |
46 | }, |
47 | { |
48 | .hook = ip6t_do_table, |
49 | .pf = NFPROTO_IPV6, |
50 | .hooknum = NF_INET_LOCAL_OUT, |
51 | .priority = NF_IP6_PRI_NAT_DST, |
52 | }, |
53 | { |
54 | .hook = ip6t_do_table, |
55 | .pf = NFPROTO_IPV6, |
56 | .hooknum = NF_INET_LOCAL_IN, |
57 | .priority = NF_IP6_PRI_NAT_SRC, |
58 | }, |
59 | }; |
60 | |
61 | static int ip6t_nat_register_lookups(struct net *net) |
62 | { |
63 | struct ip6table_nat_pernet *xt_nat_net; |
64 | struct nf_hook_ops *ops; |
65 | struct xt_table *table; |
66 | int i, ret; |
67 | |
68 | table = xt_find_table(net, af: NFPROTO_IPV6, name: "nat" ); |
69 | if (WARN_ON_ONCE(!table)) |
70 | return -ENOENT; |
71 | |
72 | xt_nat_net = net_generic(net, id: ip6table_nat_net_id); |
73 | ops = kmemdup(p: nf_nat_ipv6_ops, size: sizeof(nf_nat_ipv6_ops), GFP_KERNEL); |
74 | if (!ops) |
75 | return -ENOMEM; |
76 | |
77 | for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++) { |
78 | ops[i].priv = table; |
79 | ret = nf_nat_ipv6_register_fn(net, ops: &ops[i]); |
80 | if (ret) { |
81 | while (i) |
82 | nf_nat_ipv6_unregister_fn(net, ops: &ops[--i]); |
83 | |
84 | kfree(objp: ops); |
85 | return ret; |
86 | } |
87 | } |
88 | |
89 | xt_nat_net->nf_nat_ops = ops; |
90 | return 0; |
91 | } |
92 | |
93 | static void ip6t_nat_unregister_lookups(struct net *net) |
94 | { |
95 | struct ip6table_nat_pernet *xt_nat_net = net_generic(net, id: ip6table_nat_net_id); |
96 | struct nf_hook_ops *ops = xt_nat_net->nf_nat_ops; |
97 | int i; |
98 | |
99 | if (!ops) |
100 | return; |
101 | |
102 | for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++) |
103 | nf_nat_ipv6_unregister_fn(net, ops: &ops[i]); |
104 | |
105 | kfree(objp: ops); |
106 | } |
107 | |
108 | static int ip6table_nat_table_init(struct net *net) |
109 | { |
110 | struct ip6t_replace *repl; |
111 | int ret; |
112 | |
113 | repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table); |
114 | if (repl == NULL) |
115 | return -ENOMEM; |
116 | ret = ip6t_register_table(net, table: &nf_nat_ipv6_table, repl, |
117 | NULL); |
118 | if (ret < 0) { |
119 | kfree(objp: repl); |
120 | return ret; |
121 | } |
122 | |
123 | ret = ip6t_nat_register_lookups(net); |
124 | if (ret < 0) |
125 | ip6t_unregister_table_exit(net, name: "nat" ); |
126 | |
127 | kfree(objp: repl); |
128 | return ret; |
129 | } |
130 | |
131 | static void __net_exit ip6table_nat_net_pre_exit(struct net *net) |
132 | { |
133 | ip6t_nat_unregister_lookups(net); |
134 | } |
135 | |
136 | static void __net_exit ip6table_nat_net_exit(struct net *net) |
137 | { |
138 | ip6t_unregister_table_exit(net, name: "nat" ); |
139 | } |
140 | |
141 | static struct pernet_operations ip6table_nat_net_ops = { |
142 | .pre_exit = ip6table_nat_net_pre_exit, |
143 | .exit = ip6table_nat_net_exit, |
144 | .id = &ip6table_nat_net_id, |
145 | .size = sizeof(struct ip6table_nat_pernet), |
146 | }; |
147 | |
148 | static int __init ip6table_nat_init(void) |
149 | { |
150 | int ret = xt_register_template(t: &nf_nat_ipv6_table, |
151 | table_init: ip6table_nat_table_init); |
152 | |
153 | if (ret < 0) |
154 | return ret; |
155 | |
156 | ret = register_pernet_subsys(&ip6table_nat_net_ops); |
157 | if (ret) |
158 | xt_unregister_template(t: &nf_nat_ipv6_table); |
159 | |
160 | return ret; |
161 | } |
162 | |
163 | static void __exit ip6table_nat_exit(void) |
164 | { |
165 | unregister_pernet_subsys(&ip6table_nat_net_ops); |
166 | xt_unregister_template(t: &nf_nat_ipv6_table); |
167 | } |
168 | |
169 | module_init(ip6table_nat_init); |
170 | module_exit(ip6table_nat_exit); |
171 | |
172 | MODULE_LICENSE("GPL" ); |
173 | MODULE_DESCRIPTION("Ip6tables legacy nat table" ); |
174 | |