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