[NETFILTER]: Convert ip6_tables matches/targets to centralized error checking
[linux-2.6/suspend2-2.6.18.git] / net / ipv6 / netfilter / ip6_tables.c
blobe2e8d0140d7bac971bf3861faf1be05c85abec7f
1 /*
2 * Packet matching code.
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
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.
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
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>
36 #include <linux/netfilter_ipv6/ip6_tables.h>
37 #include <linux/netfilter/x_tables.h>
39 MODULE_LICENSE("GPL");
40 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
41 MODULE_DESCRIPTION("IPv6 packet filter");
43 #define IPV6_HDR_LEN (sizeof(struct ipv6hdr))
44 #define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
46 /*#define DEBUG_IP_FIREWALL*/
47 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
48 /*#define DEBUG_IP_FIREWALL_USER*/
50 #ifdef DEBUG_IP_FIREWALL
51 #define dprintf(format, args...) printk(format , ## args)
52 #else
53 #define dprintf(format, args...)
54 #endif
56 #ifdef DEBUG_IP_FIREWALL_USER
57 #define duprintf(format, args...) printk(format , ## args)
58 #else
59 #define duprintf(format, args...)
60 #endif
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
74 #include <linux/netfilter_ipv4/listhelp.h>
76 #if 0
77 /* All the better to debug you with... */
78 #define static
79 #define inline
80 #endif
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.
89 Hence the start of any table is given by get_table() below. */
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
97 int
98 ip6_masked_addrcmp(const struct in6_addr *addr1, const struct in6_addr *mask,
99 const struct in6_addr *addr2)
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;
107 return 0;
110 /* Check for an extension */
111 int
112 ip6t_ext_hdr(u8 nexthdr)
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) );
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)
132 size_t i;
133 unsigned long ret;
134 const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
136 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
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");
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;
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];
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;
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];
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;
180 /* ... might want to do something with class and flowlabel here ... */
182 /* look for the desired protocol header */
183 if((ip6info->flags & IP6T_F_PROTO)) {
184 int protohdr;
185 unsigned short _frag_off;
187 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
188 if (protohdr < 0)
189 return 0;
191 *fragoff = _frag_off;
193 dprintf("Packet protocol %hi ?= %s%hi.\n",
194 protohdr,
195 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
196 ip6info->proto);
198 if (ip6info->proto == protohdr) {
199 if(ip6info->invflags & IP6T_INV_PROTO) {
200 return 0;
202 return 1;
205 /* We need match for the '-p all', too! */
206 if ((ip6info->proto != 0) &&
207 !(ip6info->invflags & IP6T_INV_PROTO))
208 return 0;
210 return 1;
213 /* should be ip6 safe */
214 static inline int
215 ip6_checkentry(const struct ip6t_ip6 *ipv6)
217 if (ipv6->flags & ~IP6T_F_MASK) {
218 duprintf("Unknown flag bits set: %08X\n",
219 ipv6->flags & ~IP6T_F_MASK);
220 return 0;
222 if (ipv6->invflags & ~IP6T_INV_MASK) {
223 duprintf("Unknown invflag bits set: %08X\n",
224 ipv6->invflags & ~IP6T_INV_MASK);
225 return 0;
227 return 1;
230 static unsigned int
231 ip6t_error(struct sk_buff **pskb,
232 const struct net_device *in,
233 const struct net_device *out,
234 unsigned int hooknum,
235 const void *targinfo,
236 void *userinfo)
238 if (net_ratelimit())
239 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
241 return NF_DROP;
244 static inline
245 int do_match(struct ip6t_entry_match *m,
246 const struct sk_buff *skb,
247 const struct net_device *in,
248 const struct net_device *out,
249 int offset,
250 unsigned int protoff,
251 int *hotdrop)
253 /* Stop iteration if it doesn't match */
254 if (!m->u.kernel.match->match(skb, in, out, m->data,
255 offset, protoff, hotdrop))
256 return 1;
257 else
258 return 0;
261 static inline struct ip6t_entry *
262 get_entry(void *base, unsigned int offset)
264 return (struct ip6t_entry *)(base + offset);
267 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
268 unsigned int
269 ip6t_do_table(struct sk_buff **pskb,
270 unsigned int hook,
271 const struct net_device *in,
272 const struct net_device *out,
273 struct xt_table *table,
274 void *userdata)
276 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
277 int offset = 0;
278 unsigned int protoff = 0;
279 int hotdrop = 0;
280 /* Initializing verdict to NF_DROP keeps gcc happy. */
281 unsigned int verdict = NF_DROP;
282 const char *indev, *outdev;
283 void *table_base;
284 struct ip6t_entry *e, *back;
285 struct xt_table_info *private;
287 /* Initialization */
288 indev = in ? in->name : nulldevname;
289 outdev = out ? out->name : nulldevname;
290 /* We handle fragments by dealing with the first fragment as
291 * if it was a normal packet. All other fragments are treated
292 * normally, except that they will NEVER match rules that ask
293 * things we don't know, ie. tcp syn flag or ports). If the
294 * rule is also a fragment-specific rule, non-fragments won't
295 * match it. */
297 read_lock_bh(&table->lock);
298 private = table->private;
299 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
300 table_base = (void *)private->entries[smp_processor_id()];
301 e = get_entry(table_base, private->hook_entry[hook]);
303 #ifdef CONFIG_NETFILTER_DEBUG
304 /* Check noone else using our table */
305 if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
306 && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
307 printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
308 smp_processor_id(),
309 table->name,
310 &((struct ip6t_entry *)table_base)->comefrom,
311 ((struct ip6t_entry *)table_base)->comefrom);
313 ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
314 #endif
316 /* For return from builtin chain */
317 back = get_entry(table_base, private->underflow[hook]);
319 do {
320 IP_NF_ASSERT(e);
321 IP_NF_ASSERT(back);
322 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
323 &protoff, &offset)) {
324 struct ip6t_entry_target *t;
326 if (IP6T_MATCH_ITERATE(e, do_match,
327 *pskb, in, out,
328 offset, protoff, &hotdrop) != 0)
329 goto no_match;
331 ADD_COUNTER(e->counters,
332 ntohs((*pskb)->nh.ipv6h->payload_len)
333 + IPV6_HDR_LEN,
336 t = ip6t_get_target(e);
337 IP_NF_ASSERT(t->u.kernel.target);
338 /* Standard target? */
339 if (!t->u.kernel.target->target) {
340 int v;
342 v = ((struct ip6t_standard_target *)t)->verdict;
343 if (v < 0) {
344 /* Pop from stack? */
345 if (v != IP6T_RETURN) {
346 verdict = (unsigned)(-v) - 1;
347 break;
349 e = back;
350 back = get_entry(table_base,
351 back->comefrom);
352 continue;
354 if (table_base + v != (void *)e + e->next_offset
355 && !(e->ipv6.flags & IP6T_F_GOTO)) {
356 /* Save old back ptr in next entry */
357 struct ip6t_entry *next
358 = (void *)e + e->next_offset;
359 next->comefrom
360 = (void *)back - table_base;
361 /* set back pointer to next entry */
362 back = next;
365 e = get_entry(table_base, v);
366 } else {
367 /* Targets which reenter must return
368 abs. verdicts */
369 #ifdef CONFIG_NETFILTER_DEBUG
370 ((struct ip6t_entry *)table_base)->comefrom
371 = 0xeeeeeeec;
372 #endif
373 verdict = t->u.kernel.target->target(pskb,
374 in, out,
375 hook,
376 t->data,
377 userdata);
379 #ifdef CONFIG_NETFILTER_DEBUG
380 if (((struct ip6t_entry *)table_base)->comefrom
381 != 0xeeeeeeec
382 && verdict == IP6T_CONTINUE) {
383 printk("Target %s reentered!\n",
384 t->u.kernel.target->name);
385 verdict = NF_DROP;
387 ((struct ip6t_entry *)table_base)->comefrom
388 = 0x57acc001;
389 #endif
390 if (verdict == IP6T_CONTINUE)
391 e = (void *)e + e->next_offset;
392 else
393 /* Verdict */
394 break;
396 } else {
398 no_match:
399 e = (void *)e + e->next_offset;
401 } while (!hotdrop);
403 #ifdef CONFIG_NETFILTER_DEBUG
404 ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
405 #endif
406 read_unlock_bh(&table->lock);
408 #ifdef DEBUG_ALLOW_ALL
409 return NF_ACCEPT;
410 #else
411 if (hotdrop)
412 return NF_DROP;
413 else return verdict;
414 #endif
417 /* All zeroes == unconditional rule. */
418 static inline int
419 unconditional(const struct ip6t_ip6 *ipv6)
421 unsigned int i;
423 for (i = 0; i < sizeof(*ipv6); i++)
424 if (((char *)ipv6)[i])
425 break;
427 return (i == sizeof(*ipv6));
430 /* Figures out from what hook each rule can be called: returns 0 if
431 there are loops. Puts hook bitmask in comefrom. */
432 static int
433 mark_source_chains(struct xt_table_info *newinfo,
434 unsigned int valid_hooks, void *entry0)
436 unsigned int hook;
438 /* No recursion; use packet counter to save back ptrs (reset
439 to 0 as we leave), and comefrom to save source hook bitmask */
440 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
441 unsigned int pos = newinfo->hook_entry[hook];
442 struct ip6t_entry *e
443 = (struct ip6t_entry *)(entry0 + pos);
445 if (!(valid_hooks & (1 << hook)))
446 continue;
448 /* Set initial back pointer. */
449 e->counters.pcnt = pos;
451 for (;;) {
452 struct ip6t_standard_target *t
453 = (void *)ip6t_get_target(e);
455 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
456 printk("iptables: loop hook %u pos %u %08X.\n",
457 hook, pos, e->comefrom);
458 return 0;
460 e->comefrom
461 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
463 /* Unconditional return/END. */
464 if (e->target_offset == sizeof(struct ip6t_entry)
465 && (strcmp(t->target.u.user.name,
466 IP6T_STANDARD_TARGET) == 0)
467 && t->verdict < 0
468 && unconditional(&e->ipv6)) {
469 unsigned int oldpos, size;
471 /* Return: backtrack through the last
472 big jump. */
473 do {
474 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
475 #ifdef DEBUG_IP_FIREWALL_USER
476 if (e->comefrom
477 & (1 << NF_IP6_NUMHOOKS)) {
478 duprintf("Back unset "
479 "on hook %u "
480 "rule %u\n",
481 hook, pos);
483 #endif
484 oldpos = pos;
485 pos = e->counters.pcnt;
486 e->counters.pcnt = 0;
488 /* We're at the start. */
489 if (pos == oldpos)
490 goto next;
492 e = (struct ip6t_entry *)
493 (entry0 + pos);
494 } while (oldpos == pos + e->next_offset);
496 /* Move along one */
497 size = e->next_offset;
498 e = (struct ip6t_entry *)
499 (entry0 + pos + size);
500 e->counters.pcnt = pos;
501 pos += size;
502 } else {
503 int newpos = t->verdict;
505 if (strcmp(t->target.u.user.name,
506 IP6T_STANDARD_TARGET) == 0
507 && newpos >= 0) {
508 /* This a jump; chase it. */
509 duprintf("Jump rule %u -> %u\n",
510 pos, newpos);
511 } else {
512 /* ... this is a fallthru */
513 newpos = pos + e->next_offset;
515 e = (struct ip6t_entry *)
516 (entry0 + newpos);
517 e->counters.pcnt = pos;
518 pos = newpos;
521 next:
522 duprintf("Finished chain %u\n", hook);
524 return 1;
527 static inline int
528 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
530 if (i && (*i)-- == 0)
531 return 1;
533 if (m->u.kernel.match->destroy)
534 m->u.kernel.match->destroy(m->data,
535 m->u.match_size - sizeof(*m));
536 module_put(m->u.kernel.match->me);
537 return 0;
540 static inline int
541 standard_check(const struct ip6t_entry_target *t,
542 unsigned int max_offset)
544 struct ip6t_standard_target *targ = (void *)t;
546 /* Check standard info. */
547 if (targ->verdict >= 0
548 && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
549 duprintf("ip6t_standard_check: bad verdict (%i)\n",
550 targ->verdict);
551 return 0;
553 if (targ->verdict < -NF_MAX_VERDICT - 1) {
554 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
555 targ->verdict);
556 return 0;
558 return 1;
561 static inline int
562 check_match(struct ip6t_entry_match *m,
563 const char *name,
564 const struct ip6t_ip6 *ipv6,
565 unsigned int hookmask,
566 unsigned int *i)
568 struct ip6t_match *match;
569 int ret;
571 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
572 m->u.user.revision),
573 "ip6t_%s", m->u.user.name);
574 if (IS_ERR(match) || !match) {
575 duprintf("check_match: `%s' not found\n", m->u.user.name);
576 return match ? PTR_ERR(match) : -ENOENT;
578 m->u.kernel.match = match;
580 ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
581 name, hookmask, ipv6->proto,
582 ipv6->invflags & IP6T_INV_PROTO);
583 if (ret)
584 goto err;
586 if (m->u.kernel.match->checkentry
587 && !m->u.kernel.match->checkentry(name, ipv6, m->data,
588 m->u.match_size - sizeof(*m),
589 hookmask)) {
590 duprintf("ip_tables: check failed for `%s'.\n",
591 m->u.kernel.match->name);
592 ret = -EINVAL;
593 goto err;
596 (*i)++;
597 return 0;
598 err:
599 module_put(m->u.kernel.match->me);
600 return ret;
603 static struct ip6t_target ip6t_standard_target;
605 static inline int
606 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
607 unsigned int *i)
609 struct ip6t_entry_target *t;
610 struct ip6t_target *target;
611 int ret;
612 unsigned int j;
614 if (!ip6_checkentry(&e->ipv6)) {
615 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
616 return -EINVAL;
619 j = 0;
620 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
621 if (ret != 0)
622 goto cleanup_matches;
624 t = ip6t_get_target(e);
625 target = try_then_request_module(xt_find_target(AF_INET6,
626 t->u.user.name,
627 t->u.user.revision),
628 "ip6t_%s", t->u.user.name);
629 if (IS_ERR(target) || !target) {
630 duprintf("check_entry: `%s' not found\n", t->u.user.name);
631 ret = target ? PTR_ERR(target) : -ENOENT;
632 goto cleanup_matches;
634 t->u.kernel.target = target;
636 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
637 name, e->comefrom, e->ipv6.proto,
638 e->ipv6.invflags & IP6T_INV_PROTO);
639 if (ret)
640 goto err;
642 if (t->u.kernel.target == &ip6t_standard_target) {
643 if (!standard_check(t, size)) {
644 ret = -EINVAL;
645 goto cleanup_matches;
647 } else if (t->u.kernel.target->checkentry
648 && !t->u.kernel.target->checkentry(name, e, t->data,
649 t->u.target_size
650 - sizeof(*t),
651 e->comefrom)) {
652 duprintf("ip_tables: check failed for `%s'.\n",
653 t->u.kernel.target->name);
654 ret = -EINVAL;
655 goto err;
658 (*i)++;
659 return 0;
660 err:
661 module_put(t->u.kernel.target->me);
662 cleanup_matches:
663 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
664 return ret;
667 static inline int
668 check_entry_size_and_hooks(struct ip6t_entry *e,
669 struct xt_table_info *newinfo,
670 unsigned char *base,
671 unsigned char *limit,
672 const unsigned int *hook_entries,
673 const unsigned int *underflows,
674 unsigned int *i)
676 unsigned int h;
678 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
679 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
680 duprintf("Bad offset %p\n", e);
681 return -EINVAL;
684 if (e->next_offset
685 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
686 duprintf("checking: element %p size %u\n",
687 e, e->next_offset);
688 return -EINVAL;
691 /* Check hooks & underflows */
692 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
693 if ((unsigned char *)e - base == hook_entries[h])
694 newinfo->hook_entry[h] = hook_entries[h];
695 if ((unsigned char *)e - base == underflows[h])
696 newinfo->underflow[h] = underflows[h];
699 /* FIXME: underflows must be unconditional, standard verdicts
700 < 0 (not IP6T_RETURN). --RR */
702 /* Clear counters and comefrom */
703 e->counters = ((struct xt_counters) { 0, 0 });
704 e->comefrom = 0;
706 (*i)++;
707 return 0;
710 static inline int
711 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
713 struct ip6t_entry_target *t;
715 if (i && (*i)-- == 0)
716 return 1;
718 /* Cleanup all matches */
719 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
720 t = ip6t_get_target(e);
721 if (t->u.kernel.target->destroy)
722 t->u.kernel.target->destroy(t->data,
723 t->u.target_size - sizeof(*t));
724 module_put(t->u.kernel.target->me);
725 return 0;
728 /* Checks and translates the user-supplied table segment (held in
729 newinfo) */
730 static int
731 translate_table(const char *name,
732 unsigned int valid_hooks,
733 struct xt_table_info *newinfo,
734 void *entry0,
735 unsigned int size,
736 unsigned int number,
737 const unsigned int *hook_entries,
738 const unsigned int *underflows)
740 unsigned int i;
741 int ret;
743 newinfo->size = size;
744 newinfo->number = number;
746 /* Init all hooks to impossible value. */
747 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
748 newinfo->hook_entry[i] = 0xFFFFFFFF;
749 newinfo->underflow[i] = 0xFFFFFFFF;
752 duprintf("translate_table: size %u\n", newinfo->size);
753 i = 0;
754 /* Walk through entries, checking offsets. */
755 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
756 check_entry_size_and_hooks,
757 newinfo,
758 entry0,
759 entry0 + size,
760 hook_entries, underflows, &i);
761 if (ret != 0)
762 return ret;
764 if (i != number) {
765 duprintf("translate_table: %u not %u entries\n",
766 i, number);
767 return -EINVAL;
770 /* Check hooks all assigned */
771 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
772 /* Only hooks which are valid */
773 if (!(valid_hooks & (1 << i)))
774 continue;
775 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
776 duprintf("Invalid hook entry %u %u\n",
777 i, hook_entries[i]);
778 return -EINVAL;
780 if (newinfo->underflow[i] == 0xFFFFFFFF) {
781 duprintf("Invalid underflow %u %u\n",
782 i, underflows[i]);
783 return -EINVAL;
787 if (!mark_source_chains(newinfo, valid_hooks, entry0))
788 return -ELOOP;
790 /* Finally, each sanity check must pass */
791 i = 0;
792 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
793 check_entry, name, size, &i);
795 if (ret != 0) {
796 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
797 cleanup_entry, &i);
798 return ret;
801 /* And one copy for every other CPU */
802 for_each_cpu(i) {
803 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
804 memcpy(newinfo->entries[i], entry0, newinfo->size);
807 return ret;
810 /* Gets counters. */
811 static inline int
812 add_entry_to_counter(const struct ip6t_entry *e,
813 struct xt_counters total[],
814 unsigned int *i)
816 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
818 (*i)++;
819 return 0;
822 static inline int
823 set_entry_to_counter(const struct ip6t_entry *e,
824 struct ip6t_counters total[],
825 unsigned int *i)
827 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
829 (*i)++;
830 return 0;
833 static void
834 get_counters(const struct xt_table_info *t,
835 struct xt_counters counters[])
837 unsigned int cpu;
838 unsigned int i;
839 unsigned int curcpu;
841 /* Instead of clearing (by a previous call to memset())
842 * the counters and using adds, we set the counters
843 * with data used by 'current' CPU
844 * We dont care about preemption here.
846 curcpu = raw_smp_processor_id();
848 i = 0;
849 IP6T_ENTRY_ITERATE(t->entries[curcpu],
850 t->size,
851 set_entry_to_counter,
852 counters,
853 &i);
855 for_each_cpu(cpu) {
856 if (cpu == curcpu)
857 continue;
858 i = 0;
859 IP6T_ENTRY_ITERATE(t->entries[cpu],
860 t->size,
861 add_entry_to_counter,
862 counters,
863 &i);
867 static int
868 copy_entries_to_user(unsigned int total_size,
869 struct xt_table *table,
870 void __user *userptr)
872 unsigned int off, num, countersize;
873 struct ip6t_entry *e;
874 struct xt_counters *counters;
875 struct xt_table_info *private = table->private;
876 int ret = 0;
877 void *loc_cpu_entry;
879 /* We need atomic snapshot of counters: rest doesn't change
880 (other than comefrom, which userspace doesn't care
881 about). */
882 countersize = sizeof(struct xt_counters) * private->number;
883 counters = vmalloc(countersize);
885 if (counters == NULL)
886 return -ENOMEM;
888 /* First, sum counters... */
889 write_lock_bh(&table->lock);
890 get_counters(private, counters);
891 write_unlock_bh(&table->lock);
893 /* choose the copy that is on ourc node/cpu */
894 loc_cpu_entry = private->entries[raw_smp_processor_id()];
895 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
896 ret = -EFAULT;
897 goto free_counters;
900 /* FIXME: use iterator macros --RR */
901 /* ... then go back and fix counters and names */
902 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
903 unsigned int i;
904 struct ip6t_entry_match *m;
905 struct ip6t_entry_target *t;
907 e = (struct ip6t_entry *)(loc_cpu_entry + off);
908 if (copy_to_user(userptr + off
909 + offsetof(struct ip6t_entry, counters),
910 &counters[num],
911 sizeof(counters[num])) != 0) {
912 ret = -EFAULT;
913 goto free_counters;
916 for (i = sizeof(struct ip6t_entry);
917 i < e->target_offset;
918 i += m->u.match_size) {
919 m = (void *)e + i;
921 if (copy_to_user(userptr + off + i
922 + offsetof(struct ip6t_entry_match,
923 u.user.name),
924 m->u.kernel.match->name,
925 strlen(m->u.kernel.match->name)+1)
926 != 0) {
927 ret = -EFAULT;
928 goto free_counters;
932 t = ip6t_get_target(e);
933 if (copy_to_user(userptr + off + e->target_offset
934 + offsetof(struct ip6t_entry_target,
935 u.user.name),
936 t->u.kernel.target->name,
937 strlen(t->u.kernel.target->name)+1) != 0) {
938 ret = -EFAULT;
939 goto free_counters;
943 free_counters:
944 vfree(counters);
945 return ret;
948 static int
949 get_entries(const struct ip6t_get_entries *entries,
950 struct ip6t_get_entries __user *uptr)
952 int ret;
953 struct xt_table *t;
955 t = xt_find_table_lock(AF_INET6, entries->name);
956 if (t && !IS_ERR(t)) {
957 struct xt_table_info *private = t->private;
958 duprintf("t->private->number = %u\n", private->number);
959 if (entries->size == private->size)
960 ret = copy_entries_to_user(private->size,
961 t, uptr->entrytable);
962 else {
963 duprintf("get_entries: I've got %u not %u!\n",
964 private->size, entries->size);
965 ret = -EINVAL;
967 module_put(t->me);
968 xt_table_unlock(t);
969 } else
970 ret = t ? PTR_ERR(t) : -ENOENT;
972 return ret;
975 static int
976 do_replace(void __user *user, unsigned int len)
978 int ret;
979 struct ip6t_replace tmp;
980 struct xt_table *t;
981 struct xt_table_info *newinfo, *oldinfo;
982 struct xt_counters *counters;
983 void *loc_cpu_entry, *loc_cpu_old_entry;
985 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
986 return -EFAULT;
988 /* overflow check */
989 if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
990 SMP_CACHE_BYTES)
991 return -ENOMEM;
992 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
993 return -ENOMEM;
995 newinfo = xt_alloc_table_info(tmp.size);
996 if (!newinfo)
997 return -ENOMEM;
999 /* choose the copy that is on our node/cpu */
1000 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1001 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1002 tmp.size) != 0) {
1003 ret = -EFAULT;
1004 goto free_newinfo;
1007 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
1008 if (!counters) {
1009 ret = -ENOMEM;
1010 goto free_newinfo;
1013 ret = translate_table(tmp.name, tmp.valid_hooks,
1014 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1015 tmp.hook_entry, tmp.underflow);
1016 if (ret != 0)
1017 goto free_newinfo_counters;
1019 duprintf("ip_tables: Translated table\n");
1021 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
1022 "ip6table_%s", tmp.name);
1023 if (!t || IS_ERR(t)) {
1024 ret = t ? PTR_ERR(t) : -ENOENT;
1025 goto free_newinfo_counters_untrans;
1028 /* You lied! */
1029 if (tmp.valid_hooks != t->valid_hooks) {
1030 duprintf("Valid hook crap: %08X vs %08X\n",
1031 tmp.valid_hooks, t->valid_hooks);
1032 ret = -EINVAL;
1033 goto put_module;
1036 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1037 if (!oldinfo)
1038 goto put_module;
1040 /* Update module usage count based on number of rules */
1041 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1042 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1043 if ((oldinfo->number > oldinfo->initial_entries) ||
1044 (newinfo->number <= oldinfo->initial_entries))
1045 module_put(t->me);
1046 if ((oldinfo->number > oldinfo->initial_entries) &&
1047 (newinfo->number <= oldinfo->initial_entries))
1048 module_put(t->me);
1050 /* Get the old counters. */
1051 get_counters(oldinfo, counters);
1052 /* Decrease module usage counts and free resource */
1053 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1054 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1055 xt_free_table_info(oldinfo);
1056 if (copy_to_user(tmp.counters, counters,
1057 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1058 ret = -EFAULT;
1059 vfree(counters);
1060 xt_table_unlock(t);
1061 return ret;
1063 put_module:
1064 module_put(t->me);
1065 xt_table_unlock(t);
1066 free_newinfo_counters_untrans:
1067 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1068 free_newinfo_counters:
1069 vfree(counters);
1070 free_newinfo:
1071 xt_free_table_info(newinfo);
1072 return ret;
1075 /* We're lazy, and add to the first CPU; overflow works its fey magic
1076 * and everything is OK. */
1077 static inline int
1078 add_counter_to_entry(struct ip6t_entry *e,
1079 const struct xt_counters addme[],
1080 unsigned int *i)
1082 #if 0
1083 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1085 (long unsigned int)e->counters.pcnt,
1086 (long unsigned int)e->counters.bcnt,
1087 (long unsigned int)addme[*i].pcnt,
1088 (long unsigned int)addme[*i].bcnt);
1089 #endif
1091 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1093 (*i)++;
1094 return 0;
1097 static int
1098 do_add_counters(void __user *user, unsigned int len)
1100 unsigned int i;
1101 struct xt_counters_info tmp, *paddc;
1102 struct xt_table_info *private;
1103 struct xt_table *t;
1104 int ret = 0;
1105 void *loc_cpu_entry;
1107 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1108 return -EFAULT;
1110 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1111 return -EINVAL;
1113 paddc = vmalloc(len);
1114 if (!paddc)
1115 return -ENOMEM;
1117 if (copy_from_user(paddc, user, len) != 0) {
1118 ret = -EFAULT;
1119 goto free;
1122 t = xt_find_table_lock(AF_INET6, tmp.name);
1123 if (!t || IS_ERR(t)) {
1124 ret = t ? PTR_ERR(t) : -ENOENT;
1125 goto free;
1128 write_lock_bh(&t->lock);
1129 private = t->private;
1130 if (private->number != paddc->num_counters) {
1131 ret = -EINVAL;
1132 goto unlock_up_free;
1135 i = 0;
1136 /* Choose the copy that is on our node */
1137 loc_cpu_entry = private->entries[smp_processor_id()];
1138 IP6T_ENTRY_ITERATE(loc_cpu_entry,
1139 private->size,
1140 add_counter_to_entry,
1141 paddc->counters,
1142 &i);
1143 unlock_up_free:
1144 write_unlock_bh(&t->lock);
1145 xt_table_unlock(t);
1146 module_put(t->me);
1147 free:
1148 vfree(paddc);
1150 return ret;
1153 static int
1154 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1156 int ret;
1158 if (!capable(CAP_NET_ADMIN))
1159 return -EPERM;
1161 switch (cmd) {
1162 case IP6T_SO_SET_REPLACE:
1163 ret = do_replace(user, len);
1164 break;
1166 case IP6T_SO_SET_ADD_COUNTERS:
1167 ret = do_add_counters(user, len);
1168 break;
1170 default:
1171 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1172 ret = -EINVAL;
1175 return ret;
1178 static int
1179 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1181 int ret;
1183 if (!capable(CAP_NET_ADMIN))
1184 return -EPERM;
1186 switch (cmd) {
1187 case IP6T_SO_GET_INFO: {
1188 char name[IP6T_TABLE_MAXNAMELEN];
1189 struct xt_table *t;
1191 if (*len != sizeof(struct ip6t_getinfo)) {
1192 duprintf("length %u != %u\n", *len,
1193 sizeof(struct ip6t_getinfo));
1194 ret = -EINVAL;
1195 break;
1198 if (copy_from_user(name, user, sizeof(name)) != 0) {
1199 ret = -EFAULT;
1200 break;
1202 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1204 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1205 "ip6table_%s", name);
1206 if (t && !IS_ERR(t)) {
1207 struct ip6t_getinfo info;
1208 struct xt_table_info *private = t->private;
1210 info.valid_hooks = t->valid_hooks;
1211 memcpy(info.hook_entry, private->hook_entry,
1212 sizeof(info.hook_entry));
1213 memcpy(info.underflow, private->underflow,
1214 sizeof(info.underflow));
1215 info.num_entries = private->number;
1216 info.size = private->size;
1217 memcpy(info.name, name, sizeof(info.name));
1219 if (copy_to_user(user, &info, *len) != 0)
1220 ret = -EFAULT;
1221 else
1222 ret = 0;
1223 xt_table_unlock(t);
1224 module_put(t->me);
1225 } else
1226 ret = t ? PTR_ERR(t) : -ENOENT;
1228 break;
1230 case IP6T_SO_GET_ENTRIES: {
1231 struct ip6t_get_entries get;
1233 if (*len < sizeof(get)) {
1234 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1235 ret = -EINVAL;
1236 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1237 ret = -EFAULT;
1238 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1239 duprintf("get_entries: %u != %u\n", *len,
1240 sizeof(struct ip6t_get_entries) + get.size);
1241 ret = -EINVAL;
1242 } else
1243 ret = get_entries(&get, user);
1244 break;
1247 case IP6T_SO_GET_REVISION_MATCH:
1248 case IP6T_SO_GET_REVISION_TARGET: {
1249 struct ip6t_get_revision rev;
1250 int target;
1252 if (*len != sizeof(rev)) {
1253 ret = -EINVAL;
1254 break;
1256 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1257 ret = -EFAULT;
1258 break;
1261 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1262 target = 1;
1263 else
1264 target = 0;
1266 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1267 rev.revision,
1268 target, &ret),
1269 "ip6t_%s", rev.name);
1270 break;
1273 default:
1274 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1275 ret = -EINVAL;
1278 return ret;
1281 int ip6t_register_table(struct xt_table *table,
1282 const struct ip6t_replace *repl)
1284 int ret;
1285 struct xt_table_info *newinfo;
1286 static struct xt_table_info bootstrap
1287 = { 0, 0, 0, { 0 }, { 0 }, { } };
1288 void *loc_cpu_entry;
1290 newinfo = xt_alloc_table_info(repl->size);
1291 if (!newinfo)
1292 return -ENOMEM;
1294 /* choose the copy on our node/cpu */
1295 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1296 memcpy(loc_cpu_entry, repl->entries, repl->size);
1298 ret = translate_table(table->name, table->valid_hooks,
1299 newinfo, loc_cpu_entry, repl->size,
1300 repl->num_entries,
1301 repl->hook_entry,
1302 repl->underflow);
1303 if (ret != 0) {
1304 xt_free_table_info(newinfo);
1305 return ret;
1308 if (xt_register_table(table, &bootstrap, newinfo) != 0) {
1309 xt_free_table_info(newinfo);
1310 return ret;
1313 return 0;
1316 void ip6t_unregister_table(struct xt_table *table)
1318 struct xt_table_info *private;
1319 void *loc_cpu_entry;
1321 private = xt_unregister_table(table);
1323 /* Decrease module usage counts and free resources */
1324 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1325 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1326 xt_free_table_info(private);
1329 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1330 static inline int
1331 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1332 u_int8_t type, u_int8_t code,
1333 int invert)
1335 return (type == test_type && code >= min_code && code <= max_code)
1336 ^ invert;
1339 static int
1340 icmp6_match(const struct sk_buff *skb,
1341 const struct net_device *in,
1342 const struct net_device *out,
1343 const void *matchinfo,
1344 int offset,
1345 unsigned int protoff,
1346 int *hotdrop)
1348 struct icmp6hdr _icmp, *ic;
1349 const struct ip6t_icmp *icmpinfo = matchinfo;
1351 /* Must not be a fragment. */
1352 if (offset)
1353 return 0;
1355 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1356 if (ic == NULL) {
1357 /* We've been asked to examine this packet, and we
1358 can't. Hence, no choice but to drop. */
1359 duprintf("Dropping evil ICMP tinygram.\n");
1360 *hotdrop = 1;
1361 return 0;
1364 return icmp6_type_code_match(icmpinfo->type,
1365 icmpinfo->code[0],
1366 icmpinfo->code[1],
1367 ic->icmp6_type, ic->icmp6_code,
1368 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1371 /* Called when user tries to insert an entry of this type. */
1372 static int
1373 icmp6_checkentry(const char *tablename,
1374 const void *entry,
1375 void *matchinfo,
1376 unsigned int matchsize,
1377 unsigned int hook_mask)
1379 const struct ip6t_icmp *icmpinfo = matchinfo;
1381 /* Must specify no unknown invflags */
1382 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1385 /* The built-in targets: standard (NULL) and error. */
1386 static struct ip6t_target ip6t_standard_target = {
1387 .name = IP6T_STANDARD_TARGET,
1388 .targetsize = sizeof(int),
1391 static struct ip6t_target ip6t_error_target = {
1392 .name = IP6T_ERROR_TARGET,
1393 .target = ip6t_error,
1394 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
1397 static struct nf_sockopt_ops ip6t_sockopts = {
1398 .pf = PF_INET6,
1399 .set_optmin = IP6T_BASE_CTL,
1400 .set_optmax = IP6T_SO_SET_MAX+1,
1401 .set = do_ip6t_set_ctl,
1402 .get_optmin = IP6T_BASE_CTL,
1403 .get_optmax = IP6T_SO_GET_MAX+1,
1404 .get = do_ip6t_get_ctl,
1407 static struct ip6t_match icmp6_matchstruct = {
1408 .name = "icmp6",
1409 .match = &icmp6_match,
1410 .matchsize = sizeof(struct ip6t_icmp),
1411 .checkentry = icmp6_checkentry,
1412 .proto = IPPROTO_ICMPV6,
1415 static int __init init(void)
1417 int ret;
1419 xt_proto_init(AF_INET6);
1421 /* Noone else will be downing sem now, so we won't sleep */
1422 xt_register_target(AF_INET6, &ip6t_standard_target);
1423 xt_register_target(AF_INET6, &ip6t_error_target);
1424 xt_register_match(AF_INET6, &icmp6_matchstruct);
1426 /* Register setsockopt */
1427 ret = nf_register_sockopt(&ip6t_sockopts);
1428 if (ret < 0) {
1429 duprintf("Unable to register sockopts.\n");
1430 xt_proto_fini(AF_INET6);
1431 return ret;
1434 printk("ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1435 return 0;
1438 static void __exit fini(void)
1440 nf_unregister_sockopt(&ip6t_sockopts);
1441 xt_unregister_match(AF_INET6, &icmp6_matchstruct);
1442 xt_unregister_target(AF_INET6, &ip6t_error_target);
1443 xt_unregister_target(AF_INET6, &ip6t_standard_target);
1444 xt_proto_fini(AF_INET6);
1448 * find the offset to specified header or the protocol number of last header
1449 * if target < 0. "last header" is transport protocol header, ESP, or
1450 * "No next header".
1452 * If target header is found, its offset is set in *offset and return protocol
1453 * number. Otherwise, return -1.
1455 * Note that non-1st fragment is special case that "the protocol number
1456 * of last header" is "next header" field in Fragment header. In this case,
1457 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1458 * isn't NULL.
1461 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1462 int target, unsigned short *fragoff)
1464 unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data;
1465 u8 nexthdr = skb->nh.ipv6h->nexthdr;
1466 unsigned int len = skb->len - start;
1468 if (fragoff)
1469 *fragoff = 0;
1471 while (nexthdr != target) {
1472 struct ipv6_opt_hdr _hdr, *hp;
1473 unsigned int hdrlen;
1475 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1476 if (target < 0)
1477 break;
1478 return -1;
1481 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1482 if (hp == NULL)
1483 return -1;
1484 if (nexthdr == NEXTHDR_FRAGMENT) {
1485 unsigned short _frag_off, *fp;
1486 fp = skb_header_pointer(skb,
1487 start+offsetof(struct frag_hdr,
1488 frag_off),
1489 sizeof(_frag_off),
1490 &_frag_off);
1491 if (fp == NULL)
1492 return -1;
1494 _frag_off = ntohs(*fp) & ~0x7;
1495 if (_frag_off) {
1496 if (target < 0 &&
1497 ((!ipv6_ext_hdr(hp->nexthdr)) ||
1498 nexthdr == NEXTHDR_NONE)) {
1499 if (fragoff)
1500 *fragoff = _frag_off;
1501 return hp->nexthdr;
1503 return -1;
1505 hdrlen = 8;
1506 } else if (nexthdr == NEXTHDR_AUTH)
1507 hdrlen = (hp->hdrlen + 2) << 2;
1508 else
1509 hdrlen = ipv6_optlen(hp);
1511 nexthdr = hp->nexthdr;
1512 len -= hdrlen;
1513 start += hdrlen;
1516 *offset = start;
1517 return nexthdr;
1520 EXPORT_SYMBOL(ip6t_register_table);
1521 EXPORT_SYMBOL(ip6t_unregister_table);
1522 EXPORT_SYMBOL(ip6t_do_table);
1523 EXPORT_SYMBOL(ip6t_ext_hdr);
1524 EXPORT_SYMBOL(ipv6_find_hdr);
1525 EXPORT_SYMBOL(ip6_masked_addrcmp);
1527 module_init(init);
1528 module_exit(fini);