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