1 // SPDX-License-Identifier: GPL-2.0-only
2 /* (C) 1999-2001 Paul `Rusty' Russell
3 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
4 * (C) 2011 Patrick McHardy <kaber@trash.net>
5 */
6
7 #include <linux/module.h>
8 #include <linux/netfilter.h>
9 #include <linux/netfilter_ipv4.h>
10 #include <linux/netfilter_ipv4/ip_tables.h>
11 #include <linux/ip.h>
12 #include <net/ip.h>
13
14 #include <net/netfilter/nf_nat.h>
15
16 struct iptable_nat_pernet {
17 struct nf_hook_ops *nf_nat_ops;
18 };
19
20 static unsigned int iptable_nat_net_id __read_mostly;
21
22 static const struct xt_table nf_nat_ipv4_table = {
23 .name = "nat",
24 .valid_hooks = (1 << NF_INET_PRE_ROUTING) |
25 (1 << NF_INET_POST_ROUTING) |
26 (1 << NF_INET_LOCAL_OUT) |
27 (1 << NF_INET_LOCAL_IN),
28 .me = THIS_MODULE,
29 .af = NFPROTO_IPV4,
30 };
31
iptable_nat_do_chain(void * priv,struct sk_buff * skb,const struct nf_hook_state * state)32 static unsigned int iptable_nat_do_chain(void *priv,
33 struct sk_buff *skb,
34 const struct nf_hook_state *state)
35 {
36 return ipt_do_table(skb, state, priv);
37 }
38
39 static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
40 {
41 .hook = iptable_nat_do_chain,
42 .pf = NFPROTO_IPV4,
43 .hooknum = NF_INET_PRE_ROUTING,
44 .priority = NF_IP_PRI_NAT_DST,
45 },
46 {
47 .hook = iptable_nat_do_chain,
48 .pf = NFPROTO_IPV4,
49 .hooknum = NF_INET_POST_ROUTING,
50 .priority = NF_IP_PRI_NAT_SRC,
51 },
52 {
53 .hook = iptable_nat_do_chain,
54 .pf = NFPROTO_IPV4,
55 .hooknum = NF_INET_LOCAL_OUT,
56 .priority = NF_IP_PRI_NAT_DST,
57 },
58 {
59 .hook = iptable_nat_do_chain,
60 .pf = NFPROTO_IPV4,
61 .hooknum = NF_INET_LOCAL_IN,
62 .priority = NF_IP_PRI_NAT_SRC,
63 },
64 };
65
ipt_nat_register_lookups(struct net * net)66 static int ipt_nat_register_lookups(struct net *net)
67 {
68 struct iptable_nat_pernet *xt_nat_net;
69 struct nf_hook_ops *ops;
70 struct xt_table *table;
71 int i, ret;
72
73 xt_nat_net = net_generic(net, iptable_nat_net_id);
74 table = xt_find_table(net, NFPROTO_IPV4, "nat");
75 if (WARN_ON_ONCE(!table))
76 return -ENOENT;
77
78 ops = kmemdup(nf_nat_ipv4_ops, sizeof(nf_nat_ipv4_ops), GFP_KERNEL);
79 if (!ops)
80 return -ENOMEM;
81
82 for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++) {
83 ops[i].priv = table;
84 ret = nf_nat_ipv4_register_fn(net, &ops[i]);
85 if (ret) {
86 while (i)
87 nf_nat_ipv4_unregister_fn(net, &ops[--i]);
88
89 kfree(ops);
90 return ret;
91 }
92 }
93
94 xt_nat_net->nf_nat_ops = ops;
95 return 0;
96 }
97
ipt_nat_unregister_lookups(struct net * net)98 static void ipt_nat_unregister_lookups(struct net *net)
99 {
100 struct iptable_nat_pernet *xt_nat_net = net_generic(net, iptable_nat_net_id);
101 struct nf_hook_ops *ops = xt_nat_net->nf_nat_ops;
102 int i;
103
104 if (!ops)
105 return;
106
107 for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++)
108 nf_nat_ipv4_unregister_fn(net, &ops[i]);
109
110 kfree(ops);
111 }
112
iptable_nat_table_init(struct net * net)113 static int iptable_nat_table_init(struct net *net)
114 {
115 struct ipt_replace *repl;
116 int ret;
117
118 repl = ipt_alloc_initial_table(&nf_nat_ipv4_table);
119 if (repl == NULL)
120 return -ENOMEM;
121
122 ret = ipt_register_table(net, &nf_nat_ipv4_table, repl, NULL);
123 if (ret < 0) {
124 kfree(repl);
125 return ret;
126 }
127
128 ret = ipt_nat_register_lookups(net);
129 if (ret < 0)
130 ipt_unregister_table_exit(net, "nat");
131
132 kfree(repl);
133 return ret;
134 }
135
iptable_nat_net_pre_exit(struct net * net)136 static void __net_exit iptable_nat_net_pre_exit(struct net *net)
137 {
138 ipt_nat_unregister_lookups(net);
139 }
140
iptable_nat_net_exit(struct net * net)141 static void __net_exit iptable_nat_net_exit(struct net *net)
142 {
143 ipt_unregister_table_exit(net, "nat");
144 }
145
146 static struct pernet_operations iptable_nat_net_ops = {
147 .pre_exit = iptable_nat_net_pre_exit,
148 .exit = iptable_nat_net_exit,
149 .id = &iptable_nat_net_id,
150 .size = sizeof(struct iptable_nat_pernet),
151 };
152
iptable_nat_init(void)153 static int __init iptable_nat_init(void)
154 {
155 int ret = xt_register_template(&nf_nat_ipv4_table,
156 iptable_nat_table_init);
157
158 if (ret < 0)
159 return ret;
160
161 ret = register_pernet_subsys(&iptable_nat_net_ops);
162 if (ret < 0) {
163 xt_unregister_template(&nf_nat_ipv4_table);
164 return ret;
165 }
166
167 return ret;
168 }
169
iptable_nat_exit(void)170 static void __exit iptable_nat_exit(void)
171 {
172 unregister_pernet_subsys(&iptable_nat_net_ops);
173 xt_unregister_template(&nf_nat_ipv4_table);
174 }
175
176 module_init(iptable_nat_init);
177 module_exit(iptable_nat_exit);
178
179 MODULE_LICENSE("GPL");
180