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