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 * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
8 * - increase module usage count as soon as we have rules inside
10 * 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu>
11 * - new extension header parser code
13 #include <linux/config.h>
14 #include <linux/skbuff.h>
15 #include <linux/kmod.h>
16 #include <linux/vmalloc.h>
17 #include <linux/netdevice.h>
18 #include <linux/module.h>
19 #include <linux/tcp.h>
20 #include <linux/udp.h>
21 #include <linux/icmpv6.h>
24 #include <asm/uaccess.h>
25 #include <asm/semaphore.h>
26 #include <linux/proc_fs.h>
28 #include <linux/netfilter_ipv6/ip6_tables.h>
30 #define IPV6_HDR_LEN (sizeof(struct ipv6hdr))
31 #define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
33 /*#define DEBUG_IP_FIREWALL*/
34 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
35 /*#define DEBUG_IP_FIREWALL_USER*/
37 #ifdef DEBUG_IP_FIREWALL
38 #define dprintf(format, args...) printk(format , ## args)
40 #define dprintf(format, args...)
43 #ifdef DEBUG_IP_FIREWALL_USER
44 #define duprintf(format, args...) printk(format , ## args)
46 #define duprintf(format, args...)
49 #ifdef CONFIG_NETFILTER_DEBUG
50 #define IP_NF_ASSERT(x) \
53 printk("IP_NF_ASSERT: %s:%s:%u\n", \
54 __FUNCTION__, __FILE__, __LINE__); \
57 #define IP_NF_ASSERT(x)
59 #define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
61 static DECLARE_MUTEX(ip6t_mutex);
64 #define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
65 #define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
66 #include <linux/netfilter_ipv4/lockhelp.h>
67 #include <linux/netfilter_ipv4/listhelp.h>
70 /* All the better to debug you with... */
75 /* Locking is simple: we assume at worst case there will be one packet
76 in user context and one from bottom halves (or soft irq if Alexey's
77 softnet patch was applied).
79 We keep a set of rules for each CPU, so we can avoid write-locking
80 them; doing a readlock_bh() stops packets coming through if we're
83 To be cache friendly on SMP, we arrange them like so:
85 ... cache-align padding ...
88 Hence the start of any table is given by get_table() below. */
90 /* The table itself */
91 struct ip6t_table_info
95 /* Number of entries: FIXME. --RR */
97 /* Initial number of entries. Needed for module usage count */
98 unsigned int initial_entries;
100 /* Entry points and underflows */
101 unsigned int hook_entry[NF_IP6_NUMHOOKS];
102 unsigned int underflow[NF_IP6_NUMHOOKS];
104 /* ip6t_entry tables: one per CPU */
105 char entries[0] ____cacheline_aligned;
108 static LIST_HEAD(ip6t_target);
109 static LIST_HEAD(ip6t_match);
110 static LIST_HEAD(ip6t_tables);
111 #define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
114 #define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
116 #define TABLE_OFFSET(t,p) 0
120 #define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
121 #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; })
122 #define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
125 static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask,
126 struct in6_addr addr2)
129 for( i = 0; i < 16; i++){
130 if((addr1.s6_addr[i] & mask.s6_addr[i]) !=
131 (addr2.s6_addr[i] & mask.s6_addr[i]))
137 /* Check for an extension */
139 ip6t_ext_hdr(u8 nexthdr)
141 return ( (nexthdr == IPPROTO_HOPOPTS) ||
142 (nexthdr == IPPROTO_ROUTING) ||
143 (nexthdr == IPPROTO_FRAGMENT) ||
144 (nexthdr == IPPROTO_ESP) ||
145 (nexthdr == IPPROTO_AH) ||
146 (nexthdr == IPPROTO_NONE) ||
147 (nexthdr == IPPROTO_DSTOPTS) );
150 /* Returns whether matches rule or not. */
152 ip6_packet_match(const struct sk_buff *skb,
155 const struct ip6t_ip6 *ip6info,
156 unsigned int *protoff,
161 const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
163 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
165 if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
167 || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
169 dprintf("Source or dest mismatch.\n");
171 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
172 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
173 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
174 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
175 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
176 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
180 /* Look for ifname matches; this should unroll nicely. */
181 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
182 ret |= (((const unsigned long *)indev)[i]
183 ^ ((const unsigned long *)ip6info->iniface)[i])
184 & ((const unsigned long *)ip6info->iniface_mask)[i];
187 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
188 dprintf("VIA in mismatch (%s vs %s).%s\n",
189 indev, ip6info->iniface,
190 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
194 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
195 ret |= (((const unsigned long *)outdev)[i]
196 ^ ((const unsigned long *)ip6info->outiface)[i])
197 & ((const unsigned long *)ip6info->outiface_mask)[i];
200 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
201 dprintf("VIA out mismatch (%s vs %s).%s\n",
202 outdev, ip6info->outiface,
203 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
207 /* ... might want to do something with class and flowlabel here ... */
209 /* look for the desired protocol header */
210 if((ip6info->flags & IP6T_F_PROTO)) {
211 u_int8_t currenthdr = ipv6->nexthdr;
212 struct ipv6_opt_hdr *hdrptr;
213 u_int16_t ptr; /* Header offset in skb */
214 u_int16_t hdrlen; /* Header */
219 while (ip6t_ext_hdr(currenthdr)) {
220 /* Is there enough space for the next ext header? */
221 if (skb->len - ptr < IPV6_OPTHDR_LEN)
224 /* NONE or ESP: there isn't protocol part */
225 /* If we want to count these packets in '-p all',
226 * we will change the return 0 to 1*/
227 if ((currenthdr == IPPROTO_NONE) ||
228 (currenthdr == IPPROTO_ESP))
231 hdrptr = (struct ipv6_opt_hdr *)(skb->data + ptr);
233 /* Size calculation */
234 if (currenthdr == IPPROTO_FRAGMENT) {
235 unsigned int foff_off;
237 foff_off = ptr + offsetof(struct frag_hdr,
239 if (skb->len - foff_off < sizeof(foff))
242 foff = ntohs(*(u_int16_t *)(skb->data
246 } else if (currenthdr == IPPROTO_AH)
247 hdrlen = (hdrptr->hdrlen+2)<<2;
249 hdrlen = ipv6_optlen(hdrptr);
251 currenthdr = hdrptr->nexthdr;
253 /* ptr is too large */
254 if ( ptr > skb->len )
257 if (ip6t_ext_hdr(currenthdr))
266 /* currenthdr contains the protocol header */
268 dprintf("Packet protocol %hi ?= %s%hi.\n",
270 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
273 if (ip6info->proto == currenthdr) {
274 if(ip6info->invflags & IP6T_INV_PROTO) {
280 /* We need match for the '-p all', too! */
281 if ((ip6info->proto != 0) &&
282 !(ip6info->invflags & IP6T_INV_PROTO))
288 /* should be ip6 safe */
290 ip6_checkentry(const struct ip6t_ip6 *ipv6)
292 if (ipv6->flags & ~IP6T_F_MASK) {
293 duprintf("Unknown flag bits set: %08X\n",
294 ipv6->flags & ~IP6T_F_MASK);
297 if (ipv6->invflags & ~IP6T_INV_MASK) {
298 duprintf("Unknown invflag bits set: %08X\n",
299 ipv6->invflags & ~IP6T_INV_MASK);
306 ip6t_error(struct sk_buff **pskb,
307 unsigned int hooknum,
308 const struct net_device *in,
309 const struct net_device *out,
310 const void *targinfo,
314 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
320 int do_match(struct ip6t_entry_match *m,
321 const struct sk_buff *skb,
322 const struct net_device *in,
323 const struct net_device *out,
329 /* Stop iteration if it doesn't match */
330 if (!m->u.kernel.match->match(skb, in, out, m->data,
331 offset, hdr, datalen, hotdrop))
337 static inline struct ip6t_entry *
338 get_entry(void *base, unsigned int offset)
340 return (struct ip6t_entry *)(base + offset);
343 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
345 ip6t_do_table(struct sk_buff **pskb,
347 const struct net_device *in,
348 const struct net_device *out,
349 struct ip6t_table *table,
352 static const char nulldevname[IFNAMSIZ] = { 0 };
354 unsigned int protoff = 0;
356 /* Initializing verdict to NF_DROP keeps gcc happy. */
357 unsigned int verdict = NF_DROP;
358 const char *indev, *outdev;
360 struct ip6t_entry *e, *back;
363 indev = in ? in->name : nulldevname;
364 outdev = out ? out->name : nulldevname;
366 /* We handle fragments by dealing with the first fragment as
367 * if it was a normal packet. All other fragments are treated
368 * normally, except that they will NEVER match rules that ask
369 * things we don't know, ie. tcp syn flag or ports). If the
370 * rule is also a fragment-specific rule, non-fragments won't
373 read_lock_bh(&table->lock);
374 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
375 table_base = (void *)table->private->entries
376 + TABLE_OFFSET(table->private,
377 cpu_number_map(smp_processor_id()));
378 e = get_entry(table_base, table->private->hook_entry[hook]);
380 #ifdef CONFIG_NETFILTER_DEBUG
381 /* Check noone else using our table */
382 if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
383 && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
384 printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
387 &((struct ip6t_entry *)table_base)->comefrom,
388 ((struct ip6t_entry *)table_base)->comefrom);
390 ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
393 /* For return from builtin chain */
394 back = get_entry(table_base, table->private->underflow[hook]);
399 (*pskb)->nfcache |= e->nfcache;
400 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
401 &protoff, &offset)) {
402 struct ip6t_entry_target *t;
404 if (IP6T_MATCH_ITERATE(e, do_match,
407 (void *)((*pskb)->data
409 (*pskb)->len - protoff,
413 ADD_COUNTER(e->counters, ntohs((*pskb)->nh.ipv6h->payload_len) + IPV6_HDR_LEN, 1);
415 t = ip6t_get_target(e);
416 IP_NF_ASSERT(t->u.kernel.target);
417 /* Standard target? */
418 if (!t->u.kernel.target->target) {
421 v = ((struct ip6t_standard_target *)t)->verdict;
423 /* Pop from stack? */
424 if (v != IP6T_RETURN) {
425 verdict = (unsigned)(-v) - 1;
429 back = get_entry(table_base,
434 != (void *)e + e->next_offset) {
435 /* Save old back ptr in next entry */
436 struct ip6t_entry *next
437 = (void *)e + e->next_offset;
439 = (void *)back - table_base;
440 /* set back pointer to next entry */
444 e = get_entry(table_base, v);
446 /* Targets which reenter must return
448 #ifdef CONFIG_NETFILTER_DEBUG
449 ((struct ip6t_entry *)table_base)->comefrom
452 verdict = t->u.kernel.target->target(pskb,
458 #ifdef CONFIG_NETFILTER_DEBUG
459 if (((struct ip6t_entry *)table_base)->comefrom
461 && verdict == IP6T_CONTINUE) {
462 printk("Target %s reentered!\n",
463 t->u.kernel.target->name);
466 ((struct ip6t_entry *)table_base)->comefrom
469 if (verdict == IP6T_CONTINUE)
470 e = (void *)e + e->next_offset;
478 e = (void *)e + e->next_offset;
482 #ifdef CONFIG_NETFILTER_DEBUG
483 ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
485 read_unlock_bh(&table->lock);
487 #ifdef DEBUG_ALLOW_ALL
496 /* If it succeeds, returns element and locks mutex */
498 find_inlist_lock_noload(struct list_head *head,
501 struct semaphore *mutex)
506 duprintf("find_inlist: searching for `%s' in %s.\n",
507 name, head == &ip6t_target ? "ip6t_target"
508 : head == &ip6t_match ? "ip6t_match"
509 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
512 *error = down_interruptible(mutex);
516 ret = list_named_find(head, name);
525 #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
528 find_inlist_lock(struct list_head *head,
532 struct semaphore *mutex)
536 ret = find_inlist_lock_noload(head, name, error, mutex);
538 char modulename[IP6T_FUNCTION_MAXNAMELEN + strlen(prefix) + 1];
539 strcpy(modulename, prefix);
540 strcat(modulename, name);
541 duprintf("find_inlist: loading `%s'.\n", modulename);
542 request_module(modulename);
543 ret = find_inlist_lock_noload(head, name, error, mutex);
550 static inline struct ip6t_table *
551 ip6t_find_table_lock(const char *name, int *error, struct semaphore *mutex)
553 return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
556 static inline struct ip6t_match *
557 find_match_lock(const char *name, int *error, struct semaphore *mutex)
559 return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
563 ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex)
565 return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
568 /* All zeroes == unconditional rule. */
570 unconditional(const struct ip6t_ip6 *ipv6)
574 for (i = 0; i < sizeof(*ipv6); i++)
575 if (((char *)ipv6)[i])
578 return (i == sizeof(*ipv6));
581 /* Figures out from what hook each rule can be called: returns 0 if
582 there are loops. Puts hook bitmask in comefrom. */
584 mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
588 /* No recursion; use packet counter to save back ptrs (reset
589 to 0 as we leave), and comefrom to save source hook bitmask */
590 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
591 unsigned int pos = newinfo->hook_entry[hook];
593 = (struct ip6t_entry *)(newinfo->entries + pos);
595 if (!(valid_hooks & (1 << hook)))
598 /* Set initial back pointer. */
599 e->counters.pcnt = pos;
602 struct ip6t_standard_target *t
603 = (void *)ip6t_get_target(e);
605 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
606 printk("iptables: loop hook %u pos %u %08X.\n",
607 hook, pos, e->comefrom);
611 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
613 /* Unconditional return/END. */
614 if (e->target_offset == sizeof(struct ip6t_entry)
615 && (strcmp(t->target.u.user.name,
616 IP6T_STANDARD_TARGET) == 0)
618 && unconditional(&e->ipv6)) {
619 unsigned int oldpos, size;
621 /* Return: backtrack through the last
624 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
625 #ifdef DEBUG_IP_FIREWALL_USER
627 & (1 << NF_IP6_NUMHOOKS)) {
628 duprintf("Back unset "
635 pos = e->counters.pcnt;
636 e->counters.pcnt = 0;
638 /* We're at the start. */
642 e = (struct ip6t_entry *)
643 (newinfo->entries + pos);
644 } while (oldpos == pos + e->next_offset);
647 size = e->next_offset;
648 e = (struct ip6t_entry *)
649 (newinfo->entries + pos + size);
650 e->counters.pcnt = pos;
653 int newpos = t->verdict;
655 if (strcmp(t->target.u.user.name,
656 IP6T_STANDARD_TARGET) == 0
658 /* This a jump; chase it. */
659 duprintf("Jump rule %u -> %u\n",
662 /* ... this is a fallthru */
663 newpos = pos + e->next_offset;
665 e = (struct ip6t_entry *)
666 (newinfo->entries + newpos);
667 e->counters.pcnt = pos;
672 duprintf("Finished chain %u\n", hook);
678 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
680 if (i && (*i)-- == 0)
683 if (m->u.kernel.match->destroy)
684 m->u.kernel.match->destroy(m->data,
685 m->u.match_size - sizeof(*m));
687 if (m->u.kernel.match->me)
688 __MOD_DEC_USE_COUNT(m->u.kernel.match->me);
694 standard_check(const struct ip6t_entry_target *t,
695 unsigned int max_offset)
697 struct ip6t_standard_target *targ = (void *)t;
699 /* Check standard info. */
701 != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
702 duprintf("standard_check: target size %u != %u\n",
704 IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
708 if (targ->verdict >= 0
709 && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
710 duprintf("ip6t_standard_check: bad verdict (%i)\n",
715 if (targ->verdict < -NF_MAX_VERDICT - 1) {
716 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
724 check_match(struct ip6t_entry_match *m,
726 const struct ip6t_ip6 *ipv6,
727 unsigned int hookmask,
731 struct ip6t_match *match;
733 match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
735 // duprintf("check_match: `%s' not found\n", m->u.name);
739 __MOD_INC_USE_COUNT(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 if (m->u.kernel.match->me)
748 __MOD_DEC_USE_COUNT(m->u.kernel.match->me);
749 duprintf("ip_tables: check failed for `%s'.\n",
750 m->u.kernel.match->name);
758 static struct ip6t_target ip6t_standard_target;
761 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
764 struct ip6t_entry_target *t;
765 struct ip6t_target *target;
769 if (!ip6_checkentry(&e->ipv6)) {
770 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
775 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
777 goto cleanup_matches;
779 t = ip6t_get_target(e);
780 target = ip6t_find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
782 duprintf("check_entry: `%s' not found\n", t->u.user.name);
783 goto cleanup_matches;
786 __MOD_INC_USE_COUNT(target->me);
787 t->u.kernel.target = target;
790 if (t->u.kernel.target == &ip6t_standard_target) {
791 if (!standard_check(t, size)) {
793 goto cleanup_matches;
795 } else if (t->u.kernel.target->checkentry
796 && !t->u.kernel.target->checkentry(name, e, t->data,
800 if (t->u.kernel.target->me)
801 __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
802 duprintf("ip_tables: check failed for `%s'.\n",
803 t->u.kernel.target->name);
805 goto cleanup_matches;
812 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
817 check_entry_size_and_hooks(struct ip6t_entry *e,
818 struct ip6t_table_info *newinfo,
820 unsigned char *limit,
821 const unsigned int *hook_entries,
822 const unsigned int *underflows,
827 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
828 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
829 duprintf("Bad offset %p\n", e);
834 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
835 duprintf("checking: element %p size %u\n",
840 /* Check hooks & underflows */
841 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
842 if ((unsigned char *)e - base == hook_entries[h])
843 newinfo->hook_entry[h] = hook_entries[h];
844 if ((unsigned char *)e - base == underflows[h])
845 newinfo->underflow[h] = underflows[h];
848 /* FIXME: underflows must be unconditional, standard verdicts
849 < 0 (not IP6T_RETURN). --RR */
851 /* Clear counters and comefrom */
852 e->counters = ((struct ip6t_counters) { 0, 0 });
860 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
862 struct ip6t_entry_target *t;
864 if (i && (*i)-- == 0)
867 /* Cleanup all matches */
868 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
869 t = ip6t_get_target(e);
870 if (t->u.kernel.target->destroy)
871 t->u.kernel.target->destroy(t->data,
872 t->u.target_size - sizeof(*t));
873 if (t->u.kernel.target->me)
874 __MOD_DEC_USE_COUNT(t->u.kernel.target->me);
879 /* Checks and translates the user-supplied table segment (held in
882 translate_table(const char *name,
883 unsigned int valid_hooks,
884 struct ip6t_table_info *newinfo,
887 const unsigned int *hook_entries,
888 const unsigned int *underflows)
893 newinfo->size = size;
894 newinfo->number = number;
896 /* Init all hooks to impossible value. */
897 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
898 newinfo->hook_entry[i] = 0xFFFFFFFF;
899 newinfo->underflow[i] = 0xFFFFFFFF;
902 duprintf("translate_table: size %u\n", newinfo->size);
904 /* Walk through entries, checking offsets. */
905 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
906 check_entry_size_and_hooks,
909 newinfo->entries + size,
910 hook_entries, underflows, &i);
915 duprintf("translate_table: %u not %u entries\n",
920 /* Check hooks all assigned */
921 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
922 /* Only hooks which are valid */
923 if (!(valid_hooks & (1 << i)))
925 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
926 duprintf("Invalid hook entry %u %u\n",
930 if (newinfo->underflow[i] == 0xFFFFFFFF) {
931 duprintf("Invalid underflow %u %u\n",
937 if (!mark_source_chains(newinfo, valid_hooks))
940 /* Finally, each sanity check must pass */
942 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
943 check_entry, name, size, &i);
946 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
951 /* And one copy for every other CPU */
952 for (i = 1; i < smp_num_cpus; i++) {
953 memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
955 SMP_ALIGN(newinfo->size));
961 static struct ip6t_table_info *
962 replace_table(struct ip6t_table *table,
963 unsigned int num_counters,
964 struct ip6t_table_info *newinfo,
967 struct ip6t_table_info *oldinfo;
969 #ifdef CONFIG_NETFILTER_DEBUG
971 struct ip6t_entry *table_base;
974 for (i = 0; i < smp_num_cpus; i++) {
976 (void *)newinfo->entries
977 + TABLE_OFFSET(newinfo, i);
979 table_base->comefrom = 0xdead57ac;
984 /* Do the substitution. */
985 write_lock_bh(&table->lock);
986 /* Check inside lock: is the old number correct? */
987 if (num_counters != table->private->number) {
988 duprintf("num_counters != table->private->number (%u/%u)\n",
989 num_counters, table->private->number);
990 write_unlock_bh(&table->lock);
994 oldinfo = table->private;
995 table->private = newinfo;
996 newinfo->initial_entries = oldinfo->initial_entries;
997 write_unlock_bh(&table->lock);
1002 /* Gets counters. */
1004 add_entry_to_counter(const struct ip6t_entry *e,
1005 struct ip6t_counters total[],
1008 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
1015 get_counters(const struct ip6t_table_info *t,
1016 struct ip6t_counters counters[])
1021 for (cpu = 0; cpu < smp_num_cpus; cpu++) {
1023 IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
1025 add_entry_to_counter,
1032 copy_entries_to_user(unsigned int total_size,
1033 struct ip6t_table *table,
1036 unsigned int off, num, countersize;
1037 struct ip6t_entry *e;
1038 struct ip6t_counters *counters;
1041 /* We need atomic snapshot of counters: rest doesn't change
1042 (other than comefrom, which userspace doesn't care
1044 countersize = sizeof(struct ip6t_counters) * table->private->number;
1045 counters = vmalloc(countersize);
1047 if (counters == NULL)
1050 /* First, sum counters... */
1051 memset(counters, 0, countersize);
1052 write_lock_bh(&table->lock);
1053 get_counters(table->private, counters);
1054 write_unlock_bh(&table->lock);
1056 /* ... then copy entire thing from CPU 0... */
1057 if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
1062 /* FIXME: use iterator macros --RR */
1063 /* ... then go back and fix counters and names */
1064 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1066 struct ip6t_entry_match *m;
1067 struct ip6t_entry_target *t;
1069 e = (struct ip6t_entry *)(table->private->entries + off);
1070 if (copy_to_user(userptr + off
1071 + offsetof(struct ip6t_entry, counters),
1073 sizeof(counters[num])) != 0) {
1078 for (i = sizeof(struct ip6t_entry);
1079 i < e->target_offset;
1080 i += m->u.match_size) {
1083 if (copy_to_user(userptr + off + i
1084 + offsetof(struct ip6t_entry_match,
1086 m->u.kernel.match->name,
1087 strlen(m->u.kernel.match->name)+1)
1094 t = ip6t_get_target(e);
1095 if (copy_to_user(userptr + off + e->target_offset
1096 + offsetof(struct ip6t_entry_target,
1098 t->u.kernel.target->name,
1099 strlen(t->u.kernel.target->name)+1) != 0) {
1111 get_entries(const struct ip6t_get_entries *entries,
1112 struct ip6t_get_entries *uptr)
1115 struct ip6t_table *t;
1117 t = ip6t_find_table_lock(entries->name, &ret, &ip6t_mutex);
1119 duprintf("t->private->number = %u\n",
1120 t->private->number);
1121 if (entries->size == t->private->size)
1122 ret = copy_entries_to_user(t->private->size,
1123 t, uptr->entrytable);
1125 duprintf("get_entries: I've got %u not %u!\n",
1132 duprintf("get_entries: Can't find %s!\n",
1139 do_replace(void *user, unsigned int len)
1142 struct ip6t_replace tmp;
1143 struct ip6t_table *t;
1144 struct ip6t_table_info *newinfo, *oldinfo;
1145 struct ip6t_counters *counters;
1147 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1150 /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
1151 if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
1154 /* overflow check */
1155 if (tmp.size >= (INT_MAX - sizeof(struct ip6t_table_info)) / NR_CPUS -
1158 if (tmp.num_counters >= INT_MAX / sizeof(struct ip6t_counters))
1161 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1162 + SMP_ALIGN(tmp.size) * smp_num_cpus);
1166 if (copy_from_user(newinfo->entries, user + sizeof(tmp),
1172 counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
1177 memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
1179 ret = translate_table(tmp.name, tmp.valid_hooks,
1180 newinfo, tmp.size, tmp.num_entries,
1181 tmp.hook_entry, tmp.underflow);
1183 goto free_newinfo_counters;
1185 duprintf("ip_tables: Translated table\n");
1187 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1189 goto free_newinfo_counters_untrans;
1192 if (tmp.valid_hooks != t->valid_hooks) {
1193 duprintf("Valid hook crap: %08X vs %08X\n",
1194 tmp.valid_hooks, t->valid_hooks);
1196 goto free_newinfo_counters_untrans_unlock;
1199 oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
1201 goto free_newinfo_counters_untrans_unlock;
1203 /* Update module usage count based on number of rules */
1204 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1205 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1206 if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
1207 (newinfo->number > oldinfo->initial_entries))
1208 __MOD_INC_USE_COUNT(t->me);
1209 else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
1210 (newinfo->number <= oldinfo->initial_entries))
1211 __MOD_DEC_USE_COUNT(t->me);
1213 /* Get the old counters. */
1214 get_counters(oldinfo, counters);
1215 /* Decrease module usage counts and free resource */
1216 IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
1218 /* Silent error: too late now. */
1219 copy_to_user(tmp.counters, counters,
1220 sizeof(struct ip6t_counters) * tmp.num_counters);
1225 free_newinfo_counters_untrans_unlock:
1227 free_newinfo_counters_untrans:
1228 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
1229 free_newinfo_counters:
1236 /* We're lazy, and add to the first CPU; overflow works its fey magic
1237 * and everything is OK. */
1239 add_counter_to_entry(struct ip6t_entry *e,
1240 const struct ip6t_counters addme[],
1244 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1246 (long unsigned int)e->counters.pcnt,
1247 (long unsigned int)e->counters.bcnt,
1248 (long unsigned int)addme[*i].pcnt,
1249 (long unsigned int)addme[*i].bcnt);
1252 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1259 do_add_counters(void *user, unsigned int len)
1262 struct ip6t_counters_info tmp, *paddc;
1263 struct ip6t_table *t;
1266 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1269 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
1272 paddc = vmalloc(len);
1276 if (copy_from_user(paddc, user, len) != 0) {
1281 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1285 write_lock_bh(&t->lock);
1286 if (t->private->number != tmp.num_counters) {
1288 goto unlock_up_free;
1292 IP6T_ENTRY_ITERATE(t->private->entries,
1294 add_counter_to_entry,
1298 write_unlock_bh(&t->lock);
1307 do_ip6t_set_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
1311 if (!capable(CAP_NET_ADMIN))
1315 case IP6T_SO_SET_REPLACE:
1316 ret = do_replace(user, len);
1319 case IP6T_SO_SET_ADD_COUNTERS:
1320 ret = do_add_counters(user, len);
1324 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1332 do_ip6t_get_ctl(struct sock *sk, int cmd, void *user, int *len)
1336 if (!capable(CAP_NET_ADMIN))
1340 case IP6T_SO_GET_INFO: {
1341 char name[IP6T_TABLE_MAXNAMELEN];
1342 struct ip6t_table *t;
1344 if (*len != sizeof(struct ip6t_getinfo)) {
1345 duprintf("length %u != %u\n", *len,
1346 sizeof(struct ip6t_getinfo));
1351 if (copy_from_user(name, user, sizeof(name)) != 0) {
1355 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1356 t = ip6t_find_table_lock(name, &ret, &ip6t_mutex);
1358 struct ip6t_getinfo info;
1360 info.valid_hooks = t->valid_hooks;
1361 memcpy(info.hook_entry, t->private->hook_entry,
1362 sizeof(info.hook_entry));
1363 memcpy(info.underflow, t->private->underflow,
1364 sizeof(info.underflow));
1365 info.num_entries = t->private->number;
1366 info.size = t->private->size;
1367 memcpy(info.name, name, sizeof(info.name));
1369 if (copy_to_user(user, &info, *len) != 0)
1379 case IP6T_SO_GET_ENTRIES: {
1380 struct ip6t_get_entries get;
1382 if (*len < sizeof(get)) {
1383 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1385 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1387 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1388 duprintf("get_entries: %u != %u\n", *len,
1389 sizeof(struct ip6t_get_entries) + get.size);
1392 ret = get_entries(&get, user);
1397 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1404 /* Registration hooks for targets. */
1406 ip6t_register_target(struct ip6t_target *target)
1411 ret = down_interruptible(&ip6t_mutex);
1416 if (!list_named_insert(&ip6t_target, target)) {
1417 duprintf("ip6t_register_target: `%s' already in list!\n",
1427 ip6t_unregister_target(struct ip6t_target *target)
1430 LIST_DELETE(&ip6t_target, target);
1436 ip6t_register_match(struct ip6t_match *match)
1441 ret = down_interruptible(&ip6t_mutex);
1446 if (!list_named_insert(&ip6t_match, match)) {
1447 duprintf("ip6t_register_match: `%s' already in list!\n",
1458 ip6t_unregister_match(struct ip6t_match *match)
1461 LIST_DELETE(&ip6t_match, match);
1466 int ip6t_register_table(struct ip6t_table *table)
1469 struct ip6t_table_info *newinfo;
1470 static struct ip6t_table_info bootstrap
1471 = { 0, 0, 0, { 0 }, { 0 }, { } };
1474 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1475 + SMP_ALIGN(table->table->size) * smp_num_cpus);
1481 memcpy(newinfo->entries, table->table->entries, table->table->size);
1483 ret = translate_table(table->name, table->valid_hooks,
1484 newinfo, table->table->size,
1485 table->table->num_entries,
1486 table->table->hook_entry,
1487 table->table->underflow);
1494 ret = down_interruptible(&ip6t_mutex);
1501 /* Don't autoload: we'd eat our tail... */
1502 if (list_named_find(&ip6t_tables, table->name)) {
1507 /* Simplifies replace_table code. */
1508 table->private = &bootstrap;
1509 if (!replace_table(table, 0, newinfo, &ret))
1512 duprintf("table->private->number = %u\n",
1513 table->private->number);
1515 /* save number of initial entries */
1516 table->private->initial_entries = table->private->number;
1518 table->lock = RW_LOCK_UNLOCKED;
1519 list_prepend(&ip6t_tables, table);
1531 void ip6t_unregister_table(struct ip6t_table *table)
1534 LIST_DELETE(&ip6t_tables, table);
1537 /* Decrease module usage counts and free resources */
1538 IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
1539 cleanup_entry, NULL);
1540 vfree(table->private);
1544 /* Returns 1 if the port is matched by the range, 0 otherwise */
1546 port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
1550 ret = (port >= min && port <= max) ^ invert;
1555 tcp_find_option(u_int8_t option,
1556 const struct tcphdr *tcp,
1561 unsigned int i = sizeof(struct tcphdr);
1562 const u_int8_t *opt = (u_int8_t *)tcp;
1564 duprintf("tcp_match: finding option\n");
1565 /* If we don't have the whole header, drop packet. */
1566 if (tcp->doff * 4 < sizeof(struct tcphdr) ||
1567 tcp->doff * 4 > datalen) {
1572 while (i < tcp->doff * 4) {
1573 if (opt[i] == option) return !invert;
1574 if (opt[i] < 2) i++;
1575 else i += opt[i+1]?:1;
1582 tcp_match(const struct sk_buff *skb,
1583 const struct net_device *in,
1584 const struct net_device *out,
1585 const void *matchinfo,
1591 const struct tcphdr *tcp = hdr;
1592 const struct ip6t_tcp *tcpinfo = matchinfo;
1596 Don't allow a fragment of TCP 8 bytes in. Nobody normal
1597 causes this. Its a cracker trying to break in by doing a
1598 flag overwrite to pass the direction checks.
1602 duprintf("Dropping evil TCP offset=1 frag.\n");
1605 } else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
1606 /* We've been asked to examine this packet, and we
1607 can't. Hence, no choice but to drop. */
1608 duprintf("Dropping evil TCP offset=0 tinygram.\n");
1613 /* FIXME: Try tcp doff >> packet len against various stacks --RR */
1615 #define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
1617 /* Must not be a fragment. */
1619 && port_match(tcpinfo->spts[0], tcpinfo->spts[1],
1621 !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT))
1622 && port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
1624 !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT))
1625 && FWINVTCP((((unsigned char *)tcp)[13]
1626 & tcpinfo->flg_mask)
1627 == tcpinfo->flg_cmp,
1629 && (!tcpinfo->option
1630 || tcp_find_option(tcpinfo->option, tcp, datalen,
1632 & IP6T_TCP_INV_OPTION,
1636 /* Called when user tries to insert an entry of this type. */
1638 tcp_checkentry(const char *tablename,
1639 const struct ip6t_ip6 *ipv6,
1641 unsigned int matchsize,
1642 unsigned int hook_mask)
1644 const struct ip6t_tcp *tcpinfo = matchinfo;
1646 /* Must specify proto == TCP, and no unknown invflags */
1647 return ipv6->proto == IPPROTO_TCP
1648 && !(ipv6->invflags & IP6T_INV_PROTO)
1649 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
1650 && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
1654 udp_match(const struct sk_buff *skb,
1655 const struct net_device *in,
1656 const struct net_device *out,
1657 const void *matchinfo,
1663 const struct udphdr *udp = hdr;
1664 const struct ip6t_udp *udpinfo = matchinfo;
1666 if (offset == 0 && datalen < sizeof(struct udphdr)) {
1667 /* We've been asked to examine this packet, and we
1668 can't. Hence, no choice but to drop. */
1669 duprintf("Dropping evil UDP tinygram.\n");
1674 /* Must not be a fragment. */
1676 && port_match(udpinfo->spts[0], udpinfo->spts[1],
1678 !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
1679 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
1681 !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
1684 /* Called when user tries to insert an entry of this type. */
1686 udp_checkentry(const char *tablename,
1687 const struct ip6t_ip6 *ipv6,
1689 unsigned int matchinfosize,
1690 unsigned int hook_mask)
1692 const struct ip6t_udp *udpinfo = matchinfo;
1694 /* Must specify proto == UDP, and no unknown invflags */
1695 if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
1696 duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
1700 if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
1701 duprintf("ip6t_udp: matchsize %u != %u\n",
1702 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
1705 if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
1706 duprintf("ip6t_udp: unknown flags %X\n",
1714 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1716 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1717 u_int8_t type, u_int8_t code,
1720 return (type == test_type && code >= min_code && code <= max_code)
1725 icmp6_match(const struct sk_buff *skb,
1726 const struct net_device *in,
1727 const struct net_device *out,
1728 const void *matchinfo,
1734 const struct icmp6hdr *icmp = hdr;
1735 const struct ip6t_icmp *icmpinfo = matchinfo;
1737 if (offset == 0 && datalen < 2) {
1738 /* We've been asked to examine this packet, and we
1739 can't. Hence, no choice but to drop. */
1740 duprintf("Dropping evil ICMP tinygram.\n");
1745 /* Must not be a fragment. */
1747 && icmp6_type_code_match(icmpinfo->type,
1750 icmp->icmp6_type, icmp->icmp6_code,
1751 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1754 /* Called when user tries to insert an entry of this type. */
1756 icmp6_checkentry(const char *tablename,
1757 const struct ip6t_ip6 *ipv6,
1759 unsigned int matchsize,
1760 unsigned int hook_mask)
1762 const struct ip6t_icmp *icmpinfo = matchinfo;
1764 /* Must specify proto == ICMP, and no unknown invflags */
1765 return ipv6->proto == IPPROTO_ICMPV6
1766 && !(ipv6->invflags & IP6T_INV_PROTO)
1767 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
1768 && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1771 /* The built-in targets: standard (NULL) and error. */
1772 static struct ip6t_target ip6t_standard_target
1773 = { { NULL, NULL }, IP6T_STANDARD_TARGET, NULL, NULL, NULL };
1774 static struct ip6t_target ip6t_error_target
1775 = { { NULL, NULL }, IP6T_ERROR_TARGET, ip6t_error, NULL, NULL };
1777 static struct nf_sockopt_ops ip6t_sockopts
1778 = { { NULL, NULL }, PF_INET6, IP6T_BASE_CTL, IP6T_SO_SET_MAX+1, do_ip6t_set_ctl,
1779 IP6T_BASE_CTL, IP6T_SO_GET_MAX+1, do_ip6t_get_ctl, 0, NULL };
1781 static struct ip6t_match tcp_matchstruct
1782 = { { NULL, NULL }, "tcp", &tcp_match, &tcp_checkentry, NULL };
1783 static struct ip6t_match udp_matchstruct
1784 = { { NULL, NULL }, "udp", &udp_match, &udp_checkentry, NULL };
1785 static struct ip6t_match icmp6_matchstruct
1786 = { { NULL, NULL }, "icmp6", &icmp6_match, &icmp6_checkentry, NULL };
1788 #ifdef CONFIG_PROC_FS
1789 static inline int print_name(const char *i,
1790 off_t start_offset, char *buffer, int length,
1791 off_t *pos, unsigned int *count)
1793 if ((*count)++ >= start_offset) {
1794 unsigned int namelen;
1796 namelen = sprintf(buffer + *pos, "%s\n",
1797 i + sizeof(struct list_head));
1798 if (*pos + namelen > length) {
1799 /* Stop iterating */
1807 static inline int print_target(const struct ip6t_target *t,
1808 off_t start_offset, char *buffer, int length,
1809 off_t *pos, unsigned int *count)
1811 if (t == &ip6t_standard_target || t == &ip6t_error_target)
1813 return print_name((char *)t, start_offset, buffer, length, pos, count);
1816 static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
1819 unsigned int count = 0;
1821 if (down_interruptible(&ip6t_mutex) != 0)
1824 LIST_FIND(&ip6t_tables, print_name, char *,
1825 offset, buffer, length, &pos, &count);
1829 /* `start' hack - see fs/proc/generic.c line ~105 */
1830 *start=(char *)((unsigned long)count-offset);
1834 static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
1837 unsigned int count = 0;
1839 if (down_interruptible(&ip6t_mutex) != 0)
1842 LIST_FIND(&ip6t_target, print_target, struct ip6t_target *,
1843 offset, buffer, length, &pos, &count);
1847 *start = (char *)((unsigned long)count - offset);
1851 static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
1854 unsigned int count = 0;
1856 if (down_interruptible(&ip6t_mutex) != 0)
1859 LIST_FIND(&ip6t_match, print_name, char *,
1860 offset, buffer, length, &pos, &count);
1864 *start = (char *)((unsigned long)count - offset);
1868 static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
1869 { { "ip6_tables_names", ip6t_get_tables },
1870 { "ip6_tables_targets", ip6t_get_targets },
1871 { "ip6_tables_matches", ip6t_get_matches },
1873 #endif /*CONFIG_PROC_FS*/
1875 static int __init init(void)
1879 /* Noone else will be downing sem now, so we won't sleep */
1881 list_append(&ip6t_target, &ip6t_standard_target);
1882 list_append(&ip6t_target, &ip6t_error_target);
1883 list_append(&ip6t_match, &tcp_matchstruct);
1884 list_append(&ip6t_match, &udp_matchstruct);
1885 list_append(&ip6t_match, &icmp6_matchstruct);
1888 /* Register setsockopt */
1889 ret = nf_register_sockopt(&ip6t_sockopts);
1891 duprintf("Unable to register sockopts.\n");
1895 #ifdef CONFIG_PROC_FS
1897 struct proc_dir_entry *proc;
1900 for (i = 0; ip6t_proc_entry[i].name; i++) {
1901 proc = proc_net_create(ip6t_proc_entry[i].name, 0,
1902 ip6t_proc_entry[i].get_info);
1905 proc_net_remove(ip6t_proc_entry[i].name);
1906 nf_unregister_sockopt(&ip6t_sockopts);
1909 proc->owner = THIS_MODULE;
1914 printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
1918 static void __exit fini(void)
1920 nf_unregister_sockopt(&ip6t_sockopts);
1921 #ifdef CONFIG_PROC_FS
1924 for (i = 0; ip6t_proc_entry[i].name; i++)
1925 proc_net_remove(ip6t_proc_entry[i].name);
1930 EXPORT_SYMBOL(ip6t_register_table);
1931 EXPORT_SYMBOL(ip6t_unregister_table);
1932 EXPORT_SYMBOL(ip6t_do_table);
1933 EXPORT_SYMBOL(ip6t_find_target_lock);
1934 EXPORT_SYMBOL(ip6t_register_match);
1935 EXPORT_SYMBOL(ip6t_unregister_match);
1936 EXPORT_SYMBOL(ip6t_register_target);
1937 EXPORT_SYMBOL(ip6t_unregister_target);
1938 EXPORT_SYMBOL(ip6t_ext_hdr);
1942 MODULE_LICENSE("GPL");