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