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