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