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/lockhelp.h>
75 #include <linux/netfilter_ipv4/listhelp.h>
78 /* All the better to debug you with... */
83 /* Locking is simple: we assume at worst case there will be one packet
84 in user context and one from bottom halves (or soft irq if Alexey's
85 softnet patch was applied).
87 We keep a set of rules for each CPU, so we can avoid write-locking
88 them; doing a readlock_bh() stops packets coming through if we're
91 To be cache friendly on SMP, we arrange them like so:
93 ... cache-align padding ...
96 Hence the start of any table is given by get_table() below. */
98 /* The table itself */
99 struct ip6t_table_info
103 /* Number of entries: FIXME. --RR */
105 /* Initial number of entries. Needed for module usage count */
106 unsigned int initial_entries;
108 /* Entry points and underflows */
109 unsigned int hook_entry[NF_IP6_NUMHOOKS];
110 unsigned int underflow[NF_IP6_NUMHOOKS];
112 /* ip6t_entry tables: one per CPU */
113 char entries[0] ____cacheline_aligned;
116 static LIST_HEAD(ip6t_target);
117 static LIST_HEAD(ip6t_match);
118 static LIST_HEAD(ip6t_tables);
119 #define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
122 #define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
124 #define TABLE_OFFSET(t,p) 0
128 #define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
129 #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; })
130 #define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
133 static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask,
134 struct in6_addr addr2)
137 for( i = 0; i < 16; i++){
138 if((addr1.s6_addr[i] & mask.s6_addr[i]) !=
139 (addr2.s6_addr[i] & mask.s6_addr[i]))
145 /* Check for an extension */
147 ip6t_ext_hdr(u8 nexthdr)
149 return ( (nexthdr == IPPROTO_HOPOPTS) ||
150 (nexthdr == IPPROTO_ROUTING) ||
151 (nexthdr == IPPROTO_FRAGMENT) ||
152 (nexthdr == IPPROTO_ESP) ||
153 (nexthdr == IPPROTO_AH) ||
154 (nexthdr == IPPROTO_NONE) ||
155 (nexthdr == IPPROTO_DSTOPTS) );
158 /* Returns whether matches rule or not. */
160 ip6_packet_match(const struct sk_buff *skb,
163 const struct ip6t_ip6 *ip6info,
164 unsigned int *protoff,
169 const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
171 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
173 if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
175 || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
177 dprintf("Source or dest mismatch.\n");
179 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
180 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
181 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
182 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
183 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
184 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
188 /* Look for ifname matches; this should unroll nicely. */
189 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
190 ret |= (((const unsigned long *)indev)[i]
191 ^ ((const unsigned long *)ip6info->iniface)[i])
192 & ((const unsigned long *)ip6info->iniface_mask)[i];
195 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
196 dprintf("VIA in mismatch (%s vs %s).%s\n",
197 indev, ip6info->iniface,
198 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
202 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
203 ret |= (((const unsigned long *)outdev)[i]
204 ^ ((const unsigned long *)ip6info->outiface)[i])
205 & ((const unsigned long *)ip6info->outiface_mask)[i];
208 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
209 dprintf("VIA out mismatch (%s vs %s).%s\n",
210 outdev, ip6info->outiface,
211 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
215 /* ... might want to do something with class and flowlabel here ... */
217 /* look for the desired protocol header */
218 if((ip6info->flags & IP6T_F_PROTO)) {
219 u_int8_t currenthdr = ipv6->nexthdr;
220 struct ipv6_opt_hdr _hdr, *hp;
221 u_int16_t ptr; /* Header offset in skb */
222 u_int16_t hdrlen; /* Header */
223 u_int16_t _fragoff = 0, *fp = NULL;
227 while (ip6t_ext_hdr(currenthdr)) {
228 /* Is there enough space for the next ext header? */
229 if (skb->len - ptr < IPV6_OPTHDR_LEN)
232 /* NONE or ESP: there isn't protocol part */
233 /* If we want to count these packets in '-p all',
234 * we will change the return 0 to 1*/
235 if ((currenthdr == IPPROTO_NONE) ||
236 (currenthdr == IPPROTO_ESP))
239 hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
242 /* Size calculation */
243 if (currenthdr == IPPROTO_FRAGMENT) {
244 fp = skb_header_pointer(skb,
245 ptr+offsetof(struct frag_hdr,
252 _fragoff = ntohs(*fp) & ~0x7;
254 } else if (currenthdr == IPPROTO_AH)
255 hdrlen = (hp->hdrlen+2)<<2;
257 hdrlen = ipv6_optlen(hp);
259 currenthdr = hp->nexthdr;
261 /* ptr is too large */
262 if ( ptr > skb->len )
265 if (ip6t_ext_hdr(currenthdr))
274 /* currenthdr contains the protocol header */
276 dprintf("Packet protocol %hi ?= %s%hi.\n",
278 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
281 if (ip6info->proto == currenthdr) {
282 if(ip6info->invflags & IP6T_INV_PROTO) {
288 /* We need match for the '-p all', too! */
289 if ((ip6info->proto != 0) &&
290 !(ip6info->invflags & IP6T_INV_PROTO))
296 /* should be ip6 safe */
298 ip6_checkentry(const struct ip6t_ip6 *ipv6)
300 if (ipv6->flags & ~IP6T_F_MASK) {
301 duprintf("Unknown flag bits set: %08X\n",
302 ipv6->flags & ~IP6T_F_MASK);
305 if (ipv6->invflags & ~IP6T_INV_MASK) {
306 duprintf("Unknown invflag bits set: %08X\n",
307 ipv6->invflags & ~IP6T_INV_MASK);
314 ip6t_error(struct sk_buff **pskb,
315 const struct net_device *in,
316 const struct net_device *out,
317 unsigned int hooknum,
318 const void *targinfo,
322 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
328 int do_match(struct ip6t_entry_match *m,
329 const struct sk_buff *skb,
330 const struct net_device *in,
331 const struct net_device *out,
333 unsigned int protoff,
336 /* Stop iteration if it doesn't match */
337 if (!m->u.kernel.match->match(skb, in, out, m->data,
338 offset, protoff, hotdrop))
344 static inline struct ip6t_entry *
345 get_entry(void *base, unsigned int offset)
347 return (struct ip6t_entry *)(base + offset);
350 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
352 ip6t_do_table(struct sk_buff **pskb,
354 const struct net_device *in,
355 const struct net_device *out,
356 struct ip6t_table *table,
359 static const char nulldevname[IFNAMSIZ];
361 unsigned int protoff = 0;
363 /* Initializing verdict to NF_DROP keeps gcc happy. */
364 unsigned int verdict = NF_DROP;
365 const char *indev, *outdev;
367 struct ip6t_entry *e, *back;
370 indev = in ? in->name : nulldevname;
371 outdev = out ? out->name : nulldevname;
373 /* We handle fragments by dealing with the first fragment as
374 * if it was a normal packet. All other fragments are treated
375 * normally, except that they will NEVER match rules that ask
376 * things we don't know, ie. tcp syn flag or ports). If the
377 * rule is also a fragment-specific rule, non-fragments won't
380 read_lock_bh(&table->lock);
381 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
382 table_base = (void *)table->private->entries
383 + TABLE_OFFSET(table->private, smp_processor_id());
384 e = get_entry(table_base, table->private->hook_entry[hook]);
386 #ifdef CONFIG_NETFILTER_DEBUG
387 /* Check noone else using our table */
388 if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
389 && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
390 printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
393 &((struct ip6t_entry *)table_base)->comefrom,
394 ((struct ip6t_entry *)table_base)->comefrom);
396 ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
399 /* For return from builtin chain */
400 back = get_entry(table_base, table->private->underflow[hook]);
405 (*pskb)->nfcache |= e->nfcache;
406 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
407 &protoff, &offset)) {
408 struct ip6t_entry_target *t;
410 if (IP6T_MATCH_ITERATE(e, do_match,
412 offset, protoff, &hotdrop) != 0)
415 ADD_COUNTER(e->counters,
416 ntohs((*pskb)->nh.ipv6h->payload_len)
420 t = ip6t_get_target(e);
421 IP_NF_ASSERT(t->u.kernel.target);
422 /* Standard target? */
423 if (!t->u.kernel.target->target) {
426 v = ((struct ip6t_standard_target *)t)->verdict;
428 /* Pop from stack? */
429 if (v != IP6T_RETURN) {
430 verdict = (unsigned)(-v) - 1;
434 back = get_entry(table_base,
439 != (void *)e + e->next_offset) {
440 /* Save old back ptr in next entry */
441 struct ip6t_entry *next
442 = (void *)e + e->next_offset;
444 = (void *)back - table_base;
445 /* set back pointer to next entry */
449 e = get_entry(table_base, v);
451 /* Targets which reenter must return
453 #ifdef CONFIG_NETFILTER_DEBUG
454 ((struct ip6t_entry *)table_base)->comefrom
457 verdict = t->u.kernel.target->target(pskb,
463 #ifdef CONFIG_NETFILTER_DEBUG
464 if (((struct ip6t_entry *)table_base)->comefrom
466 && verdict == IP6T_CONTINUE) {
467 printk("Target %s reentered!\n",
468 t->u.kernel.target->name);
471 ((struct ip6t_entry *)table_base)->comefrom
474 if (verdict == IP6T_CONTINUE)
475 e = (void *)e + e->next_offset;
483 e = (void *)e + e->next_offset;
487 #ifdef CONFIG_NETFILTER_DEBUG
488 ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
490 read_unlock_bh(&table->lock);
492 #ifdef DEBUG_ALLOW_ALL
501 /* If it succeeds, returns element and locks mutex */
503 find_inlist_lock_noload(struct list_head *head,
506 struct semaphore *mutex)
511 duprintf("find_inlist: searching for `%s' in %s.\n",
512 name, head == &ip6t_target ? "ip6t_target"
513 : head == &ip6t_match ? "ip6t_match"
514 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
517 *error = down_interruptible(mutex);
521 ret = list_named_find(head, name);
530 #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
533 find_inlist_lock(struct list_head *head,
537 struct semaphore *mutex)
541 ret = find_inlist_lock_noload(head, name, error, mutex);
543 duprintf("find_inlist: loading `%s%s'.\n", prefix, name);
544 request_module("%s%s", prefix, name);
545 ret = find_inlist_lock_noload(head, name, error, mutex);
552 static inline struct ip6t_table *
553 ip6t_find_table_lock(const char *name, int *error, struct semaphore *mutex)
555 return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
558 static inline struct ip6t_match *
559 find_match_lock(const char *name, int *error, struct semaphore *mutex)
561 return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
564 static struct ip6t_target *
565 ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex)
567 return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
570 /* All zeroes == unconditional rule. */
572 unconditional(const struct ip6t_ip6 *ipv6)
576 for (i = 0; i < sizeof(*ipv6); i++)
577 if (((char *)ipv6)[i])
580 return (i == sizeof(*ipv6));
583 /* Figures out from what hook each rule can be called: returns 0 if
584 there are loops. Puts hook bitmask in comefrom. */
586 mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
590 /* No recursion; use packet counter to save back ptrs (reset
591 to 0 as we leave), and comefrom to save source hook bitmask */
592 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
593 unsigned int pos = newinfo->hook_entry[hook];
595 = (struct ip6t_entry *)(newinfo->entries + pos);
597 if (!(valid_hooks & (1 << hook)))
600 /* Set initial back pointer. */
601 e->counters.pcnt = pos;
604 struct ip6t_standard_target *t
605 = (void *)ip6t_get_target(e);
607 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
608 printk("iptables: loop hook %u pos %u %08X.\n",
609 hook, pos, e->comefrom);
613 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
615 /* Unconditional return/END. */
616 if (e->target_offset == sizeof(struct ip6t_entry)
617 && (strcmp(t->target.u.user.name,
618 IP6T_STANDARD_TARGET) == 0)
620 && unconditional(&e->ipv6)) {
621 unsigned int oldpos, size;
623 /* Return: backtrack through the last
626 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
627 #ifdef DEBUG_IP_FIREWALL_USER
629 & (1 << NF_IP6_NUMHOOKS)) {
630 duprintf("Back unset "
637 pos = e->counters.pcnt;
638 e->counters.pcnt = 0;
640 /* We're at the start. */
644 e = (struct ip6t_entry *)
645 (newinfo->entries + pos);
646 } while (oldpos == pos + e->next_offset);
649 size = e->next_offset;
650 e = (struct ip6t_entry *)
651 (newinfo->entries + pos + size);
652 e->counters.pcnt = pos;
655 int newpos = t->verdict;
657 if (strcmp(t->target.u.user.name,
658 IP6T_STANDARD_TARGET) == 0
660 /* This a jump; chase it. */
661 duprintf("Jump rule %u -> %u\n",
664 /* ... this is a fallthru */
665 newpos = pos + e->next_offset;
667 e = (struct ip6t_entry *)
668 (newinfo->entries + newpos);
669 e->counters.pcnt = pos;
674 duprintf("Finished chain %u\n", hook);
680 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
682 if (i && (*i)-- == 0)
685 if (m->u.kernel.match->destroy)
686 m->u.kernel.match->destroy(m->data,
687 m->u.match_size - sizeof(*m));
688 module_put(m->u.kernel.match->me);
693 standard_check(const struct ip6t_entry_target *t,
694 unsigned int max_offset)
696 struct ip6t_standard_target *targ = (void *)t;
698 /* Check standard info. */
700 != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
701 duprintf("standard_check: target size %u != %u\n",
703 IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
707 if (targ->verdict >= 0
708 && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
709 duprintf("ip6t_standard_check: bad verdict (%i)\n",
714 if (targ->verdict < -NF_MAX_VERDICT - 1) {
715 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
723 check_match(struct ip6t_entry_match *m,
725 const struct ip6t_ip6 *ipv6,
726 unsigned int hookmask,
730 struct ip6t_match *match;
732 match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
734 // duprintf("check_match: `%s' not found\n", m->u.name);
737 if (!try_module_get(match->me)) {
741 m->u.kernel.match = match;
744 if (m->u.kernel.match->checkentry
745 && !m->u.kernel.match->checkentry(name, ipv6, m->data,
746 m->u.match_size - sizeof(*m),
748 module_put(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;
785 if (!try_module_get(target->me)) {
788 goto cleanup_matches;
790 t->u.kernel.target = target;
792 if (!t->u.kernel.target) {
794 goto cleanup_matches;
796 if (t->u.kernel.target == &ip6t_standard_target) {
797 if (!standard_check(t, size)) {
799 goto cleanup_matches;
801 } else if (t->u.kernel.target->checkentry
802 && !t->u.kernel.target->checkentry(name, e, t->data,
806 module_put(t->u.kernel.target->me);
807 duprintf("ip_tables: check failed for `%s'.\n",
808 t->u.kernel.target->name);
810 goto cleanup_matches;
817 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
822 check_entry_size_and_hooks(struct ip6t_entry *e,
823 struct ip6t_table_info *newinfo,
825 unsigned char *limit,
826 const unsigned int *hook_entries,
827 const unsigned int *underflows,
832 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
833 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
834 duprintf("Bad offset %p\n", e);
839 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
840 duprintf("checking: element %p size %u\n",
845 /* Check hooks & underflows */
846 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
847 if ((unsigned char *)e - base == hook_entries[h])
848 newinfo->hook_entry[h] = hook_entries[h];
849 if ((unsigned char *)e - base == underflows[h])
850 newinfo->underflow[h] = underflows[h];
853 /* FIXME: underflows must be unconditional, standard verdicts
854 < 0 (not IP6T_RETURN). --RR */
856 /* Clear counters and comefrom */
857 e->counters = ((struct ip6t_counters) { 0, 0 });
865 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
867 struct ip6t_entry_target *t;
869 if (i && (*i)-- == 0)
872 /* Cleanup all matches */
873 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
874 t = ip6t_get_target(e);
875 if (t->u.kernel.target->destroy)
876 t->u.kernel.target->destroy(t->data,
877 t->u.target_size - sizeof(*t));
878 module_put(t->u.kernel.target->me);
882 /* Checks and translates the user-supplied table segment (held in
885 translate_table(const char *name,
886 unsigned int valid_hooks,
887 struct ip6t_table_info *newinfo,
890 const unsigned int *hook_entries,
891 const unsigned int *underflows)
896 newinfo->size = size;
897 newinfo->number = number;
899 /* Init all hooks to impossible value. */
900 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
901 newinfo->hook_entry[i] = 0xFFFFFFFF;
902 newinfo->underflow[i] = 0xFFFFFFFF;
905 duprintf("translate_table: size %u\n", newinfo->size);
907 /* Walk through entries, checking offsets. */
908 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
909 check_entry_size_and_hooks,
912 newinfo->entries + size,
913 hook_entries, underflows, &i);
918 duprintf("translate_table: %u not %u entries\n",
923 /* Check hooks all assigned */
924 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
925 /* Only hooks which are valid */
926 if (!(valid_hooks & (1 << i)))
928 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
929 duprintf("Invalid hook entry %u %u\n",
933 if (newinfo->underflow[i] == 0xFFFFFFFF) {
934 duprintf("Invalid underflow %u %u\n",
940 if (!mark_source_chains(newinfo, valid_hooks))
943 /* Finally, each sanity check must pass */
945 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
946 check_entry, name, size, &i);
949 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
954 /* And one copy for every other CPU */
955 for (i = 1; i < num_possible_cpus(); i++) {
956 memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
958 SMP_ALIGN(newinfo->size));
964 static struct ip6t_table_info *
965 replace_table(struct ip6t_table *table,
966 unsigned int num_counters,
967 struct ip6t_table_info *newinfo,
970 struct ip6t_table_info *oldinfo;
972 #ifdef CONFIG_NETFILTER_DEBUG
974 struct ip6t_entry *table_base;
977 for (i = 0; i < num_possible_cpus(); i++) {
979 (void *)newinfo->entries
980 + TABLE_OFFSET(newinfo, i);
982 table_base->comefrom = 0xdead57ac;
987 /* Do the substitution. */
988 write_lock_bh(&table->lock);
989 /* Check inside lock: is the old number correct? */
990 if (num_counters != table->private->number) {
991 duprintf("num_counters != table->private->number (%u/%u)\n",
992 num_counters, table->private->number);
993 write_unlock_bh(&table->lock);
997 oldinfo = table->private;
998 table->private = newinfo;
999 newinfo->initial_entries = oldinfo->initial_entries;
1000 write_unlock_bh(&table->lock);
1005 /* Gets counters. */
1007 add_entry_to_counter(const struct ip6t_entry *e,
1008 struct ip6t_counters total[],
1011 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
1018 get_counters(const struct ip6t_table_info *t,
1019 struct ip6t_counters counters[])
1024 for (cpu = 0; cpu < num_possible_cpus(); cpu++) {
1026 IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
1028 add_entry_to_counter,
1035 copy_entries_to_user(unsigned int total_size,
1036 struct ip6t_table *table,
1037 void __user *userptr)
1039 unsigned int off, num, countersize;
1040 struct ip6t_entry *e;
1041 struct ip6t_counters *counters;
1044 /* We need atomic snapshot of counters: rest doesn't change
1045 (other than comefrom, which userspace doesn't care
1047 countersize = sizeof(struct ip6t_counters) * table->private->number;
1048 counters = vmalloc(countersize);
1050 if (counters == NULL)
1053 /* First, sum counters... */
1054 memset(counters, 0, countersize);
1055 write_lock_bh(&table->lock);
1056 get_counters(table->private, counters);
1057 write_unlock_bh(&table->lock);
1059 /* ... then copy entire thing from CPU 0... */
1060 if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
1065 /* FIXME: use iterator macros --RR */
1066 /* ... then go back and fix counters and names */
1067 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1069 struct ip6t_entry_match *m;
1070 struct ip6t_entry_target *t;
1072 e = (struct ip6t_entry *)(table->private->entries + off);
1073 if (copy_to_user(userptr + off
1074 + offsetof(struct ip6t_entry, counters),
1076 sizeof(counters[num])) != 0) {
1081 for (i = sizeof(struct ip6t_entry);
1082 i < e->target_offset;
1083 i += m->u.match_size) {
1086 if (copy_to_user(userptr + off + i
1087 + offsetof(struct ip6t_entry_match,
1089 m->u.kernel.match->name,
1090 strlen(m->u.kernel.match->name)+1)
1097 t = ip6t_get_target(e);
1098 if (copy_to_user(userptr + off + e->target_offset
1099 + offsetof(struct ip6t_entry_target,
1101 t->u.kernel.target->name,
1102 strlen(t->u.kernel.target->name)+1) != 0) {
1114 get_entries(const struct ip6t_get_entries *entries,
1115 struct ip6t_get_entries __user *uptr)
1118 struct ip6t_table *t;
1120 t = ip6t_find_table_lock(entries->name, &ret, &ip6t_mutex);
1122 duprintf("t->private->number = %u\n",
1123 t->private->number);
1124 if (entries->size == t->private->size)
1125 ret = copy_entries_to_user(t->private->size,
1126 t, uptr->entrytable);
1128 duprintf("get_entries: I've got %u not %u!\n",
1135 duprintf("get_entries: Can't find %s!\n",
1142 do_replace(void __user *user, unsigned int len)
1145 struct ip6t_replace tmp;
1146 struct ip6t_table *t;
1147 struct ip6t_table_info *newinfo, *oldinfo;
1148 struct ip6t_counters *counters;
1150 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1153 /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
1154 if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
1157 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1158 + SMP_ALIGN(tmp.size) * num_possible_cpus());
1162 if (copy_from_user(newinfo->entries, user + sizeof(tmp),
1168 counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
1173 memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
1175 ret = translate_table(tmp.name, tmp.valid_hooks,
1176 newinfo, tmp.size, tmp.num_entries,
1177 tmp.hook_entry, tmp.underflow);
1179 goto free_newinfo_counters;
1181 duprintf("ip_tables: Translated table\n");
1183 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1185 goto free_newinfo_counters_untrans;
1188 if (tmp.valid_hooks != t->valid_hooks) {
1189 duprintf("Valid hook crap: %08X vs %08X\n",
1190 tmp.valid_hooks, t->valid_hooks);
1192 goto free_newinfo_counters_untrans_unlock;
1195 /* Get a reference in advance, we're not allowed fail later */
1196 if (!try_module_get(t->me)) {
1198 goto free_newinfo_counters_untrans_unlock;
1201 oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
1205 /* Update module usage count based on number of rules */
1206 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1207 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1208 if ((oldinfo->number > oldinfo->initial_entries) ||
1209 (newinfo->number <= oldinfo->initial_entries))
1211 if ((oldinfo->number > oldinfo->initial_entries) &&
1212 (newinfo->number <= oldinfo->initial_entries))
1215 /* Get the old counters. */
1216 get_counters(oldinfo, counters);
1217 /* Decrease module usage counts and free resource */
1218 IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
1220 /* Silent error: too late now. */
1221 if (copy_to_user(tmp.counters, counters,
1222 sizeof(struct ip6t_counters) * tmp.num_counters) != 0)
1230 free_newinfo_counters_untrans_unlock:
1232 free_newinfo_counters_untrans:
1233 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
1234 free_newinfo_counters:
1241 /* We're lazy, and add to the first CPU; overflow works its fey magic
1242 * and everything is OK. */
1244 add_counter_to_entry(struct ip6t_entry *e,
1245 const struct ip6t_counters addme[],
1249 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1251 (long unsigned int)e->counters.pcnt,
1252 (long unsigned int)e->counters.bcnt,
1253 (long unsigned int)addme[*i].pcnt,
1254 (long unsigned int)addme[*i].bcnt);
1257 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1264 do_add_counters(void __user *user, unsigned int len)
1267 struct ip6t_counters_info tmp, *paddc;
1268 struct ip6t_table *t;
1271 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1274 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
1277 paddc = vmalloc(len);
1281 if (copy_from_user(paddc, user, len) != 0) {
1286 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1290 write_lock_bh(&t->lock);
1291 if (t->private->number != paddc->num_counters) {
1293 goto unlock_up_free;
1297 IP6T_ENTRY_ITERATE(t->private->entries,
1299 add_counter_to_entry,
1303 write_unlock_bh(&t->lock);
1312 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1316 if (!capable(CAP_NET_ADMIN))
1320 case IP6T_SO_SET_REPLACE:
1321 ret = do_replace(user, len);
1324 case IP6T_SO_SET_ADD_COUNTERS:
1325 ret = do_add_counters(user, len);
1329 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1337 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1341 if (!capable(CAP_NET_ADMIN))
1345 case IP6T_SO_GET_INFO: {
1346 char name[IP6T_TABLE_MAXNAMELEN];
1347 struct ip6t_table *t;
1349 if (*len != sizeof(struct ip6t_getinfo)) {
1350 duprintf("length %u != %u\n", *len,
1351 sizeof(struct ip6t_getinfo));
1356 if (copy_from_user(name, user, sizeof(name)) != 0) {
1360 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1361 t = ip6t_find_table_lock(name, &ret, &ip6t_mutex);
1363 struct ip6t_getinfo info;
1365 info.valid_hooks = t->valid_hooks;
1366 memcpy(info.hook_entry, t->private->hook_entry,
1367 sizeof(info.hook_entry));
1368 memcpy(info.underflow, t->private->underflow,
1369 sizeof(info.underflow));
1370 info.num_entries = t->private->number;
1371 info.size = t->private->size;
1372 memcpy(info.name, name, sizeof(info.name));
1374 if (copy_to_user(user, &info, *len) != 0)
1384 case IP6T_SO_GET_ENTRIES: {
1385 struct ip6t_get_entries get;
1387 if (*len < sizeof(get)) {
1388 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1390 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1392 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1393 duprintf("get_entries: %u != %u\n", *len,
1394 sizeof(struct ip6t_get_entries) + get.size);
1397 ret = get_entries(&get, user);
1402 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1409 /* Registration hooks for targets. */
1411 ip6t_register_target(struct ip6t_target *target)
1415 ret = down_interruptible(&ip6t_mutex);
1419 if (!list_named_insert(&ip6t_target, target)) {
1420 duprintf("ip6t_register_target: `%s' already in list!\n",
1429 ip6t_unregister_target(struct ip6t_target *target)
1432 LIST_DELETE(&ip6t_target, target);
1437 ip6t_register_match(struct ip6t_match *match)
1441 ret = down_interruptible(&ip6t_mutex);
1445 if (!list_named_insert(&ip6t_match, match)) {
1446 duprintf("ip6t_register_match: `%s' already in list!\n",
1456 ip6t_unregister_match(struct ip6t_match *match)
1459 LIST_DELETE(&ip6t_match, match);
1463 int ip6t_register_table(struct ip6t_table *table,
1464 const struct ip6t_replace *repl)
1467 struct ip6t_table_info *newinfo;
1468 static struct ip6t_table_info bootstrap
1469 = { 0, 0, 0, { 0 }, { 0 }, { } };
1471 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1472 + SMP_ALIGN(repl->size) * num_possible_cpus());
1476 memcpy(newinfo->entries, repl->entries, repl->size);
1478 ret = translate_table(table->name, table->valid_hooks,
1479 newinfo, repl->size,
1488 ret = down_interruptible(&ip6t_mutex);
1494 /* Don't autoload: we'd eat our tail... */
1495 if (list_named_find(&ip6t_tables, table->name)) {
1500 /* Simplifies replace_table code. */
1501 table->private = &bootstrap;
1502 if (!replace_table(table, 0, newinfo, &ret))
1505 duprintf("table->private->number = %u\n",
1506 table->private->number);
1508 /* save number of initial entries */
1509 table->private->initial_entries = table->private->number;
1511 rwlock_init(&table->lock);
1512 list_prepend(&ip6t_tables, table);
1523 void ip6t_unregister_table(struct ip6t_table *table)
1526 LIST_DELETE(&ip6t_tables, table);
1529 /* Decrease module usage counts and free resources */
1530 IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
1531 cleanup_entry, NULL);
1532 vfree(table->private);
1535 /* Returns 1 if the port is matched by the range, 0 otherwise */
1537 port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
1541 ret = (port >= min && port <= max) ^ invert;
1546 tcp_find_option(u_int8_t option,
1547 const struct sk_buff *skb,
1548 unsigned int tcpoff,
1549 unsigned int optlen,
1553 /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
1554 u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
1557 duprintf("tcp_match: finding option\n");
1560 /* If we don't have the whole header, drop packet. */
1561 op = skb_header_pointer(skb, tcpoff + sizeof(struct tcphdr), optlen,
1568 for (i = 0; i < optlen; ) {
1569 if (op[i] == option) return !invert;
1571 else i += op[i+1]?:1;
1578 tcp_match(const struct sk_buff *skb,
1579 const struct net_device *in,
1580 const struct net_device *out,
1581 const void *matchinfo,
1583 unsigned int protoff,
1586 struct tcphdr _tcph, *th;
1587 const struct ip6t_tcp *tcpinfo = matchinfo;
1592 Don't allow a fragment of TCP 8 bytes in. Nobody normal
1593 causes this. Its a cracker trying to break in by doing a
1594 flag overwrite to pass the direction checks.
1597 duprintf("Dropping evil TCP offset=1 frag.\n");
1600 /* Must not be a fragment. */
1604 #define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
1606 th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
1608 /* We've been asked to examine this packet, and we
1609 can't. Hence, no choice but to drop. */
1610 duprintf("Dropping evil TCP offset=0 tinygram.\n");
1615 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
1617 !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT)))
1619 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
1621 !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT)))
1623 if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
1624 == tcpinfo->flg_cmp,
1625 IP6T_TCP_INV_FLAGS))
1627 if (tcpinfo->option) {
1628 if (th->doff * 4 < sizeof(_tcph)) {
1632 if (!tcp_find_option(tcpinfo->option, skb, protoff,
1633 th->doff*4 - sizeof(*th),
1634 tcpinfo->invflags & IP6T_TCP_INV_OPTION,
1641 /* Called when user tries to insert an entry of this type. */
1643 tcp_checkentry(const char *tablename,
1644 const struct ip6t_ip6 *ipv6,
1646 unsigned int matchsize,
1647 unsigned int hook_mask)
1649 const struct ip6t_tcp *tcpinfo = matchinfo;
1651 /* Must specify proto == TCP, and no unknown invflags */
1652 return ipv6->proto == IPPROTO_TCP
1653 && !(ipv6->invflags & IP6T_INV_PROTO)
1654 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
1655 && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
1659 udp_match(const struct sk_buff *skb,
1660 const struct net_device *in,
1661 const struct net_device *out,
1662 const void *matchinfo,
1664 unsigned int protoff,
1667 struct udphdr _udph, *uh;
1668 const struct ip6t_udp *udpinfo = matchinfo;
1670 /* Must not be a fragment. */
1674 uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
1676 /* We've been asked to examine this packet, and we
1677 can't. Hence, no choice but to drop. */
1678 duprintf("Dropping evil UDP tinygram.\n");
1683 return port_match(udpinfo->spts[0], udpinfo->spts[1],
1685 !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
1686 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
1688 !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
1691 /* Called when user tries to insert an entry of this type. */
1693 udp_checkentry(const char *tablename,
1694 const struct ip6t_ip6 *ipv6,
1696 unsigned int matchinfosize,
1697 unsigned int hook_mask)
1699 const struct ip6t_udp *udpinfo = matchinfo;
1701 /* Must specify proto == UDP, and no unknown invflags */
1702 if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
1703 duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
1707 if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
1708 duprintf("ip6t_udp: matchsize %u != %u\n",
1709 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
1712 if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
1713 duprintf("ip6t_udp: unknown flags %X\n",
1721 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1723 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1724 u_int8_t type, u_int8_t code,
1727 return (type == test_type && code >= min_code && code <= max_code)
1732 icmp6_match(const struct sk_buff *skb,
1733 const struct net_device *in,
1734 const struct net_device *out,
1735 const void *matchinfo,
1737 unsigned int protoff,
1740 struct icmp6hdr _icmp, *ic;
1741 const struct ip6t_icmp *icmpinfo = matchinfo;
1743 /* Must not be a fragment. */
1747 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1749 /* We've been asked to examine this packet, and we
1750 can't. Hence, no choice but to drop. */
1751 duprintf("Dropping evil ICMP tinygram.\n");
1756 return icmp6_type_code_match(icmpinfo->type,
1759 ic->icmp6_type, ic->icmp6_code,
1760 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1763 /* Called when user tries to insert an entry of this type. */
1765 icmp6_checkentry(const char *tablename,
1766 const struct ip6t_ip6 *ipv6,
1768 unsigned int matchsize,
1769 unsigned int hook_mask)
1771 const struct ip6t_icmp *icmpinfo = matchinfo;
1773 /* Must specify proto == ICMP, and no unknown invflags */
1774 return ipv6->proto == IPPROTO_ICMPV6
1775 && !(ipv6->invflags & IP6T_INV_PROTO)
1776 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
1777 && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1780 /* The built-in targets: standard (NULL) and error. */
1781 static struct ip6t_target ip6t_standard_target = {
1782 .name = IP6T_STANDARD_TARGET,
1785 static struct ip6t_target ip6t_error_target = {
1786 .name = IP6T_ERROR_TARGET,
1787 .target = ip6t_error,
1790 static struct nf_sockopt_ops ip6t_sockopts = {
1792 .set_optmin = IP6T_BASE_CTL,
1793 .set_optmax = IP6T_SO_SET_MAX+1,
1794 .set = do_ip6t_set_ctl,
1795 .get_optmin = IP6T_BASE_CTL,
1796 .get_optmax = IP6T_SO_GET_MAX+1,
1797 .get = do_ip6t_get_ctl,
1800 static struct ip6t_match tcp_matchstruct = {
1802 .match = &tcp_match,
1803 .checkentry = &tcp_checkentry,
1806 static struct ip6t_match udp_matchstruct = {
1808 .match = &udp_match,
1809 .checkentry = &udp_checkentry,
1812 static struct ip6t_match icmp6_matchstruct = {
1814 .match = &icmp6_match,
1815 .checkentry = &icmp6_checkentry,
1818 #ifdef CONFIG_PROC_FS
1819 static inline int print_name(const char *i,
1820 off_t start_offset, char *buffer, int length,
1821 off_t *pos, unsigned int *count)
1823 if ((*count)++ >= start_offset) {
1824 unsigned int namelen;
1826 namelen = sprintf(buffer + *pos, "%s\n",
1827 i + sizeof(struct list_head));
1828 if (*pos + namelen > length) {
1829 /* Stop iterating */
1837 static inline int print_target(const struct ip6t_target *t,
1838 off_t start_offset, char *buffer, int length,
1839 off_t *pos, unsigned int *count)
1841 if (t == &ip6t_standard_target || t == &ip6t_error_target)
1843 return print_name((char *)t, start_offset, buffer, length, pos, count);
1846 static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
1849 unsigned int count = 0;
1851 if (down_interruptible(&ip6t_mutex) != 0)
1854 LIST_FIND(&ip6t_tables, print_name, char *,
1855 offset, buffer, length, &pos, &count);
1859 /* `start' hack - see fs/proc/generic.c line ~105 */
1860 *start=(char *)((unsigned long)count-offset);
1864 static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
1867 unsigned int count = 0;
1869 if (down_interruptible(&ip6t_mutex) != 0)
1872 LIST_FIND(&ip6t_target, print_target, struct ip6t_target *,
1873 offset, buffer, length, &pos, &count);
1877 *start = (char *)((unsigned long)count - offset);
1881 static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
1884 unsigned int count = 0;
1886 if (down_interruptible(&ip6t_mutex) != 0)
1889 LIST_FIND(&ip6t_match, print_name, char *,
1890 offset, buffer, length, &pos, &count);
1894 *start = (char *)((unsigned long)count - offset);
1898 static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
1899 { { "ip6_tables_names", ip6t_get_tables },
1900 { "ip6_tables_targets", ip6t_get_targets },
1901 { "ip6_tables_matches", ip6t_get_matches },
1903 #endif /*CONFIG_PROC_FS*/
1905 static int __init init(void)
1909 /* Noone else will be downing sem now, so we won't sleep */
1911 list_append(&ip6t_target, &ip6t_standard_target);
1912 list_append(&ip6t_target, &ip6t_error_target);
1913 list_append(&ip6t_match, &tcp_matchstruct);
1914 list_append(&ip6t_match, &udp_matchstruct);
1915 list_append(&ip6t_match, &icmp6_matchstruct);
1918 /* Register setsockopt */
1919 ret = nf_register_sockopt(&ip6t_sockopts);
1921 duprintf("Unable to register sockopts.\n");
1925 #ifdef CONFIG_PROC_FS
1927 struct proc_dir_entry *proc;
1930 for (i = 0; ip6t_proc_entry[i].name; i++) {
1931 proc = proc_net_create(ip6t_proc_entry[i].name, 0,
1932 ip6t_proc_entry[i].get_info);
1935 proc_net_remove(ip6t_proc_entry[i].name);
1936 nf_unregister_sockopt(&ip6t_sockopts);
1939 proc->owner = THIS_MODULE;
1944 printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
1948 static void __exit fini(void)
1950 nf_unregister_sockopt(&ip6t_sockopts);
1951 #ifdef CONFIG_PROC_FS
1954 for (i = 0; ip6t_proc_entry[i].name; i++)
1955 proc_net_remove(ip6t_proc_entry[i].name);
1960 EXPORT_SYMBOL(ip6t_register_table);
1961 EXPORT_SYMBOL(ip6t_unregister_table);
1962 EXPORT_SYMBOL(ip6t_do_table);
1963 EXPORT_SYMBOL(ip6t_register_match);
1964 EXPORT_SYMBOL(ip6t_unregister_match);
1965 EXPORT_SYMBOL(ip6t_register_target);
1966 EXPORT_SYMBOL(ip6t_unregister_target);
1967 EXPORT_SYMBOL(ip6t_ext_hdr);