[PATCH] for_each_possible_cpu: network codes
[usb.git] / net / ipv6 / netfilter / ip6_tables.c
blob642b4b11464f1594c28cc99550fccd809d96d4df
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 <linux/mutex.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 /* Check for an extension */
98 int
99 ip6t_ext_hdr(u8 nexthdr)
101 return ( (nexthdr == IPPROTO_HOPOPTS) ||
102 (nexthdr == IPPROTO_ROUTING) ||
103 (nexthdr == IPPROTO_FRAGMENT) ||
104 (nexthdr == IPPROTO_ESP) ||
105 (nexthdr == IPPROTO_AH) ||
106 (nexthdr == IPPROTO_NONE) ||
107 (nexthdr == IPPROTO_DSTOPTS) );
110 /* Returns whether matches rule or not. */
111 static inline int
112 ip6_packet_match(const struct sk_buff *skb,
113 const char *indev,
114 const char *outdev,
115 const struct ip6t_ip6 *ip6info,
116 unsigned int *protoff,
117 int *fragoff)
119 size_t i;
120 unsigned long ret;
121 const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
123 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
125 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
126 &ip6info->src), IP6T_INV_SRCIP)
127 || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
128 &ip6info->dst), IP6T_INV_DSTIP)) {
129 dprintf("Source or dest mismatch.\n");
131 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
132 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
133 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
134 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
135 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
136 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
137 return 0;
140 /* Look for ifname matches; this should unroll nicely. */
141 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
142 ret |= (((const unsigned long *)indev)[i]
143 ^ ((const unsigned long *)ip6info->iniface)[i])
144 & ((const unsigned long *)ip6info->iniface_mask)[i];
147 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
148 dprintf("VIA in mismatch (%s vs %s).%s\n",
149 indev, ip6info->iniface,
150 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
151 return 0;
154 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
155 ret |= (((const unsigned long *)outdev)[i]
156 ^ ((const unsigned long *)ip6info->outiface)[i])
157 & ((const unsigned long *)ip6info->outiface_mask)[i];
160 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
161 dprintf("VIA out mismatch (%s vs %s).%s\n",
162 outdev, ip6info->outiface,
163 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
164 return 0;
167 /* ... might want to do something with class and flowlabel here ... */
169 /* look for the desired protocol header */
170 if((ip6info->flags & IP6T_F_PROTO)) {
171 int protohdr;
172 unsigned short _frag_off;
174 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
175 if (protohdr < 0)
176 return 0;
178 *fragoff = _frag_off;
180 dprintf("Packet protocol %hi ?= %s%hi.\n",
181 protohdr,
182 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
183 ip6info->proto);
185 if (ip6info->proto == protohdr) {
186 if(ip6info->invflags & IP6T_INV_PROTO) {
187 return 0;
189 return 1;
192 /* We need match for the '-p all', too! */
193 if ((ip6info->proto != 0) &&
194 !(ip6info->invflags & IP6T_INV_PROTO))
195 return 0;
197 return 1;
200 /* should be ip6 safe */
201 static inline int
202 ip6_checkentry(const struct ip6t_ip6 *ipv6)
204 if (ipv6->flags & ~IP6T_F_MASK) {
205 duprintf("Unknown flag bits set: %08X\n",
206 ipv6->flags & ~IP6T_F_MASK);
207 return 0;
209 if (ipv6->invflags & ~IP6T_INV_MASK) {
210 duprintf("Unknown invflag bits set: %08X\n",
211 ipv6->invflags & ~IP6T_INV_MASK);
212 return 0;
214 return 1;
217 static unsigned int
218 ip6t_error(struct sk_buff **pskb,
219 const struct net_device *in,
220 const struct net_device *out,
221 unsigned int hooknum,
222 const struct xt_target *target,
223 const void *targinfo,
224 void *userinfo)
226 if (net_ratelimit())
227 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
229 return NF_DROP;
232 static inline
233 int do_match(struct ip6t_entry_match *m,
234 const struct sk_buff *skb,
235 const struct net_device *in,
236 const struct net_device *out,
237 int offset,
238 unsigned int protoff,
239 int *hotdrop)
241 /* Stop iteration if it doesn't match */
242 if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
243 offset, protoff, hotdrop))
244 return 1;
245 else
246 return 0;
249 static inline struct ip6t_entry *
250 get_entry(void *base, unsigned int offset)
252 return (struct ip6t_entry *)(base + offset);
255 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
256 unsigned int
257 ip6t_do_table(struct sk_buff **pskb,
258 unsigned int hook,
259 const struct net_device *in,
260 const struct net_device *out,
261 struct xt_table *table,
262 void *userdata)
264 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
265 int offset = 0;
266 unsigned int protoff = 0;
267 int hotdrop = 0;
268 /* Initializing verdict to NF_DROP keeps gcc happy. */
269 unsigned int verdict = NF_DROP;
270 const char *indev, *outdev;
271 void *table_base;
272 struct ip6t_entry *e, *back;
273 struct xt_table_info *private;
275 /* Initialization */
276 indev = in ? in->name : nulldevname;
277 outdev = out ? out->name : nulldevname;
278 /* We handle fragments by dealing with the first fragment as
279 * if it was a normal packet. All other fragments are treated
280 * normally, except that they will NEVER match rules that ask
281 * things we don't know, ie. tcp syn flag or ports). If the
282 * rule is also a fragment-specific rule, non-fragments won't
283 * match it. */
285 read_lock_bh(&table->lock);
286 private = table->private;
287 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
288 table_base = (void *)private->entries[smp_processor_id()];
289 e = get_entry(table_base, private->hook_entry[hook]);
291 #ifdef CONFIG_NETFILTER_DEBUG
292 /* Check noone else using our table */
293 if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
294 && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
295 printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
296 smp_processor_id(),
297 table->name,
298 &((struct ip6t_entry *)table_base)->comefrom,
299 ((struct ip6t_entry *)table_base)->comefrom);
301 ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
302 #endif
304 /* For return from builtin chain */
305 back = get_entry(table_base, private->underflow[hook]);
307 do {
308 IP_NF_ASSERT(e);
309 IP_NF_ASSERT(back);
310 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
311 &protoff, &offset)) {
312 struct ip6t_entry_target *t;
314 if (IP6T_MATCH_ITERATE(e, do_match,
315 *pskb, in, out,
316 offset, protoff, &hotdrop) != 0)
317 goto no_match;
319 ADD_COUNTER(e->counters,
320 ntohs((*pskb)->nh.ipv6h->payload_len)
321 + IPV6_HDR_LEN,
324 t = ip6t_get_target(e);
325 IP_NF_ASSERT(t->u.kernel.target);
326 /* Standard target? */
327 if (!t->u.kernel.target->target) {
328 int v;
330 v = ((struct ip6t_standard_target *)t)->verdict;
331 if (v < 0) {
332 /* Pop from stack? */
333 if (v != IP6T_RETURN) {
334 verdict = (unsigned)(-v) - 1;
335 break;
337 e = back;
338 back = get_entry(table_base,
339 back->comefrom);
340 continue;
342 if (table_base + v != (void *)e + e->next_offset
343 && !(e->ipv6.flags & IP6T_F_GOTO)) {
344 /* Save old back ptr in next entry */
345 struct ip6t_entry *next
346 = (void *)e + e->next_offset;
347 next->comefrom
348 = (void *)back - table_base;
349 /* set back pointer to next entry */
350 back = next;
353 e = get_entry(table_base, v);
354 } else {
355 /* Targets which reenter must return
356 abs. verdicts */
357 #ifdef CONFIG_NETFILTER_DEBUG
358 ((struct ip6t_entry *)table_base)->comefrom
359 = 0xeeeeeeec;
360 #endif
361 verdict = t->u.kernel.target->target(pskb,
362 in, out,
363 hook,
364 t->u.kernel.target,
365 t->data,
366 userdata);
368 #ifdef CONFIG_NETFILTER_DEBUG
369 if (((struct ip6t_entry *)table_base)->comefrom
370 != 0xeeeeeeec
371 && verdict == IP6T_CONTINUE) {
372 printk("Target %s reentered!\n",
373 t->u.kernel.target->name);
374 verdict = NF_DROP;
376 ((struct ip6t_entry *)table_base)->comefrom
377 = 0x57acc001;
378 #endif
379 if (verdict == IP6T_CONTINUE)
380 e = (void *)e + e->next_offset;
381 else
382 /* Verdict */
383 break;
385 } else {
387 no_match:
388 e = (void *)e + e->next_offset;
390 } while (!hotdrop);
392 #ifdef CONFIG_NETFILTER_DEBUG
393 ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
394 #endif
395 read_unlock_bh(&table->lock);
397 #ifdef DEBUG_ALLOW_ALL
398 return NF_ACCEPT;
399 #else
400 if (hotdrop)
401 return NF_DROP;
402 else return verdict;
403 #endif
406 /* All zeroes == unconditional rule. */
407 static inline int
408 unconditional(const struct ip6t_ip6 *ipv6)
410 unsigned int i;
412 for (i = 0; i < sizeof(*ipv6); i++)
413 if (((char *)ipv6)[i])
414 break;
416 return (i == sizeof(*ipv6));
419 /* Figures out from what hook each rule can be called: returns 0 if
420 there are loops. Puts hook bitmask in comefrom. */
421 static int
422 mark_source_chains(struct xt_table_info *newinfo,
423 unsigned int valid_hooks, void *entry0)
425 unsigned int hook;
427 /* No recursion; use packet counter to save back ptrs (reset
428 to 0 as we leave), and comefrom to save source hook bitmask */
429 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
430 unsigned int pos = newinfo->hook_entry[hook];
431 struct ip6t_entry *e
432 = (struct ip6t_entry *)(entry0 + pos);
434 if (!(valid_hooks & (1 << hook)))
435 continue;
437 /* Set initial back pointer. */
438 e->counters.pcnt = pos;
440 for (;;) {
441 struct ip6t_standard_target *t
442 = (void *)ip6t_get_target(e);
444 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
445 printk("iptables: loop hook %u pos %u %08X.\n",
446 hook, pos, e->comefrom);
447 return 0;
449 e->comefrom
450 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
452 /* Unconditional return/END. */
453 if (e->target_offset == sizeof(struct ip6t_entry)
454 && (strcmp(t->target.u.user.name,
455 IP6T_STANDARD_TARGET) == 0)
456 && t->verdict < 0
457 && unconditional(&e->ipv6)) {
458 unsigned int oldpos, size;
460 /* Return: backtrack through the last
461 big jump. */
462 do {
463 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
464 #ifdef DEBUG_IP_FIREWALL_USER
465 if (e->comefrom
466 & (1 << NF_IP6_NUMHOOKS)) {
467 duprintf("Back unset "
468 "on hook %u "
469 "rule %u\n",
470 hook, pos);
472 #endif
473 oldpos = pos;
474 pos = e->counters.pcnt;
475 e->counters.pcnt = 0;
477 /* We're at the start. */
478 if (pos == oldpos)
479 goto next;
481 e = (struct ip6t_entry *)
482 (entry0 + pos);
483 } while (oldpos == pos + e->next_offset);
485 /* Move along one */
486 size = e->next_offset;
487 e = (struct ip6t_entry *)
488 (entry0 + pos + size);
489 e->counters.pcnt = pos;
490 pos += size;
491 } else {
492 int newpos = t->verdict;
494 if (strcmp(t->target.u.user.name,
495 IP6T_STANDARD_TARGET) == 0
496 && newpos >= 0) {
497 /* This a jump; chase it. */
498 duprintf("Jump rule %u -> %u\n",
499 pos, newpos);
500 } else {
501 /* ... this is a fallthru */
502 newpos = pos + e->next_offset;
504 e = (struct ip6t_entry *)
505 (entry0 + newpos);
506 e->counters.pcnt = pos;
507 pos = newpos;
510 next:
511 duprintf("Finished chain %u\n", hook);
513 return 1;
516 static inline int
517 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
519 if (i && (*i)-- == 0)
520 return 1;
522 if (m->u.kernel.match->destroy)
523 m->u.kernel.match->destroy(m->u.kernel.match, m->data,
524 m->u.match_size - sizeof(*m));
525 module_put(m->u.kernel.match->me);
526 return 0;
529 static inline int
530 standard_check(const struct ip6t_entry_target *t,
531 unsigned int max_offset)
533 struct ip6t_standard_target *targ = (void *)t;
535 /* Check standard info. */
536 if (targ->verdict >= 0
537 && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
538 duprintf("ip6t_standard_check: bad verdict (%i)\n",
539 targ->verdict);
540 return 0;
542 if (targ->verdict < -NF_MAX_VERDICT - 1) {
543 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
544 targ->verdict);
545 return 0;
547 return 1;
550 static inline int
551 check_match(struct ip6t_entry_match *m,
552 const char *name,
553 const struct ip6t_ip6 *ipv6,
554 unsigned int hookmask,
555 unsigned int *i)
557 struct ip6t_match *match;
558 int ret;
560 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
561 m->u.user.revision),
562 "ip6t_%s", m->u.user.name);
563 if (IS_ERR(match) || !match) {
564 duprintf("check_match: `%s' not found\n", m->u.user.name);
565 return match ? PTR_ERR(match) : -ENOENT;
567 m->u.kernel.match = match;
569 ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
570 name, hookmask, ipv6->proto,
571 ipv6->invflags & IP6T_INV_PROTO);
572 if (ret)
573 goto err;
575 if (m->u.kernel.match->checkentry
576 && !m->u.kernel.match->checkentry(name, ipv6, match, m->data,
577 m->u.match_size - sizeof(*m),
578 hookmask)) {
579 duprintf("ip_tables: check failed for `%s'.\n",
580 m->u.kernel.match->name);
581 ret = -EINVAL;
582 goto err;
585 (*i)++;
586 return 0;
587 err:
588 module_put(m->u.kernel.match->me);
589 return ret;
592 static struct ip6t_target ip6t_standard_target;
594 static inline int
595 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
596 unsigned int *i)
598 struct ip6t_entry_target *t;
599 struct ip6t_target *target;
600 int ret;
601 unsigned int j;
603 if (!ip6_checkentry(&e->ipv6)) {
604 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
605 return -EINVAL;
608 j = 0;
609 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
610 if (ret != 0)
611 goto cleanup_matches;
613 t = ip6t_get_target(e);
614 target = try_then_request_module(xt_find_target(AF_INET6,
615 t->u.user.name,
616 t->u.user.revision),
617 "ip6t_%s", t->u.user.name);
618 if (IS_ERR(target) || !target) {
619 duprintf("check_entry: `%s' not found\n", t->u.user.name);
620 ret = target ? PTR_ERR(target) : -ENOENT;
621 goto cleanup_matches;
623 t->u.kernel.target = target;
625 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
626 name, e->comefrom, e->ipv6.proto,
627 e->ipv6.invflags & IP6T_INV_PROTO);
628 if (ret)
629 goto err;
631 if (t->u.kernel.target == &ip6t_standard_target) {
632 if (!standard_check(t, size)) {
633 ret = -EINVAL;
634 goto cleanup_matches;
636 } else if (t->u.kernel.target->checkentry
637 && !t->u.kernel.target->checkentry(name, e, target, t->data,
638 t->u.target_size
639 - sizeof(*t),
640 e->comefrom)) {
641 duprintf("ip_tables: check failed for `%s'.\n",
642 t->u.kernel.target->name);
643 ret = -EINVAL;
644 goto err;
647 (*i)++;
648 return 0;
649 err:
650 module_put(t->u.kernel.target->me);
651 cleanup_matches:
652 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
653 return ret;
656 static inline int
657 check_entry_size_and_hooks(struct ip6t_entry *e,
658 struct xt_table_info *newinfo,
659 unsigned char *base,
660 unsigned char *limit,
661 const unsigned int *hook_entries,
662 const unsigned int *underflows,
663 unsigned int *i)
665 unsigned int h;
667 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
668 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
669 duprintf("Bad offset %p\n", e);
670 return -EINVAL;
673 if (e->next_offset
674 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
675 duprintf("checking: element %p size %u\n",
676 e, e->next_offset);
677 return -EINVAL;
680 /* Check hooks & underflows */
681 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
682 if ((unsigned char *)e - base == hook_entries[h])
683 newinfo->hook_entry[h] = hook_entries[h];
684 if ((unsigned char *)e - base == underflows[h])
685 newinfo->underflow[h] = underflows[h];
688 /* FIXME: underflows must be unconditional, standard verdicts
689 < 0 (not IP6T_RETURN). --RR */
691 /* Clear counters and comefrom */
692 e->counters = ((struct xt_counters) { 0, 0 });
693 e->comefrom = 0;
695 (*i)++;
696 return 0;
699 static inline int
700 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
702 struct ip6t_entry_target *t;
704 if (i && (*i)-- == 0)
705 return 1;
707 /* Cleanup all matches */
708 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
709 t = ip6t_get_target(e);
710 if (t->u.kernel.target->destroy)
711 t->u.kernel.target->destroy(t->u.kernel.target, t->data,
712 t->u.target_size - sizeof(*t));
713 module_put(t->u.kernel.target->me);
714 return 0;
717 /* Checks and translates the user-supplied table segment (held in
718 newinfo) */
719 static int
720 translate_table(const char *name,
721 unsigned int valid_hooks,
722 struct xt_table_info *newinfo,
723 void *entry0,
724 unsigned int size,
725 unsigned int number,
726 const unsigned int *hook_entries,
727 const unsigned int *underflows)
729 unsigned int i;
730 int ret;
732 newinfo->size = size;
733 newinfo->number = number;
735 /* Init all hooks to impossible value. */
736 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
737 newinfo->hook_entry[i] = 0xFFFFFFFF;
738 newinfo->underflow[i] = 0xFFFFFFFF;
741 duprintf("translate_table: size %u\n", newinfo->size);
742 i = 0;
743 /* Walk through entries, checking offsets. */
744 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
745 check_entry_size_and_hooks,
746 newinfo,
747 entry0,
748 entry0 + size,
749 hook_entries, underflows, &i);
750 if (ret != 0)
751 return ret;
753 if (i != number) {
754 duprintf("translate_table: %u not %u entries\n",
755 i, number);
756 return -EINVAL;
759 /* Check hooks all assigned */
760 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
761 /* Only hooks which are valid */
762 if (!(valid_hooks & (1 << i)))
763 continue;
764 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
765 duprintf("Invalid hook entry %u %u\n",
766 i, hook_entries[i]);
767 return -EINVAL;
769 if (newinfo->underflow[i] == 0xFFFFFFFF) {
770 duprintf("Invalid underflow %u %u\n",
771 i, underflows[i]);
772 return -EINVAL;
776 if (!mark_source_chains(newinfo, valid_hooks, entry0))
777 return -ELOOP;
779 /* Finally, each sanity check must pass */
780 i = 0;
781 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
782 check_entry, name, size, &i);
784 if (ret != 0) {
785 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
786 cleanup_entry, &i);
787 return ret;
790 /* And one copy for every other CPU */
791 for_each_possible_cpu(i) {
792 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
793 memcpy(newinfo->entries[i], entry0, newinfo->size);
796 return ret;
799 /* Gets counters. */
800 static inline int
801 add_entry_to_counter(const struct ip6t_entry *e,
802 struct xt_counters total[],
803 unsigned int *i)
805 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
807 (*i)++;
808 return 0;
811 static inline int
812 set_entry_to_counter(const struct ip6t_entry *e,
813 struct ip6t_counters total[],
814 unsigned int *i)
816 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
818 (*i)++;
819 return 0;
822 static void
823 get_counters(const struct xt_table_info *t,
824 struct xt_counters counters[])
826 unsigned int cpu;
827 unsigned int i;
828 unsigned int curcpu;
830 /* Instead of clearing (by a previous call to memset())
831 * the counters and using adds, we set the counters
832 * with data used by 'current' CPU
833 * We dont care about preemption here.
835 curcpu = raw_smp_processor_id();
837 i = 0;
838 IP6T_ENTRY_ITERATE(t->entries[curcpu],
839 t->size,
840 set_entry_to_counter,
841 counters,
842 &i);
844 for_each_possible_cpu(cpu) {
845 if (cpu == curcpu)
846 continue;
847 i = 0;
848 IP6T_ENTRY_ITERATE(t->entries[cpu],
849 t->size,
850 add_entry_to_counter,
851 counters,
852 &i);
856 static int
857 copy_entries_to_user(unsigned int total_size,
858 struct xt_table *table,
859 void __user *userptr)
861 unsigned int off, num, countersize;
862 struct ip6t_entry *e;
863 struct xt_counters *counters;
864 struct xt_table_info *private = table->private;
865 int ret = 0;
866 void *loc_cpu_entry;
868 /* We need atomic snapshot of counters: rest doesn't change
869 (other than comefrom, which userspace doesn't care
870 about). */
871 countersize = sizeof(struct xt_counters) * private->number;
872 counters = vmalloc(countersize);
874 if (counters == NULL)
875 return -ENOMEM;
877 /* First, sum counters... */
878 write_lock_bh(&table->lock);
879 get_counters(private, counters);
880 write_unlock_bh(&table->lock);
882 /* choose the copy that is on ourc node/cpu */
883 loc_cpu_entry = private->entries[raw_smp_processor_id()];
884 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
885 ret = -EFAULT;
886 goto free_counters;
889 /* FIXME: use iterator macros --RR */
890 /* ... then go back and fix counters and names */
891 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
892 unsigned int i;
893 struct ip6t_entry_match *m;
894 struct ip6t_entry_target *t;
896 e = (struct ip6t_entry *)(loc_cpu_entry + off);
897 if (copy_to_user(userptr + off
898 + offsetof(struct ip6t_entry, counters),
899 &counters[num],
900 sizeof(counters[num])) != 0) {
901 ret = -EFAULT;
902 goto free_counters;
905 for (i = sizeof(struct ip6t_entry);
906 i < e->target_offset;
907 i += m->u.match_size) {
908 m = (void *)e + i;
910 if (copy_to_user(userptr + off + i
911 + offsetof(struct ip6t_entry_match,
912 u.user.name),
913 m->u.kernel.match->name,
914 strlen(m->u.kernel.match->name)+1)
915 != 0) {
916 ret = -EFAULT;
917 goto free_counters;
921 t = ip6t_get_target(e);
922 if (copy_to_user(userptr + off + e->target_offset
923 + offsetof(struct ip6t_entry_target,
924 u.user.name),
925 t->u.kernel.target->name,
926 strlen(t->u.kernel.target->name)+1) != 0) {
927 ret = -EFAULT;
928 goto free_counters;
932 free_counters:
933 vfree(counters);
934 return ret;
937 static int
938 get_entries(const struct ip6t_get_entries *entries,
939 struct ip6t_get_entries __user *uptr)
941 int ret;
942 struct xt_table *t;
944 t = xt_find_table_lock(AF_INET6, entries->name);
945 if (t && !IS_ERR(t)) {
946 struct xt_table_info *private = t->private;
947 duprintf("t->private->number = %u\n", private->number);
948 if (entries->size == private->size)
949 ret = copy_entries_to_user(private->size,
950 t, uptr->entrytable);
951 else {
952 duprintf("get_entries: I've got %u not %u!\n",
953 private->size, entries->size);
954 ret = -EINVAL;
956 module_put(t->me);
957 xt_table_unlock(t);
958 } else
959 ret = t ? PTR_ERR(t) : -ENOENT;
961 return ret;
964 static int
965 do_replace(void __user *user, unsigned int len)
967 int ret;
968 struct ip6t_replace tmp;
969 struct xt_table *t;
970 struct xt_table_info *newinfo, *oldinfo;
971 struct xt_counters *counters;
972 void *loc_cpu_entry, *loc_cpu_old_entry;
974 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
975 return -EFAULT;
977 /* overflow check */
978 if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
979 SMP_CACHE_BYTES)
980 return -ENOMEM;
981 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
982 return -ENOMEM;
984 newinfo = xt_alloc_table_info(tmp.size);
985 if (!newinfo)
986 return -ENOMEM;
988 /* choose the copy that is on our node/cpu */
989 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
990 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
991 tmp.size) != 0) {
992 ret = -EFAULT;
993 goto free_newinfo;
996 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
997 if (!counters) {
998 ret = -ENOMEM;
999 goto free_newinfo;
1002 ret = translate_table(tmp.name, tmp.valid_hooks,
1003 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1004 tmp.hook_entry, tmp.underflow);
1005 if (ret != 0)
1006 goto free_newinfo_counters;
1008 duprintf("ip_tables: Translated table\n");
1010 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
1011 "ip6table_%s", tmp.name);
1012 if (!t || IS_ERR(t)) {
1013 ret = t ? PTR_ERR(t) : -ENOENT;
1014 goto free_newinfo_counters_untrans;
1017 /* You lied! */
1018 if (tmp.valid_hooks != t->valid_hooks) {
1019 duprintf("Valid hook crap: %08X vs %08X\n",
1020 tmp.valid_hooks, t->valid_hooks);
1021 ret = -EINVAL;
1022 goto put_module;
1025 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1026 if (!oldinfo)
1027 goto put_module;
1029 /* Update module usage count based on number of rules */
1030 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1031 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1032 if ((oldinfo->number > oldinfo->initial_entries) ||
1033 (newinfo->number <= oldinfo->initial_entries))
1034 module_put(t->me);
1035 if ((oldinfo->number > oldinfo->initial_entries) &&
1036 (newinfo->number <= oldinfo->initial_entries))
1037 module_put(t->me);
1039 /* Get the old counters. */
1040 get_counters(oldinfo, counters);
1041 /* Decrease module usage counts and free resource */
1042 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1043 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1044 xt_free_table_info(oldinfo);
1045 if (copy_to_user(tmp.counters, counters,
1046 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1047 ret = -EFAULT;
1048 vfree(counters);
1049 xt_table_unlock(t);
1050 return ret;
1052 put_module:
1053 module_put(t->me);
1054 xt_table_unlock(t);
1055 free_newinfo_counters_untrans:
1056 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1057 free_newinfo_counters:
1058 vfree(counters);
1059 free_newinfo:
1060 xt_free_table_info(newinfo);
1061 return ret;
1064 /* We're lazy, and add to the first CPU; overflow works its fey magic
1065 * and everything is OK. */
1066 static inline int
1067 add_counter_to_entry(struct ip6t_entry *e,
1068 const struct xt_counters addme[],
1069 unsigned int *i)
1071 #if 0
1072 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1074 (long unsigned int)e->counters.pcnt,
1075 (long unsigned int)e->counters.bcnt,
1076 (long unsigned int)addme[*i].pcnt,
1077 (long unsigned int)addme[*i].bcnt);
1078 #endif
1080 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1082 (*i)++;
1083 return 0;
1086 static int
1087 do_add_counters(void __user *user, unsigned int len)
1089 unsigned int i;
1090 struct xt_counters_info tmp, *paddc;
1091 struct xt_table_info *private;
1092 struct xt_table *t;
1093 int ret = 0;
1094 void *loc_cpu_entry;
1096 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1097 return -EFAULT;
1099 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1100 return -EINVAL;
1102 paddc = vmalloc(len);
1103 if (!paddc)
1104 return -ENOMEM;
1106 if (copy_from_user(paddc, user, len) != 0) {
1107 ret = -EFAULT;
1108 goto free;
1111 t = xt_find_table_lock(AF_INET6, tmp.name);
1112 if (!t || IS_ERR(t)) {
1113 ret = t ? PTR_ERR(t) : -ENOENT;
1114 goto free;
1117 write_lock_bh(&t->lock);
1118 private = t->private;
1119 if (private->number != paddc->num_counters) {
1120 ret = -EINVAL;
1121 goto unlock_up_free;
1124 i = 0;
1125 /* Choose the copy that is on our node */
1126 loc_cpu_entry = private->entries[smp_processor_id()];
1127 IP6T_ENTRY_ITERATE(loc_cpu_entry,
1128 private->size,
1129 add_counter_to_entry,
1130 paddc->counters,
1131 &i);
1132 unlock_up_free:
1133 write_unlock_bh(&t->lock);
1134 xt_table_unlock(t);
1135 module_put(t->me);
1136 free:
1137 vfree(paddc);
1139 return ret;
1142 static int
1143 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1145 int ret;
1147 if (!capable(CAP_NET_ADMIN))
1148 return -EPERM;
1150 switch (cmd) {
1151 case IP6T_SO_SET_REPLACE:
1152 ret = do_replace(user, len);
1153 break;
1155 case IP6T_SO_SET_ADD_COUNTERS:
1156 ret = do_add_counters(user, len);
1157 break;
1159 default:
1160 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1161 ret = -EINVAL;
1164 return ret;
1167 static int
1168 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1170 int ret;
1172 if (!capable(CAP_NET_ADMIN))
1173 return -EPERM;
1175 switch (cmd) {
1176 case IP6T_SO_GET_INFO: {
1177 char name[IP6T_TABLE_MAXNAMELEN];
1178 struct xt_table *t;
1180 if (*len != sizeof(struct ip6t_getinfo)) {
1181 duprintf("length %u != %u\n", *len,
1182 sizeof(struct ip6t_getinfo));
1183 ret = -EINVAL;
1184 break;
1187 if (copy_from_user(name, user, sizeof(name)) != 0) {
1188 ret = -EFAULT;
1189 break;
1191 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1193 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1194 "ip6table_%s", name);
1195 if (t && !IS_ERR(t)) {
1196 struct ip6t_getinfo info;
1197 struct xt_table_info *private = t->private;
1199 info.valid_hooks = t->valid_hooks;
1200 memcpy(info.hook_entry, private->hook_entry,
1201 sizeof(info.hook_entry));
1202 memcpy(info.underflow, private->underflow,
1203 sizeof(info.underflow));
1204 info.num_entries = private->number;
1205 info.size = private->size;
1206 memcpy(info.name, name, sizeof(info.name));
1208 if (copy_to_user(user, &info, *len) != 0)
1209 ret = -EFAULT;
1210 else
1211 ret = 0;
1212 xt_table_unlock(t);
1213 module_put(t->me);
1214 } else
1215 ret = t ? PTR_ERR(t) : -ENOENT;
1217 break;
1219 case IP6T_SO_GET_ENTRIES: {
1220 struct ip6t_get_entries get;
1222 if (*len < sizeof(get)) {
1223 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1224 ret = -EINVAL;
1225 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1226 ret = -EFAULT;
1227 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1228 duprintf("get_entries: %u != %u\n", *len,
1229 sizeof(struct ip6t_get_entries) + get.size);
1230 ret = -EINVAL;
1231 } else
1232 ret = get_entries(&get, user);
1233 break;
1236 case IP6T_SO_GET_REVISION_MATCH:
1237 case IP6T_SO_GET_REVISION_TARGET: {
1238 struct ip6t_get_revision rev;
1239 int target;
1241 if (*len != sizeof(rev)) {
1242 ret = -EINVAL;
1243 break;
1245 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1246 ret = -EFAULT;
1247 break;
1250 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1251 target = 1;
1252 else
1253 target = 0;
1255 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1256 rev.revision,
1257 target, &ret),
1258 "ip6t_%s", rev.name);
1259 break;
1262 default:
1263 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1264 ret = -EINVAL;
1267 return ret;
1270 int ip6t_register_table(struct xt_table *table,
1271 const struct ip6t_replace *repl)
1273 int ret;
1274 struct xt_table_info *newinfo;
1275 static struct xt_table_info bootstrap
1276 = { 0, 0, 0, { 0 }, { 0 }, { } };
1277 void *loc_cpu_entry;
1279 newinfo = xt_alloc_table_info(repl->size);
1280 if (!newinfo)
1281 return -ENOMEM;
1283 /* choose the copy on our node/cpu */
1284 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1285 memcpy(loc_cpu_entry, repl->entries, repl->size);
1287 ret = translate_table(table->name, table->valid_hooks,
1288 newinfo, loc_cpu_entry, repl->size,
1289 repl->num_entries,
1290 repl->hook_entry,
1291 repl->underflow);
1292 if (ret != 0) {
1293 xt_free_table_info(newinfo);
1294 return ret;
1297 if (xt_register_table(table, &bootstrap, newinfo) != 0) {
1298 xt_free_table_info(newinfo);
1299 return ret;
1302 return 0;
1305 void ip6t_unregister_table(struct xt_table *table)
1307 struct xt_table_info *private;
1308 void *loc_cpu_entry;
1310 private = xt_unregister_table(table);
1312 /* Decrease module usage counts and free resources */
1313 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1314 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1315 xt_free_table_info(private);
1318 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1319 static inline int
1320 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1321 u_int8_t type, u_int8_t code,
1322 int invert)
1324 return (type == test_type && code >= min_code && code <= max_code)
1325 ^ invert;
1328 static int
1329 icmp6_match(const struct sk_buff *skb,
1330 const struct net_device *in,
1331 const struct net_device *out,
1332 const struct xt_match *match,
1333 const void *matchinfo,
1334 int offset,
1335 unsigned int protoff,
1336 int *hotdrop)
1338 struct icmp6hdr _icmp, *ic;
1339 const struct ip6t_icmp *icmpinfo = matchinfo;
1341 /* Must not be a fragment. */
1342 if (offset)
1343 return 0;
1345 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1346 if (ic == NULL) {
1347 /* We've been asked to examine this packet, and we
1348 can't. Hence, no choice but to drop. */
1349 duprintf("Dropping evil ICMP tinygram.\n");
1350 *hotdrop = 1;
1351 return 0;
1354 return icmp6_type_code_match(icmpinfo->type,
1355 icmpinfo->code[0],
1356 icmpinfo->code[1],
1357 ic->icmp6_type, ic->icmp6_code,
1358 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1361 /* Called when user tries to insert an entry of this type. */
1362 static int
1363 icmp6_checkentry(const char *tablename,
1364 const void *entry,
1365 const struct xt_match *match,
1366 void *matchinfo,
1367 unsigned int matchsize,
1368 unsigned int hook_mask)
1370 const struct ip6t_icmp *icmpinfo = matchinfo;
1372 /* Must specify no unknown invflags */
1373 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1376 /* The built-in targets: standard (NULL) and error. */
1377 static struct ip6t_target ip6t_standard_target = {
1378 .name = IP6T_STANDARD_TARGET,
1379 .targetsize = sizeof(int),
1380 .family = AF_INET6,
1383 static struct ip6t_target ip6t_error_target = {
1384 .name = IP6T_ERROR_TARGET,
1385 .target = ip6t_error,
1386 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
1387 .family = AF_INET6,
1390 static struct nf_sockopt_ops ip6t_sockopts = {
1391 .pf = PF_INET6,
1392 .set_optmin = IP6T_BASE_CTL,
1393 .set_optmax = IP6T_SO_SET_MAX+1,
1394 .set = do_ip6t_set_ctl,
1395 .get_optmin = IP6T_BASE_CTL,
1396 .get_optmax = IP6T_SO_GET_MAX+1,
1397 .get = do_ip6t_get_ctl,
1400 static struct ip6t_match icmp6_matchstruct = {
1401 .name = "icmp6",
1402 .match = &icmp6_match,
1403 .matchsize = sizeof(struct ip6t_icmp),
1404 .checkentry = icmp6_checkentry,
1405 .proto = IPPROTO_ICMPV6,
1406 .family = AF_INET6,
1409 static int __init ip6_tables_init(void)
1411 int ret;
1413 xt_proto_init(AF_INET6);
1415 /* Noone else will be downing sem now, so we won't sleep */
1416 xt_register_target(&ip6t_standard_target);
1417 xt_register_target(&ip6t_error_target);
1418 xt_register_match(&icmp6_matchstruct);
1420 /* Register setsockopt */
1421 ret = nf_register_sockopt(&ip6t_sockopts);
1422 if (ret < 0) {
1423 duprintf("Unable to register sockopts.\n");
1424 xt_proto_fini(AF_INET6);
1425 return ret;
1428 printk("ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1429 return 0;
1432 static void __exit ip6_tables_fini(void)
1434 nf_unregister_sockopt(&ip6t_sockopts);
1435 xt_unregister_match(&icmp6_matchstruct);
1436 xt_unregister_target(&ip6t_error_target);
1437 xt_unregister_target(&ip6t_standard_target);
1438 xt_proto_fini(AF_INET6);
1442 * find the offset to specified header or the protocol number of last header
1443 * if target < 0. "last header" is transport protocol header, ESP, or
1444 * "No next header".
1446 * If target header is found, its offset is set in *offset and return protocol
1447 * number. Otherwise, return -1.
1449 * Note that non-1st fragment is special case that "the protocol number
1450 * of last header" is "next header" field in Fragment header. In this case,
1451 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1452 * isn't NULL.
1455 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1456 int target, unsigned short *fragoff)
1458 unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data;
1459 u8 nexthdr = skb->nh.ipv6h->nexthdr;
1460 unsigned int len = skb->len - start;
1462 if (fragoff)
1463 *fragoff = 0;
1465 while (nexthdr != target) {
1466 struct ipv6_opt_hdr _hdr, *hp;
1467 unsigned int hdrlen;
1469 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1470 if (target < 0)
1471 break;
1472 return -1;
1475 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1476 if (hp == NULL)
1477 return -1;
1478 if (nexthdr == NEXTHDR_FRAGMENT) {
1479 unsigned short _frag_off, *fp;
1480 fp = skb_header_pointer(skb,
1481 start+offsetof(struct frag_hdr,
1482 frag_off),
1483 sizeof(_frag_off),
1484 &_frag_off);
1485 if (fp == NULL)
1486 return -1;
1488 _frag_off = ntohs(*fp) & ~0x7;
1489 if (_frag_off) {
1490 if (target < 0 &&
1491 ((!ipv6_ext_hdr(hp->nexthdr)) ||
1492 nexthdr == NEXTHDR_NONE)) {
1493 if (fragoff)
1494 *fragoff = _frag_off;
1495 return hp->nexthdr;
1497 return -1;
1499 hdrlen = 8;
1500 } else if (nexthdr == NEXTHDR_AUTH)
1501 hdrlen = (hp->hdrlen + 2) << 2;
1502 else
1503 hdrlen = ipv6_optlen(hp);
1505 nexthdr = hp->nexthdr;
1506 len -= hdrlen;
1507 start += hdrlen;
1510 *offset = start;
1511 return nexthdr;
1514 EXPORT_SYMBOL(ip6t_register_table);
1515 EXPORT_SYMBOL(ip6t_unregister_table);
1516 EXPORT_SYMBOL(ip6t_do_table);
1517 EXPORT_SYMBOL(ip6t_ext_hdr);
1518 EXPORT_SYMBOL(ipv6_find_hdr);
1520 module_init(ip6_tables_init);
1521 module_exit(ip6_tables_fini);