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 | |
32 | static const struct nf_hook_ops nf_nat_ipv4_ops[] = { |
33 | { |
34 | .hook = ipt_do_table, |
35 | .pf = NFPROTO_IPV4, |
36 | .hooknum = NF_INET_PRE_ROUTING, |
37 | .priority = NF_IP_PRI_NAT_DST, |
38 | }, |
39 | { |
40 | .hook = ipt_do_table, |
41 | .pf = NFPROTO_IPV4, |
42 | .hooknum = NF_INET_POST_ROUTING, |
43 | .priority = NF_IP_PRI_NAT_SRC, |
44 | }, |
45 | { |
46 | .hook = ipt_do_table, |
47 | .pf = NFPROTO_IPV4, |
48 | .hooknum = NF_INET_LOCAL_OUT, |
49 | .priority = NF_IP_PRI_NAT_DST, |
50 | }, |
51 | { |
52 | .hook = ipt_do_table, |
53 | .pf = NFPROTO_IPV4, |
54 | .hooknum = NF_INET_LOCAL_IN, |
55 | .priority = NF_IP_PRI_NAT_SRC, |
56 | }, |
57 | }; |
58 | |
59 | static int ipt_nat_register_lookups(struct net *net) |
60 | { |
61 | struct iptable_nat_pernet *xt_nat_net; |
62 | struct nf_hook_ops *ops; |
63 | struct xt_table *table; |
64 | int i, ret; |
65 | |
66 | xt_nat_net = net_generic(net, id: iptable_nat_net_id); |
67 | table = xt_find_table(net, af: NFPROTO_IPV4, name: "nat" ); |
68 | if (WARN_ON_ONCE(!table)) |
69 | return -ENOENT; |
70 | |
71 | ops = kmemdup(p: nf_nat_ipv4_ops, size: sizeof(nf_nat_ipv4_ops), GFP_KERNEL); |
72 | if (!ops) |
73 | return -ENOMEM; |
74 | |
75 | for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++) { |
76 | ops[i].priv = table; |
77 | ret = nf_nat_ipv4_register_fn(net, ops: &ops[i]); |
78 | if (ret) { |
79 | while (i) |
80 | nf_nat_ipv4_unregister_fn(net, ops: &ops[--i]); |
81 | |
82 | kfree(objp: ops); |
83 | return ret; |
84 | } |
85 | } |
86 | |
87 | xt_nat_net->nf_nat_ops = ops; |
88 | return 0; |
89 | } |
90 | |
91 | static void ipt_nat_unregister_lookups(struct net *net) |
92 | { |
93 | struct iptable_nat_pernet *xt_nat_net = net_generic(net, id: iptable_nat_net_id); |
94 | struct nf_hook_ops *ops = xt_nat_net->nf_nat_ops; |
95 | int i; |
96 | |
97 | if (!ops) |
98 | return; |
99 | |
100 | for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++) |
101 | nf_nat_ipv4_unregister_fn(net, ops: &ops[i]); |
102 | |
103 | kfree(objp: ops); |
104 | } |
105 | |
106 | static int iptable_nat_table_init(struct net *net) |
107 | { |
108 | struct ipt_replace *repl; |
109 | int ret; |
110 | |
111 | repl = ipt_alloc_initial_table(&nf_nat_ipv4_table); |
112 | if (repl == NULL) |
113 | return -ENOMEM; |
114 | |
115 | ret = ipt_register_table(net, table: &nf_nat_ipv4_table, repl, NULL); |
116 | if (ret < 0) { |
117 | kfree(objp: repl); |
118 | return ret; |
119 | } |
120 | |
121 | ret = ipt_nat_register_lookups(net); |
122 | if (ret < 0) |
123 | ipt_unregister_table_exit(net, name: "nat" ); |
124 | |
125 | kfree(objp: repl); |
126 | return ret; |
127 | } |
128 | |
129 | static void __net_exit iptable_nat_net_pre_exit(struct net *net) |
130 | { |
131 | ipt_nat_unregister_lookups(net); |
132 | } |
133 | |
134 | static void __net_exit iptable_nat_net_exit(struct net *net) |
135 | { |
136 | ipt_unregister_table_exit(net, name: "nat" ); |
137 | } |
138 | |
139 | static struct pernet_operations iptable_nat_net_ops = { |
140 | .pre_exit = iptable_nat_net_pre_exit, |
141 | .exit = iptable_nat_net_exit, |
142 | .id = &iptable_nat_net_id, |
143 | .size = sizeof(struct iptable_nat_pernet), |
144 | }; |
145 | |
146 | static int __init iptable_nat_init(void) |
147 | { |
148 | int ret = xt_register_template(t: &nf_nat_ipv4_table, |
149 | table_init: iptable_nat_table_init); |
150 | |
151 | if (ret < 0) |
152 | return ret; |
153 | |
154 | ret = register_pernet_subsys(&iptable_nat_net_ops); |
155 | if (ret < 0) { |
156 | xt_unregister_template(t: &nf_nat_ipv4_table); |
157 | return ret; |
158 | } |
159 | |
160 | return ret; |
161 | } |
162 | |
163 | static void __exit iptable_nat_exit(void) |
164 | { |
165 | unregister_pernet_subsys(&iptable_nat_net_ops); |
166 | xt_unregister_template(t: &nf_nat_ipv4_table); |
167 | } |
168 | |
169 | module_init(iptable_nat_init); |
170 | module_exit(iptable_nat_exit); |
171 | |
172 | MODULE_LICENSE("GPL" ); |
173 | |