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