Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next
[linux-2.6-microblaze.git] / net / ipv6 / netfilter / nf_nat_l3proto_ipv6.c
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  * Development of IPv6 NAT funded by Astaro.
9  */
10 #include <linux/types.h>
11 #include <linux/module.h>
12 #include <linux/skbuff.h>
13 #include <linux/ipv6.h>
14 #include <linux/netfilter.h>
15 #include <linux/netfilter_ipv6.h>
16 #include <net/secure_seq.h>
17 #include <net/checksum.h>
18 #include <net/ip6_checksum.h>
19 #include <net/ip6_route.h>
20 #include <net/xfrm.h>
21 #include <net/ipv6.h>
22
23 #include <net/netfilter/nf_conntrack_core.h>
24 #include <net/netfilter/nf_conntrack.h>
25 #include <net/netfilter/nf_nat_core.h>
26 #include <net/netfilter/nf_nat_l3proto.h>
27 #include <net/netfilter/nf_nat_l4proto.h>
28
29 static const struct nf_nat_l3proto nf_nat_l3proto_ipv6;
30
31 #ifdef CONFIG_XFRM
32 static void nf_nat_ipv6_decode_session(struct sk_buff *skb,
33                                        const struct nf_conn *ct,
34                                        enum ip_conntrack_dir dir,
35                                        unsigned long statusbit,
36                                        struct flowi *fl)
37 {
38         const struct nf_conntrack_tuple *t = &ct->tuplehash[dir].tuple;
39         struct flowi6 *fl6 = &fl->u.ip6;
40
41         if (ct->status & statusbit) {
42                 fl6->daddr = t->dst.u3.in6;
43                 if (t->dst.protonum == IPPROTO_TCP ||
44                     t->dst.protonum == IPPROTO_UDP ||
45                     t->dst.protonum == IPPROTO_UDPLITE ||
46                     t->dst.protonum == IPPROTO_DCCP ||
47                     t->dst.protonum == IPPROTO_SCTP)
48                         fl6->fl6_dport = t->dst.u.all;
49         }
50
51         statusbit ^= IPS_NAT_MASK;
52
53         if (ct->status & statusbit) {
54                 fl6->saddr = t->src.u3.in6;
55                 if (t->dst.protonum == IPPROTO_TCP ||
56                     t->dst.protonum == IPPROTO_UDP ||
57                     t->dst.protonum == IPPROTO_UDPLITE ||
58                     t->dst.protonum == IPPROTO_DCCP ||
59                     t->dst.protonum == IPPROTO_SCTP)
60                         fl6->fl6_sport = t->src.u.all;
61         }
62 }
63 #endif
64
65 static bool nf_nat_ipv6_manip_pkt(struct sk_buff *skb,
66                                   unsigned int iphdroff,
67                                   const struct nf_conntrack_tuple *target,
68                                   enum nf_nat_manip_type maniptype)
69 {
70         struct ipv6hdr *ipv6h;
71         __be16 frag_off;
72         int hdroff;
73         u8 nexthdr;
74
75         if (!skb_make_writable(skb, iphdroff + sizeof(*ipv6h)))
76                 return false;
77
78         ipv6h = (void *)skb->data + iphdroff;
79         nexthdr = ipv6h->nexthdr;
80         hdroff = ipv6_skip_exthdr(skb, iphdroff + sizeof(*ipv6h),
81                                   &nexthdr, &frag_off);
82         if (hdroff < 0)
83                 goto manip_addr;
84
85         if ((frag_off & htons(~0x7)) == 0 &&
86             !nf_nat_l4proto_manip_pkt(skb, &nf_nat_l3proto_ipv6, iphdroff, hdroff,
87                                       target, maniptype))
88                 return false;
89
90         /* must reload, offset might have changed */
91         ipv6h = (void *)skb->data + iphdroff;
92
93 manip_addr:
94         if (maniptype == NF_NAT_MANIP_SRC)
95                 ipv6h->saddr = target->src.u3.in6;
96         else
97                 ipv6h->daddr = target->dst.u3.in6;
98
99         return true;
100 }
101
102 static void nf_nat_ipv6_csum_update(struct sk_buff *skb,
103                                     unsigned int iphdroff, __sum16 *check,
104                                     const struct nf_conntrack_tuple *t,
105                                     enum nf_nat_manip_type maniptype)
106 {
107         const struct ipv6hdr *ipv6h = (struct ipv6hdr *)(skb->data + iphdroff);
108         const struct in6_addr *oldip, *newip;
109
110         if (maniptype == NF_NAT_MANIP_SRC) {
111                 oldip = &ipv6h->saddr;
112                 newip = &t->src.u3.in6;
113         } else {
114                 oldip = &ipv6h->daddr;
115                 newip = &t->dst.u3.in6;
116         }
117         inet_proto_csum_replace16(check, skb, oldip->s6_addr32,
118                                   newip->s6_addr32, true);
119 }
120
121 static void nf_nat_ipv6_csum_recalc(struct sk_buff *skb,
122                                     u8 proto, void *data, __sum16 *check,
123                                     int datalen, int oldlen)
124 {
125         if (skb->ip_summed != CHECKSUM_PARTIAL) {
126                 const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
127
128                 skb->ip_summed = CHECKSUM_PARTIAL;
129                 skb->csum_start = skb_headroom(skb) + skb_network_offset(skb) +
130                         (data - (void *)skb->data);
131                 skb->csum_offset = (void *)check - data;
132                 *check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
133                                           datalen, proto, 0);
134         } else
135                 inet_proto_csum_replace2(check, skb,
136                                          htons(oldlen), htons(datalen), true);
137 }
138
139 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
140 static int nf_nat_ipv6_nlattr_to_range(struct nlattr *tb[],
141                                        struct nf_nat_range2 *range)
142 {
143         if (tb[CTA_NAT_V6_MINIP]) {
144                 nla_memcpy(&range->min_addr.ip6, tb[CTA_NAT_V6_MINIP],
145                            sizeof(struct in6_addr));
146                 range->flags |= NF_NAT_RANGE_MAP_IPS;
147         }
148
149         if (tb[CTA_NAT_V6_MAXIP])
150                 nla_memcpy(&range->max_addr.ip6, tb[CTA_NAT_V6_MAXIP],
151                            sizeof(struct in6_addr));
152         else
153                 range->max_addr = range->min_addr;
154
155         return 0;
156 }
157 #endif
158
159 static const struct nf_nat_l3proto nf_nat_l3proto_ipv6 = {
160         .l3proto                = NFPROTO_IPV6,
161         .manip_pkt              = nf_nat_ipv6_manip_pkt,
162         .csum_update            = nf_nat_ipv6_csum_update,
163         .csum_recalc            = nf_nat_ipv6_csum_recalc,
164 #if IS_ENABLED(CONFIG_NF_CT_NETLINK)
165         .nlattr_to_range        = nf_nat_ipv6_nlattr_to_range,
166 #endif
167 #ifdef CONFIG_XFRM
168         .decode_session = nf_nat_ipv6_decode_session,
169 #endif
170 };
171
172 int nf_nat_icmpv6_reply_translation(struct sk_buff *skb,
173                                     struct nf_conn *ct,
174                                     enum ip_conntrack_info ctinfo,
175                                     unsigned int hooknum,
176                                     unsigned int hdrlen)
177 {
178         struct {
179                 struct icmp6hdr icmp6;
180                 struct ipv6hdr  ip6;
181         } *inside;
182         enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
183         enum nf_nat_manip_type manip = HOOK2MANIP(hooknum);
184         struct nf_conntrack_tuple target;
185         unsigned long statusbit;
186
187         WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY);
188
189         if (!skb_make_writable(skb, hdrlen + sizeof(*inside)))
190                 return 0;
191         if (nf_ip6_checksum(skb, hooknum, hdrlen, IPPROTO_ICMPV6))
192                 return 0;
193
194         inside = (void *)skb->data + hdrlen;
195         if (inside->icmp6.icmp6_type == NDISC_REDIRECT) {
196                 if ((ct->status & IPS_NAT_DONE_MASK) != IPS_NAT_DONE_MASK)
197                         return 0;
198                 if (ct->status & IPS_NAT_MASK)
199                         return 0;
200         }
201
202         if (manip == NF_NAT_MANIP_SRC)
203                 statusbit = IPS_SRC_NAT;
204         else
205                 statusbit = IPS_DST_NAT;
206
207         /* Invert if this is reply direction */
208         if (dir == IP_CT_DIR_REPLY)
209                 statusbit ^= IPS_NAT_MASK;
210
211         if (!(ct->status & statusbit))
212                 return 1;
213
214         if (!nf_nat_ipv6_manip_pkt(skb, hdrlen + sizeof(inside->icmp6),
215                                    &ct->tuplehash[!dir].tuple, !manip))
216                 return 0;
217
218         if (skb->ip_summed != CHECKSUM_PARTIAL) {
219                 struct ipv6hdr *ipv6h = ipv6_hdr(skb);
220                 inside = (void *)skb->data + hdrlen;
221                 inside->icmp6.icmp6_cksum = 0;
222                 inside->icmp6.icmp6_cksum =
223                         csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr,
224                                         skb->len - hdrlen, IPPROTO_ICMPV6,
225                                         skb_checksum(skb, hdrlen,
226                                                      skb->len - hdrlen, 0));
227         }
228
229         nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple);
230         target.dst.protonum = IPPROTO_ICMPV6;
231         if (!nf_nat_ipv6_manip_pkt(skb, 0, &target, manip))
232                 return 0;
233
234         return 1;
235 }
236 EXPORT_SYMBOL_GPL(nf_nat_icmpv6_reply_translation);
237
238 static unsigned int
239 nf_nat_ipv6_fn(void *priv, struct sk_buff *skb,
240                const struct nf_hook_state *state)
241 {
242         struct nf_conn *ct;
243         enum ip_conntrack_info ctinfo;
244         __be16 frag_off;
245         int hdrlen;
246         u8 nexthdr;
247
248         ct = nf_ct_get(skb, &ctinfo);
249         /* Can't track?  It's not due to stress, or conntrack would
250          * have dropped it.  Hence it's the user's responsibilty to
251          * packet filter it out, or implement conntrack/NAT for that
252          * protocol. 8) --RR
253          */
254         if (!ct)
255                 return NF_ACCEPT;
256
257         if (ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY) {
258                 nexthdr = ipv6_hdr(skb)->nexthdr;
259                 hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
260                                           &nexthdr, &frag_off);
261
262                 if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) {
263                         if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo,
264                                                              state->hook,
265                                                              hdrlen))
266                                 return NF_DROP;
267                         else
268                                 return NF_ACCEPT;
269                 }
270         }
271
272         return nf_nat_inet_fn(priv, skb, state);
273 }
274
275 static unsigned int
276 nf_nat_ipv6_in(void *priv, struct sk_buff *skb,
277                const struct nf_hook_state *state)
278 {
279         unsigned int ret;
280         struct in6_addr daddr = ipv6_hdr(skb)->daddr;
281
282         ret = nf_nat_ipv6_fn(priv, skb, state);
283         if (ret != NF_DROP && ret != NF_STOLEN &&
284             ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr))
285                 skb_dst_drop(skb);
286
287         return ret;
288 }
289
290 static unsigned int
291 nf_nat_ipv6_out(void *priv, struct sk_buff *skb,
292                 const struct nf_hook_state *state)
293 {
294 #ifdef CONFIG_XFRM
295         const struct nf_conn *ct;
296         enum ip_conntrack_info ctinfo;
297         int err;
298 #endif
299         unsigned int ret;
300
301         ret = nf_nat_ipv6_fn(priv, skb, state);
302 #ifdef CONFIG_XFRM
303         if (ret != NF_DROP && ret != NF_STOLEN &&
304             !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
305             (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
306                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
307
308                 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3,
309                                       &ct->tuplehash[!dir].tuple.dst.u3) ||
310                     (ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
311                      ct->tuplehash[dir].tuple.src.u.all !=
312                      ct->tuplehash[!dir].tuple.dst.u.all)) {
313                         err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
314                         if (err < 0)
315                                 ret = NF_DROP_ERR(err);
316                 }
317         }
318 #endif
319         return ret;
320 }
321
322 static int nat_route_me_harder(struct net *net, struct sk_buff *skb)
323 {
324 #ifdef CONFIG_IPV6_MODULE
325         const struct nf_ipv6_ops *v6_ops = nf_get_ipv6_ops();
326
327         if (!v6_ops)
328                 return -EHOSTUNREACH;
329
330         return v6_ops->route_me_harder(net, skb);
331 #else
332         return ip6_route_me_harder(net, skb);
333 #endif
334 }
335
336 static unsigned int
337 nf_nat_ipv6_local_fn(void *priv, struct sk_buff *skb,
338                      const struct nf_hook_state *state)
339 {
340         const struct nf_conn *ct;
341         enum ip_conntrack_info ctinfo;
342         unsigned int ret;
343         int err;
344
345         ret = nf_nat_ipv6_fn(priv, skb, state);
346         if (ret != NF_DROP && ret != NF_STOLEN &&
347             (ct = nf_ct_get(skb, &ctinfo)) != NULL) {
348                 enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo);
349
350                 if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3,
351                                       &ct->tuplehash[!dir].tuple.src.u3)) {
352                         err = nat_route_me_harder(state->net, skb);
353                         if (err < 0)
354                                 ret = NF_DROP_ERR(err);
355                 }
356 #ifdef CONFIG_XFRM
357                 else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) &&
358                          ct->tuplehash[dir].tuple.dst.protonum != IPPROTO_ICMPV6 &&
359                          ct->tuplehash[dir].tuple.dst.u.all !=
360                          ct->tuplehash[!dir].tuple.src.u.all) {
361                         err = nf_xfrm_me_harder(state->net, skb, AF_INET6);
362                         if (err < 0)
363                                 ret = NF_DROP_ERR(err);
364                 }
365 #endif
366         }
367         return ret;
368 }
369
370 static const struct nf_hook_ops nf_nat_ipv6_ops[] = {
371         /* Before packet filtering, change destination */
372         {
373                 .hook           = nf_nat_ipv6_in,
374                 .pf             = NFPROTO_IPV6,
375                 .hooknum        = NF_INET_PRE_ROUTING,
376                 .priority       = NF_IP6_PRI_NAT_DST,
377         },
378         /* After packet filtering, change source */
379         {
380                 .hook           = nf_nat_ipv6_out,
381                 .pf             = NFPROTO_IPV6,
382                 .hooknum        = NF_INET_POST_ROUTING,
383                 .priority       = NF_IP6_PRI_NAT_SRC,
384         },
385         /* Before packet filtering, change destination */
386         {
387                 .hook           = nf_nat_ipv6_local_fn,
388                 .pf             = NFPROTO_IPV6,
389                 .hooknum        = NF_INET_LOCAL_OUT,
390                 .priority       = NF_IP6_PRI_NAT_DST,
391         },
392         /* After packet filtering, change source */
393         {
394                 .hook           = nf_nat_ipv6_fn,
395                 .pf             = NFPROTO_IPV6,
396                 .hooknum        = NF_INET_LOCAL_IN,
397                 .priority       = NF_IP6_PRI_NAT_SRC,
398         },
399 };
400
401 int nf_nat_l3proto_ipv6_register_fn(struct net *net, const struct nf_hook_ops *ops)
402 {
403         return nf_nat_register_fn(net, ops, nf_nat_ipv6_ops, ARRAY_SIZE(nf_nat_ipv6_ops));
404 }
405 EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv6_register_fn);
406
407 void nf_nat_l3proto_ipv6_unregister_fn(struct net *net, const struct nf_hook_ops *ops)
408 {
409         nf_nat_unregister_fn(net, ops, ARRAY_SIZE(nf_nat_ipv6_ops));
410 }
411 EXPORT_SYMBOL_GPL(nf_nat_l3proto_ipv6_unregister_fn);
412
413 static int __init nf_nat_l3proto_ipv6_init(void)
414 {
415         return nf_nat_l3proto_register(&nf_nat_l3proto_ipv6);
416 }
417
418 static void __exit nf_nat_l3proto_ipv6_exit(void)
419 {
420         nf_nat_l3proto_unregister(&nf_nat_l3proto_ipv6);
421 }
422
423 MODULE_LICENSE("GPL");
424 MODULE_ALIAS("nf-nat-" __stringify(AF_INET6));
425
426 module_init(nf_nat_l3proto_ipv6_init);
427 module_exit(nf_nat_l3proto_ipv6_exit);