2 * Packet matching code.
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5 * Copyright (C) 2000-2002 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
14 * 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu>
15 * - new extension header parser code
17 #include <linux/config.h>
18 #include <linux/skbuff.h>
19 #include <linux/kmod.h>
20 #include <linux/vmalloc.h>
21 #include <linux/netdevice.h>
22 #include <linux/module.h>
23 #include <linux/tcp.h>
24 #include <linux/udp.h>
25 #include <linux/icmpv6.h>
28 #include <asm/uaccess.h>
29 #include <asm/semaphore.h>
30 #include <linux/proc_fs.h>
32 #include <linux/netfilter_ipv6/ip6_tables.h>
34 MODULE_LICENSE("GPL");
35 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
36 MODULE_DESCRIPTION("IPv6 packet filter");
38 #define IPV6_HDR_LEN (sizeof(struct ipv6hdr))
39 #define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
41 /*#define DEBUG_IP_FIREWALL*/
42 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
43 /*#define DEBUG_IP_FIREWALL_USER*/
45 #ifdef DEBUG_IP_FIREWALL
46 #define dprintf(format, args...) printk(format , ## args)
48 #define dprintf(format, args...)
51 #ifdef DEBUG_IP_FIREWALL_USER
52 #define duprintf(format, args...) printk(format , ## args)
54 #define duprintf(format, args...)
57 #ifdef CONFIG_NETFILTER_DEBUG
58 #define IP_NF_ASSERT(x) \
61 printk("IP_NF_ASSERT: %s:%s:%u\n", \
62 __FUNCTION__, __FILE__, __LINE__); \
65 #define IP_NF_ASSERT(x)
67 #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
69 static DECLARE_MUTEX(ip6t_mutex);
72 #define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
73 #define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
74 #include <linux/netfilter_ipv4/listhelp.h>
77 /* All the better to debug you with... */
82 /* Locking is simple: we assume at worst case there will be one packet
83 in user context and one from bottom halves (or soft irq if Alexey's
84 softnet patch was applied).
86 We keep a set of rules for each CPU, so we can avoid write-locking
87 them; doing a readlock_bh() stops packets coming through if we're
90 To be cache friendly on SMP, we arrange them like so:
92 ... cache-align padding ...
95 Hence the start of any table is given by get_table() below. */
97 /* The table itself */
98 struct ip6t_table_info
102 /* Number of entries: FIXME. --RR */
104 /* Initial number of entries. Needed for module usage count */
105 unsigned int initial_entries;
107 /* Entry points and underflows */
108 unsigned int hook_entry[NF_IP6_NUMHOOKS];
109 unsigned int underflow[NF_IP6_NUMHOOKS];
111 /* ip6t_entry tables: one per CPU */
112 char entries[0] ____cacheline_aligned;
115 static LIST_HEAD(ip6t_target);
116 static LIST_HEAD(ip6t_match);
117 static LIST_HEAD(ip6t_tables);
118 #define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
121 #define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
123 #define TABLE_OFFSET(t,p) 0
127 #define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
128 #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; })
129 #define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
132 static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask,
133 struct in6_addr addr2)
136 for( i = 0; i < 16; i++){
137 if((addr1.s6_addr[i] & mask.s6_addr[i]) !=
138 (addr2.s6_addr[i] & mask.s6_addr[i]))
144 /* Check for an extension */
146 ip6t_ext_hdr(u8 nexthdr)
148 return ( (nexthdr == IPPROTO_HOPOPTS) ||
149 (nexthdr == IPPROTO_ROUTING) ||
150 (nexthdr == IPPROTO_FRAGMENT) ||
151 (nexthdr == IPPROTO_ESP) ||
152 (nexthdr == IPPROTO_AH) ||
153 (nexthdr == IPPROTO_NONE) ||
154 (nexthdr == IPPROTO_DSTOPTS) );
157 /* Returns whether matches rule or not. */
159 ip6_packet_match(const struct sk_buff *skb,
162 const struct ip6t_ip6 *ip6info,
163 unsigned int *protoff,
168 const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
170 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
172 if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
174 || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
176 dprintf("Source or dest mismatch.\n");
178 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
179 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
180 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
181 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
182 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
183 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
187 /* Look for ifname matches; this should unroll nicely. */
188 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
189 ret |= (((const unsigned long *)indev)[i]
190 ^ ((const unsigned long *)ip6info->iniface)[i])
191 & ((const unsigned long *)ip6info->iniface_mask)[i];
194 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
195 dprintf("VIA in mismatch (%s vs %s).%s\n",
196 indev, ip6info->iniface,
197 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
201 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
202 ret |= (((const unsigned long *)outdev)[i]
203 ^ ((const unsigned long *)ip6info->outiface)[i])
204 & ((const unsigned long *)ip6info->outiface_mask)[i];
207 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
208 dprintf("VIA out mismatch (%s vs %s).%s\n",
209 outdev, ip6info->outiface,
210 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
214 /* ... might want to do something with class and flowlabel here ... */
216 /* look for the desired protocol header */
217 if((ip6info->flags & IP6T_F_PROTO)) {
218 u_int8_t currenthdr = ipv6->nexthdr;
219 struct ipv6_opt_hdr _hdr, *hp;
220 u_int16_t ptr; /* Header offset in skb */
221 u_int16_t hdrlen; /* Header */
222 u_int16_t _fragoff = 0, *fp = NULL;
226 while (ip6t_ext_hdr(currenthdr)) {
227 /* Is there enough space for the next ext header? */
228 if (skb->len - ptr < IPV6_OPTHDR_LEN)
231 /* NONE or ESP: there isn't protocol part */
232 /* If we want to count these packets in '-p all',
233 * we will change the return 0 to 1*/
234 if ((currenthdr == IPPROTO_NONE) ||
235 (currenthdr == IPPROTO_ESP))
238 hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
241 /* Size calculation */
242 if (currenthdr == IPPROTO_FRAGMENT) {
243 fp = skb_header_pointer(skb,
244 ptr+offsetof(struct frag_hdr,
251 _fragoff = ntohs(*fp) & ~0x7;
253 } else if (currenthdr == IPPROTO_AH)
254 hdrlen = (hp->hdrlen+2)<<2;
256 hdrlen = ipv6_optlen(hp);
258 currenthdr = hp->nexthdr;
260 /* ptr is too large */
261 if ( ptr > skb->len )
264 if (ip6t_ext_hdr(currenthdr))
273 /* currenthdr contains the protocol header */
275 dprintf("Packet protocol %hi ?= %s%hi.\n",
277 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
280 if (ip6info->proto == currenthdr) {
281 if(ip6info->invflags & IP6T_INV_PROTO) {
287 /* We need match for the '-p all', too! */
288 if ((ip6info->proto != 0) &&
289 !(ip6info->invflags & IP6T_INV_PROTO))
295 /* should be ip6 safe */
297 ip6_checkentry(const struct ip6t_ip6 *ipv6)
299 if (ipv6->flags & ~IP6T_F_MASK) {
300 duprintf("Unknown flag bits set: %08X\n",
301 ipv6->flags & ~IP6T_F_MASK);
304 if (ipv6->invflags & ~IP6T_INV_MASK) {
305 duprintf("Unknown invflag bits set: %08X\n",
306 ipv6->invflags & ~IP6T_INV_MASK);
313 ip6t_error(struct sk_buff **pskb,
314 const struct net_device *in,
315 const struct net_device *out,
316 unsigned int hooknum,
317 const void *targinfo,
321 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
327 int do_match(struct ip6t_entry_match *m,
328 const struct sk_buff *skb,
329 const struct net_device *in,
330 const struct net_device *out,
332 unsigned int protoff,
335 /* Stop iteration if it doesn't match */
336 if (!m->u.kernel.match->match(skb, in, out, m->data,
337 offset, protoff, hotdrop))
343 static inline struct ip6t_entry *
344 get_entry(void *base, unsigned int offset)
346 return (struct ip6t_entry *)(base + offset);
349 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
351 ip6t_do_table(struct sk_buff **pskb,
353 const struct net_device *in,
354 const struct net_device *out,
355 struct ip6t_table *table,
358 static const char nulldevname[IFNAMSIZ];
360 unsigned int protoff = 0;
362 /* Initializing verdict to NF_DROP keeps gcc happy. */
363 unsigned int verdict = NF_DROP;
364 const char *indev, *outdev;
366 struct ip6t_entry *e, *back;
369 indev = in ? in->name : nulldevname;
370 outdev = out ? out->name : nulldevname;
372 /* We handle fragments by dealing with the first fragment as
373 * if it was a normal packet. All other fragments are treated
374 * normally, except that they will NEVER match rules that ask
375 * things we don't know, ie. tcp syn flag or ports). If the
376 * rule is also a fragment-specific rule, non-fragments won't
379 read_lock_bh(&table->lock);
380 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
381 table_base = (void *)table->private->entries
382 + TABLE_OFFSET(table->private, smp_processor_id());
383 e = get_entry(table_base, table->private->hook_entry[hook]);
385 #ifdef CONFIG_NETFILTER_DEBUG
386 /* Check noone else using our table */
387 if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
388 && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
389 printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
392 &((struct ip6t_entry *)table_base)->comefrom,
393 ((struct ip6t_entry *)table_base)->comefrom);
395 ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
398 /* For return from builtin chain */
399 back = get_entry(table_base, table->private->underflow[hook]);
404 (*pskb)->nfcache |= e->nfcache;
405 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
406 &protoff, &offset)) {
407 struct ip6t_entry_target *t;
409 if (IP6T_MATCH_ITERATE(e, do_match,
411 offset, protoff, &hotdrop) != 0)
414 ADD_COUNTER(e->counters,
415 ntohs((*pskb)->nh.ipv6h->payload_len)
419 t = ip6t_get_target(e);
420 IP_NF_ASSERT(t->u.kernel.target);
421 /* Standard target? */
422 if (!t->u.kernel.target->target) {
425 v = ((struct ip6t_standard_target *)t)->verdict;
427 /* Pop from stack? */
428 if (v != IP6T_RETURN) {
429 verdict = (unsigned)(-v) - 1;
433 back = get_entry(table_base,
438 != (void *)e + e->next_offset) {
439 /* Save old back ptr in next entry */
440 struct ip6t_entry *next
441 = (void *)e + e->next_offset;
443 = (void *)back - table_base;
444 /* set back pointer to next entry */
448 e = get_entry(table_base, v);
450 /* Targets which reenter must return
452 #ifdef CONFIG_NETFILTER_DEBUG
453 ((struct ip6t_entry *)table_base)->comefrom
456 verdict = t->u.kernel.target->target(pskb,
462 #ifdef CONFIG_NETFILTER_DEBUG
463 if (((struct ip6t_entry *)table_base)->comefrom
465 && verdict == IP6T_CONTINUE) {
466 printk("Target %s reentered!\n",
467 t->u.kernel.target->name);
470 ((struct ip6t_entry *)table_base)->comefrom
473 if (verdict == IP6T_CONTINUE)
474 e = (void *)e + e->next_offset;
482 e = (void *)e + e->next_offset;
486 #ifdef CONFIG_NETFILTER_DEBUG
487 ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
489 read_unlock_bh(&table->lock);
491 #ifdef DEBUG_ALLOW_ALL
500 /* If it succeeds, returns element and locks mutex */
502 find_inlist_lock_noload(struct list_head *head,
505 struct semaphore *mutex)
510 duprintf("find_inlist: searching for `%s' in %s.\n",
511 name, head == &ip6t_target ? "ip6t_target"
512 : head == &ip6t_match ? "ip6t_match"
513 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
516 *error = down_interruptible(mutex);
520 ret = list_named_find(head, name);
529 #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
532 find_inlist_lock(struct list_head *head,
536 struct semaphore *mutex)
540 ret = find_inlist_lock_noload(head, name, error, mutex);
542 duprintf("find_inlist: loading `%s%s'.\n", prefix, name);
543 request_module("%s%s", prefix, name);
544 ret = find_inlist_lock_noload(head, name, error, mutex);
551 static inline struct ip6t_table *
552 ip6t_find_table_lock(const char *name, int *error, struct semaphore *mutex)
554 return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
557 static inline struct ip6t_match *
558 find_match_lock(const char *name, int *error, struct semaphore *mutex)
560 return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
563 static struct ip6t_target *
564 ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex)
566 return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
569 /* All zeroes == unconditional rule. */
571 unconditional(const struct ip6t_ip6 *ipv6)
575 for (i = 0; i < sizeof(*ipv6); i++)
576 if (((char *)ipv6)[i])
579 return (i == sizeof(*ipv6));
582 /* Figures out from what hook each rule can be called: returns 0 if
583 there are loops. Puts hook bitmask in comefrom. */
585 mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
589 /* No recursion; use packet counter to save back ptrs (reset
590 to 0 as we leave), and comefrom to save source hook bitmask */
591 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
592 unsigned int pos = newinfo->hook_entry[hook];
594 = (struct ip6t_entry *)(newinfo->entries + pos);
596 if (!(valid_hooks & (1 << hook)))
599 /* Set initial back pointer. */
600 e->counters.pcnt = pos;
603 struct ip6t_standard_target *t
604 = (void *)ip6t_get_target(e);
606 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
607 printk("iptables: loop hook %u pos %u %08X.\n",
608 hook, pos, e->comefrom);
612 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
614 /* Unconditional return/END. */
615 if (e->target_offset == sizeof(struct ip6t_entry)
616 && (strcmp(t->target.u.user.name,
617 IP6T_STANDARD_TARGET) == 0)
619 && unconditional(&e->ipv6)) {
620 unsigned int oldpos, size;
622 /* Return: backtrack through the last
625 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
626 #ifdef DEBUG_IP_FIREWALL_USER
628 & (1 << NF_IP6_NUMHOOKS)) {
629 duprintf("Back unset "
636 pos = e->counters.pcnt;
637 e->counters.pcnt = 0;
639 /* We're at the start. */
643 e = (struct ip6t_entry *)
644 (newinfo->entries + pos);
645 } while (oldpos == pos + e->next_offset);
648 size = e->next_offset;
649 e = (struct ip6t_entry *)
650 (newinfo->entries + pos + size);
651 e->counters.pcnt = pos;
654 int newpos = t->verdict;
656 if (strcmp(t->target.u.user.name,
657 IP6T_STANDARD_TARGET) == 0
659 /* This a jump; chase it. */
660 duprintf("Jump rule %u -> %u\n",
663 /* ... this is a fallthru */
664 newpos = pos + e->next_offset;
666 e = (struct ip6t_entry *)
667 (newinfo->entries + newpos);
668 e->counters.pcnt = pos;
673 duprintf("Finished chain %u\n", hook);
679 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
681 if (i && (*i)-- == 0)
684 if (m->u.kernel.match->destroy)
685 m->u.kernel.match->destroy(m->data,
686 m->u.match_size - sizeof(*m));
687 module_put(m->u.kernel.match->me);
692 standard_check(const struct ip6t_entry_target *t,
693 unsigned int max_offset)
695 struct ip6t_standard_target *targ = (void *)t;
697 /* Check standard info. */
699 != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
700 duprintf("standard_check: target size %u != %u\n",
702 IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
706 if (targ->verdict >= 0
707 && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
708 duprintf("ip6t_standard_check: bad verdict (%i)\n",
713 if (targ->verdict < -NF_MAX_VERDICT - 1) {
714 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
722 check_match(struct ip6t_entry_match *m,
724 const struct ip6t_ip6 *ipv6,
725 unsigned int hookmask,
729 struct ip6t_match *match;
731 match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
733 // duprintf("check_match: `%s' not found\n", m->u.name);
736 if (!try_module_get(match->me)) {
740 m->u.kernel.match = match;
743 if (m->u.kernel.match->checkentry
744 && !m->u.kernel.match->checkentry(name, ipv6, m->data,
745 m->u.match_size - sizeof(*m),
747 module_put(m->u.kernel.match->me);
748 duprintf("ip_tables: check failed for `%s'.\n",
749 m->u.kernel.match->name);
757 static struct ip6t_target ip6t_standard_target;
760 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
763 struct ip6t_entry_target *t;
764 struct ip6t_target *target;
768 if (!ip6_checkentry(&e->ipv6)) {
769 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
774 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
776 goto cleanup_matches;
778 t = ip6t_get_target(e);
779 target = ip6t_find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
781 duprintf("check_entry: `%s' not found\n", t->u.user.name);
782 goto cleanup_matches;
784 if (!try_module_get(target->me)) {
787 goto cleanup_matches;
789 t->u.kernel.target = target;
791 if (!t->u.kernel.target) {
793 goto cleanup_matches;
795 if (t->u.kernel.target == &ip6t_standard_target) {
796 if (!standard_check(t, size)) {
798 goto cleanup_matches;
800 } else if (t->u.kernel.target->checkentry
801 && !t->u.kernel.target->checkentry(name, e, t->data,
805 module_put(t->u.kernel.target->me);
806 duprintf("ip_tables: check failed for `%s'.\n",
807 t->u.kernel.target->name);
809 goto cleanup_matches;
816 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
821 check_entry_size_and_hooks(struct ip6t_entry *e,
822 struct ip6t_table_info *newinfo,
824 unsigned char *limit,
825 const unsigned int *hook_entries,
826 const unsigned int *underflows,
831 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
832 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
833 duprintf("Bad offset %p\n", e);
838 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
839 duprintf("checking: element %p size %u\n",
844 /* Check hooks & underflows */
845 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
846 if ((unsigned char *)e - base == hook_entries[h])
847 newinfo->hook_entry[h] = hook_entries[h];
848 if ((unsigned char *)e - base == underflows[h])
849 newinfo->underflow[h] = underflows[h];
852 /* FIXME: underflows must be unconditional, standard verdicts
853 < 0 (not IP6T_RETURN). --RR */
855 /* Clear counters and comefrom */
856 e->counters = ((struct ip6t_counters) { 0, 0 });
864 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
866 struct ip6t_entry_target *t;
868 if (i && (*i)-- == 0)
871 /* Cleanup all matches */
872 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
873 t = ip6t_get_target(e);
874 if (t->u.kernel.target->destroy)
875 t->u.kernel.target->destroy(t->data,
876 t->u.target_size - sizeof(*t));
877 module_put(t->u.kernel.target->me);
881 /* Checks and translates the user-supplied table segment (held in
884 translate_table(const char *name,
885 unsigned int valid_hooks,
886 struct ip6t_table_info *newinfo,
889 const unsigned int *hook_entries,
890 const unsigned int *underflows)
895 newinfo->size = size;
896 newinfo->number = number;
898 /* Init all hooks to impossible value. */
899 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
900 newinfo->hook_entry[i] = 0xFFFFFFFF;
901 newinfo->underflow[i] = 0xFFFFFFFF;
904 duprintf("translate_table: size %u\n", newinfo->size);
906 /* Walk through entries, checking offsets. */
907 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
908 check_entry_size_and_hooks,
911 newinfo->entries + size,
912 hook_entries, underflows, &i);
917 duprintf("translate_table: %u not %u entries\n",
922 /* Check hooks all assigned */
923 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
924 /* Only hooks which are valid */
925 if (!(valid_hooks & (1 << i)))
927 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
928 duprintf("Invalid hook entry %u %u\n",
932 if (newinfo->underflow[i] == 0xFFFFFFFF) {
933 duprintf("Invalid underflow %u %u\n",
939 if (!mark_source_chains(newinfo, valid_hooks))
942 /* Finally, each sanity check must pass */
944 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
945 check_entry, name, size, &i);
948 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
953 /* And one copy for every other CPU */
954 for (i = 1; i < num_possible_cpus(); i++) {
955 memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
957 SMP_ALIGN(newinfo->size));
963 static struct ip6t_table_info *
964 replace_table(struct ip6t_table *table,
965 unsigned int num_counters,
966 struct ip6t_table_info *newinfo,
969 struct ip6t_table_info *oldinfo;
971 #ifdef CONFIG_NETFILTER_DEBUG
973 struct ip6t_entry *table_base;
976 for (i = 0; i < num_possible_cpus(); i++) {
978 (void *)newinfo->entries
979 + TABLE_OFFSET(newinfo, i);
981 table_base->comefrom = 0xdead57ac;
986 /* Do the substitution. */
987 write_lock_bh(&table->lock);
988 /* Check inside lock: is the old number correct? */
989 if (num_counters != table->private->number) {
990 duprintf("num_counters != table->private->number (%u/%u)\n",
991 num_counters, table->private->number);
992 write_unlock_bh(&table->lock);
996 oldinfo = table->private;
997 table->private = newinfo;
998 newinfo->initial_entries = oldinfo->initial_entries;
999 write_unlock_bh(&table->lock);
1004 /* Gets counters. */
1006 add_entry_to_counter(const struct ip6t_entry *e,
1007 struct ip6t_counters total[],
1010 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
1017 get_counters(const struct ip6t_table_info *t,
1018 struct ip6t_counters counters[])
1023 for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
1025 IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
1027 add_entry_to_counter,
1034 copy_entries_to_user(unsigned int total_size,
1035 struct ip6t_table *table,
1036 void __user *userptr)
1038 unsigned int off, num, countersize;
1039 struct ip6t_entry *e;
1040 struct ip6t_counters *counters;
1043 /* We need atomic snapshot of counters: rest doesn't change
1044 (other than comefrom, which userspace doesn't care
1046 countersize = sizeof(struct ip6t_counters) * table->private->number;
1047 counters = vmalloc(countersize);
1049 if (counters == NULL)
1052 /* First, sum counters... */
1053 memset(counters, 0, countersize);
1054 write_lock_bh(&table->lock);
1055 get_counters(table->private, counters);
1056 write_unlock_bh(&table->lock);
1058 /* ... then copy entire thing from CPU 0... */
1059 if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
1064 /* FIXME: use iterator macros --RR */
1065 /* ... then go back and fix counters and names */
1066 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1068 struct ip6t_entry_match *m;
1069 struct ip6t_entry_target *t;
1071 e = (struct ip6t_entry *)(table->private->entries + off);
1072 if (copy_to_user(userptr + off
1073 + offsetof(struct ip6t_entry, counters),
1075 sizeof(counters[num])) != 0) {
1080 for (i = sizeof(struct ip6t_entry);
1081 i < e->target_offset;
1082 i += m->u.match_size) {
1085 if (copy_to_user(userptr + off + i
1086 + offsetof(struct ip6t_entry_match,
1088 m->u.kernel.match->name,
1089 strlen(m->u.kernel.match->name)+1)
1096 t = ip6t_get_target(e);
1097 if (copy_to_user(userptr + off + e->target_offset
1098 + offsetof(struct ip6t_entry_target,
1100 t->u.kernel.target->name,
1101 strlen(t->u.kernel.target->name)+1) != 0) {
1113 get_entries(const struct ip6t_get_entries *entries,
1114 struct ip6t_get_entries __user *uptr)
1117 struct ip6t_table *t;
1119 t = ip6t_find_table_lock(entries->name, &ret, &ip6t_mutex);
1121 duprintf("t->private->number = %u\n",
1122 t->private->number);
1123 if (entries->size == t->private->size)
1124 ret = copy_entries_to_user(t->private->size,
1125 t, uptr->entrytable);
1127 duprintf("get_entries: I've got %u not %u!\n",
1134 duprintf("get_entries: Can't find %s!\n",
1141 do_replace(void __user *user, unsigned int len)
1144 struct ip6t_replace tmp;
1145 struct ip6t_table *t;
1146 struct ip6t_table_info *newinfo, *oldinfo;
1147 struct ip6t_counters *counters;
1149 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1152 /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
1153 if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
1156 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1157 + SMP_ALIGN(tmp.size) * num_possible_cpus());
1161 if (copy_from_user(newinfo->entries, user + sizeof(tmp),
1167 counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
1172 memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
1174 ret = translate_table(tmp.name, tmp.valid_hooks,
1175 newinfo, tmp.size, tmp.num_entries,
1176 tmp.hook_entry, tmp.underflow);
1178 goto free_newinfo_counters;
1180 duprintf("ip_tables: Translated table\n");
1182 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1184 goto free_newinfo_counters_untrans;
1187 if (tmp.valid_hooks != t->valid_hooks) {
1188 duprintf("Valid hook crap: %08X vs %08X\n",
1189 tmp.valid_hooks, t->valid_hooks);
1191 goto free_newinfo_counters_untrans_unlock;
1194 /* Get a reference in advance, we're not allowed fail later */
1195 if (!try_module_get(t->me)) {
1197 goto free_newinfo_counters_untrans_unlock;
1200 oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
1204 /* Update module usage count based on number of rules */
1205 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1206 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1207 if ((oldinfo->number > oldinfo->initial_entries) ||
1208 (newinfo->number <= oldinfo->initial_entries))
1210 if ((oldinfo->number > oldinfo->initial_entries) &&
1211 (newinfo->number <= oldinfo->initial_entries))
1214 /* Get the old counters. */
1215 get_counters(oldinfo, counters);
1216 /* Decrease module usage counts and free resource */
1217 IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
1219 /* Silent error: too late now. */
1220 if (copy_to_user(tmp.counters, counters,
1221 sizeof(struct ip6t_counters) * tmp.num_counters) != 0)
1229 free_newinfo_counters_untrans_unlock:
1231 free_newinfo_counters_untrans:
1232 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
1233 free_newinfo_counters:
1240 /* We're lazy, and add to the first CPU; overflow works its fey magic
1241 * and everything is OK. */
1243 add_counter_to_entry(struct ip6t_entry *e,
1244 const struct ip6t_counters addme[],
1248 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1250 (long unsigned int)e->counters.pcnt,
1251 (long unsigned int)e->counters.bcnt,
1252 (long unsigned int)addme[*i].pcnt,
1253 (long unsigned int)addme[*i].bcnt);
1256 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1263 do_add_counters(void __user *user, unsigned int len)
1266 struct ip6t_counters_info tmp, *paddc;
1267 struct ip6t_table *t;
1270 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1273 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
1276 paddc = vmalloc(len);
1280 if (copy_from_user(paddc, user, len) != 0) {
1285 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1289 write_lock_bh(&t->lock);
1290 if (t->private->number != paddc->num_counters) {
1292 goto unlock_up_free;
1296 IP6T_ENTRY_ITERATE(t->private->entries,
1298 add_counter_to_entry,
1302 write_unlock_bh(&t->lock);
1311 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1315 if (!capable(CAP_NET_ADMIN))
1319 case IP6T_SO_SET_REPLACE:
1320 ret = do_replace(user, len);
1323 case IP6T_SO_SET_ADD_COUNTERS:
1324 ret = do_add_counters(user, len);
1328 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1336 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1340 if (!capable(CAP_NET_ADMIN))
1344 case IP6T_SO_GET_INFO: {
1345 char name[IP6T_TABLE_MAXNAMELEN];
1346 struct ip6t_table *t;
1348 if (*len != sizeof(struct ip6t_getinfo)) {
1349 duprintf("length %u != %u\n", *len,
1350 sizeof(struct ip6t_getinfo));
1355 if (copy_from_user(name, user, sizeof(name)) != 0) {
1359 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1360 t = ip6t_find_table_lock(name, &ret, &ip6t_mutex);
1362 struct ip6t_getinfo info;
1364 info.valid_hooks = t->valid_hooks;
1365 memcpy(info.hook_entry, t->private->hook_entry,
1366 sizeof(info.hook_entry));
1367 memcpy(info.underflow, t->private->underflow,
1368 sizeof(info.underflow));
1369 info.num_entries = t->private->number;
1370 info.size = t->private->size;
1371 memcpy(info.name, name, sizeof(info.name));
1373 if (copy_to_user(user, &info, *len) != 0)
1383 case IP6T_SO_GET_ENTRIES: {
1384 struct ip6t_get_entries get;
1386 if (*len < sizeof(get)) {
1387 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1389 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1391 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1392 duprintf("get_entries: %u != %u\n", *len,
1393 sizeof(struct ip6t_get_entries) + get.size);
1396 ret = get_entries(&get, user);
1401 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1408 /* Registration hooks for targets. */
1410 ip6t_register_target(struct ip6t_target *target)
1414 ret = down_interruptible(&ip6t_mutex);
1418 if (!list_named_insert(&ip6t_target, target)) {
1419 duprintf("ip6t_register_target: `%s' already in list!\n",
1428 ip6t_unregister_target(struct ip6t_target *target)
1431 LIST_DELETE(&ip6t_target, target);
1436 ip6t_register_match(struct ip6t_match *match)
1440 ret = down_interruptible(&ip6t_mutex);
1444 if (!list_named_insert(&ip6t_match, match)) {
1445 duprintf("ip6t_register_match: `%s' already in list!\n",
1455 ip6t_unregister_match(struct ip6t_match *match)
1458 LIST_DELETE(&ip6t_match, match);
1462 int ip6t_register_table(struct ip6t_table *table,
1463 const struct ip6t_replace *repl)
1466 struct ip6t_table_info *newinfo;
1467 static struct ip6t_table_info bootstrap
1468 = { 0, 0, 0, { 0 }, { 0 }, { } };
1470 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1471 + SMP_ALIGN(repl->size) * num_possible_cpus());
1475 memcpy(newinfo->entries, repl->entries, repl->size);
1477 ret = translate_table(table->name, table->valid_hooks,
1478 newinfo, repl->size,
1487 ret = down_interruptible(&ip6t_mutex);
1493 /* Don't autoload: we'd eat our tail... */
1494 if (list_named_find(&ip6t_tables, table->name)) {
1499 /* Simplifies replace_table code. */
1500 table->private = &bootstrap;
1501 if (!replace_table(table, 0, newinfo, &ret))
1504 duprintf("table->private->number = %u\n",
1505 table->private->number);
1507 /* save number of initial entries */
1508 table->private->initial_entries = table->private->number;
1510 rwlock_init(&table->lock);
1511 list_prepend(&ip6t_tables, table);
1522 void ip6t_unregister_table(struct ip6t_table *table)
1525 LIST_DELETE(&ip6t_tables, table);
1528 /* Decrease module usage counts and free resources */
1529 IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
1530 cleanup_entry, NULL);
1531 vfree(table->private);
1534 /* Returns 1 if the port is matched by the range, 0 otherwise */
1536 port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
1540 ret = (port >= min && port <= max) ^ invert;
1545 tcp_find_option(u_int8_t option,
1546 const struct sk_buff *skb,
1547 unsigned int tcpoff,
1548 unsigned int optlen,
1552 /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
1553 u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
1556 duprintf("tcp_match: finding option\n");
1559 /* If we don't have the whole header, drop packet. */
1560 op = skb_header_pointer(skb, tcpoff + sizeof(struct tcphdr), optlen,
1567 for (i = 0; i < optlen; ) {
1568 if (op[i] == option) return !invert;
1570 else i += op[i+1]?:1;
1577 tcp_match(const struct sk_buff *skb,
1578 const struct net_device *in,
1579 const struct net_device *out,
1580 const void *matchinfo,
1582 unsigned int protoff,
1585 struct tcphdr _tcph, *th;
1586 const struct ip6t_tcp *tcpinfo = matchinfo;
1591 Don't allow a fragment of TCP 8 bytes in. Nobody normal
1592 causes this. Its a cracker trying to break in by doing a
1593 flag overwrite to pass the direction checks.
1596 duprintf("Dropping evil TCP offset=1 frag.\n");
1599 /* Must not be a fragment. */
1603 #define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
1605 th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
1607 /* We've been asked to examine this packet, and we
1608 can't. Hence, no choice but to drop. */
1609 duprintf("Dropping evil TCP offset=0 tinygram.\n");
1614 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
1616 !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT)))
1618 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
1620 !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT)))
1622 if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
1623 == tcpinfo->flg_cmp,
1624 IP6T_TCP_INV_FLAGS))
1626 if (tcpinfo->option) {
1627 if (th->doff * 4 < sizeof(_tcph)) {
1631 if (!tcp_find_option(tcpinfo->option, skb, protoff,
1632 th->doff*4 - sizeof(*th),
1633 tcpinfo->invflags & IP6T_TCP_INV_OPTION,
1640 /* Called when user tries to insert an entry of this type. */
1642 tcp_checkentry(const char *tablename,
1643 const struct ip6t_ip6 *ipv6,
1645 unsigned int matchsize,
1646 unsigned int hook_mask)
1648 const struct ip6t_tcp *tcpinfo = matchinfo;
1650 /* Must specify proto == TCP, and no unknown invflags */
1651 return ipv6->proto == IPPROTO_TCP
1652 && !(ipv6->invflags & IP6T_INV_PROTO)
1653 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
1654 && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
1658 udp_match(const struct sk_buff *skb,
1659 const struct net_device *in,
1660 const struct net_device *out,
1661 const void *matchinfo,
1663 unsigned int protoff,
1666 struct udphdr _udph, *uh;
1667 const struct ip6t_udp *udpinfo = matchinfo;
1669 /* Must not be a fragment. */
1673 uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
1675 /* We've been asked to examine this packet, and we
1676 can't. Hence, no choice but to drop. */
1677 duprintf("Dropping evil UDP tinygram.\n");
1682 return port_match(udpinfo->spts[0], udpinfo->spts[1],
1684 !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
1685 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
1687 !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
1690 /* Called when user tries to insert an entry of this type. */
1692 udp_checkentry(const char *tablename,
1693 const struct ip6t_ip6 *ipv6,
1695 unsigned int matchinfosize,
1696 unsigned int hook_mask)
1698 const struct ip6t_udp *udpinfo = matchinfo;
1700 /* Must specify proto == UDP, and no unknown invflags */
1701 if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
1702 duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
1706 if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
1707 duprintf("ip6t_udp: matchsize %u != %u\n",
1708 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
1711 if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
1712 duprintf("ip6t_udp: unknown flags %X\n",
1720 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1722 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1723 u_int8_t type, u_int8_t code,
1726 return (type == test_type && code >= min_code && code <= max_code)
1731 icmp6_match(const struct sk_buff *skb,
1732 const struct net_device *in,
1733 const struct net_device *out,
1734 const void *matchinfo,
1736 unsigned int protoff,
1739 struct icmp6hdr _icmp, *ic;
1740 const struct ip6t_icmp *icmpinfo = matchinfo;
1742 /* Must not be a fragment. */
1746 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1748 /* We've been asked to examine this packet, and we
1749 can't. Hence, no choice but to drop. */
1750 duprintf("Dropping evil ICMP tinygram.\n");
1755 return icmp6_type_code_match(icmpinfo->type,
1758 ic->icmp6_type, ic->icmp6_code,
1759 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1762 /* Called when user tries to insert an entry of this type. */
1764 icmp6_checkentry(const char *tablename,
1765 const struct ip6t_ip6 *ipv6,
1767 unsigned int matchsize,
1768 unsigned int hook_mask)
1770 const struct ip6t_icmp *icmpinfo = matchinfo;
1772 /* Must specify proto == ICMP, and no unknown invflags */
1773 return ipv6->proto == IPPROTO_ICMPV6
1774 && !(ipv6->invflags & IP6T_INV_PROTO)
1775 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
1776 && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1779 /* The built-in targets: standard (NULL) and error. */
1780 static struct ip6t_target ip6t_standard_target = {
1781 .name = IP6T_STANDARD_TARGET,
1784 static struct ip6t_target ip6t_error_target = {
1785 .name = IP6T_ERROR_TARGET,
1786 .target = ip6t_error,
1789 static struct nf_sockopt_ops ip6t_sockopts = {
1791 .set_optmin = IP6T_BASE_CTL,
1792 .set_optmax = IP6T_SO_SET_MAX+1,
1793 .set = do_ip6t_set_ctl,
1794 .get_optmin = IP6T_BASE_CTL,
1795 .get_optmax = IP6T_SO_GET_MAX+1,
1796 .get = do_ip6t_get_ctl,
1799 static struct ip6t_match tcp_matchstruct = {
1801 .match = &tcp_match,
1802 .checkentry = &tcp_checkentry,
1805 static struct ip6t_match udp_matchstruct = {
1807 .match = &udp_match,
1808 .checkentry = &udp_checkentry,
1811 static struct ip6t_match icmp6_matchstruct = {
1813 .match = &icmp6_match,
1814 .checkentry = &icmp6_checkentry,
1817 #ifdef CONFIG_PROC_FS
1818 static inline int print_name(const char *i,
1819 off_t start_offset, char *buffer, int length,
1820 off_t *pos, unsigned int *count)
1822 if ((*count)++ >= start_offset) {
1823 unsigned int namelen;
1825 namelen = sprintf(buffer + *pos, "%s\n",
1826 i + sizeof(struct list_head));
1827 if (*pos + namelen > length) {
1828 /* Stop iterating */
1836 static inline int print_target(const struct ip6t_target *t,
1837 off_t start_offset, char *buffer, int length,
1838 off_t *pos, unsigned int *count)
1840 if (t == &ip6t_standard_target || t == &ip6t_error_target)
1842 return print_name((char *)t, start_offset, buffer, length, pos, count);
1845 static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
1848 unsigned int count = 0;
1850 if (down_interruptible(&ip6t_mutex) != 0)
1853 LIST_FIND(&ip6t_tables, print_name, char *,
1854 offset, buffer, length, &pos, &count);
1858 /* `start' hack - see fs/proc/generic.c line ~105 */
1859 *start=(char *)((unsigned long)count-offset);
1863 static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
1866 unsigned int count = 0;
1868 if (down_interruptible(&ip6t_mutex) != 0)
1871 LIST_FIND(&ip6t_target, print_target, struct ip6t_target *,
1872 offset, buffer, length, &pos, &count);
1876 *start = (char *)((unsigned long)count - offset);
1880 static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
1883 unsigned int count = 0;
1885 if (down_interruptible(&ip6t_mutex) != 0)
1888 LIST_FIND(&ip6t_match, print_name, char *,
1889 offset, buffer, length, &pos, &count);
1893 *start = (char *)((unsigned long)count - offset);
1897 static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
1898 { { "ip6_tables_names", ip6t_get_tables },
1899 { "ip6_tables_targets", ip6t_get_targets },
1900 { "ip6_tables_matches", ip6t_get_matches },
1902 #endif /*CONFIG_PROC_FS*/
1904 static int __init init(void)
1908 /* Noone else will be downing sem now, so we won't sleep */
1910 list_append(&ip6t_target, &ip6t_standard_target);
1911 list_append(&ip6t_target, &ip6t_error_target);
1912 list_append(&ip6t_match, &tcp_matchstruct);
1913 list_append(&ip6t_match, &udp_matchstruct);
1914 list_append(&ip6t_match, &icmp6_matchstruct);
1917 /* Register setsockopt */
1918 ret = nf_register_sockopt(&ip6t_sockopts);
1920 duprintf("Unable to register sockopts.\n");
1924 #ifdef CONFIG_PROC_FS
1926 struct proc_dir_entry *proc;
1929 for (i = 0; ip6t_proc_entry[i].name; i++) {
1930 proc = proc_net_create(ip6t_proc_entry[i].name, 0,
1931 ip6t_proc_entry[i].get_info);
1934 proc_net_remove(ip6t_proc_entry[i].name);
1935 nf_unregister_sockopt(&ip6t_sockopts);
1938 proc->owner = THIS_MODULE;
1943 printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
1947 static void __exit fini(void)
1949 nf_unregister_sockopt(&ip6t_sockopts);
1950 #ifdef CONFIG_PROC_FS
1953 for (i = 0; ip6t_proc_entry[i].name; i++)
1954 proc_net_remove(ip6t_proc_entry[i].name);
1959 EXPORT_SYMBOL(ip6t_register_table);
1960 EXPORT_SYMBOL(ip6t_unregister_table);
1961 EXPORT_SYMBOL(ip6t_do_table);
1962 EXPORT_SYMBOL(ip6t_register_match);
1963 EXPORT_SYMBOL(ip6t_unregister_match);
1964 EXPORT_SYMBOL(ip6t_register_target);
1965 EXPORT_SYMBOL(ip6t_unregister_target);
1966 EXPORT_SYMBOL(ip6t_ext_hdr);