Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / net / ipv6 / netfilter / nf_conntrack_l3proto_ipv6.c
1 /*
2  * Copyright (C)2004 USAGI/WIDE Project
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  * Author:
9  *      Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
10  *
11  * 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
12  *      - support Layer 3 protocol independent connection tracking.
13  *        Based on the original ip_conntrack code which had the following
14  *        copyright information:
15  *              (C) 1999-2001 Paul `Rusty' Russell
16  *              (C) 2002-2004 Netfilter Core Team <coreteam@netfilter.org>
17  *
18  * 23 Mar 2004: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
19  *      - add get_features() to support various size of conntrack
20  *        structures.
21  */
22
23 #include <linux/types.h>
24 #include <linux/ipv6.h>
25 #include <linux/in6.h>
26 #include <linux/netfilter.h>
27 #include <linux/module.h>
28 #include <linux/skbuff.h>
29 #include <linux/icmp.h>
30 #include <linux/sysctl.h>
31 #include <net/ipv6.h>
32
33 #include <linux/netfilter_ipv6.h>
34 #include <net/netfilter/nf_conntrack.h>
35 #include <net/netfilter/nf_conntrack_helper.h>
36 #include <net/netfilter/nf_conntrack_protocol.h>
37 #include <net/netfilter/nf_conntrack_l3proto.h>
38 #include <net/netfilter/nf_conntrack_core.h>
39
40 #if 0
41 #define DEBUGP printk
42 #else
43 #define DEBUGP(format, args...)
44 #endif
45
46 DECLARE_PER_CPU(struct ip_conntrack_stat, nf_conntrack_stat);
47
48 static int ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
49                              struct nf_conntrack_tuple *tuple)
50 {
51         u_int32_t _addrs[8], *ap;
52
53         ap = skb_header_pointer(skb, nhoff + offsetof(struct ipv6hdr, saddr),
54                                 sizeof(_addrs), _addrs);
55         if (ap == NULL)
56                 return 0;
57
58         memcpy(tuple->src.u3.ip6, ap, sizeof(tuple->src.u3.ip6));
59         memcpy(tuple->dst.u3.ip6, ap + 4, sizeof(tuple->dst.u3.ip6));
60
61         return 1;
62 }
63
64 static int ipv6_invert_tuple(struct nf_conntrack_tuple *tuple,
65                              const struct nf_conntrack_tuple *orig)
66 {
67         memcpy(tuple->src.u3.ip6, orig->dst.u3.ip6, sizeof(tuple->src.u3.ip6));
68         memcpy(tuple->dst.u3.ip6, orig->src.u3.ip6, sizeof(tuple->dst.u3.ip6));
69
70         return 1;
71 }
72
73 static int ipv6_print_tuple(struct seq_file *s,
74                             const struct nf_conntrack_tuple *tuple)
75 {
76         return seq_printf(s, "src=" NIP6_FMT " dst=" NIP6_FMT " ",
77                           NIP6(*((struct in6_addr *)tuple->src.u3.ip6)),
78                           NIP6(*((struct in6_addr *)tuple->dst.u3.ip6)));
79 }
80
81 static int ipv6_print_conntrack(struct seq_file *s,
82                                 const struct nf_conn *conntrack)
83 {
84         return 0;
85 }
86
87 /*
88  * Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c
89  *
90  * This function parses (probably truncated) exthdr set "hdr"
91  * of length "len". "nexthdrp" initially points to some place,
92  * where type of the first header can be found.
93  *
94  * It skips all well-known exthdrs, and returns pointer to the start
95  * of unparsable area i.e. the first header with unknown type.
96  * if success, *nexthdr is updated by type/protocol of this header.
97  *
98  * NOTES: - it may return pointer pointing beyond end of packet,
99  *          if the last recognized header is truncated in the middle.
100  *        - if packet is truncated, so that all parsed headers are skipped,
101  *          it returns -1.
102  *        - if packet is fragmented, return pointer of the fragment header.
103  *        - ESP is unparsable for now and considered like
104  *          normal payload protocol.
105  *        - Note also special handling of AUTH header. Thanks to IPsec wizards.
106  */
107
108 int nf_ct_ipv6_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp,
109                            int len)
110 {
111         u8 nexthdr = *nexthdrp;
112
113         while (ipv6_ext_hdr(nexthdr)) {
114                 struct ipv6_opt_hdr hdr;
115                 int hdrlen;
116
117                 if (len < (int)sizeof(struct ipv6_opt_hdr))
118                         return -1;
119                 if (nexthdr == NEXTHDR_NONE)
120                         break;
121                 if (nexthdr == NEXTHDR_FRAGMENT)
122                         break;
123                 if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
124                         BUG();
125                 if (nexthdr == NEXTHDR_AUTH)
126                         hdrlen = (hdr.hdrlen+2)<<2;
127                 else
128                         hdrlen = ipv6_optlen(&hdr);
129
130                 nexthdr = hdr.nexthdr;
131                 len -= hdrlen;
132                 start += hdrlen;
133         }
134
135         *nexthdrp = nexthdr;
136         return start;
137 }
138
139 static int
140 ipv6_prepare(struct sk_buff **pskb, unsigned int hooknum, unsigned int *dataoff,
141              u_int8_t *protonum)
142 {
143         unsigned int extoff;
144         unsigned char pnum;
145         int protoff;
146
147         extoff = (u8*)((*pskb)->nh.ipv6h + 1) - (*pskb)->data;
148         pnum = (*pskb)->nh.ipv6h->nexthdr;
149
150         protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum,
151                                          (*pskb)->len - extoff);
152
153         /*
154          * (protoff == (*pskb)->len) mean that the packet doesn't have no data
155          * except of IPv6 & ext headers. but it's tracked anyway. - YK
156          */
157         if ((protoff < 0) || (protoff > (*pskb)->len)) {
158                 DEBUGP("ip6_conntrack_core: can't find proto in pkt\n");
159                 NF_CT_STAT_INC(error);
160                 NF_CT_STAT_INC(invalid);
161                 return -NF_ACCEPT;
162         }
163
164         *dataoff = protoff;
165         *protonum = pnum;
166         return NF_ACCEPT;
167 }
168
169 static u_int32_t ipv6_get_features(const struct nf_conntrack_tuple *tuple)
170 {
171         return NF_CT_F_BASIC;
172 }
173
174 static unsigned int ipv6_confirm(unsigned int hooknum,
175                                  struct sk_buff **pskb,
176                                  const struct net_device *in,
177                                  const struct net_device *out,
178                                  int (*okfn)(struct sk_buff *))
179 {
180         struct nf_conn *ct;
181         struct nf_conn_help *help;
182         enum ip_conntrack_info ctinfo;
183         unsigned int ret, protoff;
184         unsigned int extoff = (u8*)((*pskb)->nh.ipv6h + 1)
185                               - (*pskb)->data;
186         unsigned char pnum = (*pskb)->nh.ipv6h->nexthdr;
187
188
189         /* This is where we call the helper: as the packet goes out. */
190         ct = nf_ct_get(*pskb, &ctinfo);
191         if (!ct || ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY)
192                 goto out;
193
194         help = nfct_help(ct);
195         if (!help || !help->helper)
196                 goto out;
197
198         protoff = nf_ct_ipv6_skip_exthdr(*pskb, extoff, &pnum,
199                                          (*pskb)->len - extoff);
200         if (protoff < 0 || protoff > (*pskb)->len ||
201             pnum == NEXTHDR_FRAGMENT) {
202                 DEBUGP("proto header not found\n");
203                 return NF_ACCEPT;
204         }
205
206         ret = help->helper->help(pskb, protoff, ct, ctinfo);
207         if (ret != NF_ACCEPT)
208                 return ret;
209 out:
210         /* We've seen it coming out the other side: confirm it */
211         return nf_conntrack_confirm(pskb);
212 }
213
214 extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb);
215 extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb,
216                                struct net_device *in,
217                                struct net_device *out,
218                                int (*okfn)(struct sk_buff *));
219 static unsigned int ipv6_defrag(unsigned int hooknum,
220                                 struct sk_buff **pskb,
221                                 const struct net_device *in,
222                                 const struct net_device *out,
223                                 int (*okfn)(struct sk_buff *))
224 {
225         struct sk_buff *reasm;
226
227         /* Previously seen (loopback)?  */
228         if ((*pskb)->nfct)
229                 return NF_ACCEPT;
230
231         reasm = nf_ct_frag6_gather(*pskb);
232
233         /* queued */
234         if (reasm == NULL)
235                 return NF_STOLEN;
236
237         /* error occured or not fragmented */
238         if (reasm == *pskb)
239                 return NF_ACCEPT;
240
241         nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in,
242                            (struct net_device *)out, okfn);
243
244         return NF_STOLEN;
245 }
246
247 static unsigned int ipv6_conntrack_in(unsigned int hooknum,
248                                       struct sk_buff **pskb,
249                                       const struct net_device *in,
250                                       const struct net_device *out,
251                                       int (*okfn)(struct sk_buff *))
252 {
253         struct sk_buff *reasm = (*pskb)->nfct_reasm;
254
255         /* This packet is fragmented and has reassembled packet. */
256         if (reasm) {
257                 /* Reassembled packet isn't parsed yet ? */
258                 if (!reasm->nfct) {
259                         unsigned int ret;
260
261                         ret = nf_conntrack_in(PF_INET6, hooknum, &reasm);
262                         if (ret != NF_ACCEPT)
263                                 return ret;
264                 }
265                 nf_conntrack_get(reasm->nfct);
266                 (*pskb)->nfct = reasm->nfct;
267                 return NF_ACCEPT;
268         }
269
270         return nf_conntrack_in(PF_INET6, hooknum, pskb);
271 }
272
273 static unsigned int ipv6_conntrack_local(unsigned int hooknum,
274                                          struct sk_buff **pskb,
275                                          const struct net_device *in,
276                                          const struct net_device *out,
277                                          int (*okfn)(struct sk_buff *))
278 {
279         /* root is playing with raw sockets. */
280         if ((*pskb)->len < sizeof(struct ipv6hdr)) {
281                 if (net_ratelimit())
282                         printk("ipv6_conntrack_local: packet too short\n");
283                 return NF_ACCEPT;
284         }
285         return ipv6_conntrack_in(hooknum, pskb, in, out, okfn);
286 }
287
288 static struct nf_hook_ops ipv6_conntrack_ops[] = {
289         {
290                 .hook           = ipv6_defrag,
291                 .owner          = THIS_MODULE,
292                 .pf             = PF_INET6,
293                 .hooknum        = NF_IP6_PRE_ROUTING,
294                 .priority       = NF_IP6_PRI_CONNTRACK_DEFRAG,
295         },
296         {
297                 .hook           = ipv6_conntrack_in,
298                 .owner          = THIS_MODULE,
299                 .pf             = PF_INET6,
300                 .hooknum        = NF_IP6_PRE_ROUTING,
301                 .priority       = NF_IP6_PRI_CONNTRACK,
302         },
303         {
304                 .hook           = ipv6_conntrack_local,
305                 .owner          = THIS_MODULE,
306                 .pf             = PF_INET6,
307                 .hooknum        = NF_IP6_LOCAL_OUT,
308                 .priority       = NF_IP6_PRI_CONNTRACK,
309         },
310         {
311                 .hook           = ipv6_defrag,
312                 .owner          = THIS_MODULE,
313                 .pf             = PF_INET6,
314                 .hooknum        = NF_IP6_LOCAL_OUT,
315                 .priority       = NF_IP6_PRI_CONNTRACK_DEFRAG,
316         },
317         {
318                 .hook           = ipv6_confirm,
319                 .owner          = THIS_MODULE,
320                 .pf             = PF_INET6,
321                 .hooknum        = NF_IP6_POST_ROUTING,
322                 .priority       = NF_IP6_PRI_LAST,
323         },
324         {
325                 .hook           = ipv6_confirm,
326                 .owner          = THIS_MODULE,
327                 .pf             = PF_INET6,
328                 .hooknum        = NF_IP6_LOCAL_IN,
329                 .priority       = NF_IP6_PRI_LAST-1,
330         },
331 };
332
333 #ifdef CONFIG_SYSCTL
334
335 /* From nf_conntrack_proto_icmpv6.c */
336 extern unsigned int nf_ct_icmpv6_timeout;
337
338 /* From nf_conntrack_frag6.c */
339 extern unsigned int nf_ct_frag6_timeout;
340 extern unsigned int nf_ct_frag6_low_thresh;
341 extern unsigned int nf_ct_frag6_high_thresh;
342
343 static struct ctl_table_header *nf_ct_ipv6_sysctl_header;
344
345 static ctl_table nf_ct_sysctl_table[] = {
346         {
347                 .ctl_name       = NET_NF_CONNTRACK_ICMPV6_TIMEOUT,
348                 .procname       = "nf_conntrack_icmpv6_timeout",
349                 .data           = &nf_ct_icmpv6_timeout,
350                 .maxlen         = sizeof(unsigned int),
351                 .mode           = 0644,
352                 .proc_handler   = &proc_dointvec_jiffies,
353         },
354         {
355                 .ctl_name       = NET_NF_CONNTRACK_FRAG6_TIMEOUT,
356                 .procname       = "nf_conntrack_frag6_timeout",
357                 .data           = &nf_ct_frag6_timeout,
358                 .maxlen         = sizeof(unsigned int),
359                 .mode           = 0644,
360                 .proc_handler   = &proc_dointvec_jiffies,
361         },
362         {
363                 .ctl_name       = NET_NF_CONNTRACK_FRAG6_LOW_THRESH,
364                 .procname       = "nf_conntrack_frag6_low_thresh",
365                 .data           = &nf_ct_frag6_low_thresh,
366                 .maxlen         = sizeof(unsigned int),
367                 .mode           = 0644,
368                 .proc_handler   = &proc_dointvec,
369         },
370         {
371                 .ctl_name       = NET_NF_CONNTRACK_FRAG6_HIGH_THRESH,
372                 .procname       = "nf_conntrack_frag6_high_thresh",
373                 .data           = &nf_ct_frag6_high_thresh,
374                 .maxlen         = sizeof(unsigned int),
375                 .mode           = 0644,
376                 .proc_handler   = &proc_dointvec,
377         },
378         { .ctl_name = 0 }
379 };
380
381 static ctl_table nf_ct_netfilter_table[] = {
382         {
383                 .ctl_name       = NET_NETFILTER,
384                 .procname       = "netfilter",
385                 .mode           = 0555,
386                 .child          = nf_ct_sysctl_table,
387         },
388         { .ctl_name = 0 }
389 };
390
391 static ctl_table nf_ct_net_table[] = {
392         {
393                 .ctl_name       = CTL_NET,
394                 .procname       = "net",
395                 .mode           = 0555,
396                 .child          = nf_ct_netfilter_table,
397         },
398         { .ctl_name = 0 }
399 };
400 #endif
401
402 #if defined(CONFIG_NF_CT_NETLINK) || \
403     defined(CONFIG_NF_CT_NETLINK_MODULE)
404
405 #include <linux/netfilter/nfnetlink.h>
406 #include <linux/netfilter/nfnetlink_conntrack.h>
407
408 static int ipv6_tuple_to_nfattr(struct sk_buff *skb,
409                                 const struct nf_conntrack_tuple *tuple)
410 {
411         NFA_PUT(skb, CTA_IP_V6_SRC, sizeof(u_int32_t) * 4,
412                 &tuple->src.u3.ip6);
413         NFA_PUT(skb, CTA_IP_V6_DST, sizeof(u_int32_t) * 4,
414                 &tuple->dst.u3.ip6);
415         return 0;
416
417 nfattr_failure:
418         return -1;
419 }
420
421 static const size_t cta_min_ip[CTA_IP_MAX] = {
422         [CTA_IP_V6_SRC-1]       = sizeof(u_int32_t)*4,
423         [CTA_IP_V6_DST-1]       = sizeof(u_int32_t)*4,
424 };
425
426 static int ipv6_nfattr_to_tuple(struct nfattr *tb[],
427                                 struct nf_conntrack_tuple *t)
428 {
429         if (!tb[CTA_IP_V6_SRC-1] || !tb[CTA_IP_V6_DST-1])
430                 return -EINVAL;
431
432         if (nfattr_bad_size(tb, CTA_IP_MAX, cta_min_ip))
433                 return -EINVAL;
434
435         memcpy(&t->src.u3.ip6, NFA_DATA(tb[CTA_IP_V6_SRC-1]), 
436                sizeof(u_int32_t) * 4);
437         memcpy(&t->dst.u3.ip6, NFA_DATA(tb[CTA_IP_V6_DST-1]),
438                sizeof(u_int32_t) * 4);
439
440         return 0;
441 }
442 #endif
443
444 struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = {
445         .l3proto                = PF_INET6,
446         .name                   = "ipv6",
447         .pkt_to_tuple           = ipv6_pkt_to_tuple,
448         .invert_tuple           = ipv6_invert_tuple,
449         .print_tuple            = ipv6_print_tuple,
450         .print_conntrack        = ipv6_print_conntrack,
451         .prepare                = ipv6_prepare,
452 #if defined(CONFIG_NF_CT_NETLINK) || \
453     defined(CONFIG_NF_CT_NETLINK_MODULE)
454         .tuple_to_nfattr        = ipv6_tuple_to_nfattr,
455         .nfattr_to_tuple        = ipv6_nfattr_to_tuple,
456 #endif
457         .get_features           = ipv6_get_features,
458         .me                     = THIS_MODULE,
459 };
460
461 extern struct nf_conntrack_protocol nf_conntrack_protocol_tcp6;
462 extern struct nf_conntrack_protocol nf_conntrack_protocol_udp6;
463 extern struct nf_conntrack_protocol nf_conntrack_protocol_icmpv6;
464 extern int nf_ct_frag6_init(void);
465 extern void nf_ct_frag6_cleanup(void);
466
467 MODULE_ALIAS("nf_conntrack-" __stringify(AF_INET6));
468 MODULE_LICENSE("GPL");
469 MODULE_AUTHOR("Yasuyuki KOZAKAI @USAGI <yasuyuki.kozakai@toshiba.co.jp>");
470
471 static int __init nf_conntrack_l3proto_ipv6_init(void)
472 {
473         int ret = 0;
474
475         need_conntrack();
476
477         ret = nf_ct_frag6_init();
478         if (ret < 0) {
479                 printk("nf_conntrack_ipv6: can't initialize frag6.\n");
480                 return ret;
481         }
482         ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_tcp6);
483         if (ret < 0) {
484                 printk("nf_conntrack_ipv6: can't register tcp.\n");
485                 goto cleanup_frag6;
486         }
487
488         ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_udp6);
489         if (ret < 0) {
490                 printk("nf_conntrack_ipv6: can't register udp.\n");
491                 goto cleanup_tcp;
492         }
493
494         ret = nf_conntrack_protocol_register(&nf_conntrack_protocol_icmpv6);
495         if (ret < 0) {
496                 printk("nf_conntrack_ipv6: can't register icmpv6.\n");
497                 goto cleanup_udp;
498         }
499
500         ret = nf_conntrack_l3proto_register(&nf_conntrack_l3proto_ipv6);
501         if (ret < 0) {
502                 printk("nf_conntrack_ipv6: can't register ipv6\n");
503                 goto cleanup_icmpv6;
504         }
505
506         ret = nf_register_hooks(ipv6_conntrack_ops,
507                                 ARRAY_SIZE(ipv6_conntrack_ops));
508         if (ret < 0) {
509                 printk("nf_conntrack_ipv6: can't register pre-routing defrag "
510                        "hook.\n");
511                 goto cleanup_ipv6;
512         }
513 #ifdef CONFIG_SYSCTL
514         nf_ct_ipv6_sysctl_header = register_sysctl_table(nf_ct_net_table, 0);
515         if (nf_ct_ipv6_sysctl_header == NULL) {
516                 printk("nf_conntrack: can't register to sysctl.\n");
517                 ret = -ENOMEM;
518                 goto cleanup_hooks;
519         }
520 #endif
521         return ret;
522
523 #ifdef CONFIG_SYSCTL
524  cleanup_hooks:
525         nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
526 #endif
527  cleanup_ipv6:
528         nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
529  cleanup_icmpv6:
530         nf_conntrack_protocol_unregister(&nf_conntrack_protocol_icmpv6);
531  cleanup_udp:
532         nf_conntrack_protocol_unregister(&nf_conntrack_protocol_udp6);
533  cleanup_tcp:
534         nf_conntrack_protocol_unregister(&nf_conntrack_protocol_tcp6);
535  cleanup_frag6:
536         nf_ct_frag6_cleanup();
537         return ret;
538 }
539
540 static void __exit nf_conntrack_l3proto_ipv6_fini(void)
541 {
542         synchronize_net();
543 #ifdef CONFIG_SYSCTL
544         unregister_sysctl_table(nf_ct_ipv6_sysctl_header);
545 #endif
546         nf_unregister_hooks(ipv6_conntrack_ops, ARRAY_SIZE(ipv6_conntrack_ops));
547         nf_conntrack_l3proto_unregister(&nf_conntrack_l3proto_ipv6);
548         nf_conntrack_protocol_unregister(&nf_conntrack_protocol_icmpv6);
549         nf_conntrack_protocol_unregister(&nf_conntrack_protocol_udp6);
550         nf_conntrack_protocol_unregister(&nf_conntrack_protocol_tcp6);
551         nf_ct_frag6_cleanup();
552 }
553
554 module_init(nf_conntrack_l3proto_ipv6_init);
555 module_exit(nf_conntrack_l3proto_ipv6_fini);