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