[NETFILTER]: Fix ip6_tables protocol bypass bug
[safe/jmp/linux-2.6] / net / ipv6 / netfilter / ip6_tables.c
1 /*
2  * Packet matching code.
3  *
4  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5  * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License version 2 as
9  * published by the Free Software Foundation.
10  *
11  * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
12  *      - increase module usage count as soon as we have rules inside
13  *        a table
14  * 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu>
15  *      - new extension header parser code
16  * 15 Oct 2005 Harald Welte <laforge@netfilter.org>
17  *      - Unification of {ip,ip6}_tables into x_tables
18  *      - Removed tcp and udp code, since it's not ipv6 specific
19  */
20
21 #include <linux/capability.h>
22 #include <linux/in.h>
23 #include <linux/skbuff.h>
24 #include <linux/kmod.h>
25 #include <linux/vmalloc.h>
26 #include <linux/netdevice.h>
27 #include <linux/module.h>
28 #include <linux/poison.h>
29 #include <linux/icmpv6.h>
30 #include <net/ipv6.h>
31 #include <asm/uaccess.h>
32 #include <linux/mutex.h>
33 #include <linux/proc_fs.h>
34 #include <linux/cpumask.h>
35
36 #include <linux/netfilter_ipv6/ip6_tables.h>
37 #include <linux/netfilter/x_tables.h>
38
39 MODULE_LICENSE("GPL");
40 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
41 MODULE_DESCRIPTION("IPv6 packet filter");
42
43 #define IPV6_HDR_LEN    (sizeof(struct ipv6hdr))
44 #define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
45
46 /*#define DEBUG_IP_FIREWALL*/
47 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
48 /*#define DEBUG_IP_FIREWALL_USER*/
49
50 #ifdef DEBUG_IP_FIREWALL
51 #define dprintf(format, args...)  printk(format , ## args)
52 #else
53 #define dprintf(format, args...)
54 #endif
55
56 #ifdef DEBUG_IP_FIREWALL_USER
57 #define duprintf(format, args...) printk(format , ## args)
58 #else
59 #define duprintf(format, args...)
60 #endif
61
62 #ifdef CONFIG_NETFILTER_DEBUG
63 #define IP_NF_ASSERT(x)                                         \
64 do {                                                            \
65         if (!(x))                                               \
66                 printk("IP_NF_ASSERT: %s:%s:%u\n",              \
67                        __FUNCTION__, __FILE__, __LINE__);       \
68 } while(0)
69 #else
70 #define IP_NF_ASSERT(x)
71 #endif
72
73 #if 0
74 /* All the better to debug you with... */
75 #define static
76 #define inline
77 #endif
78
79 /*
80    We keep a set of rules for each CPU, so we can avoid write-locking
81    them in the softirq when updating the counters and therefore
82    only need to read-lock in the softirq; doing a write_lock_bh() in user
83    context stops packets coming through and allows user context to read
84    the counters or update the rules.
85
86    Hence the start of any table is given by get_table() below.  */
87
88 #if 0
89 #define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
90 #define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
91 #define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
92 #endif
93
94 /* Check for an extension */
95 int 
96 ip6t_ext_hdr(u8 nexthdr)
97 {
98         return ( (nexthdr == IPPROTO_HOPOPTS)   ||
99                  (nexthdr == IPPROTO_ROUTING)   ||
100                  (nexthdr == IPPROTO_FRAGMENT)  ||
101                  (nexthdr == IPPROTO_ESP)       ||
102                  (nexthdr == IPPROTO_AH)        ||
103                  (nexthdr == IPPROTO_NONE)      ||
104                  (nexthdr == IPPROTO_DSTOPTS) );
105 }
106
107 /* Returns whether matches rule or not. */
108 static inline int
109 ip6_packet_match(const struct sk_buff *skb,
110                  const char *indev,
111                  const char *outdev,
112                  const struct ip6t_ip6 *ip6info,
113                  unsigned int *protoff,
114                  int *fragoff, int *hotdrop)
115 {
116         size_t i;
117         unsigned long ret;
118         const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
119
120 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
121
122         if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
123                                        &ip6info->src), IP6T_INV_SRCIP)
124             || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
125                                           &ip6info->dst), IP6T_INV_DSTIP)) {
126                 dprintf("Source or dest mismatch.\n");
127 /*
128                 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
129                         ipinfo->smsk.s_addr, ipinfo->src.s_addr,
130                         ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
131                 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
132                         ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
133                         ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
134                 return 0;
135         }
136
137         /* Look for ifname matches; this should unroll nicely. */
138         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
139                 ret |= (((const unsigned long *)indev)[i]
140                         ^ ((const unsigned long *)ip6info->iniface)[i])
141                         & ((const unsigned long *)ip6info->iniface_mask)[i];
142         }
143
144         if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
145                 dprintf("VIA in mismatch (%s vs %s).%s\n",
146                         indev, ip6info->iniface,
147                         ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
148                 return 0;
149         }
150
151         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
152                 ret |= (((const unsigned long *)outdev)[i]
153                         ^ ((const unsigned long *)ip6info->outiface)[i])
154                         & ((const unsigned long *)ip6info->outiface_mask)[i];
155         }
156
157         if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
158                 dprintf("VIA out mismatch (%s vs %s).%s\n",
159                         outdev, ip6info->outiface,
160                         ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
161                 return 0;
162         }
163
164 /* ... might want to do something with class and flowlabel here ... */
165
166         /* look for the desired protocol header */
167         if((ip6info->flags & IP6T_F_PROTO)) {
168                 int protohdr;
169                 unsigned short _frag_off;
170
171                 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
172                 if (protohdr < 0) {
173                         if (_frag_off == 0)
174                                 *hotdrop = 1;
175                         return 0;
176                 }
177                 *fragoff = _frag_off;
178
179                 dprintf("Packet protocol %hi ?= %s%hi.\n",
180                                 protohdr, 
181                                 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
182                                 ip6info->proto);
183
184                 if (ip6info->proto == protohdr) {
185                         if(ip6info->invflags & IP6T_INV_PROTO) {
186                                 return 0;
187                         }
188                         return 1;
189                 }
190
191                 /* We need match for the '-p all', too! */
192                 if ((ip6info->proto != 0) &&
193                         !(ip6info->invflags & IP6T_INV_PROTO))
194                         return 0;
195         }
196         return 1;
197 }
198
199 /* should be ip6 safe */
200 static inline int 
201 ip6_checkentry(const struct ip6t_ip6 *ipv6)
202 {
203         if (ipv6->flags & ~IP6T_F_MASK) {
204                 duprintf("Unknown flag bits set: %08X\n",
205                          ipv6->flags & ~IP6T_F_MASK);
206                 return 0;
207         }
208         if (ipv6->invflags & ~IP6T_INV_MASK) {
209                 duprintf("Unknown invflag bits set: %08X\n",
210                          ipv6->invflags & ~IP6T_INV_MASK);
211                 return 0;
212         }
213         return 1;
214 }
215
216 static unsigned int
217 ip6t_error(struct sk_buff **pskb,
218           const struct net_device *in,
219           const struct net_device *out,
220           unsigned int hooknum,
221           const struct xt_target *target,
222           const void *targinfo)
223 {
224         if (net_ratelimit())
225                 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
226
227         return NF_DROP;
228 }
229
230 static inline
231 int do_match(struct ip6t_entry_match *m,
232              const struct sk_buff *skb,
233              const struct net_device *in,
234              const struct net_device *out,
235              int offset,
236              unsigned int protoff,
237              int *hotdrop)
238 {
239         /* Stop iteration if it doesn't match */
240         if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
241                                       offset, protoff, hotdrop))
242                 return 1;
243         else
244                 return 0;
245 }
246
247 static inline struct ip6t_entry *
248 get_entry(void *base, unsigned int offset)
249 {
250         return (struct ip6t_entry *)(base + offset);
251 }
252
253 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
254 unsigned int
255 ip6t_do_table(struct sk_buff **pskb,
256               unsigned int hook,
257               const struct net_device *in,
258               const struct net_device *out,
259               struct xt_table *table)
260 {
261         static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
262         int offset = 0;
263         unsigned int protoff = 0;
264         int hotdrop = 0;
265         /* Initializing verdict to NF_DROP keeps gcc happy. */
266         unsigned int verdict = NF_DROP;
267         const char *indev, *outdev;
268         void *table_base;
269         struct ip6t_entry *e, *back;
270         struct xt_table_info *private;
271
272         /* Initialization */
273         indev = in ? in->name : nulldevname;
274         outdev = out ? out->name : nulldevname;
275         /* We handle fragments by dealing with the first fragment as
276          * if it was a normal packet.  All other fragments are treated
277          * normally, except that they will NEVER match rules that ask
278          * things we don't know, ie. tcp syn flag or ports).  If the
279          * rule is also a fragment-specific rule, non-fragments won't
280          * match it. */
281
282         read_lock_bh(&table->lock);
283         private = table->private;
284         IP_NF_ASSERT(table->valid_hooks & (1 << hook));
285         table_base = (void *)private->entries[smp_processor_id()];
286         e = get_entry(table_base, private->hook_entry[hook]);
287
288         /* For return from builtin chain */
289         back = get_entry(table_base, private->underflow[hook]);
290
291         do {
292                 IP_NF_ASSERT(e);
293                 IP_NF_ASSERT(back);
294                 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
295                         &protoff, &offset, &hotdrop)) {
296                         struct ip6t_entry_target *t;
297
298                         if (IP6T_MATCH_ITERATE(e, do_match,
299                                                *pskb, in, out,
300                                                offset, protoff, &hotdrop) != 0)
301                                 goto no_match;
302
303                         ADD_COUNTER(e->counters,
304                                     ntohs((*pskb)->nh.ipv6h->payload_len)
305                                     + IPV6_HDR_LEN,
306                                     1);
307
308                         t = ip6t_get_target(e);
309                         IP_NF_ASSERT(t->u.kernel.target);
310                         /* Standard target? */
311                         if (!t->u.kernel.target->target) {
312                                 int v;
313
314                                 v = ((struct ip6t_standard_target *)t)->verdict;
315                                 if (v < 0) {
316                                         /* Pop from stack? */
317                                         if (v != IP6T_RETURN) {
318                                                 verdict = (unsigned)(-v) - 1;
319                                                 break;
320                                         }
321                                         e = back;
322                                         back = get_entry(table_base,
323                                                          back->comefrom);
324                                         continue;
325                                 }
326                                 if (table_base + v != (void *)e + e->next_offset
327                                     && !(e->ipv6.flags & IP6T_F_GOTO)) {
328                                         /* Save old back ptr in next entry */
329                                         struct ip6t_entry *next
330                                                 = (void *)e + e->next_offset;
331                                         next->comefrom
332                                                 = (void *)back - table_base;
333                                         /* set back pointer to next entry */
334                                         back = next;
335                                 }
336
337                                 e = get_entry(table_base, v);
338                         } else {
339                                 /* Targets which reenter must return
340                                    abs. verdicts */
341 #ifdef CONFIG_NETFILTER_DEBUG
342                                 ((struct ip6t_entry *)table_base)->comefrom
343                                         = 0xeeeeeeec;
344 #endif
345                                 verdict = t->u.kernel.target->target(pskb,
346                                                                      in, out,
347                                                                      hook,
348                                                                      t->u.kernel.target,
349                                                                      t->data);
350
351 #ifdef CONFIG_NETFILTER_DEBUG
352                                 if (((struct ip6t_entry *)table_base)->comefrom
353                                     != 0xeeeeeeec
354                                     && verdict == IP6T_CONTINUE) {
355                                         printk("Target %s reentered!\n",
356                                                t->u.kernel.target->name);
357                                         verdict = NF_DROP;
358                                 }
359                                 ((struct ip6t_entry *)table_base)->comefrom
360                                         = 0x57acc001;
361 #endif
362                                 if (verdict == IP6T_CONTINUE)
363                                         e = (void *)e + e->next_offset;
364                                 else
365                                         /* Verdict */
366                                         break;
367                         }
368                 } else {
369
370                 no_match:
371                         e = (void *)e + e->next_offset;
372                 }
373         } while (!hotdrop);
374
375 #ifdef CONFIG_NETFILTER_DEBUG
376         ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
377 #endif
378         read_unlock_bh(&table->lock);
379
380 #ifdef DEBUG_ALLOW_ALL
381         return NF_ACCEPT;
382 #else
383         if (hotdrop)
384                 return NF_DROP;
385         else return verdict;
386 #endif
387 }
388
389 /* All zeroes == unconditional rule. */
390 static inline int
391 unconditional(const struct ip6t_ip6 *ipv6)
392 {
393         unsigned int i;
394
395         for (i = 0; i < sizeof(*ipv6); i++)
396                 if (((char *)ipv6)[i])
397                         break;
398
399         return (i == sizeof(*ipv6));
400 }
401
402 /* Figures out from what hook each rule can be called: returns 0 if
403    there are loops.  Puts hook bitmask in comefrom. */
404 static int
405 mark_source_chains(struct xt_table_info *newinfo,
406                    unsigned int valid_hooks, void *entry0)
407 {
408         unsigned int hook;
409
410         /* No recursion; use packet counter to save back ptrs (reset
411            to 0 as we leave), and comefrom to save source hook bitmask */
412         for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
413                 unsigned int pos = newinfo->hook_entry[hook];
414                 struct ip6t_entry *e
415                         = (struct ip6t_entry *)(entry0 + pos);
416
417                 if (!(valid_hooks & (1 << hook)))
418                         continue;
419
420                 /* Set initial back pointer. */
421                 e->counters.pcnt = pos;
422
423                 for (;;) {
424                         struct ip6t_standard_target *t
425                                 = (void *)ip6t_get_target(e);
426
427                         if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
428                                 printk("iptables: loop hook %u pos %u %08X.\n",
429                                        hook, pos, e->comefrom);
430                                 return 0;
431                         }
432                         e->comefrom
433                                 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
434
435                         /* Unconditional return/END. */
436                         if (e->target_offset == sizeof(struct ip6t_entry)
437                             && (strcmp(t->target.u.user.name,
438                                        IP6T_STANDARD_TARGET) == 0)
439                             && t->verdict < 0
440                             && unconditional(&e->ipv6)) {
441                                 unsigned int oldpos, size;
442
443                                 /* Return: backtrack through the last
444                                    big jump. */
445                                 do {
446                                         e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
447 #ifdef DEBUG_IP_FIREWALL_USER
448                                         if (e->comefrom
449                                             & (1 << NF_IP6_NUMHOOKS)) {
450                                                 duprintf("Back unset "
451                                                          "on hook %u "
452                                                          "rule %u\n",
453                                                          hook, pos);
454                                         }
455 #endif
456                                         oldpos = pos;
457                                         pos = e->counters.pcnt;
458                                         e->counters.pcnt = 0;
459
460                                         /* We're at the start. */
461                                         if (pos == oldpos)
462                                                 goto next;
463
464                                         e = (struct ip6t_entry *)
465                                                 (entry0 + pos);
466                                 } while (oldpos == pos + e->next_offset);
467
468                                 /* Move along one */
469                                 size = e->next_offset;
470                                 e = (struct ip6t_entry *)
471                                         (entry0 + pos + size);
472                                 e->counters.pcnt = pos;
473                                 pos += size;
474                         } else {
475                                 int newpos = t->verdict;
476
477                                 if (strcmp(t->target.u.user.name,
478                                            IP6T_STANDARD_TARGET) == 0
479                                     && newpos >= 0) {
480                                         /* This a jump; chase it. */
481                                         duprintf("Jump rule %u -> %u\n",
482                                                  pos, newpos);
483                                 } else {
484                                         /* ... this is a fallthru */
485                                         newpos = pos + e->next_offset;
486                                 }
487                                 e = (struct ip6t_entry *)
488                                         (entry0 + newpos);
489                                 e->counters.pcnt = pos;
490                                 pos = newpos;
491                         }
492                 }
493                 next:
494                 duprintf("Finished chain %u\n", hook);
495         }
496         return 1;
497 }
498
499 static inline int
500 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
501 {
502         if (i && (*i)-- == 0)
503                 return 1;
504
505         if (m->u.kernel.match->destroy)
506                 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
507         module_put(m->u.kernel.match->me);
508         return 0;
509 }
510
511 static inline int
512 standard_check(const struct ip6t_entry_target *t,
513                unsigned int max_offset)
514 {
515         struct ip6t_standard_target *targ = (void *)t;
516
517         /* Check standard info. */
518         if (targ->verdict >= 0
519             && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
520                 duprintf("ip6t_standard_check: bad verdict (%i)\n",
521                          targ->verdict);
522                 return 0;
523         }
524         if (targ->verdict < -NF_MAX_VERDICT - 1) {
525                 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
526                          targ->verdict);
527                 return 0;
528         }
529         return 1;
530 }
531
532 static inline int
533 check_match(struct ip6t_entry_match *m,
534             const char *name,
535             const struct ip6t_ip6 *ipv6,
536             unsigned int hookmask,
537             unsigned int *i)
538 {
539         struct ip6t_match *match;
540         int ret;
541
542         match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
543                                         m->u.user.revision),
544                                         "ip6t_%s", m->u.user.name);
545         if (IS_ERR(match) || !match) {
546                 duprintf("check_match: `%s' not found\n", m->u.user.name);
547                 return match ? PTR_ERR(match) : -ENOENT;
548         }
549         m->u.kernel.match = match;
550
551         ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
552                              name, hookmask, ipv6->proto,
553                              ipv6->invflags & IP6T_INV_PROTO);
554         if (ret)
555                 goto err;
556
557         if (m->u.kernel.match->checkentry
558             && !m->u.kernel.match->checkentry(name, ipv6, match,  m->data,
559                                               hookmask)) {
560                 duprintf("ip_tables: check failed for `%s'.\n",
561                          m->u.kernel.match->name);
562                 ret = -EINVAL;
563                 goto err;
564         }
565
566         (*i)++;
567         return 0;
568 err:
569         module_put(m->u.kernel.match->me);
570         return ret;
571 }
572
573 static struct ip6t_target ip6t_standard_target;
574
575 static inline int
576 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
577             unsigned int *i)
578 {
579         struct ip6t_entry_target *t;
580         struct ip6t_target *target;
581         int ret;
582         unsigned int j;
583
584         if (!ip6_checkentry(&e->ipv6)) {
585                 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
586                 return -EINVAL;
587         }
588
589         j = 0;
590         ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
591         if (ret != 0)
592                 goto cleanup_matches;
593
594         t = ip6t_get_target(e);
595         target = try_then_request_module(xt_find_target(AF_INET6,
596                                                         t->u.user.name,
597                                                         t->u.user.revision),
598                                          "ip6t_%s", t->u.user.name);
599         if (IS_ERR(target) || !target) {
600                 duprintf("check_entry: `%s' not found\n", t->u.user.name);
601                 ret = target ? PTR_ERR(target) : -ENOENT;
602                 goto cleanup_matches;
603         }
604         t->u.kernel.target = target;
605
606         ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
607                               name, e->comefrom, e->ipv6.proto,
608                               e->ipv6.invflags & IP6T_INV_PROTO);
609         if (ret)
610                 goto err;
611
612         if (t->u.kernel.target == &ip6t_standard_target) {
613                 if (!standard_check(t, size)) {
614                         ret = -EINVAL;
615                         goto err;
616                 }
617         } else if (t->u.kernel.target->checkentry
618                    && !t->u.kernel.target->checkentry(name, e, target, t->data,
619                                                       e->comefrom)) {
620                 duprintf("ip_tables: check failed for `%s'.\n",
621                          t->u.kernel.target->name);
622                 ret = -EINVAL;
623                 goto err;
624         }
625
626         (*i)++;
627         return 0;
628  err:
629         module_put(t->u.kernel.target->me);
630  cleanup_matches:
631         IP6T_MATCH_ITERATE(e, cleanup_match, &j);
632         return ret;
633 }
634
635 static inline int
636 check_entry_size_and_hooks(struct ip6t_entry *e,
637                            struct xt_table_info *newinfo,
638                            unsigned char *base,
639                            unsigned char *limit,
640                            const unsigned int *hook_entries,
641                            const unsigned int *underflows,
642                            unsigned int *i)
643 {
644         unsigned int h;
645
646         if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
647             || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
648                 duprintf("Bad offset %p\n", e);
649                 return -EINVAL;
650         }
651
652         if (e->next_offset
653             < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
654                 duprintf("checking: element %p size %u\n",
655                          e, e->next_offset);
656                 return -EINVAL;
657         }
658
659         /* Check hooks & underflows */
660         for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
661                 if ((unsigned char *)e - base == hook_entries[h])
662                         newinfo->hook_entry[h] = hook_entries[h];
663                 if ((unsigned char *)e - base == underflows[h])
664                         newinfo->underflow[h] = underflows[h];
665         }
666
667         /* FIXME: underflows must be unconditional, standard verdicts
668            < 0 (not IP6T_RETURN). --RR */
669
670         /* Clear counters and comefrom */
671         e->counters = ((struct xt_counters) { 0, 0 });
672         e->comefrom = 0;
673
674         (*i)++;
675         return 0;
676 }
677
678 static inline int
679 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
680 {
681         struct ip6t_entry_target *t;
682
683         if (i && (*i)-- == 0)
684                 return 1;
685
686         /* Cleanup all matches */
687         IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
688         t = ip6t_get_target(e);
689         if (t->u.kernel.target->destroy)
690                 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
691         module_put(t->u.kernel.target->me);
692         return 0;
693 }
694
695 /* Checks and translates the user-supplied table segment (held in
696    newinfo) */
697 static int
698 translate_table(const char *name,
699                 unsigned int valid_hooks,
700                 struct xt_table_info *newinfo,
701                 void *entry0,
702                 unsigned int size,
703                 unsigned int number,
704                 const unsigned int *hook_entries,
705                 const unsigned int *underflows)
706 {
707         unsigned int i;
708         int ret;
709
710         newinfo->size = size;
711         newinfo->number = number;
712
713         /* Init all hooks to impossible value. */
714         for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
715                 newinfo->hook_entry[i] = 0xFFFFFFFF;
716                 newinfo->underflow[i] = 0xFFFFFFFF;
717         }
718
719         duprintf("translate_table: size %u\n", newinfo->size);
720         i = 0;
721         /* Walk through entries, checking offsets. */
722         ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
723                                 check_entry_size_and_hooks,
724                                 newinfo,
725                                 entry0,
726                                 entry0 + size,
727                                 hook_entries, underflows, &i);
728         if (ret != 0)
729                 return ret;
730
731         if (i != number) {
732                 duprintf("translate_table: %u not %u entries\n",
733                          i, number);
734                 return -EINVAL;
735         }
736
737         /* Check hooks all assigned */
738         for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
739                 /* Only hooks which are valid */
740                 if (!(valid_hooks & (1 << i)))
741                         continue;
742                 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
743                         duprintf("Invalid hook entry %u %u\n",
744                                  i, hook_entries[i]);
745                         return -EINVAL;
746                 }
747                 if (newinfo->underflow[i] == 0xFFFFFFFF) {
748                         duprintf("Invalid underflow %u %u\n",
749                                  i, underflows[i]);
750                         return -EINVAL;
751                 }
752         }
753
754         if (!mark_source_chains(newinfo, valid_hooks, entry0))
755                 return -ELOOP;
756
757         /* Finally, each sanity check must pass */
758         i = 0;
759         ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
760                                 check_entry, name, size, &i);
761
762         if (ret != 0) {
763                 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
764                                   cleanup_entry, &i);
765                 return ret;
766         }
767
768         /* And one copy for every other CPU */
769         for_each_possible_cpu(i) {
770                 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
771                         memcpy(newinfo->entries[i], entry0, newinfo->size);
772         }
773
774         return ret;
775 }
776
777 /* Gets counters. */
778 static inline int
779 add_entry_to_counter(const struct ip6t_entry *e,
780                      struct xt_counters total[],
781                      unsigned int *i)
782 {
783         ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
784
785         (*i)++;
786         return 0;
787 }
788
789 static inline int
790 set_entry_to_counter(const struct ip6t_entry *e,
791                      struct ip6t_counters total[],
792                      unsigned int *i)
793 {
794         SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
795
796         (*i)++;
797         return 0;
798 }
799
800 static void
801 get_counters(const struct xt_table_info *t,
802              struct xt_counters counters[])
803 {
804         unsigned int cpu;
805         unsigned int i;
806         unsigned int curcpu;
807
808         /* Instead of clearing (by a previous call to memset())
809          * the counters and using adds, we set the counters
810          * with data used by 'current' CPU
811          * We dont care about preemption here.
812          */
813         curcpu = raw_smp_processor_id();
814
815         i = 0;
816         IP6T_ENTRY_ITERATE(t->entries[curcpu],
817                            t->size,
818                            set_entry_to_counter,
819                            counters,
820                            &i);
821
822         for_each_possible_cpu(cpu) {
823                 if (cpu == curcpu)
824                         continue;
825                 i = 0;
826                 IP6T_ENTRY_ITERATE(t->entries[cpu],
827                                   t->size,
828                                   add_entry_to_counter,
829                                   counters,
830                                   &i);
831         }
832 }
833
834 static int
835 copy_entries_to_user(unsigned int total_size,
836                      struct xt_table *table,
837                      void __user *userptr)
838 {
839         unsigned int off, num, countersize;
840         struct ip6t_entry *e;
841         struct xt_counters *counters;
842         struct xt_table_info *private = table->private;
843         int ret = 0;
844         void *loc_cpu_entry;
845
846         /* We need atomic snapshot of counters: rest doesn't change
847            (other than comefrom, which userspace doesn't care
848            about). */
849         countersize = sizeof(struct xt_counters) * private->number;
850         counters = vmalloc(countersize);
851
852         if (counters == NULL)
853                 return -ENOMEM;
854
855         /* First, sum counters... */
856         write_lock_bh(&table->lock);
857         get_counters(private, counters);
858         write_unlock_bh(&table->lock);
859
860         /* choose the copy that is on ourc node/cpu */
861         loc_cpu_entry = private->entries[raw_smp_processor_id()];
862         if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
863                 ret = -EFAULT;
864                 goto free_counters;
865         }
866
867         /* FIXME: use iterator macros --RR */
868         /* ... then go back and fix counters and names */
869         for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
870                 unsigned int i;
871                 struct ip6t_entry_match *m;
872                 struct ip6t_entry_target *t;
873
874                 e = (struct ip6t_entry *)(loc_cpu_entry + off);
875                 if (copy_to_user(userptr + off
876                                  + offsetof(struct ip6t_entry, counters),
877                                  &counters[num],
878                                  sizeof(counters[num])) != 0) {
879                         ret = -EFAULT;
880                         goto free_counters;
881                 }
882
883                 for (i = sizeof(struct ip6t_entry);
884                      i < e->target_offset;
885                      i += m->u.match_size) {
886                         m = (void *)e + i;
887
888                         if (copy_to_user(userptr + off + i
889                                          + offsetof(struct ip6t_entry_match,
890                                                     u.user.name),
891                                          m->u.kernel.match->name,
892                                          strlen(m->u.kernel.match->name)+1)
893                             != 0) {
894                                 ret = -EFAULT;
895                                 goto free_counters;
896                         }
897                 }
898
899                 t = ip6t_get_target(e);
900                 if (copy_to_user(userptr + off + e->target_offset
901                                  + offsetof(struct ip6t_entry_target,
902                                             u.user.name),
903                                  t->u.kernel.target->name,
904                                  strlen(t->u.kernel.target->name)+1) != 0) {
905                         ret = -EFAULT;
906                         goto free_counters;
907                 }
908         }
909
910  free_counters:
911         vfree(counters);
912         return ret;
913 }
914
915 static int
916 get_entries(const struct ip6t_get_entries *entries,
917             struct ip6t_get_entries __user *uptr)
918 {
919         int ret;
920         struct xt_table *t;
921
922         t = xt_find_table_lock(AF_INET6, entries->name);
923         if (t && !IS_ERR(t)) {
924                 struct xt_table_info *private = t->private;
925                 duprintf("t->private->number = %u\n", private->number);
926                 if (entries->size == private->size)
927                         ret = copy_entries_to_user(private->size,
928                                                    t, uptr->entrytable);
929                 else {
930                         duprintf("get_entries: I've got %u not %u!\n",
931                                  private->size, entries->size);
932                         ret = -EINVAL;
933                 }
934                 module_put(t->me);
935                 xt_table_unlock(t);
936         } else
937                 ret = t ? PTR_ERR(t) : -ENOENT;
938
939         return ret;
940 }
941
942 static int
943 do_replace(void __user *user, unsigned int len)
944 {
945         int ret;
946         struct ip6t_replace tmp;
947         struct xt_table *t;
948         struct xt_table_info *newinfo, *oldinfo;
949         struct xt_counters *counters;
950         void *loc_cpu_entry, *loc_cpu_old_entry;
951
952         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
953                 return -EFAULT;
954
955         /* overflow check */
956         if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
957                         SMP_CACHE_BYTES)
958                 return -ENOMEM;
959         if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
960                 return -ENOMEM;
961
962         newinfo = xt_alloc_table_info(tmp.size);
963         if (!newinfo)
964                 return -ENOMEM;
965
966         /* choose the copy that is on our node/cpu */
967         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
968         if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
969                            tmp.size) != 0) {
970                 ret = -EFAULT;
971                 goto free_newinfo;
972         }
973
974         counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
975         if (!counters) {
976                 ret = -ENOMEM;
977                 goto free_newinfo;
978         }
979
980         ret = translate_table(tmp.name, tmp.valid_hooks,
981                               newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
982                               tmp.hook_entry, tmp.underflow);
983         if (ret != 0)
984                 goto free_newinfo_counters;
985
986         duprintf("ip_tables: Translated table\n");
987
988         t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
989                                     "ip6table_%s", tmp.name);
990         if (!t || IS_ERR(t)) {
991                 ret = t ? PTR_ERR(t) : -ENOENT;
992                 goto free_newinfo_counters_untrans;
993         }
994
995         /* You lied! */
996         if (tmp.valid_hooks != t->valid_hooks) {
997                 duprintf("Valid hook crap: %08X vs %08X\n",
998                          tmp.valid_hooks, t->valid_hooks);
999                 ret = -EINVAL;
1000                 goto put_module;
1001         }
1002
1003         oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1004         if (!oldinfo)
1005                 goto put_module;
1006
1007         /* Update module usage count based on number of rules */
1008         duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1009                 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1010         if ((oldinfo->number > oldinfo->initial_entries) || 
1011             (newinfo->number <= oldinfo->initial_entries)) 
1012                 module_put(t->me);
1013         if ((oldinfo->number > oldinfo->initial_entries) &&
1014             (newinfo->number <= oldinfo->initial_entries))
1015                 module_put(t->me);
1016
1017         /* Get the old counters. */
1018         get_counters(oldinfo, counters);
1019         /* Decrease module usage counts and free resource */
1020         loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1021         IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1022         xt_free_table_info(oldinfo);
1023         if (copy_to_user(tmp.counters, counters,
1024                          sizeof(struct xt_counters) * tmp.num_counters) != 0)
1025                 ret = -EFAULT;
1026         vfree(counters);
1027         xt_table_unlock(t);
1028         return ret;
1029
1030  put_module:
1031         module_put(t->me);
1032         xt_table_unlock(t);
1033  free_newinfo_counters_untrans:
1034         IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1035  free_newinfo_counters:
1036         vfree(counters);
1037  free_newinfo:
1038         xt_free_table_info(newinfo);
1039         return ret;
1040 }
1041
1042 /* We're lazy, and add to the first CPU; overflow works its fey magic
1043  * and everything is OK. */
1044 static inline int
1045 add_counter_to_entry(struct ip6t_entry *e,
1046                      const struct xt_counters addme[],
1047                      unsigned int *i)
1048 {
1049 #if 0
1050         duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1051                  *i,
1052                  (long unsigned int)e->counters.pcnt,
1053                  (long unsigned int)e->counters.bcnt,
1054                  (long unsigned int)addme[*i].pcnt,
1055                  (long unsigned int)addme[*i].bcnt);
1056 #endif
1057
1058         ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1059
1060         (*i)++;
1061         return 0;
1062 }
1063
1064 static int
1065 do_add_counters(void __user *user, unsigned int len)
1066 {
1067         unsigned int i;
1068         struct xt_counters_info tmp, *paddc;
1069         struct xt_table_info *private;
1070         struct xt_table *t;
1071         int ret = 0;
1072         void *loc_cpu_entry;
1073
1074         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1075                 return -EFAULT;
1076
1077         if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1078                 return -EINVAL;
1079
1080         paddc = vmalloc(len);
1081         if (!paddc)
1082                 return -ENOMEM;
1083
1084         if (copy_from_user(paddc, user, len) != 0) {
1085                 ret = -EFAULT;
1086                 goto free;
1087         }
1088
1089         t = xt_find_table_lock(AF_INET6, tmp.name);
1090         if (!t || IS_ERR(t)) {
1091                 ret = t ? PTR_ERR(t) : -ENOENT;
1092                 goto free;
1093         }
1094
1095         write_lock_bh(&t->lock);
1096         private = t->private;
1097         if (private->number != tmp.num_counters) {
1098                 ret = -EINVAL;
1099                 goto unlock_up_free;
1100         }
1101
1102         i = 0;
1103         /* Choose the copy that is on our node */
1104         loc_cpu_entry = private->entries[smp_processor_id()];
1105         IP6T_ENTRY_ITERATE(loc_cpu_entry,
1106                           private->size,
1107                           add_counter_to_entry,
1108                           paddc->counters,
1109                           &i);
1110  unlock_up_free:
1111         write_unlock_bh(&t->lock);
1112         xt_table_unlock(t);
1113         module_put(t->me);
1114  free:
1115         vfree(paddc);
1116
1117         return ret;
1118 }
1119
1120 static int
1121 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1122 {
1123         int ret;
1124
1125         if (!capable(CAP_NET_ADMIN))
1126                 return -EPERM;
1127
1128         switch (cmd) {
1129         case IP6T_SO_SET_REPLACE:
1130                 ret = do_replace(user, len);
1131                 break;
1132
1133         case IP6T_SO_SET_ADD_COUNTERS:
1134                 ret = do_add_counters(user, len);
1135                 break;
1136
1137         default:
1138                 duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1139                 ret = -EINVAL;
1140         }
1141
1142         return ret;
1143 }
1144
1145 static int
1146 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1147 {
1148         int ret;
1149
1150         if (!capable(CAP_NET_ADMIN))
1151                 return -EPERM;
1152
1153         switch (cmd) {
1154         case IP6T_SO_GET_INFO: {
1155                 char name[IP6T_TABLE_MAXNAMELEN];
1156                 struct xt_table *t;
1157
1158                 if (*len != sizeof(struct ip6t_getinfo)) {
1159                         duprintf("length %u != %u\n", *len,
1160                                  sizeof(struct ip6t_getinfo));
1161                         ret = -EINVAL;
1162                         break;
1163                 }
1164
1165                 if (copy_from_user(name, user, sizeof(name)) != 0) {
1166                         ret = -EFAULT;
1167                         break;
1168                 }
1169                 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1170
1171                 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1172                                             "ip6table_%s", name);
1173                 if (t && !IS_ERR(t)) {
1174                         struct ip6t_getinfo info;
1175                         struct xt_table_info *private = t->private;
1176
1177                         info.valid_hooks = t->valid_hooks;
1178                         memcpy(info.hook_entry, private->hook_entry,
1179                                sizeof(info.hook_entry));
1180                         memcpy(info.underflow, private->underflow,
1181                                sizeof(info.underflow));
1182                         info.num_entries = private->number;
1183                         info.size = private->size;
1184                         memcpy(info.name, name, sizeof(info.name));
1185
1186                         if (copy_to_user(user, &info, *len) != 0)
1187                                 ret = -EFAULT;
1188                         else
1189                                 ret = 0;
1190                         xt_table_unlock(t);
1191                         module_put(t->me);
1192                 } else
1193                         ret = t ? PTR_ERR(t) : -ENOENT;
1194         }
1195         break;
1196
1197         case IP6T_SO_GET_ENTRIES: {
1198                 struct ip6t_get_entries get;
1199
1200                 if (*len < sizeof(get)) {
1201                         duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1202                         ret = -EINVAL;
1203                 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1204                         ret = -EFAULT;
1205                 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1206                         duprintf("get_entries: %u != %u\n", *len,
1207                                  sizeof(struct ip6t_get_entries) + get.size);
1208                         ret = -EINVAL;
1209                 } else
1210                         ret = get_entries(&get, user);
1211                 break;
1212         }
1213
1214         case IP6T_SO_GET_REVISION_MATCH:
1215         case IP6T_SO_GET_REVISION_TARGET: {
1216                 struct ip6t_get_revision rev;
1217                 int target;
1218
1219                 if (*len != sizeof(rev)) {
1220                         ret = -EINVAL;
1221                         break;
1222                 }
1223                 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1224                         ret = -EFAULT;
1225                         break;
1226                 }
1227
1228                 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1229                         target = 1;
1230                 else
1231                         target = 0;
1232
1233                 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1234                                                          rev.revision,
1235                                                          target, &ret),
1236                                         "ip6t_%s", rev.name);
1237                 break;
1238         }
1239
1240         default:
1241                 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1242                 ret = -EINVAL;
1243         }
1244
1245         return ret;
1246 }
1247
1248 int ip6t_register_table(struct xt_table *table,
1249                         const struct ip6t_replace *repl)
1250 {
1251         int ret;
1252         struct xt_table_info *newinfo;
1253         static struct xt_table_info bootstrap
1254                 = { 0, 0, 0, { 0 }, { 0 }, { } };
1255         void *loc_cpu_entry;
1256
1257         newinfo = xt_alloc_table_info(repl->size);
1258         if (!newinfo)
1259                 return -ENOMEM;
1260
1261         /* choose the copy on our node/cpu */
1262         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1263         memcpy(loc_cpu_entry, repl->entries, repl->size);
1264
1265         ret = translate_table(table->name, table->valid_hooks,
1266                               newinfo, loc_cpu_entry, repl->size,
1267                               repl->num_entries,
1268                               repl->hook_entry,
1269                               repl->underflow);
1270         if (ret != 0) {
1271                 xt_free_table_info(newinfo);
1272                 return ret;
1273         }
1274
1275         ret = xt_register_table(table, &bootstrap, newinfo);
1276         if (ret != 0) {
1277                 xt_free_table_info(newinfo);
1278                 return ret;
1279         }
1280
1281         return 0;
1282 }
1283
1284 void ip6t_unregister_table(struct xt_table *table)
1285 {
1286         struct xt_table_info *private;
1287         void *loc_cpu_entry;
1288
1289         private = xt_unregister_table(table);
1290
1291         /* Decrease module usage counts and free resources */
1292         loc_cpu_entry = private->entries[raw_smp_processor_id()];
1293         IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1294         xt_free_table_info(private);
1295 }
1296
1297 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1298 static inline int
1299 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1300                      u_int8_t type, u_int8_t code,
1301                      int invert)
1302 {
1303         return (type == test_type && code >= min_code && code <= max_code)
1304                 ^ invert;
1305 }
1306
1307 static int
1308 icmp6_match(const struct sk_buff *skb,
1309            const struct net_device *in,
1310            const struct net_device *out,
1311            const struct xt_match *match,
1312            const void *matchinfo,
1313            int offset,
1314            unsigned int protoff,
1315            int *hotdrop)
1316 {
1317         struct icmp6hdr _icmp, *ic;
1318         const struct ip6t_icmp *icmpinfo = matchinfo;
1319
1320         /* Must not be a fragment. */
1321         if (offset)
1322                 return 0;
1323
1324         ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1325         if (ic == NULL) {
1326                 /* We've been asked to examine this packet, and we
1327                    can't.  Hence, no choice but to drop. */
1328                 duprintf("Dropping evil ICMP tinygram.\n");
1329                 *hotdrop = 1;
1330                 return 0;
1331         }
1332
1333         return icmp6_type_code_match(icmpinfo->type,
1334                                      icmpinfo->code[0],
1335                                      icmpinfo->code[1],
1336                                      ic->icmp6_type, ic->icmp6_code,
1337                                      !!(icmpinfo->invflags&IP6T_ICMP_INV));
1338 }
1339
1340 /* Called when user tries to insert an entry of this type. */
1341 static int
1342 icmp6_checkentry(const char *tablename,
1343            const void *entry,
1344            const struct xt_match *match,
1345            void *matchinfo,
1346            unsigned int hook_mask)
1347 {
1348         const struct ip6t_icmp *icmpinfo = matchinfo;
1349
1350         /* Must specify no unknown invflags */
1351         return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1352 }
1353
1354 /* The built-in targets: standard (NULL) and error. */
1355 static struct ip6t_target ip6t_standard_target = {
1356         .name           = IP6T_STANDARD_TARGET,
1357         .targetsize     = sizeof(int),
1358         .family         = AF_INET6,
1359 };
1360
1361 static struct ip6t_target ip6t_error_target = {
1362         .name           = IP6T_ERROR_TARGET,
1363         .target         = ip6t_error,
1364         .targetsize     = IP6T_FUNCTION_MAXNAMELEN,
1365         .family         = AF_INET6,
1366 };
1367
1368 static struct nf_sockopt_ops ip6t_sockopts = {
1369         .pf             = PF_INET6,
1370         .set_optmin     = IP6T_BASE_CTL,
1371         .set_optmax     = IP6T_SO_SET_MAX+1,
1372         .set            = do_ip6t_set_ctl,
1373         .get_optmin     = IP6T_BASE_CTL,
1374         .get_optmax     = IP6T_SO_GET_MAX+1,
1375         .get            = do_ip6t_get_ctl,
1376 };
1377
1378 static struct ip6t_match icmp6_matchstruct = {
1379         .name           = "icmp6",
1380         .match          = &icmp6_match,
1381         .matchsize      = sizeof(struct ip6t_icmp),
1382         .checkentry     = icmp6_checkentry,
1383         .proto          = IPPROTO_ICMPV6,
1384         .family         = AF_INET6,
1385 };
1386
1387 static int __init ip6_tables_init(void)
1388 {
1389         int ret;
1390
1391         ret = xt_proto_init(AF_INET6);
1392         if (ret < 0)
1393                 goto err1;
1394
1395         /* Noone else will be downing sem now, so we won't sleep */
1396         ret = xt_register_target(&ip6t_standard_target);
1397         if (ret < 0)
1398                 goto err2;
1399         ret = xt_register_target(&ip6t_error_target);
1400         if (ret < 0)
1401                 goto err3;
1402         ret = xt_register_match(&icmp6_matchstruct);
1403         if (ret < 0)
1404                 goto err4;
1405
1406         /* Register setsockopt */
1407         ret = nf_register_sockopt(&ip6t_sockopts);
1408         if (ret < 0)
1409                 goto err5;
1410
1411         printk("ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1412         return 0;
1413
1414 err5:
1415         xt_unregister_match(&icmp6_matchstruct);
1416 err4:
1417         xt_unregister_target(&ip6t_error_target);
1418 err3:
1419         xt_unregister_target(&ip6t_standard_target);
1420 err2:
1421         xt_proto_fini(AF_INET6);
1422 err1:
1423         return ret;
1424 }
1425
1426 static void __exit ip6_tables_fini(void)
1427 {
1428         nf_unregister_sockopt(&ip6t_sockopts);
1429         xt_unregister_match(&icmp6_matchstruct);
1430         xt_unregister_target(&ip6t_error_target);
1431         xt_unregister_target(&ip6t_standard_target);
1432         xt_proto_fini(AF_INET6);
1433 }
1434
1435 /*
1436  * find the offset to specified header or the protocol number of last header
1437  * if target < 0. "last header" is transport protocol header, ESP, or
1438  * "No next header".
1439  *
1440  * If target header is found, its offset is set in *offset and return protocol
1441  * number. Otherwise, return -1.
1442  *
1443  * Note that non-1st fragment is special case that "the protocol number
1444  * of last header" is "next header" field in Fragment header. In this case,
1445  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1446  * isn't NULL.
1447  *
1448  */
1449 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1450                   int target, unsigned short *fragoff)
1451 {
1452         unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data;
1453         u8 nexthdr = skb->nh.ipv6h->nexthdr;
1454         unsigned int len = skb->len - start;
1455
1456         if (fragoff)
1457                 *fragoff = 0;
1458
1459         while (nexthdr != target) {
1460                 struct ipv6_opt_hdr _hdr, *hp;
1461                 unsigned int hdrlen;
1462
1463                 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1464                         if (target < 0)
1465                                 break;
1466                         return -1;
1467                 }
1468
1469                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1470                 if (hp == NULL)
1471                         return -1;
1472                 if (nexthdr == NEXTHDR_FRAGMENT) {
1473                         unsigned short _frag_off, *fp;
1474                         fp = skb_header_pointer(skb,
1475                                                 start+offsetof(struct frag_hdr,
1476                                                                frag_off),
1477                                                 sizeof(_frag_off),
1478                                                 &_frag_off);
1479                         if (fp == NULL)
1480                                 return -1;
1481
1482                         _frag_off = ntohs(*fp) & ~0x7;
1483                         if (_frag_off) {
1484                                 if (target < 0 &&
1485                                     ((!ipv6_ext_hdr(hp->nexthdr)) ||
1486                                      nexthdr == NEXTHDR_NONE)) {
1487                                         if (fragoff)
1488                                                 *fragoff = _frag_off;
1489                                         return hp->nexthdr;
1490                                 }
1491                                 return -1;
1492                         }
1493                         hdrlen = 8;
1494                 } else if (nexthdr == NEXTHDR_AUTH)
1495                         hdrlen = (hp->hdrlen + 2) << 2; 
1496                 else
1497                         hdrlen = ipv6_optlen(hp); 
1498
1499                 nexthdr = hp->nexthdr;
1500                 len -= hdrlen;
1501                 start += hdrlen;
1502         }
1503
1504         *offset = start;
1505         return nexthdr;
1506 }
1507
1508 EXPORT_SYMBOL(ip6t_register_table);
1509 EXPORT_SYMBOL(ip6t_unregister_table);
1510 EXPORT_SYMBOL(ip6t_do_table);
1511 EXPORT_SYMBOL(ip6t_ext_hdr);
1512 EXPORT_SYMBOL(ipv6_find_hdr);
1513
1514 module_init(ip6_tables_init);
1515 module_exit(ip6_tables_fini);