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