[ALSA] soc - ASoC 0.13 spitz machine
[linux-2.6.git] / net / ipv6 / netfilter / ip6_tables.c
blob99502c5da4c45336cf2cd1b5ed2c71b3ea685744
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/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>
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
73 #if 0
74 /* All the better to debug you with... */
75 #define static
76 #define inline
77 #endif
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.
86 Hence the start of any table is given by get_table() below. */
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
94 /* Check for an extension */
95 int
96 ip6t_ext_hdr(u8 nexthdr)
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) );
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)
116 size_t i;
117 unsigned long ret;
118 const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
120 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
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");
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;
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];
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;
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];
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;
164 /* ... might want to do something with class and flowlabel here ... */
166 /* look for the desired protocol header */
167 if((ip6info->flags & IP6T_F_PROTO)) {
168 int protohdr;
169 unsigned short _frag_off;
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;
177 *fragoff = _frag_off;
179 dprintf("Packet protocol %hi ?= %s%hi.\n",
180 protohdr,
181 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
182 ip6info->proto);
184 if (ip6info->proto == protohdr) {
185 if(ip6info->invflags & IP6T_INV_PROTO) {
186 return 0;
188 return 1;
191 /* We need match for the '-p all', too! */
192 if ((ip6info->proto != 0) &&
193 !(ip6info->invflags & IP6T_INV_PROTO))
194 return 0;
196 return 1;
199 /* should be ip6 safe */
200 static inline int
201 ip6_checkentry(const struct ip6t_ip6 *ipv6)
203 if (ipv6->flags & ~IP6T_F_MASK) {
204 duprintf("Unknown flag bits set: %08X\n",
205 ipv6->flags & ~IP6T_F_MASK);
206 return 0;
208 if (ipv6->invflags & ~IP6T_INV_MASK) {
209 duprintf("Unknown invflag bits set: %08X\n",
210 ipv6->invflags & ~IP6T_INV_MASK);
211 return 0;
213 return 1;
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)
224 if (net_ratelimit())
225 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
227 return NF_DROP;
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)
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;
247 static inline struct ip6t_entry *
248 get_entry(void *base, unsigned int offset)
250 return (struct ip6t_entry *)(base + offset);
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)
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;
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. */
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]);
288 /* For return from builtin chain */
289 back = get_entry(table_base, private->underflow[hook]);
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;
298 if (IP6T_MATCH_ITERATE(e, do_match,
299 *pskb, in, out,
300 offset, protoff, &hotdrop) != 0)
301 goto no_match;
303 ADD_COUNTER(e->counters,
304 ntohs((*pskb)->nh.ipv6h->payload_len)
305 + IPV6_HDR_LEN,
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;
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;
321 e = back;
322 back = get_entry(table_base,
323 back->comefrom);
324 continue;
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;
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);
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;
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;
368 } else {
370 no_match:
371 e = (void *)e + e->next_offset;
373 } while (!hotdrop);
375 #ifdef CONFIG_NETFILTER_DEBUG
376 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
377 #endif
378 read_unlock_bh(&table->lock);
380 #ifdef DEBUG_ALLOW_ALL
381 return NF_ACCEPT;
382 #else
383 if (hotdrop)
384 return NF_DROP;
385 else return verdict;
386 #endif
389 /* All zeroes == unconditional rule. */
390 static inline int
391 unconditional(const struct ip6t_ip6 *ipv6)
393 unsigned int i;
395 for (i = 0; i < sizeof(*ipv6); i++)
396 if (((char *)ipv6)[i])
397 break;
399 return (i == sizeof(*ipv6));
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)
408 unsigned int hook;
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);
418 if (!(valid_hooks & (1 << hook)))
419 continue;
421 /* Set initial back pointer. */
422 e->counters.pcnt = pos;
424 for (;;) {
425 struct ip6t_standard_target *t
426 = (void *)ip6t_get_target(e);
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;
433 e->comefrom
434 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
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;
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;
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);
463 #endif
464 oldpos = pos;
465 pos = e->counters.pcnt;
466 e->counters.pcnt = 0;
468 /* We're at the start. */
469 if (pos == oldpos)
470 goto next;
472 e = (struct ip6t_entry *)
473 (entry0 + pos);
474 } while (oldpos == pos + e->next_offset);
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;
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;
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;
502 e = (struct ip6t_entry *)
503 (entry0 + newpos);
504 e->counters.pcnt = pos;
505 pos = newpos;
508 next:
509 duprintf("Finished chain %u\n", hook);
511 return 1;
514 static inline int
515 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
517 if (i && (*i)-- == 0)
518 return 1;
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;
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)
533 struct ip6t_match *match;
534 int ret;
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;
543 m->u.kernel.match = match;
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;
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;
560 (*i)++;
561 return 0;
562 err:
563 module_put(m->u.kernel.match->me);
564 return ret;
567 static struct ip6t_target ip6t_standard_target;
569 static inline int
570 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
571 unsigned int *i)
573 struct ip6t_entry_target *t;
574 struct ip6t_target *target;
575 int ret;
576 unsigned int j;
578 if (!ip6_checkentry(&e->ipv6)) {
579 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
580 return -EINVAL;
583 if (e->target_offset + sizeof(struct ip6t_entry_target) >
584 e->next_offset)
585 return -EINVAL;
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;
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;
605 t->u.kernel.target = target;
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;
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;
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;
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)
640 unsigned int h;
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;
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;
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];
663 /* FIXME: underflows must be unconditional, standard verdicts
664 < 0 (not IP6T_RETURN). --RR */
666 /* Clear counters and comefrom */
667 e->counters = ((struct xt_counters) { 0, 0 });
668 e->comefrom = 0;
670 (*i)++;
671 return 0;
674 static inline int
675 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
677 struct ip6t_entry_target *t;
679 if (i && (*i)-- == 0)
680 return 1;
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;
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)
703 unsigned int i;
704 int ret;
706 newinfo->size = size;
707 newinfo->number = number;
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;
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;
727 if (i != number) {
728 duprintf("translate_table: %u not %u entries\n",
729 i, number);
730 return -EINVAL;
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;
743 if (newinfo->underflow[i] == 0xFFFFFFFF) {
744 duprintf("Invalid underflow %u %u\n",
745 i, underflows[i]);
746 return -EINVAL;
750 if (!mark_source_chains(newinfo, valid_hooks, entry0))
751 return -ELOOP;
753 /* Finally, each sanity check must pass */
754 i = 0;
755 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
756 check_entry, name, size, &i);
758 if (ret != 0) {
759 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
760 cleanup_entry, &i);
761 return ret;
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);
770 return 0;
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)
779 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
781 (*i)++;
782 return 0;
785 static inline int
786 set_entry_to_counter(const struct ip6t_entry *e,
787 struct ip6t_counters total[],
788 unsigned int *i)
790 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
792 (*i)++;
793 return 0;
796 static void
797 get_counters(const struct xt_table_info *t,
798 struct xt_counters counters[])
800 unsigned int cpu;
801 unsigned int i;
802 unsigned int curcpu;
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.
809 curcpu = raw_smp_processor_id();
811 i = 0;
812 IP6T_ENTRY_ITERATE(t->entries[curcpu],
813 t->size,
814 set_entry_to_counter,
815 counters,
816 &i);
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);
830 static int
831 copy_entries_to_user(unsigned int total_size,
832 struct xt_table *table,
833 void __user *userptr)
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;
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);
848 if (counters == NULL)
849 return -ENOMEM;
851 /* First, sum counters... */
852 write_lock_bh(&table->lock);
853 get_counters(private, counters);
854 write_unlock_bh(&table->lock);
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;
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;
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;
879 for (i = sizeof(struct ip6t_entry);
880 i < e->target_offset;
881 i += m->u.match_size) {
882 m = (void *)e + i;
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;
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;
906 free_counters:
907 vfree(counters);
908 return ret;
911 static int
912 get_entries(const struct ip6t_get_entries *entries,
913 struct ip6t_get_entries __user *uptr)
915 int ret;
916 struct xt_table *t;
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;
930 module_put(t->me);
931 xt_table_unlock(t);
932 } else
933 ret = t ? PTR_ERR(t) : -ENOENT;
935 return ret;
938 static int
939 do_replace(void __user *user, unsigned int len)
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;
948 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
949 return -EFAULT;
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;
958 newinfo = xt_alloc_table_info(tmp.size);
959 if (!newinfo)
960 return -ENOMEM;
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;
970 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
971 if (!counters) {
972 ret = -ENOMEM;
973 goto free_newinfo;
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;
982 duprintf("ip_tables: Translated table\n");
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;
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;
999 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1000 if (!oldinfo)
1001 goto put_module;
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);
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;
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;
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)
1045 #if 0
1046 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
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
1054 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1056 (*i)++;
1057 return 0;
1060 static int
1061 do_add_counters(void __user *user, unsigned int len)
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;
1070 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1071 return -EFAULT;
1073 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1074 return -EINVAL;
1076 paddc = vmalloc(len);
1077 if (!paddc)
1078 return -ENOMEM;
1080 if (copy_from_user(paddc, user, len) != 0) {
1081 ret = -EFAULT;
1082 goto free;
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;
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;
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);
1113 return ret;
1116 static int
1117 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1119 int ret;
1121 if (!capable(CAP_NET_ADMIN))
1122 return -EPERM;
1124 switch (cmd) {
1125 case IP6T_SO_SET_REPLACE:
1126 ret = do_replace(user, len);
1127 break;
1129 case IP6T_SO_SET_ADD_COUNTERS:
1130 ret = do_add_counters(user, len);
1131 break;
1133 default:
1134 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1135 ret = -EINVAL;
1138 return ret;
1141 static int
1142 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1144 int ret;
1146 if (!capable(CAP_NET_ADMIN))
1147 return -EPERM;
1149 switch (cmd) {
1150 case IP6T_SO_GET_INFO: {
1151 char name[IP6T_TABLE_MAXNAMELEN];
1152 struct xt_table *t;
1154 if (*len != sizeof(struct ip6t_getinfo)) {
1155 duprintf("length %u != %u\n", *len,
1156 sizeof(struct ip6t_getinfo));
1157 ret = -EINVAL;
1158 break;
1161 if (copy_from_user(name, user, sizeof(name)) != 0) {
1162 ret = -EFAULT;
1163 break;
1165 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
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;
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));
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;
1191 break;
1193 case IP6T_SO_GET_ENTRIES: {
1194 struct ip6t_get_entries get;
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;
1210 case IP6T_SO_GET_REVISION_MATCH:
1211 case IP6T_SO_GET_REVISION_TARGET: {
1212 struct ip6t_get_revision rev;
1213 int target;
1215 if (*len != sizeof(rev)) {
1216 ret = -EINVAL;
1217 break;
1219 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1220 ret = -EFAULT;
1221 break;
1224 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1225 target = 1;
1226 else
1227 target = 0;
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;
1236 default:
1237 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1238 ret = -EINVAL;
1241 return ret;
1244 int ip6t_register_table(struct xt_table *table,
1245 const struct ip6t_replace *repl)
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;
1253 newinfo = xt_alloc_table_info(repl->size);
1254 if (!newinfo)
1255 return -ENOMEM;
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);
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;
1271 ret = xt_register_table(table, &bootstrap, newinfo);
1272 if (ret != 0) {
1273 xt_free_table_info(newinfo);
1274 return ret;
1277 return 0;
1280 void ip6t_unregister_table(struct xt_table *table)
1282 struct xt_table_info *private;
1283 void *loc_cpu_entry;
1285 private = xt_unregister_table(table);
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);
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)
1299 return (type == test_type && code >= min_code && code <= max_code)
1300 ^ invert;
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)
1313 struct icmp6hdr _icmp, *ic;
1314 const struct ip6t_icmp *icmpinfo = matchinfo;
1316 /* Must not be a fragment. */
1317 if (offset)
1318 return 0;
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;
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));
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)
1344 const struct ip6t_icmp *icmpinfo = matchinfo;
1346 /* Must specify no unknown invflags */
1347 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1350 /* The built-in targets: standard (NULL) and error. */
1351 static struct ip6t_target ip6t_standard_target = {
1352 .name = IP6T_STANDARD_TARGET,
1353 .targetsize = sizeof(int),
1354 .family = AF_INET6,
1357 static struct ip6t_target ip6t_error_target = {
1358 .name = IP6T_ERROR_TARGET,
1359 .target = ip6t_error,
1360 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
1361 .family = AF_INET6,
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,
1374 static struct ip6t_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,
1383 static int __init ip6_tables_init(void)
1385 int ret;
1387 ret = xt_proto_init(AF_INET6);
1388 if (ret < 0)
1389 goto err1;
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;
1402 /* Register setsockopt */
1403 ret = nf_register_sockopt(&ip6t_sockopts);
1404 if (ret < 0)
1405 goto err5;
1407 printk("ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1408 return 0;
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;
1422 static void __exit ip6_tables_fini(void)
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);
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".
1436 * If target header is found, its offset is set in *offset and return protocol
1437 * number. Otherwise, return -1.
1439 * If the first fragment doesn't contain the final protocol header or
1440 * NEXTHDR_NONE it is considered invalid.
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.
1448 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1449 int target, unsigned short *fragoff)
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;
1455 if (fragoff)
1456 *fragoff = 0;
1458 while (nexthdr != target) {
1459 struct ipv6_opt_hdr _hdr, *hp;
1460 unsigned int hdrlen;
1462 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1463 if (target < 0)
1464 break;
1465 return -ENOENT;
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;
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;
1491 return -ENOENT;
1493 hdrlen = 8;
1494 } else if (nexthdr == NEXTHDR_AUTH)
1495 hdrlen = (hp->hdrlen + 2) << 2;
1496 else
1497 hdrlen = ipv6_optlen(hp);
1499 nexthdr = hp->nexthdr;
1500 len -= hdrlen;
1501 start += hdrlen;
1504 *offset = start;
1505 return nexthdr;
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);
1514 module_init(ip6_tables_init);
1515 module_exit(ip6_tables_fini);