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

TOMOYO Linux Cross Reference
Linux/net/ipv6/netfilter/ip6table_nat.c

Version: ~ [ linux-5.15-rc5 ] ~ [ linux-5.14.11 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.72 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.152 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.210 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.250 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.286 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.288 ] ~ [ 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 /*
  2  * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
  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  * Based on Rusty Russell's IPv4 NAT code. Development of IPv6 NAT
  9  * funded by Astaro.
 10  */
 11 
 12 #include <linux/module.h>
 13 #include <linux/netfilter.h>
 14 #include <linux/netfilter_ipv6.h>
 15 #include <linux/netfilter_ipv6/ip6_tables.h>
 16 #include <linux/ipv6.h>
 17 #include <net/ipv6.h>
 18 
 19 #include <net/netfilter/nf_nat.h>
 20 #include <net/netfilter/nf_nat_core.h>
 21 #include <net/netfilter/nf_nat_l3proto.h>
 22 
 23 static const struct xt_table nf_nat_ipv6_table = {
 24         .name           = "nat",
 25         .valid_hooks    = (1 << NF_INET_PRE_ROUTING) |
 26                           (1 << NF_INET_POST_ROUTING) |
 27                           (1 << NF_INET_LOCAL_OUT) |
 28                           (1 << NF_INET_LOCAL_IN),
 29         .me             = THIS_MODULE,
 30         .af             = NFPROTO_IPV6,
 31 };
 32 
 33 static unsigned int alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
 34 {
 35         /* Force range to this IP; let proto decide mapping for
 36          * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
 37          */
 38         struct nf_nat_range range;
 39 
 40         range.flags = 0;
 41         pr_debug("Allocating NULL binding for %p (%pI6)\n", ct,
 42                  HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ?
 43                  &ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip6 :
 44                  &ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip6);
 45 
 46         return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum));
 47 }
 48 
 49 static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum,
 50                                      const struct net_device *in,
 51                                      const struct net_device *out,
 52                                      struct nf_conn *ct)
 53 {
 54         struct net *net = nf_ct_net(ct);
 55         unsigned int ret;
 56 
 57         ret = ip6t_do_table(skb, hooknum, in, out, net->ipv6.ip6table_nat);
 58         if (ret == NF_ACCEPT) {
 59                 if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum)))
 60                         ret = alloc_null_binding(ct, hooknum);
 61         }
 62         return ret;
 63 }
 64 
 65 static unsigned int
 66 nf_nat_ipv6_fn(unsigned int hooknum,
 67                struct sk_buff *skb,
 68                const struct net_device *in,
 69                const struct net_device *out,
 70                int (*okfn)(struct sk_buff *))
 71 {
 72         struct nf_conn *ct;
 73         enum ip_conntrack_info ctinfo;
 74         struct nf_conn_nat *nat;
 75         enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum);
 76         __be16 frag_off;
 77         int hdrlen;
 78         u8 nexthdr;
 79 
 80         ct = nf_ct_get(skb, &ctinfo);
 81         /* Can't track?  It's not due to stress, or conntrack would
 82          * have dropped it.  Hence it's the user's responsibilty to
 83          * packet filter it out, or implement conntrack/NAT for that
 84          * protocol. 8) --RR
 85          */
 86         if (!ct)
 87                 return NF_ACCEPT;
 88 
 89         /* Don't try to NAT if this packet is not conntracked */
 90         if (nf_ct_is_untracked(ct))
 91                 return NF_ACCEPT;
 92 
 93         nat = nfct_nat(ct);
 94         if (!nat) {
 95                 /* NAT module was loaded late. */
 96                 if (nf_ct_is_confirmed(ct))
 97                         return NF_ACCEPT;
 98                 nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC);
 99                 if (nat == NULL) {
100                         pr_debug("failed to add NAT extension\n");
101                         return NF_ACCEPT;
102                 }
103         }
104 
105         switch (ctinfo) {
106         case IP_CT_RELATED:
107         case IP_CT_RELATED_REPLY:
108                 nexthdr = ipv6_hdr(skb)->nexthdr;
109                 hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
110                                           &nexthdr, &frag_off);
111 
112                 if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
113                         if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
114                                                              hooknum, hdrlen))
115                                 return NF_DROP;
116                         else
117                                 return NF_ACCEPT;
118                 }
119                 /* Fall thru... (Only ICMPs can be IP_CT_IS_REPLY) */
120         case IP_CT_NEW:
121                 /* Seen it before?  This can happen for loopback, retrans,
122                  * or local packets.
123                  */
124                 if (!nf_nat_initialized(ct, maniptype)) {
125                         unsigned int ret;
126 
127                         ret = nf_nat_rule_find(skb, hooknum, in, out, ct);
128                         if (ret != NF_ACCEPT)
129                                 return ret;
130                 } else {
131                         pr_debug("Already setup manip %s for ct %p\n",
132                                  maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST",
133                                  ct);
134                         if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
135                                 goto oif_changed;
136                 }
137                 break;
138 
139         default:
140                 /* ESTABLISHED */
141                 NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED ||
142                              ctinfo == IP_CT_ESTABLISHED_REPLY);
143                 if (nf_nat_oif_changed(hooknum, ctinfo, nat, out))
144                         goto oif_changed;
145         }
146 
147         return nf_nat_packet(ct, ctinfo, hooknum, skb);
148 
149 oif_changed:
150         nf_ct_kill_acct(ct, ctinfo, skb);
151         return NF_DROP;
152 }
153 
154 static unsigned int
155 nf_nat_ipv6_in(unsigned int hooknum,
156                struct sk_buff *skb,
157                const struct net_device *in,
158                const struct net_device *out,
159                int (*okfn)(struct sk_buff *))
160 {
161         unsigned int ret;
162         struct in6_addr daddr = ipv6_hdr(skb)->daddr;
163 
164         ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
165         if (ret != NF_DROP && ret != NF_STOLEN &&
166             ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
167                 skb_dst_drop(skb);
168 
169         return ret;
170 }
171 
172 static unsigned int
173 nf_nat_ipv6_out(unsigned int hooknum,
174                 struct sk_buff *skb,
175                 const struct net_device *in,
176                 const struct net_device *out,
177                 int (*okfn)(struct sk_buff *))
178 {
179 #ifdef CONFIG_XFRM
180         const struct nf_conn *ct;
181         enum ip_conntrack_info ctinfo;
182         int err;
183 #endif
184         unsigned int ret;
185 
186         /* root is playing with raw sockets. */
187         if (skb->len < sizeof(struct ipv6hdr))
188                 return NF_ACCEPT;
189 
190         ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
191 #ifdef CONFIG_XFRM
192         if (ret != NF_DROP && ret != NF_STOLEN &&
193             !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
194             (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
195                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
196 
197                 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
198                                       &ct->tuplehash[!dir].tuple.dst.u3) ||
199                     (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
200                      ct->tuplehash[dir].tuple.src.u.all !=
201                      ct->tuplehash[!dir].tuple.dst.u.all)) {
202                         err = nf_xfrm_me_harder(skb, AF_INET6);
203                         if (err < 0)
204                                 ret = NF_DROP_ERR(err);
205                 }
206         }
207 #endif
208         return ret;
209 }
210 
211 static unsigned int
212 nf_nat_ipv6_local_fn(unsigned int hooknum,
213                      struct sk_buff *skb,
214                      const struct net_device *in,
215                      const struct net_device *out,
216                      int (*okfn)(struct sk_buff *))
217 {
218         const struct nf_conn *ct;
219         enum ip_conntrack_info ctinfo;
220         unsigned int ret;
221         int err;
222 
223         /* root is playing with raw sockets. */
224         if (skb->len < sizeof(struct ipv6hdr))
225                 return NF_ACCEPT;
226 
227         ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn);
228         if (ret != NF_DROP && ret != NF_STOLEN &&
229             (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
230                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
231 
232                 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
233                                       &ct->tuplehash[!dir].tuple.src.u3)) {
234                         err = ip6_route_me_harder(skb);
235                         if (err < 0)
236                                 ret = NF_DROP_ERR(err);
237                 }
238 #ifdef CONFIG_XFRM
239                 else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
240                          ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
241                          ct->tuplehash[dir].tuple.dst.u.all !=
242                          ct->tuplehash[!dir].tuple.src.u.all) {
243                         err = nf_xfrm_me_harder(skb, AF_INET6);
244                         if (err < 0)
245                                 ret = NF_DROP_ERR(err);
246                 }
247 #endif
248         }
249         return ret;
250 }
251 
252 static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = {
253         /* Before packet filtering, change destination */
254         {
255                 .hook           = nf_nat_ipv6_in,
256                 .owner          = THIS_MODULE,
257                 .pf             = NFPROTO_IPV6,
258                 .hooknum        = NF_INET_PRE_ROUTING,
259                 .priority       = NF_IP6_PRI_NAT_DST,
260         },
261         /* After packet filtering, change source */
262         {
263                 .hook           = nf_nat_ipv6_out,
264                 .owner          = THIS_MODULE,
265                 .pf             = NFPROTO_IPV6,
266                 .hooknum        = NF_INET_POST_ROUTING,
267                 .priority       = NF_IP6_PRI_NAT_SRC,
268         },
269         /* Before packet filtering, change destination */
270         {
271                 .hook           = nf_nat_ipv6_local_fn,
272                 .owner          = THIS_MODULE,
273                 .pf             = NFPROTO_IPV6,
274                 .hooknum        = NF_INET_LOCAL_OUT,
275                 .priority       = NF_IP6_PRI_NAT_DST,
276         },
277         /* After packet filtering, change source */
278         {
279                 .hook           = nf_nat_ipv6_fn,
280                 .owner          = THIS_MODULE,
281                 .pf             = NFPROTO_IPV6,
282                 .hooknum        = NF_INET_LOCAL_IN,
283                 .priority       = NF_IP6_PRI_NAT_SRC,
284         },
285 };
286 
287 static int __net_init ip6table_nat_net_init(struct net *net)
288 {
289         struct ip6t_replace *repl;
290 
291         repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table);
292         if (repl == NULL)
293                 return -ENOMEM;
294         net->ipv6.ip6table_nat = ip6t_register_table(net, &nf_nat_ipv6_table, repl);
295         kfree(repl);
296         return PTR_ERR_OR_ZERO(net->ipv6.ip6table_nat);
297 }
298 
299 static void __net_exit ip6table_nat_net_exit(struct net *net)
300 {
301         ip6t_unregister_table(net, net->ipv6.ip6table_nat);
302 }
303 
304 static struct pernet_operations ip6table_nat_net_ops = {
305         .init   = ip6table_nat_net_init,
306         .exit   = ip6table_nat_net_exit,
307 };
308 
309 static int __init ip6table_nat_init(void)
310 {
311         int err;
312 
313         err = register_pernet_subsys(&ip6table_nat_net_ops);
314         if (err < 0)
315                 goto err1;
316 
317         err = nf_register_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
318         if (err < 0)
319                 goto err2;
320         return 0;
321 
322 err2:
323         unregister_pernet_subsys(&ip6table_nat_net_ops);
324 err1:
325         return err;
326 }
327 
328 static void __exit ip6table_nat_exit(void)
329 {
330         nf_unregister_hooks(nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
331         unregister_pernet_subsys(&ip6table_nat_net_ops);
332 }
333 
334 module_init(ip6table_nat_init);
335 module_exit(ip6table_nat_exit);
336 
337 MODULE_LICENSE("GPL");
338 

~ [ 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