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