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