2 * Packet matching code.
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/capability.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/poison.h>
20 #include <linux/icmpv6.h>
22 #include <asm/uaccess.h>
23 #include <linux/mutex.h>
24 #include <linux/proc_fs.h>
25 #include <linux/cpumask.h>
27 #include <linux/netfilter_ipv6/ip6_tables.h>
28 #include <linux/netfilter/x_tables.h>
30 MODULE_LICENSE("GPL");
31 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
32 MODULE_DESCRIPTION("IPv6 packet filter");
34 #define IPV6_HDR_LEN (sizeof(struct ipv6hdr))
35 #define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
37 /*#define DEBUG_IP_FIREWALL*/
38 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
39 /*#define DEBUG_IP_FIREWALL_USER*/
41 #ifdef DEBUG_IP_FIREWALL
42 #define dprintf(format, args...) printk(format , ## args)
44 #define dprintf(format, args...)
47 #ifdef DEBUG_IP_FIREWALL_USER
48 #define duprintf(format, args...) printk(format , ## args)
50 #define duprintf(format, args...)
53 #ifdef CONFIG_NETFILTER_DEBUG
54 #define IP_NF_ASSERT(x) \
57 printk("IP_NF_ASSERT: %s:%s:%u\n", \
58 __FUNCTION__, __FILE__, __LINE__); \
61 #define IP_NF_ASSERT(x)
65 /* All the better to debug you with... */
71 We keep a set of rules for each CPU, so we can avoid write-locking
72 them in the softirq when updating the counters and therefore
73 only need to read-lock in the softirq; doing a write_lock_bh() in user
74 context stops packets coming through and allows user context to read
75 the counters or update the rules.
77 Hence the start of any table is given by get_table() below. */
80 #define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
81 #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; })
82 #define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
85 /* Check for an extension */
87 ip6t_ext_hdr(u8 nexthdr)
89 return ( (nexthdr == IPPROTO_HOPOPTS) ||
90 (nexthdr == IPPROTO_ROUTING) ||
91 (nexthdr == IPPROTO_FRAGMENT) ||
92 (nexthdr == IPPROTO_ESP) ||
93 (nexthdr == IPPROTO_AH) ||
94 (nexthdr == IPPROTO_NONE) ||
95 (nexthdr == IPPROTO_DSTOPTS) );
98 /* Returns whether matches rule or not. */
100 ip6_packet_match(const struct sk_buff *skb,
103 const struct ip6t_ip6 *ip6info,
104 unsigned int *protoff,
105 int *fragoff, int *hotdrop)
109 const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
111 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
113 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
114 &ip6info->src), IP6T_INV_SRCIP)
115 || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
116 &ip6info->dst), IP6T_INV_DSTIP)) {
117 dprintf("Source or dest mismatch.\n");
119 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
120 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
121 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
122 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
123 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
124 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
128 /* Look for ifname matches; this should unroll nicely. */
129 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
130 ret |= (((const unsigned long *)indev)[i]
131 ^ ((const unsigned long *)ip6info->iniface)[i])
132 & ((const unsigned long *)ip6info->iniface_mask)[i];
135 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
136 dprintf("VIA in mismatch (%s vs %s).%s\n",
137 indev, ip6info->iniface,
138 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
142 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
143 ret |= (((const unsigned long *)outdev)[i]
144 ^ ((const unsigned long *)ip6info->outiface)[i])
145 & ((const unsigned long *)ip6info->outiface_mask)[i];
148 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
149 dprintf("VIA out mismatch (%s vs %s).%s\n",
150 outdev, ip6info->outiface,
151 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
155 /* ... might want to do something with class and flowlabel here ... */
157 /* look for the desired protocol header */
158 if((ip6info->flags & IP6T_F_PROTO)) {
160 unsigned short _frag_off;
162 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
168 *fragoff = _frag_off;
170 dprintf("Packet protocol %hi ?= %s%hi.\n",
172 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
175 if (ip6info->proto == protohdr) {
176 if(ip6info->invflags & IP6T_INV_PROTO) {
182 /* We need match for the '-p all', too! */
183 if ((ip6info->proto != 0) &&
184 !(ip6info->invflags & IP6T_INV_PROTO))
190 /* should be ip6 safe */
192 ip6_checkentry(const struct ip6t_ip6 *ipv6)
194 if (ipv6->flags & ~IP6T_F_MASK) {
195 duprintf("Unknown flag bits set: %08X\n",
196 ipv6->flags & ~IP6T_F_MASK);
199 if (ipv6->invflags & ~IP6T_INV_MASK) {
200 duprintf("Unknown invflag bits set: %08X\n",
201 ipv6->invflags & ~IP6T_INV_MASK);
208 ip6t_error(struct sk_buff **pskb,
209 const struct net_device *in,
210 const struct net_device *out,
211 unsigned int hooknum,
212 const struct xt_target *target,
213 const void *targinfo)
216 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
222 int do_match(struct ip6t_entry_match *m,
223 const struct sk_buff *skb,
224 const struct net_device *in,
225 const struct net_device *out,
227 unsigned int protoff,
230 /* Stop iteration if it doesn't match */
231 if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
232 offset, protoff, hotdrop))
238 static inline struct ip6t_entry *
239 get_entry(void *base, unsigned int offset)
241 return (struct ip6t_entry *)(base + offset);
244 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
246 ip6t_do_table(struct sk_buff **pskb,
248 const struct net_device *in,
249 const struct net_device *out,
250 struct xt_table *table)
252 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
254 unsigned int protoff = 0;
256 /* Initializing verdict to NF_DROP keeps gcc happy. */
257 unsigned int verdict = NF_DROP;
258 const char *indev, *outdev;
260 struct ip6t_entry *e, *back;
261 struct xt_table_info *private;
264 indev = in ? in->name : nulldevname;
265 outdev = out ? out->name : nulldevname;
266 /* We handle fragments by dealing with the first fragment as
267 * if it was a normal packet. All other fragments are treated
268 * normally, except that they will NEVER match rules that ask
269 * things we don't know, ie. tcp syn flag or ports). If the
270 * rule is also a fragment-specific rule, non-fragments won't
273 read_lock_bh(&table->lock);
274 private = table->private;
275 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
276 table_base = (void *)private->entries[smp_processor_id()];
277 e = get_entry(table_base, private->hook_entry[hook]);
279 /* For return from builtin chain */
280 back = get_entry(table_base, private->underflow[hook]);
285 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
286 &protoff, &offset, &hotdrop)) {
287 struct ip6t_entry_target *t;
289 if (IP6T_MATCH_ITERATE(e, do_match,
291 offset, protoff, &hotdrop) != 0)
294 ADD_COUNTER(e->counters,
295 ntohs(ipv6_hdr(*pskb)->payload_len)
299 t = ip6t_get_target(e);
300 IP_NF_ASSERT(t->u.kernel.target);
301 /* Standard target? */
302 if (!t->u.kernel.target->target) {
305 v = ((struct ip6t_standard_target *)t)->verdict;
307 /* Pop from stack? */
308 if (v != IP6T_RETURN) {
309 verdict = (unsigned)(-v) - 1;
313 back = get_entry(table_base,
317 if (table_base + v != (void *)e + e->next_offset
318 && !(e->ipv6.flags & IP6T_F_GOTO)) {
319 /* Save old back ptr in next entry */
320 struct ip6t_entry *next
321 = (void *)e + e->next_offset;
323 = (void *)back - table_base;
324 /* set back pointer to next entry */
328 e = get_entry(table_base, v);
330 /* Targets which reenter must return
332 #ifdef CONFIG_NETFILTER_DEBUG
333 ((struct ip6t_entry *)table_base)->comefrom
336 verdict = t->u.kernel.target->target(pskb,
342 #ifdef CONFIG_NETFILTER_DEBUG
343 if (((struct ip6t_entry *)table_base)->comefrom
345 && verdict == IP6T_CONTINUE) {
346 printk("Target %s reentered!\n",
347 t->u.kernel.target->name);
350 ((struct ip6t_entry *)table_base)->comefrom
353 if (verdict == IP6T_CONTINUE)
354 e = (void *)e + e->next_offset;
362 e = (void *)e + e->next_offset;
366 #ifdef CONFIG_NETFILTER_DEBUG
367 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
369 read_unlock_bh(&table->lock);
371 #ifdef DEBUG_ALLOW_ALL
380 /* All zeroes == unconditional rule. */
382 unconditional(const struct ip6t_ip6 *ipv6)
386 for (i = 0; i < sizeof(*ipv6); i++)
387 if (((char *)ipv6)[i])
390 return (i == sizeof(*ipv6));
393 /* Figures out from what hook each rule can be called: returns 0 if
394 there are loops. Puts hook bitmask in comefrom. */
396 mark_source_chains(struct xt_table_info *newinfo,
397 unsigned int valid_hooks, void *entry0)
401 /* No recursion; use packet counter to save back ptrs (reset
402 to 0 as we leave), and comefrom to save source hook bitmask */
403 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
404 unsigned int pos = newinfo->hook_entry[hook];
406 = (struct ip6t_entry *)(entry0 + pos);
407 int visited = e->comefrom & (1 << hook);
409 if (!(valid_hooks & (1 << hook)))
412 /* Set initial back pointer. */
413 e->counters.pcnt = pos;
416 struct ip6t_standard_target *t
417 = (void *)ip6t_get_target(e);
419 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
420 printk("iptables: loop hook %u pos %u %08X.\n",
421 hook, pos, e->comefrom);
425 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
427 /* Unconditional return/END. */
428 if ((e->target_offset == sizeof(struct ip6t_entry)
429 && (strcmp(t->target.u.user.name,
430 IP6T_STANDARD_TARGET) == 0)
432 && unconditional(&e->ipv6)) || visited) {
433 unsigned int oldpos, size;
435 if (t->verdict < -NF_MAX_VERDICT - 1) {
436 duprintf("mark_source_chains: bad "
437 "negative verdict (%i)\n",
442 /* Return: backtrack through the last
445 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
446 #ifdef DEBUG_IP_FIREWALL_USER
448 & (1 << NF_IP6_NUMHOOKS)) {
449 duprintf("Back unset "
456 pos = e->counters.pcnt;
457 e->counters.pcnt = 0;
459 /* We're at the start. */
463 e = (struct ip6t_entry *)
465 } while (oldpos == pos + e->next_offset);
468 size = e->next_offset;
469 e = (struct ip6t_entry *)
470 (entry0 + pos + size);
471 e->counters.pcnt = pos;
474 int newpos = t->verdict;
476 if (strcmp(t->target.u.user.name,
477 IP6T_STANDARD_TARGET) == 0
479 if (newpos > newinfo->size -
480 sizeof(struct ip6t_entry)) {
481 duprintf("mark_source_chains: "
482 "bad verdict (%i)\n",
486 /* This a jump; chase it. */
487 duprintf("Jump rule %u -> %u\n",
490 /* ... this is a fallthru */
491 newpos = pos + e->next_offset;
493 e = (struct ip6t_entry *)
495 e->counters.pcnt = pos;
500 duprintf("Finished chain %u\n", hook);
506 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
508 if (i && (*i)-- == 0)
511 if (m->u.kernel.match->destroy)
512 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
513 module_put(m->u.kernel.match->me);
518 check_match(struct ip6t_entry_match *m,
520 const struct ip6t_ip6 *ipv6,
521 unsigned int hookmask,
524 struct xt_match *match;
527 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
529 "ip6t_%s", m->u.user.name);
530 if (IS_ERR(match) || !match) {
531 duprintf("check_match: `%s' not found\n", m->u.user.name);
532 return match ? PTR_ERR(match) : -ENOENT;
534 m->u.kernel.match = match;
536 ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
537 name, hookmask, ipv6->proto,
538 ipv6->invflags & IP6T_INV_PROTO);
542 if (m->u.kernel.match->checkentry
543 && !m->u.kernel.match->checkentry(name, ipv6, match, m->data,
545 duprintf("ip_tables: check failed for `%s'.\n",
546 m->u.kernel.match->name);
554 module_put(m->u.kernel.match->me);
558 static struct xt_target ip6t_standard_target;
561 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
564 struct ip6t_entry_target *t;
565 struct xt_target *target;
569 if (!ip6_checkentry(&e->ipv6)) {
570 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
574 if (e->target_offset + sizeof(struct ip6t_entry_target) >
579 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
581 goto cleanup_matches;
583 t = ip6t_get_target(e);
585 if (e->target_offset + t->u.target_size > e->next_offset)
586 goto cleanup_matches;
587 target = try_then_request_module(xt_find_target(AF_INET6,
590 "ip6t_%s", t->u.user.name);
591 if (IS_ERR(target) || !target) {
592 duprintf("check_entry: `%s' not found\n", t->u.user.name);
593 ret = target ? PTR_ERR(target) : -ENOENT;
594 goto cleanup_matches;
596 t->u.kernel.target = target;
598 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
599 name, e->comefrom, e->ipv6.proto,
600 e->ipv6.invflags & IP6T_INV_PROTO);
604 if (t->u.kernel.target->checkentry
605 && !t->u.kernel.target->checkentry(name, e, target, t->data,
607 duprintf("ip_tables: check failed for `%s'.\n",
608 t->u.kernel.target->name);
616 module_put(t->u.kernel.target->me);
618 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
623 check_entry_size_and_hooks(struct ip6t_entry *e,
624 struct xt_table_info *newinfo,
626 unsigned char *limit,
627 const unsigned int *hook_entries,
628 const unsigned int *underflows,
633 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
634 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
635 duprintf("Bad offset %p\n", e);
640 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
641 duprintf("checking: element %p size %u\n",
646 /* Check hooks & underflows */
647 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
648 if ((unsigned char *)e - base == hook_entries[h])
649 newinfo->hook_entry[h] = hook_entries[h];
650 if ((unsigned char *)e - base == underflows[h])
651 newinfo->underflow[h] = underflows[h];
654 /* FIXME: underflows must be unconditional, standard verdicts
655 < 0 (not IP6T_RETURN). --RR */
657 /* Clear counters and comefrom */
658 e->counters = ((struct xt_counters) { 0, 0 });
666 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
668 struct ip6t_entry_target *t;
670 if (i && (*i)-- == 0)
673 /* Cleanup all matches */
674 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
675 t = ip6t_get_target(e);
676 if (t->u.kernel.target->destroy)
677 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
678 module_put(t->u.kernel.target->me);
682 /* Checks and translates the user-supplied table segment (held in
685 translate_table(const char *name,
686 unsigned int valid_hooks,
687 struct xt_table_info *newinfo,
691 const unsigned int *hook_entries,
692 const unsigned int *underflows)
697 newinfo->size = size;
698 newinfo->number = number;
700 /* Init all hooks to impossible value. */
701 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
702 newinfo->hook_entry[i] = 0xFFFFFFFF;
703 newinfo->underflow[i] = 0xFFFFFFFF;
706 duprintf("translate_table: size %u\n", newinfo->size);
708 /* Walk through entries, checking offsets. */
709 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
710 check_entry_size_and_hooks,
714 hook_entries, underflows, &i);
719 duprintf("translate_table: %u not %u entries\n",
724 /* Check hooks all assigned */
725 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
726 /* Only hooks which are valid */
727 if (!(valid_hooks & (1 << i)))
729 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
730 duprintf("Invalid hook entry %u %u\n",
734 if (newinfo->underflow[i] == 0xFFFFFFFF) {
735 duprintf("Invalid underflow %u %u\n",
741 if (!mark_source_chains(newinfo, valid_hooks, entry0))
744 /* Finally, each sanity check must pass */
746 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
747 check_entry, name, size, &i);
750 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
755 /* And one copy for every other CPU */
756 for_each_possible_cpu(i) {
757 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
758 memcpy(newinfo->entries[i], entry0, newinfo->size);
766 add_entry_to_counter(const struct ip6t_entry *e,
767 struct xt_counters total[],
770 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
777 set_entry_to_counter(const struct ip6t_entry *e,
778 struct ip6t_counters total[],
781 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
788 get_counters(const struct xt_table_info *t,
789 struct xt_counters counters[])
795 /* Instead of clearing (by a previous call to memset())
796 * the counters and using adds, we set the counters
797 * with data used by 'current' CPU
798 * We dont care about preemption here.
800 curcpu = raw_smp_processor_id();
803 IP6T_ENTRY_ITERATE(t->entries[curcpu],
805 set_entry_to_counter,
809 for_each_possible_cpu(cpu) {
813 IP6T_ENTRY_ITERATE(t->entries[cpu],
815 add_entry_to_counter,
822 copy_entries_to_user(unsigned int total_size,
823 struct xt_table *table,
824 void __user *userptr)
826 unsigned int off, num, countersize;
827 struct ip6t_entry *e;
828 struct xt_counters *counters;
829 struct xt_table_info *private = table->private;
833 /* We need atomic snapshot of counters: rest doesn't change
834 (other than comefrom, which userspace doesn't care
836 countersize = sizeof(struct xt_counters) * private->number;
837 counters = vmalloc(countersize);
839 if (counters == NULL)
842 /* First, sum counters... */
843 write_lock_bh(&table->lock);
844 get_counters(private, counters);
845 write_unlock_bh(&table->lock);
847 /* choose the copy that is on ourc node/cpu */
848 loc_cpu_entry = private->entries[raw_smp_processor_id()];
849 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
854 /* FIXME: use iterator macros --RR */
855 /* ... then go back and fix counters and names */
856 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
858 struct ip6t_entry_match *m;
859 struct ip6t_entry_target *t;
861 e = (struct ip6t_entry *)(loc_cpu_entry + off);
862 if (copy_to_user(userptr + off
863 + offsetof(struct ip6t_entry, counters),
865 sizeof(counters[num])) != 0) {
870 for (i = sizeof(struct ip6t_entry);
871 i < e->target_offset;
872 i += m->u.match_size) {
875 if (copy_to_user(userptr + off + i
876 + offsetof(struct ip6t_entry_match,
878 m->u.kernel.match->name,
879 strlen(m->u.kernel.match->name)+1)
886 t = ip6t_get_target(e);
887 if (copy_to_user(userptr + off + e->target_offset
888 + offsetof(struct ip6t_entry_target,
890 t->u.kernel.target->name,
891 strlen(t->u.kernel.target->name)+1) != 0) {
903 get_entries(const struct ip6t_get_entries *entries,
904 struct ip6t_get_entries __user *uptr)
909 t = xt_find_table_lock(AF_INET6, entries->name);
910 if (t && !IS_ERR(t)) {
911 struct xt_table_info *private = t->private;
912 duprintf("t->private->number = %u\n", private->number);
913 if (entries->size == private->size)
914 ret = copy_entries_to_user(private->size,
915 t, uptr->entrytable);
917 duprintf("get_entries: I've got %u not %u!\n",
918 private->size, entries->size);
924 ret = t ? PTR_ERR(t) : -ENOENT;
930 do_replace(void __user *user, unsigned int len)
933 struct ip6t_replace tmp;
935 struct xt_table_info *newinfo, *oldinfo;
936 struct xt_counters *counters;
937 void *loc_cpu_entry, *loc_cpu_old_entry;
939 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
943 if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
946 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
949 newinfo = xt_alloc_table_info(tmp.size);
953 /* choose the copy that is on our node/cpu */
954 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
955 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
961 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
967 ret = translate_table(tmp.name, tmp.valid_hooks,
968 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
969 tmp.hook_entry, tmp.underflow);
971 goto free_newinfo_counters;
973 duprintf("ip_tables: Translated table\n");
975 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
976 "ip6table_%s", tmp.name);
977 if (!t || IS_ERR(t)) {
978 ret = t ? PTR_ERR(t) : -ENOENT;
979 goto free_newinfo_counters_untrans;
983 if (tmp.valid_hooks != t->valid_hooks) {
984 duprintf("Valid hook crap: %08X vs %08X\n",
985 tmp.valid_hooks, t->valid_hooks);
990 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
994 /* Update module usage count based on number of rules */
995 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
996 oldinfo->number, oldinfo->initial_entries, newinfo->number);
997 if ((oldinfo->number > oldinfo->initial_entries) ||
998 (newinfo->number <= oldinfo->initial_entries))
1000 if ((oldinfo->number > oldinfo->initial_entries) &&
1001 (newinfo->number <= oldinfo->initial_entries))
1004 /* Get the old counters. */
1005 get_counters(oldinfo, counters);
1006 /* Decrease module usage counts and free resource */
1007 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1008 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1009 xt_free_table_info(oldinfo);
1010 if (copy_to_user(tmp.counters, counters,
1011 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1020 free_newinfo_counters_untrans:
1021 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1022 free_newinfo_counters:
1025 xt_free_table_info(newinfo);
1029 /* We're lazy, and add to the first CPU; overflow works its fey magic
1030 * and everything is OK. */
1032 add_counter_to_entry(struct ip6t_entry *e,
1033 const struct xt_counters addme[],
1037 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1039 (long unsigned int)e->counters.pcnt,
1040 (long unsigned int)e->counters.bcnt,
1041 (long unsigned int)addme[*i].pcnt,
1042 (long unsigned int)addme[*i].bcnt);
1045 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1052 do_add_counters(void __user *user, unsigned int len)
1055 struct xt_counters_info tmp, *paddc;
1056 struct xt_table_info *private;
1059 void *loc_cpu_entry;
1061 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1064 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1067 paddc = vmalloc(len);
1071 if (copy_from_user(paddc, user, len) != 0) {
1076 t = xt_find_table_lock(AF_INET6, tmp.name);
1077 if (!t || IS_ERR(t)) {
1078 ret = t ? PTR_ERR(t) : -ENOENT;
1082 write_lock_bh(&t->lock);
1083 private = t->private;
1084 if (private->number != tmp.num_counters) {
1086 goto unlock_up_free;
1090 /* Choose the copy that is on our node */
1091 loc_cpu_entry = private->entries[smp_processor_id()];
1092 IP6T_ENTRY_ITERATE(loc_cpu_entry,
1094 add_counter_to_entry,
1098 write_unlock_bh(&t->lock);
1108 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1112 if (!capable(CAP_NET_ADMIN))
1116 case IP6T_SO_SET_REPLACE:
1117 ret = do_replace(user, len);
1120 case IP6T_SO_SET_ADD_COUNTERS:
1121 ret = do_add_counters(user, len);
1125 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1133 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1137 if (!capable(CAP_NET_ADMIN))
1141 case IP6T_SO_GET_INFO: {
1142 char name[IP6T_TABLE_MAXNAMELEN];
1145 if (*len != sizeof(struct ip6t_getinfo)) {
1146 duprintf("length %u != %u\n", *len,
1147 sizeof(struct ip6t_getinfo));
1152 if (copy_from_user(name, user, sizeof(name)) != 0) {
1156 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1158 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1159 "ip6table_%s", name);
1160 if (t && !IS_ERR(t)) {
1161 struct ip6t_getinfo info;
1162 struct xt_table_info *private = t->private;
1164 info.valid_hooks = t->valid_hooks;
1165 memcpy(info.hook_entry, private->hook_entry,
1166 sizeof(info.hook_entry));
1167 memcpy(info.underflow, private->underflow,
1168 sizeof(info.underflow));
1169 info.num_entries = private->number;
1170 info.size = private->size;
1171 memcpy(info.name, name, sizeof(info.name));
1173 if (copy_to_user(user, &info, *len) != 0)
1180 ret = t ? PTR_ERR(t) : -ENOENT;
1184 case IP6T_SO_GET_ENTRIES: {
1185 struct ip6t_get_entries get;
1187 if (*len < sizeof(get)) {
1188 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1190 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1192 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1193 duprintf("get_entries: %u != %u\n", *len,
1194 sizeof(struct ip6t_get_entries) + get.size);
1197 ret = get_entries(&get, user);
1201 case IP6T_SO_GET_REVISION_MATCH:
1202 case IP6T_SO_GET_REVISION_TARGET: {
1203 struct ip6t_get_revision rev;
1206 if (*len != sizeof(rev)) {
1210 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1215 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1220 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1223 "ip6t_%s", rev.name);
1228 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1235 int ip6t_register_table(struct xt_table *table,
1236 const struct ip6t_replace *repl)
1239 struct xt_table_info *newinfo;
1240 static struct xt_table_info bootstrap
1241 = { 0, 0, 0, { 0 }, { 0 }, { } };
1242 void *loc_cpu_entry;
1244 newinfo = xt_alloc_table_info(repl->size);
1248 /* choose the copy on our node/cpu */
1249 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1250 memcpy(loc_cpu_entry, repl->entries, repl->size);
1252 ret = translate_table(table->name, table->valid_hooks,
1253 newinfo, loc_cpu_entry, repl->size,
1258 xt_free_table_info(newinfo);
1262 ret = xt_register_table(table, &bootstrap, newinfo);
1264 xt_free_table_info(newinfo);
1271 void ip6t_unregister_table(struct xt_table *table)
1273 struct xt_table_info *private;
1274 void *loc_cpu_entry;
1276 private = xt_unregister_table(table);
1278 /* Decrease module usage counts and free resources */
1279 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1280 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1281 xt_free_table_info(private);
1284 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1286 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1287 u_int8_t type, u_int8_t code,
1290 return (type == test_type && code >= min_code && code <= max_code)
1295 icmp6_match(const struct sk_buff *skb,
1296 const struct net_device *in,
1297 const struct net_device *out,
1298 const struct xt_match *match,
1299 const void *matchinfo,
1301 unsigned int protoff,
1304 struct icmp6hdr _icmp, *ic;
1305 const struct ip6t_icmp *icmpinfo = matchinfo;
1307 /* Must not be a fragment. */
1311 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1313 /* We've been asked to examine this packet, and we
1314 can't. Hence, no choice but to drop. */
1315 duprintf("Dropping evil ICMP tinygram.\n");
1320 return icmp6_type_code_match(icmpinfo->type,
1323 ic->icmp6_type, ic->icmp6_code,
1324 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1327 /* Called when user tries to insert an entry of this type. */
1329 icmp6_checkentry(const char *tablename,
1331 const struct xt_match *match,
1333 unsigned int hook_mask)
1335 const struct ip6t_icmp *icmpinfo = matchinfo;
1337 /* Must specify no unknown invflags */
1338 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1341 /* The built-in targets: standard (NULL) and error. */
1342 static struct xt_target ip6t_standard_target = {
1343 .name = IP6T_STANDARD_TARGET,
1344 .targetsize = sizeof(int),
1348 static struct xt_target ip6t_error_target = {
1349 .name = IP6T_ERROR_TARGET,
1350 .target = ip6t_error,
1351 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
1355 static struct nf_sockopt_ops ip6t_sockopts = {
1357 .set_optmin = IP6T_BASE_CTL,
1358 .set_optmax = IP6T_SO_SET_MAX+1,
1359 .set = do_ip6t_set_ctl,
1360 .get_optmin = IP6T_BASE_CTL,
1361 .get_optmax = IP6T_SO_GET_MAX+1,
1362 .get = do_ip6t_get_ctl,
1365 static struct xt_match icmp6_matchstruct = {
1367 .match = &icmp6_match,
1368 .matchsize = sizeof(struct ip6t_icmp),
1369 .checkentry = icmp6_checkentry,
1370 .proto = IPPROTO_ICMPV6,
1374 static int __init ip6_tables_init(void)
1378 ret = xt_proto_init(AF_INET6);
1382 /* Noone else will be downing sem now, so we won't sleep */
1383 ret = xt_register_target(&ip6t_standard_target);
1386 ret = xt_register_target(&ip6t_error_target);
1389 ret = xt_register_match(&icmp6_matchstruct);
1393 /* Register setsockopt */
1394 ret = nf_register_sockopt(&ip6t_sockopts);
1398 printk("ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1402 xt_unregister_match(&icmp6_matchstruct);
1404 xt_unregister_target(&ip6t_error_target);
1406 xt_unregister_target(&ip6t_standard_target);
1408 xt_proto_fini(AF_INET6);
1413 static void __exit ip6_tables_fini(void)
1415 nf_unregister_sockopt(&ip6t_sockopts);
1416 xt_unregister_match(&icmp6_matchstruct);
1417 xt_unregister_target(&ip6t_error_target);
1418 xt_unregister_target(&ip6t_standard_target);
1419 xt_proto_fini(AF_INET6);
1423 * find the offset to specified header or the protocol number of last header
1424 * if target < 0. "last header" is transport protocol header, ESP, or
1427 * If target header is found, its offset is set in *offset and return protocol
1428 * number. Otherwise, return -1.
1430 * If the first fragment doesn't contain the final protocol header or
1431 * NEXTHDR_NONE it is considered invalid.
1433 * Note that non-1st fragment is special case that "the protocol number
1434 * of last header" is "next header" field in Fragment header. In this case,
1435 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1439 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1440 int target, unsigned short *fragoff)
1442 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
1443 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
1444 unsigned int len = skb->len - start;
1449 while (nexthdr != target) {
1450 struct ipv6_opt_hdr _hdr, *hp;
1451 unsigned int hdrlen;
1453 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1459 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1462 if (nexthdr == NEXTHDR_FRAGMENT) {
1463 unsigned short _frag_off;
1465 fp = skb_header_pointer(skb,
1466 start+offsetof(struct frag_hdr,
1473 _frag_off = ntohs(*fp) & ~0x7;
1476 ((!ipv6_ext_hdr(hp->nexthdr)) ||
1477 hp->nexthdr == NEXTHDR_NONE)) {
1479 *fragoff = _frag_off;
1485 } else if (nexthdr == NEXTHDR_AUTH)
1486 hdrlen = (hp->hdrlen + 2) << 2;
1488 hdrlen = ipv6_optlen(hp);
1490 nexthdr = hp->nexthdr;
1499 EXPORT_SYMBOL(ip6t_register_table);
1500 EXPORT_SYMBOL(ip6t_unregister_table);
1501 EXPORT_SYMBOL(ip6t_do_table);
1502 EXPORT_SYMBOL(ip6t_ext_hdr);
1503 EXPORT_SYMBOL(ipv6_find_hdr);
1505 module_init(ip6_tables_init);
1506 module_exit(ip6_tables_fini);