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))
71 #define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
72 #define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
73 #include <linux/netfilter_ipv4/lockhelp.h>
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,
160 const struct ipv6hdr *ipv6,
163 const struct ip6t_ip6 *ip6info,
169 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
171 if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
173 || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
175 dprintf("Source or dest mismatch.\n");
177 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
178 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
179 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
180 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
181 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
182 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
186 /* Look for ifname matches; this should unroll nicely. */
187 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
188 ret |= (((const unsigned long *)indev)[i]
189 ^ ((const unsigned long *)ip6info->iniface)[i])
190 & ((const unsigned long *)ip6info->iniface_mask)[i];
193 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
194 dprintf("VIA in mismatch (%s vs %s).%s\n",
195 indev, ip6info->iniface,
196 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
200 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
201 ret |= (((const unsigned long *)outdev)[i]
202 ^ ((const unsigned long *)ip6info->outiface)[i])
203 & ((const unsigned long *)ip6info->outiface_mask)[i];
206 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
207 dprintf("VIA out mismatch (%s vs %s).%s\n",
208 outdev, ip6info->outiface,
209 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
213 /* ... might want to do something with class and flowlabel here ... */
215 /* look for the desired protocol header */
216 if((ip6info->flags & IP6T_F_PROTO)) {
217 u_int8_t currenthdr = ipv6->nexthdr;
218 struct ipv6_opt_hdr *hdrptr;
219 u_int16_t ptr; /* Header offset in skb */
220 u_int16_t hdrlen; /* Header */
224 while (ip6t_ext_hdr(currenthdr)) {
225 /* Is there enough space for the next ext header? */
226 if (skb->len - ptr < IPV6_OPTHDR_LEN)
229 /* NONE or ESP: there isn't protocol part */
230 /* If we want to count these packets in '-p all',
231 * we will change the return 0 to 1*/
232 if ((currenthdr == IPPROTO_NONE) ||
233 (currenthdr == IPPROTO_ESP))
236 hdrptr = (struct ipv6_opt_hdr *)(skb->data + ptr);
238 /* Size calculation */
239 if (currenthdr == IPPROTO_FRAGMENT) {
241 } else if (currenthdr == IPPROTO_AH)
242 hdrlen = (hdrptr->hdrlen+2)<<2;
244 hdrlen = ipv6_optlen(hdrptr);
246 currenthdr = hdrptr->nexthdr;
248 /* ptr is too large */
249 if ( ptr > skb->len )
253 /* currenthdr contains the protocol header */
255 dprintf("Packet protocol %hi ?= %s%hi.\n",
257 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
260 if (ip6info->proto == currenthdr) {
261 if(ip6info->invflags & IP6T_INV_PROTO) {
267 /* We need match for the '-p all', too! */
268 if ((ip6info->proto != 0) &&
269 !(ip6info->invflags & IP6T_INV_PROTO))
275 /* should be ip6 safe */
277 ip6_checkentry(const struct ip6t_ip6 *ipv6)
279 if (ipv6->flags & ~IP6T_F_MASK) {
280 duprintf("Unknown flag bits set: %08X\n",
281 ipv6->flags & ~IP6T_F_MASK);
284 if (ipv6->invflags & ~IP6T_INV_MASK) {
285 duprintf("Unknown invflag bits set: %08X\n",
286 ipv6->invflags & ~IP6T_INV_MASK);
293 ip6t_error(struct sk_buff **pskb,
294 unsigned int hooknum,
295 const struct net_device *in,
296 const struct net_device *out,
297 const void *targinfo,
301 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
307 int do_match(struct ip6t_entry_match *m,
308 const struct sk_buff *skb,
309 const struct net_device *in,
310 const struct net_device *out,
316 /* Stop iteration if it doesn't match */
317 if (!m->u.kernel.match->match(skb, in, out, m->data,
318 offset, hdr, datalen, hotdrop))
324 static inline struct ip6t_entry *
325 get_entry(void *base, unsigned int offset)
327 return (struct ip6t_entry *)(base + offset);
330 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
332 ip6t_do_table(struct sk_buff **pskb,
334 const struct net_device *in,
335 const struct net_device *out,
336 struct ip6t_table *table,
339 static const char nulldevname[IFNAMSIZ];
340 u_int16_t offset = 0;
341 struct ipv6hdr *ipv6;
345 /* Initializing verdict to NF_DROP keeps gcc happy. */
346 unsigned int verdict = NF_DROP;
347 const char *indev, *outdev;
349 struct ip6t_entry *e, *back;
351 /* FIXME: Push down to extensions --RR */
352 if (skb_is_nonlinear(*pskb) && skb_linearize(*pskb, GFP_ATOMIC) != 0)
356 ipv6 = (*pskb)->nh.ipv6h;
357 protohdr = (u_int32_t *)((char *)ipv6 + IPV6_HDR_LEN);
358 datalen = (*pskb)->len - IPV6_HDR_LEN;
359 indev = in ? in->name : nulldevname;
360 outdev = out ? out->name : nulldevname;
362 /* We handle fragments by dealing with the first fragment as
363 * if it was a normal packet. All other fragments are treated
364 * normally, except that they will NEVER match rules that ask
365 * things we don't know, ie. tcp syn flag or ports). If the
366 * rule is also a fragment-specific rule, non-fragments won't
369 read_lock_bh(&table->lock);
370 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
371 table_base = (void *)table->private->entries
372 + TABLE_OFFSET(table->private, smp_processor_id());
373 e = get_entry(table_base, table->private->hook_entry[hook]);
375 #ifdef CONFIG_NETFILTER_DEBUG
376 /* Check noone else using our table */
377 if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
378 && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
379 printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
382 &((struct ip6t_entry *)table_base)->comefrom,
383 ((struct ip6t_entry *)table_base)->comefrom);
385 ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
388 /* For return from builtin chain */
389 back = get_entry(table_base, table->private->underflow[hook]);
394 (*pskb)->nfcache |= e->nfcache;
395 if (ip6_packet_match(*pskb, ipv6, indev, outdev,
397 struct ip6t_entry_target *t;
399 if (IP6T_MATCH_ITERATE(e, do_match,
402 datalen, &hotdrop) != 0)
405 ADD_COUNTER(e->counters, ntohs(ipv6->payload_len) + IPV6_HDR_LEN, 1);
407 t = ip6t_get_target(e);
408 IP_NF_ASSERT(t->u.kernel.target);
409 /* Standard target? */
410 if (!t->u.kernel.target->target) {
413 v = ((struct ip6t_standard_target *)t)->verdict;
415 /* Pop from stack? */
416 if (v != IP6T_RETURN) {
417 verdict = (unsigned)(-v) - 1;
421 back = get_entry(table_base,
426 != (void *)e + e->next_offset) {
427 /* Save old back ptr in next entry */
428 struct ip6t_entry *next
429 = (void *)e + e->next_offset;
431 = (void *)back - table_base;
432 /* set back pointer to next entry */
436 e = get_entry(table_base, v);
438 /* Targets which reenter must return
440 #ifdef CONFIG_NETFILTER_DEBUG
441 ((struct ip6t_entry *)table_base)->comefrom
444 verdict = t->u.kernel.target->target(pskb,
450 #ifdef CONFIG_NETFILTER_DEBUG
451 if (((struct ip6t_entry *)table_base)->comefrom
453 && verdict == IP6T_CONTINUE) {
454 printk("Target %s reentered!\n",
455 t->u.kernel.target->name);
458 ((struct ip6t_entry *)table_base)->comefrom
461 /* Target might have changed stuff. */
462 ipv6 = (*pskb)->nh.ipv6h;
463 protohdr = (u_int32_t *)((void *)ipv6 + IPV6_HDR_LEN);
464 datalen = (*pskb)->len - IPV6_HDR_LEN;
466 if (verdict == IP6T_CONTINUE)
467 e = (void *)e + e->next_offset;
475 e = (void *)e + e->next_offset;
479 #ifdef CONFIG_NETFILTER_DEBUG
480 ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
482 read_unlock_bh(&table->lock);
484 #ifdef DEBUG_ALLOW_ALL
493 /* If it succeeds, returns element and locks mutex */
495 find_inlist_lock_noload(struct list_head *head,
498 struct semaphore *mutex)
503 duprintf("find_inlist: searching for `%s' in %s.\n",
504 name, head == &ip6t_target ? "ip6t_target"
505 : head == &ip6t_match ? "ip6t_match"
506 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
509 *error = down_interruptible(mutex);
513 ret = list_named_find(head, name);
522 #define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
525 find_inlist_lock(struct list_head *head,
529 struct semaphore *mutex)
533 ret = find_inlist_lock_noload(head, name, error, mutex);
535 duprintf("find_inlist: loading `%s%s'.\n", prefix, name);
536 request_module("%s%s", prefix, name);
537 ret = find_inlist_lock_noload(head, name, error, mutex);
544 static inline struct ip6t_table *
545 ip6t_find_table_lock(const char *name, int *error, struct semaphore *mutex)
547 return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
550 static inline struct ip6t_match *
551 find_match_lock(const char *name, int *error, struct semaphore *mutex)
553 return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
557 ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex)
559 return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
562 /* All zeroes == unconditional rule. */
564 unconditional(const struct ip6t_ip6 *ipv6)
568 for (i = 0; i < sizeof(*ipv6); i++)
569 if (((char *)ipv6)[i])
572 return (i == sizeof(*ipv6));
575 /* Figures out from what hook each rule can be called: returns 0 if
576 there are loops. Puts hook bitmask in comefrom. */
578 mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
582 /* No recursion; use packet counter to save back ptrs (reset
583 to 0 as we leave), and comefrom to save source hook bitmask */
584 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
585 unsigned int pos = newinfo->hook_entry[hook];
587 = (struct ip6t_entry *)(newinfo->entries + pos);
589 if (!(valid_hooks & (1 << hook)))
592 /* Set initial back pointer. */
593 e->counters.pcnt = pos;
596 struct ip6t_standard_target *t
597 = (void *)ip6t_get_target(e);
599 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
600 printk("iptables: loop hook %u pos %u %08X.\n",
601 hook, pos, e->comefrom);
605 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
607 /* Unconditional return/END. */
608 if (e->target_offset == sizeof(struct ip6t_entry)
609 && (strcmp(t->target.u.user.name,
610 IP6T_STANDARD_TARGET) == 0)
612 && unconditional(&e->ipv6)) {
613 unsigned int oldpos, size;
615 /* Return: backtrack through the last
618 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
619 #ifdef DEBUG_IP_FIREWALL_USER
621 & (1 << NF_IP6_NUMHOOKS)) {
622 duprintf("Back unset "
629 pos = e->counters.pcnt;
630 e->counters.pcnt = 0;
632 /* We're at the start. */
636 e = (struct ip6t_entry *)
637 (newinfo->entries + pos);
638 } while (oldpos == pos + e->next_offset);
641 size = e->next_offset;
642 e = (struct ip6t_entry *)
643 (newinfo->entries + pos + size);
644 e->counters.pcnt = pos;
647 int newpos = t->verdict;
649 if (strcmp(t->target.u.user.name,
650 IP6T_STANDARD_TARGET) == 0
652 /* This a jump; chase it. */
653 duprintf("Jump rule %u -> %u\n",
656 /* ... this is a fallthru */
657 newpos = pos + e->next_offset;
659 e = (struct ip6t_entry *)
660 (newinfo->entries + newpos);
661 e->counters.pcnt = pos;
666 duprintf("Finished chain %u\n", hook);
672 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
674 if (i && (*i)-- == 0)
677 if (m->u.kernel.match->destroy)
678 m->u.kernel.match->destroy(m->data,
679 m->u.match_size - sizeof(*m));
680 module_put(m->u.kernel.match->me);
685 standard_check(const struct ip6t_entry_target *t,
686 unsigned int max_offset)
688 struct ip6t_standard_target *targ = (void *)t;
690 /* Check standard info. */
692 != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
693 duprintf("standard_check: target size %u != %u\n",
695 IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
699 if (targ->verdict >= 0
700 && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
701 duprintf("ip6t_standard_check: bad verdict (%i)\n",
706 if (targ->verdict < -NF_MAX_VERDICT - 1) {
707 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
715 check_match(struct ip6t_entry_match *m,
717 const struct ip6t_ip6 *ipv6,
718 unsigned int hookmask,
722 struct ip6t_match *match;
724 match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
726 // duprintf("check_match: `%s' not found\n", m->u.name);
729 if (!try_module_get(match->me)) {
733 m->u.kernel.match = match;
736 if (m->u.kernel.match->checkentry
737 && !m->u.kernel.match->checkentry(name, ipv6, m->data,
738 m->u.match_size - sizeof(*m),
740 module_put(m->u.kernel.match->me);
741 duprintf("ip_tables: check failed for `%s'.\n",
742 m->u.kernel.match->name);
750 static struct ip6t_target ip6t_standard_target;
753 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
756 struct ip6t_entry_target *t;
757 struct ip6t_target *target;
761 if (!ip6_checkentry(&e->ipv6)) {
762 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
767 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
769 goto cleanup_matches;
771 t = ip6t_get_target(e);
772 target = ip6t_find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
774 duprintf("check_entry: `%s' not found\n", t->u.user.name);
775 goto cleanup_matches;
777 if (!try_module_get(target->me)) {
780 goto cleanup_matches;
782 t->u.kernel.target = target;
784 if (!t->u.kernel.target) {
786 goto cleanup_matches;
788 if (t->u.kernel.target == &ip6t_standard_target) {
789 if (!standard_check(t, size)) {
791 goto cleanup_matches;
793 } else if (t->u.kernel.target->checkentry
794 && !t->u.kernel.target->checkentry(name, e, t->data,
798 module_put(t->u.kernel.target->me);
799 duprintf("ip_tables: check failed for `%s'.\n",
800 t->u.kernel.target->name);
802 goto cleanup_matches;
809 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
814 check_entry_size_and_hooks(struct ip6t_entry *e,
815 struct ip6t_table_info *newinfo,
817 unsigned char *limit,
818 const unsigned int *hook_entries,
819 const unsigned int *underflows,
824 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
825 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
826 duprintf("Bad offset %p\n", e);
831 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
832 duprintf("checking: element %p size %u\n",
837 /* Check hooks & underflows */
838 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
839 if ((unsigned char *)e - base == hook_entries[h])
840 newinfo->hook_entry[h] = hook_entries[h];
841 if ((unsigned char *)e - base == underflows[h])
842 newinfo->underflow[h] = underflows[h];
845 /* FIXME: underflows must be unconditional, standard verdicts
846 < 0 (not IP6T_RETURN). --RR */
848 /* Clear counters and comefrom */
849 e->counters = ((struct ip6t_counters) { 0, 0 });
857 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
859 struct ip6t_entry_target *t;
861 if (i && (*i)-- == 0)
864 /* Cleanup all matches */
865 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
866 t = ip6t_get_target(e);
867 if (t->u.kernel.target->destroy)
868 t->u.kernel.target->destroy(t->data,
869 t->u.target_size - sizeof(*t));
870 module_put(t->u.kernel.target->me);
874 /* Checks and translates the user-supplied table segment (held in
877 translate_table(const char *name,
878 unsigned int valid_hooks,
879 struct ip6t_table_info *newinfo,
882 const unsigned int *hook_entries,
883 const unsigned int *underflows)
888 newinfo->size = size;
889 newinfo->number = number;
891 /* Init all hooks to impossible value. */
892 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
893 newinfo->hook_entry[i] = 0xFFFFFFFF;
894 newinfo->underflow[i] = 0xFFFFFFFF;
897 duprintf("translate_table: size %u\n", newinfo->size);
899 /* Walk through entries, checking offsets. */
900 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
901 check_entry_size_and_hooks,
904 newinfo->entries + size,
905 hook_entries, underflows, &i);
910 duprintf("translate_table: %u not %u entries\n",
915 /* Check hooks all assigned */
916 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
917 /* Only hooks which are valid */
918 if (!(valid_hooks & (1 << i)))
920 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
921 duprintf("Invalid hook entry %u %u\n",
925 if (newinfo->underflow[i] == 0xFFFFFFFF) {
926 duprintf("Invalid underflow %u %u\n",
932 if (!mark_source_chains(newinfo, valid_hooks))
935 /* Finally, each sanity check must pass */
937 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
938 check_entry, name, size, &i);
941 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
946 /* And one copy for every other CPU */
947 for (i = 1; i < NR_CPUS; i++) {
948 memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
950 SMP_ALIGN(newinfo->size));
956 static struct ip6t_table_info *
957 replace_table(struct ip6t_table *table,
958 unsigned int num_counters,
959 struct ip6t_table_info *newinfo,
962 struct ip6t_table_info *oldinfo;
964 #ifdef CONFIG_NETFILTER_DEBUG
966 struct ip6t_entry *table_base;
969 for (i = 0; i < NR_CPUS; i++) {
971 (void *)newinfo->entries
972 + TABLE_OFFSET(newinfo, i);
974 table_base->comefrom = 0xdead57ac;
979 /* Do the substitution. */
980 write_lock_bh(&table->lock);
981 /* Check inside lock: is the old number correct? */
982 if (num_counters != table->private->number) {
983 duprintf("num_counters != table->private->number (%u/%u)\n",
984 num_counters, table->private->number);
985 write_unlock_bh(&table->lock);
989 oldinfo = table->private;
990 table->private = newinfo;
991 newinfo->initial_entries = oldinfo->initial_entries;
992 write_unlock_bh(&table->lock);
999 add_entry_to_counter(const struct ip6t_entry *e,
1000 struct ip6t_counters total[],
1003 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
1010 get_counters(const struct ip6t_table_info *t,
1011 struct ip6t_counters counters[])
1016 for (cpu = 0; cpu < NR_CPUS; cpu++) {
1018 IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
1020 add_entry_to_counter,
1027 copy_entries_to_user(unsigned int total_size,
1028 struct ip6t_table *table,
1029 void __user *userptr)
1031 unsigned int off, num, countersize;
1032 struct ip6t_entry *e;
1033 struct ip6t_counters *counters;
1036 /* We need atomic snapshot of counters: rest doesn't change
1037 (other than comefrom, which userspace doesn't care
1039 countersize = sizeof(struct ip6t_counters) * table->private->number;
1040 counters = vmalloc(countersize);
1042 if (counters == NULL)
1045 /* First, sum counters... */
1046 memset(counters, 0, countersize);
1047 write_lock_bh(&table->lock);
1048 get_counters(table->private, counters);
1049 write_unlock_bh(&table->lock);
1051 /* ... then copy entire thing from CPU 0... */
1052 if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
1057 /* FIXME: use iterator macros --RR */
1058 /* ... then go back and fix counters and names */
1059 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1061 struct ip6t_entry_match *m;
1062 struct ip6t_entry_target *t;
1064 e = (struct ip6t_entry *)(table->private->entries + off);
1065 if (copy_to_user(userptr + off
1066 + offsetof(struct ip6t_entry, counters),
1068 sizeof(counters[num])) != 0) {
1073 for (i = sizeof(struct ip6t_entry);
1074 i < e->target_offset;
1075 i += m->u.match_size) {
1078 if (copy_to_user(userptr + off + i
1079 + offsetof(struct ip6t_entry_match,
1081 m->u.kernel.match->name,
1082 strlen(m->u.kernel.match->name)+1)
1089 t = ip6t_get_target(e);
1090 if (copy_to_user(userptr + off + e->target_offset
1091 + offsetof(struct ip6t_entry_target,
1093 t->u.kernel.target->name,
1094 strlen(t->u.kernel.target->name)+1) != 0) {
1106 get_entries(const struct ip6t_get_entries *entries,
1107 struct ip6t_get_entries __user *uptr)
1110 struct ip6t_table *t;
1112 t = ip6t_find_table_lock(entries->name, &ret, &ip6t_mutex);
1114 duprintf("t->private->number = %u\n",
1115 t->private->number);
1116 if (entries->size == t->private->size)
1117 ret = copy_entries_to_user(t->private->size,
1118 t, uptr->entrytable);
1120 duprintf("get_entries: I've got %u not %u!\n",
1127 duprintf("get_entries: Can't find %s!\n",
1134 do_replace(void __user *user, unsigned int len)
1137 struct ip6t_replace tmp;
1138 struct ip6t_table *t;
1139 struct ip6t_table_info *newinfo, *oldinfo;
1140 struct ip6t_counters *counters;
1142 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1145 /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
1146 if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
1149 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1150 + SMP_ALIGN(tmp.size) * NR_CPUS);
1154 if (copy_from_user(newinfo->entries, user + sizeof(tmp),
1160 counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
1165 memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
1167 ret = translate_table(tmp.name, tmp.valid_hooks,
1168 newinfo, tmp.size, tmp.num_entries,
1169 tmp.hook_entry, tmp.underflow);
1171 goto free_newinfo_counters;
1173 duprintf("ip_tables: Translated table\n");
1175 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1177 goto free_newinfo_counters_untrans;
1180 if (tmp.valid_hooks != t->valid_hooks) {
1181 duprintf("Valid hook crap: %08X vs %08X\n",
1182 tmp.valid_hooks, t->valid_hooks);
1184 goto free_newinfo_counters_untrans_unlock;
1187 /* Get a reference in advance, we're not allowed fail later */
1188 if (!try_module_get(t->me)) {
1190 goto free_newinfo_counters_untrans_unlock;
1193 oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
1197 /* Update module usage count based on number of rules */
1198 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1199 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1200 if ((oldinfo->number > oldinfo->initial_entries) ||
1201 (newinfo->number <= oldinfo->initial_entries))
1203 if ((oldinfo->number > oldinfo->initial_entries) &&
1204 (newinfo->number <= oldinfo->initial_entries))
1207 /* Get the old counters. */
1208 get_counters(oldinfo, counters);
1209 /* Decrease module usage counts and free resource */
1210 IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
1212 /* Silent error: too late now. */
1213 copy_to_user(tmp.counters, counters,
1214 sizeof(struct ip6t_counters) * tmp.num_counters);
1221 free_newinfo_counters_untrans_unlock:
1223 free_newinfo_counters_untrans:
1224 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
1225 free_newinfo_counters:
1232 /* We're lazy, and add to the first CPU; overflow works its fey magic
1233 * and everything is OK. */
1235 add_counter_to_entry(struct ip6t_entry *e,
1236 const struct ip6t_counters addme[],
1240 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1242 (long unsigned int)e->counters.pcnt,
1243 (long unsigned int)e->counters.bcnt,
1244 (long unsigned int)addme[*i].pcnt,
1245 (long unsigned int)addme[*i].bcnt);
1248 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1255 do_add_counters(void __user *user, unsigned int len)
1258 struct ip6t_counters_info tmp, *paddc;
1259 struct ip6t_table *t;
1262 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1265 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
1268 paddc = vmalloc(len);
1272 if (copy_from_user(paddc, user, len) != 0) {
1277 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1281 write_lock_bh(&t->lock);
1282 if (t->private->number != paddc->num_counters) {
1284 goto unlock_up_free;
1288 IP6T_ENTRY_ITERATE(t->private->entries,
1290 add_counter_to_entry,
1294 write_unlock_bh(&t->lock);
1303 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1307 if (!capable(CAP_NET_ADMIN))
1311 case IP6T_SO_SET_REPLACE:
1312 ret = do_replace(user, len);
1315 case IP6T_SO_SET_ADD_COUNTERS:
1316 ret = do_add_counters(user, len);
1320 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1328 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1332 if (!capable(CAP_NET_ADMIN))
1336 case IP6T_SO_GET_INFO: {
1337 char name[IP6T_TABLE_MAXNAMELEN];
1338 struct ip6t_table *t;
1340 if (*len != sizeof(struct ip6t_getinfo)) {
1341 duprintf("length %u != %u\n", *len,
1342 sizeof(struct ip6t_getinfo));
1347 if (copy_from_user(name, user, sizeof(name)) != 0) {
1351 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1352 t = ip6t_find_table_lock(name, &ret, &ip6t_mutex);
1354 struct ip6t_getinfo info;
1356 info.valid_hooks = t->valid_hooks;
1357 memcpy(info.hook_entry, t->private->hook_entry,
1358 sizeof(info.hook_entry));
1359 memcpy(info.underflow, t->private->underflow,
1360 sizeof(info.underflow));
1361 info.num_entries = t->private->number;
1362 info.size = t->private->size;
1363 strcpy(info.name, name);
1365 if (copy_to_user(user, &info, *len) != 0)
1375 case IP6T_SO_GET_ENTRIES: {
1376 struct ip6t_get_entries get;
1378 if (*len < sizeof(get)) {
1379 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1381 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1383 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1384 duprintf("get_entries: %u != %u\n", *len,
1385 sizeof(struct ip6t_get_entries) + get.size);
1388 ret = get_entries(&get, user);
1393 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1400 /* Registration hooks for targets. */
1402 ip6t_register_target(struct ip6t_target *target)
1406 ret = down_interruptible(&ip6t_mutex);
1410 if (!list_named_insert(&ip6t_target, target)) {
1411 duprintf("ip6t_register_target: `%s' already in list!\n",
1420 ip6t_unregister_target(struct ip6t_target *target)
1423 LIST_DELETE(&ip6t_target, target);
1428 ip6t_register_match(struct ip6t_match *match)
1432 ret = down_interruptible(&ip6t_mutex);
1436 if (!list_named_insert(&ip6t_match, match)) {
1437 duprintf("ip6t_register_match: `%s' already in list!\n",
1447 ip6t_unregister_match(struct ip6t_match *match)
1450 LIST_DELETE(&ip6t_match, match);
1454 int ip6t_register_table(struct ip6t_table *table)
1457 struct ip6t_table_info *newinfo;
1458 static struct ip6t_table_info bootstrap
1459 = { 0, 0, 0, { 0 }, { 0 }, { } };
1461 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1462 + SMP_ALIGN(table->table->size) * NR_CPUS);
1466 memcpy(newinfo->entries, table->table->entries, table->table->size);
1468 ret = translate_table(table->name, table->valid_hooks,
1469 newinfo, table->table->size,
1470 table->table->num_entries,
1471 table->table->hook_entry,
1472 table->table->underflow);
1478 ret = down_interruptible(&ip6t_mutex);
1484 /* Don't autoload: we'd eat our tail... */
1485 if (list_named_find(&ip6t_tables, table->name)) {
1490 /* Simplifies replace_table code. */
1491 table->private = &bootstrap;
1492 if (!replace_table(table, 0, newinfo, &ret))
1495 duprintf("table->private->number = %u\n",
1496 table->private->number);
1498 /* save number of initial entries */
1499 table->private->initial_entries = table->private->number;
1501 table->lock = RW_LOCK_UNLOCKED;
1502 list_prepend(&ip6t_tables, table);
1513 void ip6t_unregister_table(struct ip6t_table *table)
1516 LIST_DELETE(&ip6t_tables, table);
1519 /* Decrease module usage counts and free resources */
1520 IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
1521 cleanup_entry, NULL);
1522 vfree(table->private);
1525 /* Returns 1 if the port is matched by the range, 0 otherwise */
1527 port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
1531 ret = (port >= min && port <= max) ^ invert;
1536 tcp_find_option(u_int8_t option,
1537 const struct tcphdr *tcp,
1542 unsigned int i = sizeof(struct tcphdr);
1543 const u_int8_t *opt = (u_int8_t *)tcp;
1545 duprintf("tcp_match: finding option\n");
1546 /* If we don't have the whole header, drop packet. */
1547 if (tcp->doff * 4 < sizeof(struct tcphdr) ||
1548 tcp->doff * 4 > datalen) {
1553 while (i < tcp->doff * 4) {
1554 if (opt[i] == option) return !invert;
1555 if (opt[i] < 2) i++;
1556 else i += opt[i+1]?:1;
1563 tcp_match(const struct sk_buff *skb,
1564 const struct net_device *in,
1565 const struct net_device *out,
1566 const void *matchinfo,
1572 const struct tcphdr *tcp;
1573 const struct ip6t_tcp *tcpinfo = matchinfo;
1575 u8 nexthdr = skb->nh.ipv6h->nexthdr;
1579 Don't allow a fragment of TCP 8 bytes in. Nobody normal
1580 causes this. Its a cracker trying to break in by doing a
1581 flag overwrite to pass the direction checks.
1585 duprintf("Dropping evil TCP offset=1 frag.\n");
1588 } else if (offset == 0 && datalen < sizeof(struct tcphdr)) {
1589 /* We've been asked to examine this packet, and we
1590 can't. Hence, no choice but to drop. */
1591 duprintf("Dropping evil TCP offset=0 tinygram.\n");
1596 tcpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
1597 tcpoff = ipv6_skip_exthdr(skb, tcpoff, &nexthdr, skb->len - tcpoff);
1598 if (tcpoff < 0 || tcpoff > skb->len) {
1599 duprintf("tcp_match: cannot skip exthdr. Dropping.\n");
1602 } else if (nexthdr == IPPROTO_FRAGMENT)
1604 else if (nexthdr != IPPROTO_TCP ||
1605 skb->len - tcpoff < sizeof(struct tcphdr)) {
1606 /* cannot be occured */
1607 duprintf("tcp_match: cannot get TCP header. Dropping.\n");
1612 tcp = (struct tcphdr *)(skb->data + tcpoff);
1614 /* FIXME: Try tcp doff >> packet len against various stacks --RR */
1616 #define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
1618 /* Must not be a fragment. */
1620 && port_match(tcpinfo->spts[0], tcpinfo->spts[1],
1622 !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT))
1623 && port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
1625 !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT))
1626 && FWINVTCP((((unsigned char *)tcp)[13]
1627 & tcpinfo->flg_mask)
1628 == tcpinfo->flg_cmp,
1630 && (!tcpinfo->option
1631 || tcp_find_option(tcpinfo->option, tcp, datalen,
1633 & IP6T_TCP_INV_OPTION,
1637 /* Called when user tries to insert an entry of this type. */
1639 tcp_checkentry(const char *tablename,
1640 const struct ip6t_ip6 *ipv6,
1642 unsigned int matchsize,
1643 unsigned int hook_mask)
1645 const struct ip6t_tcp *tcpinfo = matchinfo;
1647 /* Must specify proto == TCP, and no unknown invflags */
1648 return ipv6->proto == IPPROTO_TCP
1649 && !(ipv6->invflags & IP6T_INV_PROTO)
1650 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
1651 && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
1655 udp_match(const struct sk_buff *skb,
1656 const struct net_device *in,
1657 const struct net_device *out,
1658 const void *matchinfo,
1664 const struct udphdr *udp;
1665 const struct ip6t_udp *udpinfo = matchinfo;
1667 u8 nexthdr = skb->nh.ipv6h->nexthdr;
1669 if (offset == 0 && datalen < sizeof(struct udphdr)) {
1670 /* We've been asked to examine this packet, and we
1671 can't. Hence, no choice but to drop. */
1672 duprintf("Dropping evil UDP tinygram.\n");
1677 udpoff = (u8*)(skb->nh.ipv6h + 1) - skb->data;
1678 udpoff = ipv6_skip_exthdr(skb, udpoff, &nexthdr, skb->len - udpoff);
1679 if (udpoff < 0 || udpoff > skb->len) {
1680 duprintf("udp_match: cannot skip exthdr. Dropping.\n");
1683 } else if (nexthdr == IPPROTO_FRAGMENT)
1685 else if (nexthdr != IPPROTO_UDP ||
1686 skb->len - udpoff < sizeof(struct udphdr)) {
1687 duprintf("udp_match: cannot get UDP header. Dropping.\n");
1692 udp = (struct udphdr *)(skb->data + udpoff);
1694 /* Must not be a fragment. */
1696 && port_match(udpinfo->spts[0], udpinfo->spts[1],
1698 !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
1699 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
1701 !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
1704 /* Called when user tries to insert an entry of this type. */
1706 udp_checkentry(const char *tablename,
1707 const struct ip6t_ip6 *ipv6,
1709 unsigned int matchinfosize,
1710 unsigned int hook_mask)
1712 const struct ip6t_udp *udpinfo = matchinfo;
1714 /* Must specify proto == UDP, and no unknown invflags */
1715 if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
1716 duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
1720 if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
1721 duprintf("ip6t_udp: matchsize %u != %u\n",
1722 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
1725 if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
1726 duprintf("ip6t_udp: unknown flags %X\n",
1734 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1736 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1737 u_int8_t type, u_int8_t code,
1740 return (type == test_type && code >= min_code && code <= max_code)
1745 icmp6_match(const struct sk_buff *skb,
1746 const struct net_device *in,
1747 const struct net_device *out,
1748 const void *matchinfo,
1754 const struct icmp6hdr *icmp = hdr;
1755 const struct ip6t_icmp *icmpinfo = matchinfo;
1757 if (offset == 0 && datalen < 2) {
1758 /* We've been asked to examine this packet, and we
1759 can't. Hence, no choice but to drop. */
1760 duprintf("Dropping evil ICMP tinygram.\n");
1765 /* Must not be a fragment. */
1767 && icmp6_type_code_match(icmpinfo->type,
1770 icmp->icmp6_type, icmp->icmp6_code,
1771 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1774 /* Called when user tries to insert an entry of this type. */
1776 icmp6_checkentry(const char *tablename,
1777 const struct ip6t_ip6 *ipv6,
1779 unsigned int matchsize,
1780 unsigned int hook_mask)
1782 const struct ip6t_icmp *icmpinfo = matchinfo;
1784 /* Must specify proto == ICMP, and no unknown invflags */
1785 return ipv6->proto == IPPROTO_ICMPV6
1786 && !(ipv6->invflags & IP6T_INV_PROTO)
1787 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
1788 && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1791 /* The built-in targets: standard (NULL) and error. */
1792 static struct ip6t_target ip6t_standard_target = {
1793 .name = IP6T_STANDARD_TARGET,
1796 static struct ip6t_target ip6t_error_target = {
1797 .name = IP6T_ERROR_TARGET,
1798 .target = ip6t_error,
1801 static struct nf_sockopt_ops ip6t_sockopts = {
1803 .set_optmin = IP6T_BASE_CTL,
1804 .set_optmax = IP6T_SO_SET_MAX+1,
1805 .set = do_ip6t_set_ctl,
1806 .get_optmin = IP6T_BASE_CTL,
1807 .get_optmax = IP6T_SO_GET_MAX+1,
1808 .get = do_ip6t_get_ctl,
1811 static struct ip6t_match tcp_matchstruct = {
1813 .match = &tcp_match,
1814 .checkentry = &tcp_checkentry,
1817 static struct ip6t_match udp_matchstruct = {
1819 .match = &udp_match,
1820 .checkentry = &udp_checkentry,
1823 static struct ip6t_match icmp6_matchstruct = {
1825 .match = &icmp6_match,
1826 .checkentry = &icmp6_checkentry,
1829 #ifdef CONFIG_PROC_FS
1830 static inline int print_name(const char *i,
1831 off_t start_offset, char *buffer, int length,
1832 off_t *pos, unsigned int *count)
1834 if ((*count)++ >= start_offset) {
1835 unsigned int namelen;
1837 namelen = sprintf(buffer + *pos, "%s\n",
1838 i + sizeof(struct list_head));
1839 if (*pos + namelen > length) {
1840 /* Stop iterating */
1848 static inline int print_target(const struct ip6t_target *t,
1849 off_t start_offset, char *buffer, int length,
1850 off_t *pos, unsigned int *count)
1852 if (t == &ip6t_standard_target || t == &ip6t_error_target)
1854 return print_name((char *)t, start_offset, buffer, length, pos, count);
1857 static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
1860 unsigned int count = 0;
1862 if (down_interruptible(&ip6t_mutex) != 0)
1865 LIST_FIND(&ip6t_tables, print_name, char *,
1866 offset, buffer, length, &pos, &count);
1870 /* `start' hack - see fs/proc/generic.c line ~105 */
1871 *start=(char *)((unsigned long)count-offset);
1875 static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
1878 unsigned int count = 0;
1880 if (down_interruptible(&ip6t_mutex) != 0)
1883 LIST_FIND(&ip6t_target, print_target, struct ip6t_target *,
1884 offset, buffer, length, &pos, &count);
1888 *start = (char *)((unsigned long)count - offset);
1892 static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
1895 unsigned int count = 0;
1897 if (down_interruptible(&ip6t_mutex) != 0)
1900 LIST_FIND(&ip6t_match, print_name, char *,
1901 offset, buffer, length, &pos, &count);
1905 *start = (char *)((unsigned long)count - offset);
1909 static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
1910 { { "ip6_tables_names", ip6t_get_tables },
1911 { "ip6_tables_targets", ip6t_get_targets },
1912 { "ip6_tables_matches", ip6t_get_matches },
1914 #endif /*CONFIG_PROC_FS*/
1916 static int __init init(void)
1920 /* Noone else will be downing sem now, so we won't sleep */
1922 list_append(&ip6t_target, &ip6t_standard_target);
1923 list_append(&ip6t_target, &ip6t_error_target);
1924 list_append(&ip6t_match, &tcp_matchstruct);
1925 list_append(&ip6t_match, &udp_matchstruct);
1926 list_append(&ip6t_match, &icmp6_matchstruct);
1929 /* Register setsockopt */
1930 ret = nf_register_sockopt(&ip6t_sockopts);
1932 duprintf("Unable to register sockopts.\n");
1936 #ifdef CONFIG_PROC_FS
1938 struct proc_dir_entry *proc;
1941 for (i = 0; ip6t_proc_entry[i].name; i++) {
1942 proc = proc_net_create(ip6t_proc_entry[i].name, 0,
1943 ip6t_proc_entry[i].get_info);
1946 proc_net_remove(ip6t_proc_entry[i].name);
1947 nf_unregister_sockopt(&ip6t_sockopts);
1950 proc->owner = THIS_MODULE;
1955 printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
1959 static void __exit fini(void)
1961 nf_unregister_sockopt(&ip6t_sockopts);
1962 #ifdef CONFIG_PROC_FS
1965 for (i = 0; ip6t_proc_entry[i].name; i++)
1966 proc_net_remove(ip6t_proc_entry[i].name);
1971 EXPORT_SYMBOL(ip6t_register_table);
1972 EXPORT_SYMBOL(ip6t_unregister_table);
1973 EXPORT_SYMBOL(ip6t_do_table);
1974 EXPORT_SYMBOL(ip6t_find_target_lock);
1975 EXPORT_SYMBOL(ip6t_register_match);
1976 EXPORT_SYMBOL(ip6t_unregister_match);
1977 EXPORT_SYMBOL(ip6t_register_target);
1978 EXPORT_SYMBOL(ip6t_unregister_target);
1979 EXPORT_SYMBOL(ip6t_ext_hdr);