Commit | Line | Data |
---|---|---|
5b1158e9 JK |
1 | /* (C) 1999-2001 Paul `Rusty' Russell |
2 | * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org> | |
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 | /* Everything about the rules for NAT. */ | |
ff67e4e4 | 10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
5b1158e9 JK |
11 | #include <linux/types.h> |
12 | #include <linux/ip.h> | |
13 | #include <linux/netfilter.h> | |
14 | #include <linux/netfilter_ipv4.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/kmod.h> | |
17 | #include <linux/skbuff.h> | |
18 | #include <linux/proc_fs.h> | |
5a0e3ad6 | 19 | #include <linux/slab.h> |
5b1158e9 JK |
20 | #include <net/checksum.h> |
21 | #include <net/route.h> | |
22 | #include <linux/bitops.h> | |
23 | ||
24 | #include <linux/netfilter_ipv4/ip_tables.h> | |
25 | #include <net/netfilter/nf_nat.h> | |
26 | #include <net/netfilter/nf_nat_core.h> | |
27 | #include <net/netfilter/nf_nat_rule.h> | |
28 | ||
6e23ae2a PM |
29 | #define NAT_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | \ |
30 | (1 << NF_INET_POST_ROUTING) | \ | |
c68cd6cc PM |
31 | (1 << NF_INET_LOCAL_OUT) | \ |
32 | (1 << NF_INET_LOCAL_IN)) | |
5b1158e9 | 33 | |
35aad0ff | 34 | static const struct xt_table nat_table = { |
5b1158e9 JK |
35 | .name = "nat", |
36 | .valid_hooks = NAT_VALID_HOOKS, | |
5b1158e9 | 37 | .me = THIS_MODULE, |
f88e6a8a | 38 | .af = NFPROTO_IPV4, |
5b1158e9 JK |
39 | }; |
40 | ||
41 | /* Source NAT */ | |
7eb35586 | 42 | static unsigned int |
4b560b44 | 43 | ipt_snat_target(struct sk_buff *skb, const struct xt_action_param *par) |
5b1158e9 JK |
44 | { |
45 | struct nf_conn *ct; | |
46 | enum ip_conntrack_info ctinfo; | |
cbc9f2f4 | 47 | const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; |
5b1158e9 | 48 | |
c68cd6cc PM |
49 | NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING || |
50 | par->hooknum == NF_INET_LOCAL_IN); | |
5b1158e9 | 51 | |
3db05fea | 52 | ct = nf_ct_get(skb, &ctinfo); |
5b1158e9 JK |
53 | |
54 | /* Connection must be valid and new. */ | |
55 | NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || | |
fb048833 | 56 | ctinfo == IP_CT_RELATED_REPLY)); |
7eb35586 | 57 | NF_CT_ASSERT(par->out != NULL); |
5b1158e9 | 58 | |
cbc9f2f4 | 59 | return nf_nat_setup_info(ct, &mr->range[0], NF_NAT_MANIP_SRC); |
5b1158e9 JK |
60 | } |
61 | ||
7eb35586 | 62 | static unsigned int |
4b560b44 | 63 | ipt_dnat_target(struct sk_buff *skb, const struct xt_action_param *par) |
5b1158e9 JK |
64 | { |
65 | struct nf_conn *ct; | |
66 | enum ip_conntrack_info ctinfo; | |
cbc9f2f4 | 67 | const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; |
5b1158e9 | 68 | |
7eb35586 JE |
69 | NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || |
70 | par->hooknum == NF_INET_LOCAL_OUT); | |
5b1158e9 | 71 | |
3db05fea | 72 | ct = nf_ct_get(skb, &ctinfo); |
5b1158e9 JK |
73 | |
74 | /* Connection must be valid and new. */ | |
75 | NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); | |
76 | ||
cbc9f2f4 | 77 | return nf_nat_setup_info(ct, &mr->range[0], NF_NAT_MANIP_DST); |
5b1158e9 JK |
78 | } |
79 | ||
135367b8 | 80 | static int ipt_snat_checkentry(const struct xt_tgchk_param *par) |
5b1158e9 | 81 | { |
cbc9f2f4 | 82 | const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; |
5b1158e9 JK |
83 | |
84 | /* Must be a valid range */ | |
85 | if (mr->rangesize != 1) { | |
ff67e4e4 | 86 | pr_info("SNAT: multiple ranges no longer supported\n"); |
d6b00a53 | 87 | return -EINVAL; |
5b1158e9 | 88 | } |
d6b00a53 | 89 | return 0; |
5b1158e9 JK |
90 | } |
91 | ||
135367b8 | 92 | static int ipt_dnat_checkentry(const struct xt_tgchk_param *par) |
5b1158e9 | 93 | { |
cbc9f2f4 | 94 | const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; |
5b1158e9 JK |
95 | |
96 | /* Must be a valid range */ | |
97 | if (mr->rangesize != 1) { | |
ff67e4e4 | 98 | pr_info("DNAT: multiple ranges no longer supported\n"); |
d6b00a53 | 99 | return -EINVAL; |
5b1158e9 | 100 | } |
d6b00a53 | 101 | return 0; |
5b1158e9 JK |
102 | } |
103 | ||
c68cd6cc | 104 | static unsigned int |
ba4c7cba | 105 | alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) |
5b1158e9 JK |
106 | { |
107 | /* Force range to this IP; let proto decide mapping for | |
cbc9f2f4 | 108 | per-proto parts (hence not NF_NAT_RANGE_PROTO_SPECIFIED). |
5b1158e9 | 109 | */ |
cbc9f2f4 | 110 | struct nf_nat_ipv4_range range; |
ed0b6d75 CG |
111 | |
112 | range.flags = 0; | |
113 | pr_debug("Allocating NULL binding for %p (%pI4)\n", ct, | |
cbc9f2f4 | 114 | HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ? |
ed0b6d75 CG |
115 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip : |
116 | &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip); | |
117 | ||
cc01dcbd | 118 | return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); |
5b1158e9 JK |
119 | } |
120 | ||
3db05fea | 121 | int nf_nat_rule_find(struct sk_buff *skb, |
5b1158e9 JK |
122 | unsigned int hooknum, |
123 | const struct net_device *in, | |
124 | const struct net_device *out, | |
ba4c7cba | 125 | struct nf_conn *ct) |
5b1158e9 | 126 | { |
e099a173 | 127 | struct net *net = nf_ct_net(ct); |
5b1158e9 JK |
128 | int ret; |
129 | ||
e099a173 | 130 | ret = ipt_do_table(skb, hooknum, in, out, net->ipv4.nat_table); |
5b1158e9 JK |
131 | |
132 | if (ret == NF_ACCEPT) { | |
133 | if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum))) | |
134 | /* NUL mapping */ | |
ba4c7cba | 135 | ret = alloc_null_binding(ct, hooknum); |
5b1158e9 JK |
136 | } |
137 | return ret; | |
138 | } | |
139 | ||
9f15c530 | 140 | static struct xt_target ipt_snat_reg __read_mostly = { |
5b1158e9 JK |
141 | .name = "SNAT", |
142 | .target = ipt_snat_target, | |
cbc9f2f4 | 143 | .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), |
5b1158e9 | 144 | .table = "nat", |
c68cd6cc | 145 | .hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_IN), |
5b1158e9 JK |
146 | .checkentry = ipt_snat_checkentry, |
147 | .family = AF_INET, | |
148 | }; | |
149 | ||
9f15c530 | 150 | static struct xt_target ipt_dnat_reg __read_mostly = { |
5b1158e9 JK |
151 | .name = "DNAT", |
152 | .target = ipt_dnat_target, | |
cbc9f2f4 | 153 | .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), |
5b1158e9 | 154 | .table = "nat", |
6e23ae2a | 155 | .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), |
5b1158e9 JK |
156 | .checkentry = ipt_dnat_checkentry, |
157 | .family = AF_INET, | |
158 | }; | |
159 | ||
e099a173 AD |
160 | static int __net_init nf_nat_rule_net_init(struct net *net) |
161 | { | |
e3eaa991 JE |
162 | struct ipt_replace *repl; |
163 | ||
164 | repl = ipt_alloc_initial_table(&nat_table); | |
165 | if (repl == NULL) | |
166 | return -ENOMEM; | |
167 | net->ipv4.nat_table = ipt_register_table(net, &nat_table, repl); | |
168 | kfree(repl); | |
e099a173 AD |
169 | if (IS_ERR(net->ipv4.nat_table)) |
170 | return PTR_ERR(net->ipv4.nat_table); | |
171 | return 0; | |
172 | } | |
173 | ||
174 | static void __net_exit nf_nat_rule_net_exit(struct net *net) | |
175 | { | |
f54e9367 | 176 | ipt_unregister_table(net, net->ipv4.nat_table); |
e099a173 AD |
177 | } |
178 | ||
179 | static struct pernet_operations nf_nat_rule_net_ops = { | |
180 | .init = nf_nat_rule_net_init, | |
181 | .exit = nf_nat_rule_net_exit, | |
182 | }; | |
183 | ||
5b1158e9 JK |
184 | int __init nf_nat_rule_init(void) |
185 | { | |
186 | int ret; | |
187 | ||
e099a173 AD |
188 | ret = register_pernet_subsys(&nf_nat_rule_net_ops); |
189 | if (ret != 0) | |
190 | goto out; | |
5b1158e9 JK |
191 | ret = xt_register_target(&ipt_snat_reg); |
192 | if (ret != 0) | |
193 | goto unregister_table; | |
194 | ||
195 | ret = xt_register_target(&ipt_dnat_reg); | |
196 | if (ret != 0) | |
197 | goto unregister_snat; | |
198 | ||
199 | return ret; | |
200 | ||
201 | unregister_snat: | |
202 | xt_unregister_target(&ipt_snat_reg); | |
203 | unregister_table: | |
e099a173 AD |
204 | unregister_pernet_subsys(&nf_nat_rule_net_ops); |
205 | out: | |
5b1158e9 JK |
206 | return ret; |
207 | } | |
208 | ||
209 | void nf_nat_rule_cleanup(void) | |
210 | { | |
211 | xt_unregister_target(&ipt_dnat_reg); | |
212 | xt_unregister_target(&ipt_snat_reg); | |
e099a173 | 213 | unregister_pernet_subsys(&nf_nat_rule_net_ops); |
5b1158e9 | 214 | } |