~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ipv4/netfilter/ip_nat_standalone.c

Version: ~ [ linux-5.13-rc7 ] ~ [ linux-5.12.12 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.45 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.127 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.195 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.237 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.273 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.273 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.18.140 ] ~ [ linux-3.16.85 ] ~ [ linux-3.14.79 ] ~ [ linux-3.12.74 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /* This file contains all the functions required for the standalone
  2    ip_nat module.
  3 
  4    These are not required by the compatibility layer.
  5 */
  6 
  7 /* (c) 1999 Paul `Rusty' Russell.  Licenced under the GNU General
  8  * Public Licence.
  9  *
 10  * 23 Apr 2001: Harald Welte <laforge@gnumonks.org>
 11  *      - new API and handling of conntrack/nat helpers
 12  *      - now capable of multiple expectations for one master
 13  * */
 14 
 15 #include <linux/config.h>
 16 #include <linux/types.h>
 17 #include <linux/icmp.h>
 18 #include <linux/ip.h>
 19 #include <linux/netfilter.h>
 20 #include <linux/netfilter_ipv4.h>
 21 #include <linux/module.h>
 22 #include <linux/skbuff.h>
 23 #include <linux/proc_fs.h>
 24 #include <net/checksum.h>
 25 #include <linux/spinlock.h>
 26 
 27 #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_nat_lock)
 28 #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_nat_lock)
 29 
 30 #include <linux/netfilter_ipv4/ip_nat.h>
 31 #include <linux/netfilter_ipv4/ip_nat_rule.h>
 32 #include <linux/netfilter_ipv4/ip_nat_protocol.h>
 33 #include <linux/netfilter_ipv4/ip_nat_core.h>
 34 #include <linux/netfilter_ipv4/ip_nat_helper.h>
 35 #include <linux/netfilter_ipv4/ip_tables.h>
 36 #include <linux/netfilter_ipv4/ip_conntrack_core.h>
 37 #include <linux/netfilter_ipv4/listhelp.h>
 38 
 39 #if 0
 40 #define DEBUGP printk
 41 #else
 42 #define DEBUGP(format, args...)
 43 #endif
 44 
 45 #define HOOKNAME(hooknum) ((hooknum) == NF_IP_POST_ROUTING ? "POST_ROUTING"  \
 46                            : ((hooknum) == NF_IP_PRE_ROUTING ? "PRE_ROUTING" \
 47                               : ((hooknum) == NF_IP_LOCAL_OUT ? "LOCAL_OUT"  \
 48                                  : ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN"  \
 49                                     : "*ERROR*")))
 50 
 51 static inline int call_expect(struct ip_conntrack *master,
 52                               struct sk_buff **pskb,
 53                               unsigned int hooknum,
 54                               struct ip_conntrack *ct,
 55                               struct ip_nat_info *info)
 56 {
 57         return master->nat.info.helper->expect(pskb, hooknum, ct, info);
 58 }
 59 
 60 static unsigned int
 61 ip_nat_fn(unsigned int hooknum,
 62           struct sk_buff **pskb,
 63           const struct net_device *in,
 64           const struct net_device *out,
 65           int (*okfn)(struct sk_buff *))
 66 {
 67         struct ip_conntrack *ct;
 68         enum ip_conntrack_info ctinfo;
 69         struct ip_nat_info *info;
 70         /* maniptype == SRC for postrouting. */
 71         enum ip_nat_manip_type maniptype = HOOK2MANIP(hooknum);
 72 
 73         /* We never see fragments: conntrack defrags on pre-routing
 74            and local-out, and ip_nat_out protects post-routing. */
 75         IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off
 76                        & htons(IP_MF|IP_OFFSET)));
 77 
 78         (*pskb)->nfcache |= NFC_UNKNOWN;
 79 
 80         /* If we had a hardware checksum before, it's now invalid */
 81         if ((*pskb)->ip_summed == CHECKSUM_HW)
 82                 (*pskb)->ip_summed = CHECKSUM_NONE;
 83 
 84         ct = ip_conntrack_get(*pskb, &ctinfo);
 85         /* Can't track?  It's not due to stress, or conntrack would
 86            have dropped it.  Hence it's the user's responsibilty to
 87            packet filter it out, or implement conntrack/NAT for that
 88            protocol. 8) --RR */
 89         if (!ct) {
 90                 /* Exception: ICMP redirect to new connection (not in
 91                    hash table yet).  We must not let this through, in
 92                    case we're doing NAT to the same network. */
 93                 if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
 94                         struct icmphdr hdr;
 95 
 96                         if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,
 97                                           &hdr, sizeof(hdr)) == 0
 98                             && hdr.type == ICMP_REDIRECT)
 99                                 return NF_DROP;
100                 }
101                 return NF_ACCEPT;
102         }
103 
104         switch (ctinfo) {
105         case IP_CT_RELATED:
106         case IP_CT_RELATED+IP_CT_IS_REPLY:
107                 if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
108                         if (!icmp_reply_translation(pskb, ct, hooknum,
109                                                     CTINFO2DIR(ctinfo)))
110                                 return NF_DROP;
111                         else
112                                 return NF_ACCEPT;
113                 }
114                 /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
115         case IP_CT_NEW:
116                 info = &ct->nat.info;
117 
118                 WRITE_LOCK(&ip_nat_lock);
119                 /* Seen it before?  This can happen for loopback, retrans,
120                    or local packets.. */
121                 if (!(info->initialized & (1 << maniptype))) {
122                         unsigned int ret;
123 
124                         if (ct->master
125                             && master_ct(ct)->nat.info.helper
126                             && master_ct(ct)->nat.info.helper->expect) {
127                                 ret = call_expect(master_ct(ct), pskb, 
128                                                   hooknum, ct, info);
129                         } else {
130 #ifdef CONFIG_IP_NF_NAT_LOCAL
131                                 /* LOCAL_IN hook doesn't have a chain!  */
132                                 if (hooknum == NF_IP_LOCAL_IN)
133                                         ret = alloc_null_binding(ct, info,
134                                                                  hooknum);
135                                 else
136 #endif
137                                 ret = ip_nat_rule_find(pskb, hooknum, in, out,
138                                                        ct, info);
139                         }
140 
141                         if (ret != NF_ACCEPT) {
142                                 WRITE_UNLOCK(&ip_nat_lock);
143                                 return ret;
144                         }
145                 } else
146                         DEBUGP("Already setup manip %s for ct %p\n",
147                                maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",
148                                ct);
149                 WRITE_UNLOCK(&ip_nat_lock);
150                 break;
151 
152         default:
153                 /* ESTABLISHED */
154                 IP_NF_ASSERT(ctinfo == IP_CT_ESTABLISHED
155                              || ctinfo == (IP_CT_ESTABLISHED+IP_CT_IS_REPLY));
156                 info = &ct->nat.info;
157         }
158 
159         IP_NF_ASSERT(info);
160         return do_bindings(ct, ctinfo, info, hooknum, pskb);
161 }
162 
163 static unsigned int
164 ip_nat_out(unsigned int hooknum,
165            struct sk_buff **pskb,
166            const struct net_device *in,
167            const struct net_device *out,
168            int (*okfn)(struct sk_buff *))
169 {
170         /* root is playing with raw sockets. */
171         if ((*pskb)->len < sizeof(struct iphdr)
172             || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
173                 return NF_ACCEPT;
174 
175         /* We can hit fragment here; forwarded packets get
176            defragmented by connection tracking coming in, then
177            fragmented (grr) by the forward code.
178 
179            In future: If we have nfct != NULL, AND we have NAT
180            initialized, AND there is no helper, then we can do full
181            NAPT on the head, and IP-address-only NAT on the rest.
182 
183            I'm starting to have nightmares about fragments.  */
184 
185         if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
186                 *pskb = ip_ct_gather_frags(*pskb);
187 
188                 if (!*pskb)
189                         return NF_STOLEN;
190         }
191 
192         return ip_nat_fn(hooknum, pskb, in, out, okfn);
193 }
194 
195 #ifdef CONFIG_IP_NF_NAT_LOCAL
196 static unsigned int
197 ip_nat_local_fn(unsigned int hooknum,
198                 struct sk_buff **pskb,
199                 const struct net_device *in,
200                 const struct net_device *out,
201                 int (*okfn)(struct sk_buff *))
202 {
203         u_int32_t saddr, daddr;
204         unsigned int ret;
205 
206         /* root is playing with raw sockets. */
207         if ((*pskb)->len < sizeof(struct iphdr)
208             || (*pskb)->nh.iph->ihl * 4 < sizeof(struct iphdr))
209                 return NF_ACCEPT;
210 
211         saddr = (*pskb)->nh.iph->saddr;
212         daddr = (*pskb)->nh.iph->daddr;
213 
214         ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
215         if (ret != NF_DROP && ret != NF_STOLEN
216             && ((*pskb)->nh.iph->saddr != saddr
217                 || (*pskb)->nh.iph->daddr != daddr))
218                 return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
219         return ret;
220 }
221 #endif
222 
223 /* We must be after connection tracking and before packet filtering. */
224 
225 /* Before packet filtering, change destination */
226 static struct nf_hook_ops ip_nat_in_ops = {
227         .hook           = ip_nat_fn,
228         .owner          = THIS_MODULE,
229         .pf             = PF_INET,
230         .hooknum        = NF_IP_PRE_ROUTING,
231         .priority       = NF_IP_PRI_NAT_DST,
232 };
233 
234 /* After packet filtering, change source */
235 static struct nf_hook_ops ip_nat_out_ops = {
236         .hook           = ip_nat_out,
237         .owner          = THIS_MODULE,
238         .pf             = PF_INET,
239         .hooknum        = NF_IP_POST_ROUTING,
240         .priority       = NF_IP_PRI_NAT_SRC,
241 };
242 
243 #ifdef CONFIG_IP_NF_NAT_LOCAL
244 /* Before packet filtering, change destination */
245 static struct nf_hook_ops ip_nat_local_out_ops = {
246         .hook           = ip_nat_local_fn,
247         .owner          = THIS_MODULE,
248         .pf             = PF_INET,
249         .hooknum        = NF_IP_LOCAL_OUT,
250         .priority       = NF_IP_PRI_NAT_DST,
251 };
252 
253 /* After packet filtering, change source for reply packets of LOCAL_OUT DNAT */
254 static struct nf_hook_ops ip_nat_local_in_ops = {
255         .hook           = ip_nat_fn,
256         .owner          = THIS_MODULE,
257         .pf             = PF_INET,
258         .hooknum        = NF_IP_LOCAL_IN,
259         .priority       = NF_IP_PRI_NAT_SRC,
260 };
261 #endif
262 
263 /* Protocol registration. */
264 int ip_nat_protocol_register(struct ip_nat_protocol *proto)
265 {
266         int ret = 0;
267         struct list_head *i;
268 
269         WRITE_LOCK(&ip_nat_lock);
270         list_for_each(i, &protos) {
271                 if (((struct ip_nat_protocol *)i)->protonum
272                     == proto->protonum) {
273                         ret = -EBUSY;
274                         goto out;
275                 }
276         }
277 
278         list_prepend(&protos, proto);
279  out:
280         WRITE_UNLOCK(&ip_nat_lock);
281         return ret;
282 }
283 
284 /* Noone stores the protocol anywhere; simply delete it. */
285 void ip_nat_protocol_unregister(struct ip_nat_protocol *proto)
286 {
287         WRITE_LOCK(&ip_nat_lock);
288         LIST_DELETE(&protos, proto);
289         WRITE_UNLOCK(&ip_nat_lock);
290 
291         /* Someone could be still looking at the proto in a bh. */
292         synchronize_net();
293 }
294 
295 static int init_or_cleanup(int init)
296 {
297         int ret = 0;
298 
299         need_ip_conntrack();
300 
301         if (!init) goto cleanup;
302 
303         ret = ip_nat_rule_init();
304         if (ret < 0) {
305                 printk("ip_nat_init: can't setup rules.\n");
306                 goto cleanup_nothing;
307         }
308         ret = ip_nat_init();
309         if (ret < 0) {
310                 printk("ip_nat_init: can't setup rules.\n");
311                 goto cleanup_rule_init;
312         }
313         ret = nf_register_hook(&ip_nat_in_ops);
314         if (ret < 0) {
315                 printk("ip_nat_init: can't register in hook.\n");
316                 goto cleanup_nat;
317         }
318         ret = nf_register_hook(&ip_nat_out_ops);
319         if (ret < 0) {
320                 printk("ip_nat_init: can't register out hook.\n");
321                 goto cleanup_inops;
322         }
323 #ifdef CONFIG_IP_NF_NAT_LOCAL
324         ret = nf_register_hook(&ip_nat_local_out_ops);
325         if (ret < 0) {
326                 printk("ip_nat_init: can't register local out hook.\n");
327                 goto cleanup_outops;
328         }
329         ret = nf_register_hook(&ip_nat_local_in_ops);
330         if (ret < 0) {
331                 printk("ip_nat_init: can't register local in hook.\n");
332                 goto cleanup_localoutops;
333         }
334 #endif
335         return ret;
336 
337  cleanup:
338 #ifdef CONFIG_IP_NF_NAT_LOCAL
339         nf_unregister_hook(&ip_nat_local_in_ops);
340  cleanup_localoutops:
341         nf_unregister_hook(&ip_nat_local_out_ops);
342  cleanup_outops:
343 #endif
344         nf_unregister_hook(&ip_nat_out_ops);
345  cleanup_inops:
346         nf_unregister_hook(&ip_nat_in_ops);
347  cleanup_nat:
348         ip_nat_cleanup();
349  cleanup_rule_init:
350         ip_nat_rule_cleanup();
351  cleanup_nothing:
352         MUST_BE_READ_WRITE_UNLOCKED(&ip_nat_lock);
353         return ret;
354 }
355 
356 static int __init init(void)
357 {
358         return init_or_cleanup(1);
359 }
360 
361 static void __exit fini(void)
362 {
363         init_or_cleanup(0);
364 }
365 
366 module_init(init);
367 module_exit(fini);
368 
369 EXPORT_SYMBOL(ip_nat_setup_info);
370 EXPORT_SYMBOL(ip_nat_protocol_register);
371 EXPORT_SYMBOL(ip_nat_protocol_unregister);
372 EXPORT_SYMBOL(ip_nat_helper_register);
373 EXPORT_SYMBOL(ip_nat_helper_unregister);
374 EXPORT_SYMBOL(ip_nat_cheat_check);
375 EXPORT_SYMBOL(ip_nat_mangle_tcp_packet);
376 EXPORT_SYMBOL(ip_nat_mangle_udp_packet);
377 EXPORT_SYMBOL(ip_nat_used_tuple);
378 MODULE_LICENSE("GPL");
379 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp