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
)
108 const struct ipv6hdr
*ipv6
= ipv6_hdr(skb
);
110 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
112 if (FWINV(ipv6_masked_addr_cmp(&ipv6
->saddr
, &ip6info
->smsk
,
113 &ip6info
->src
), IP6T_INV_SRCIP
)
114 || FWINV(ipv6_masked_addr_cmp(&ipv6
->daddr
, &ip6info
->dmsk
,
115 &ip6info
->dst
), IP6T_INV_DSTIP
)) {
116 dprintf("Source or dest mismatch.\n");
118 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
119 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
120 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
121 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
122 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
123 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
127 ret
= ifname_compare_aligned(indev
, ip6info
->iniface
, ip6info
->iniface_mask
);
129 if (FWINV(ret
!= 0, IP6T_INV_VIA_IN
)) {
130 dprintf("VIA in mismatch (%s vs %s).%s\n",
131 indev
, ip6info
->iniface
,
132 ip6info
->invflags
&IP6T_INV_VIA_IN
?" (INV)":"");
136 ret
= ifname_compare_aligned(outdev
, ip6info
->outiface
, ip6info
->outiface_mask
);
138 if (FWINV(ret
!= 0, IP6T_INV_VIA_OUT
)) {
139 dprintf("VIA out mismatch (%s vs %s).%s\n",
140 outdev
, ip6info
->outiface
,
141 ip6info
->invflags
&IP6T_INV_VIA_OUT
?" (INV)":"");
145 /* ... might want to do something with class and flowlabel here ... */
147 /* look for the desired protocol header */
148 if((ip6info
->flags
& IP6T_F_PROTO
)) {
150 unsigned short _frag_off
;
152 protohdr
= ipv6_find_hdr(skb
, protoff
, -1, &_frag_off
);
158 *fragoff
= _frag_off
;
160 dprintf("Packet protocol %hi ?= %s%hi.\n",
162 ip6info
->invflags
& IP6T_INV_PROTO
? "!":"",
165 if (ip6info
->proto
== protohdr
) {
166 if(ip6info
->invflags
& IP6T_INV_PROTO
) {
172 /* We need match for the '-p all', too! */
173 if ((ip6info
->proto
!= 0) &&
174 !(ip6info
->invflags
& IP6T_INV_PROTO
))
180 /* should be ip6 safe */
182 ip6_checkentry(const struct ip6t_ip6
*ipv6
)
184 if (ipv6
->flags
& ~IP6T_F_MASK
) {
185 duprintf("Unknown flag bits set: %08X\n",
186 ipv6
->flags
& ~IP6T_F_MASK
);
189 if (ipv6
->invflags
& ~IP6T_INV_MASK
) {
190 duprintf("Unknown invflag bits set: %08X\n",
191 ipv6
->invflags
& ~IP6T_INV_MASK
);
198 ip6t_error(struct sk_buff
*skb
,
199 const struct net_device
*in
,
200 const struct net_device
*out
,
201 unsigned int hooknum
,
202 const struct xt_target
*target
,
203 const void *targinfo
)
206 printk("ip6_tables: error: `%s'\n", (char *)targinfo
);
212 int do_match(struct ip6t_entry_match
*m
,
213 const struct sk_buff
*skb
,
214 const struct net_device
*in
,
215 const struct net_device
*out
,
217 unsigned int protoff
,
220 /* Stop iteration if it doesn't match */
221 if (!m
->u
.kernel
.match
->match(skb
, in
, out
, m
->u
.kernel
.match
, m
->data
,
222 offset
, protoff
, hotdrop
))
228 static inline struct ip6t_entry
*
229 get_entry(void *base
, unsigned int offset
)
231 return (struct ip6t_entry
*)(base
+ offset
);
234 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
236 ip6t_do_table(struct sk_buff
*skb
,
238 const struct net_device
*in
,
239 const struct net_device
*out
,
240 struct xt_table
*table
)
242 static const char nulldevname
[IFNAMSIZ
] __attribute__((aligned(sizeof(long))));
244 unsigned int protoff
= 0;
246 /* Initializing verdict to NF_DROP keeps gcc happy. */
247 unsigned int verdict
= NF_DROP
;
248 const char *indev
, *outdev
;
250 struct ip6t_entry
*e
, *back
;
251 struct xt_table_info
*private;
254 indev
= in
? in
->name
: nulldevname
;
255 outdev
= out
? out
->name
: nulldevname
;
256 /* We handle fragments by dealing with the first fragment as
257 * if it was a normal packet. All other fragments are treated
258 * normally, except that they will NEVER match rules that ask
259 * things we don't know, ie. tcp syn flag or ports). If the
260 * rule is also a fragment-specific rule, non-fragments won't
263 IP_NF_ASSERT(table
->valid_hooks
& (1 << hook
));
266 private = table
->private;
267 table_base
= private->entries
[smp_processor_id()];
269 e
= get_entry(table_base
, private->hook_entry
[hook
]);
271 /* For return from builtin chain */
272 back
= get_entry(table_base
, private->underflow
[hook
]);
277 if (ip6_packet_match(skb
, indev
, outdev
, &e
->ipv6
,
278 &protoff
, &offset
, &hotdrop
)) {
279 struct ip6t_entry_target
*t
;
281 if (IP6T_MATCH_ITERATE(e
, do_match
,
283 offset
, protoff
, &hotdrop
) != 0)
286 ADD_COUNTER(e
->counters
,
287 ntohs(ipv6_hdr(skb
)->payload_len
)
291 t
= ip6t_get_target(e
);
292 IP_NF_ASSERT(t
->u
.kernel
.target
);
293 /* Standard target? */
294 if (!t
->u
.kernel
.target
->target
) {
297 v
= ((struct ip6t_standard_target
*)t
)->verdict
;
299 /* Pop from stack? */
300 if (v
!= IP6T_RETURN
) {
301 verdict
= (unsigned)(-v
) - 1;
305 back
= get_entry(table_base
,
309 if (table_base
+ v
!= (void *)e
+ e
->next_offset
310 && !(e
->ipv6
.flags
& IP6T_F_GOTO
)) {
311 /* Save old back ptr in next entry */
312 struct ip6t_entry
*next
313 = (void *)e
+ e
->next_offset
;
315 = (void *)back
- table_base
;
316 /* set back pointer to next entry */
320 e
= get_entry(table_base
, v
);
322 /* Targets which reenter must return
324 #ifdef CONFIG_NETFILTER_DEBUG
325 ((struct ip6t_entry
*)table_base
)->comefrom
328 verdict
= t
->u
.kernel
.target
->target(skb
,
334 #ifdef CONFIG_NETFILTER_DEBUG
335 if (((struct ip6t_entry
*)table_base
)->comefrom
337 && verdict
== IP6T_CONTINUE
) {
338 printk("Target %s reentered!\n",
339 t
->u
.kernel
.target
->name
);
342 ((struct ip6t_entry
*)table_base
)->comefrom
345 if (verdict
== IP6T_CONTINUE
)
346 e
= (void *)e
+ e
->next_offset
;
347 else if (verdict
== IP6T_RETURN
) { // added -- zzz
349 back
= get_entry(table_base
, back
->comefrom
);
359 e
= (void *)e
+ e
->next_offset
;
363 #ifdef CONFIG_NETFILTER_DEBUG
364 ((struct ip6t_entry
*)table_base
)->comefrom
= NETFILTER_LINK_POISON
;
366 xt_info_rdunlock_bh();
368 #ifdef DEBUG_ALLOW_ALL
377 /* All zeroes == unconditional rule. */
379 unconditional(const struct ip6t_ip6
*ipv6
)
383 for (i
= 0; i
< sizeof(*ipv6
); i
++)
384 if (((char *)ipv6
)[i
])
387 return (i
== sizeof(*ipv6
));
390 /* Figures out from what hook each rule can be called: returns 0 if
391 there are loops. Puts hook bitmask in comefrom. */
393 mark_source_chains(struct xt_table_info
*newinfo
,
394 unsigned int valid_hooks
, void *entry0
)
398 /* No recursion; use packet counter to save back ptrs (reset
399 to 0 as we leave), and comefrom to save source hook bitmask */
400 for (hook
= 0; hook
< NF_IP6_NUMHOOKS
; hook
++) {
401 unsigned int pos
= newinfo
->hook_entry
[hook
];
403 = (struct ip6t_entry
*)(entry0
+ pos
);
404 int visited
= e
->comefrom
& (1 << hook
);
406 if (!(valid_hooks
& (1 << hook
)))
409 /* Set initial back pointer. */
410 e
->counters
.pcnt
= pos
;
413 struct ip6t_standard_target
*t
414 = (void *)ip6t_get_target(e
);
416 if (e
->comefrom
& (1 << NF_IP6_NUMHOOKS
)) {
417 printk("iptables: loop hook %u pos %u %08X.\n",
418 hook
, pos
, e
->comefrom
);
422 |= ((1 << hook
) | (1 << NF_IP6_NUMHOOKS
));
424 /* Unconditional return/END. */
425 if ((e
->target_offset
== sizeof(struct ip6t_entry
)
426 && (strcmp(t
->target
.u
.user
.name
,
427 IP6T_STANDARD_TARGET
) == 0)
429 && unconditional(&e
->ipv6
)) || visited
) {
430 unsigned int oldpos
, size
;
432 if ((strcmp(t
->target
.u
.user
.name
,
433 IP6T_STANDARD_TARGET
) == 0) &&
434 t
->verdict
< -NF_MAX_VERDICT
- 1) {
435 duprintf("mark_source_chains: bad "
436 "negative verdict (%i)\n",
441 /* Return: backtrack through the last
444 e
->comefrom
^= (1<<NF_IP6_NUMHOOKS
);
445 #ifdef DEBUG_IP_FIREWALL_USER
447 & (1 << NF_IP6_NUMHOOKS
)) {
448 duprintf("Back unset "
455 pos
= e
->counters
.pcnt
;
456 e
->counters
.pcnt
= 0;
458 /* We're at the start. */
462 e
= (struct ip6t_entry
*)
464 } while (oldpos
== pos
+ e
->next_offset
);
467 size
= e
->next_offset
;
468 e
= (struct ip6t_entry
*)
469 (entry0
+ pos
+ size
);
470 e
->counters
.pcnt
= pos
;
473 int newpos
= t
->verdict
;
475 if (strcmp(t
->target
.u
.user
.name
,
476 IP6T_STANDARD_TARGET
) == 0
478 if (newpos
> newinfo
->size
-
479 sizeof(struct ip6t_entry
)) {
480 duprintf("mark_source_chains: "
481 "bad verdict (%i)\n",
485 /* This a jump; chase it. */
486 duprintf("Jump rule %u -> %u\n",
489 /* ... this is a fallthru */
490 newpos
= pos
+ e
->next_offset
;
492 e
= (struct ip6t_entry
*)
494 e
->counters
.pcnt
= pos
;
499 duprintf("Finished chain %u\n", hook
);
505 cleanup_match(struct ip6t_entry_match
*m
, unsigned int *i
)
507 if (i
&& (*i
)-- == 0)
510 if (m
->u
.kernel
.match
->destroy
)
511 m
->u
.kernel
.match
->destroy(m
->u
.kernel
.match
, m
->data
);
512 module_put(m
->u
.kernel
.match
->me
);
517 check_match(struct ip6t_entry_match
*m
,
519 const struct ip6t_ip6
*ipv6
,
520 unsigned int hookmask
,
523 struct xt_match
*match
;
526 match
= try_then_request_module(xt_find_match(AF_INET6
, m
->u
.user
.name
,
528 "ip6t_%s", m
->u
.user
.name
);
529 if (IS_ERR(match
) || !match
) {
530 duprintf("check_match: `%s' not found\n", m
->u
.user
.name
);
531 return match
? PTR_ERR(match
) : -ENOENT
;
533 m
->u
.kernel
.match
= match
;
535 ret
= xt_check_match(match
, AF_INET6
, m
->u
.match_size
- sizeof(*m
),
536 name
, hookmask
, ipv6
->proto
,
537 ipv6
->invflags
& IP6T_INV_PROTO
);
541 if (m
->u
.kernel
.match
->checkentry
542 && !m
->u
.kernel
.match
->checkentry(name
, ipv6
, match
, m
->data
,
544 duprintf("ip_tables: check failed for `%s'.\n",
545 m
->u
.kernel
.match
->name
);
553 module_put(m
->u
.kernel
.match
->me
);
557 static struct xt_target ip6t_standard_target
;
560 check_entry(struct ip6t_entry
*e
, const char *name
, unsigned int size
,
563 struct ip6t_entry_target
*t
;
564 struct xt_target
*target
;
568 if (!ip6_checkentry(&e
->ipv6
)) {
569 duprintf("ip_tables: ip check failed %p %s.\n", e
, name
);
573 if (e
->target_offset
+ sizeof(struct ip6t_entry_target
) >
578 ret
= IP6T_MATCH_ITERATE(e
, check_match
, name
, &e
->ipv6
, e
->comefrom
, &j
);
580 goto cleanup_matches
;
582 t
= ip6t_get_target(e
);
584 if (e
->target_offset
+ t
->u
.target_size
> e
->next_offset
)
585 goto cleanup_matches
;
586 target
= try_then_request_module(xt_find_target(AF_INET6
,
589 "ip6t_%s", t
->u
.user
.name
);
590 if (IS_ERR(target
) || !target
) {
591 duprintf("check_entry: `%s' not found\n", t
->u
.user
.name
);
592 ret
= target
? PTR_ERR(target
) : -ENOENT
;
593 goto cleanup_matches
;
595 t
->u
.kernel
.target
= target
;
597 ret
= xt_check_target(target
, AF_INET6
, t
->u
.target_size
- sizeof(*t
),
598 name
, e
->comefrom
, e
->ipv6
.proto
,
599 e
->ipv6
.invflags
& IP6T_INV_PROTO
);
603 if (t
->u
.kernel
.target
->checkentry
604 && !t
->u
.kernel
.target
->checkentry(name
, e
, target
, t
->data
,
606 duprintf("ip_tables: check failed for `%s'.\n",
607 t
->u
.kernel
.target
->name
);
615 module_put(t
->u
.kernel
.target
->me
);
617 IP6T_MATCH_ITERATE(e
, cleanup_match
, &j
);
622 check_entry_size_and_hooks(struct ip6t_entry
*e
,
623 struct xt_table_info
*newinfo
,
625 unsigned char *limit
,
626 const unsigned int *hook_entries
,
627 const unsigned int *underflows
,
632 if ((unsigned long)e
% __alignof__(struct ip6t_entry
) != 0
633 || (unsigned char *)e
+ sizeof(struct ip6t_entry
) >= limit
) {
634 duprintf("Bad offset %p\n", e
);
639 < sizeof(struct ip6t_entry
) + sizeof(struct ip6t_entry_target
)) {
640 duprintf("checking: element %p size %u\n",
645 /* Check hooks & underflows */
646 for (h
= 0; h
< NF_IP6_NUMHOOKS
; h
++) {
647 if ((unsigned char *)e
- base
== hook_entries
[h
])
648 newinfo
->hook_entry
[h
] = hook_entries
[h
];
649 if ((unsigned char *)e
- base
== underflows
[h
])
650 newinfo
->underflow
[h
] = underflows
[h
];
653 /* FIXME: underflows must be unconditional, standard verdicts
654 < 0 (not IP6T_RETURN). --RR */
656 /* Clear counters and comefrom */
657 e
->counters
= ((struct xt_counters
) { 0, 0 });
665 cleanup_entry(struct ip6t_entry
*e
, unsigned int *i
)
667 struct ip6t_entry_target
*t
;
669 if (i
&& (*i
)-- == 0)
672 /* Cleanup all matches */
673 IP6T_MATCH_ITERATE(e
, cleanup_match
, NULL
);
674 t
= ip6t_get_target(e
);
675 if (t
->u
.kernel
.target
->destroy
)
676 t
->u
.kernel
.target
->destroy(t
->u
.kernel
.target
, t
->data
);
677 module_put(t
->u
.kernel
.target
->me
);
681 /* Checks and translates the user-supplied table segment (held in
684 translate_table(const char *name
,
685 unsigned int valid_hooks
,
686 struct xt_table_info
*newinfo
,
690 const unsigned int *hook_entries
,
691 const unsigned int *underflows
)
696 newinfo
->size
= size
;
697 newinfo
->number
= number
;
699 /* Init all hooks to impossible value. */
700 for (i
= 0; i
< NF_IP6_NUMHOOKS
; i
++) {
701 newinfo
->hook_entry
[i
] = 0xFFFFFFFF;
702 newinfo
->underflow
[i
] = 0xFFFFFFFF;
705 duprintf("translate_table: size %u\n", newinfo
->size
);
707 /* Walk through entries, checking offsets. */
708 ret
= IP6T_ENTRY_ITERATE(entry0
, newinfo
->size
,
709 check_entry_size_and_hooks
,
713 hook_entries
, underflows
, &i
);
718 duprintf("translate_table: %u not %u entries\n",
723 /* Check hooks all assigned */
724 for (i
= 0; i
< NF_IP6_NUMHOOKS
; i
++) {
725 /* Only hooks which are valid */
726 if (!(valid_hooks
& (1 << i
)))
728 if (newinfo
->hook_entry
[i
] == 0xFFFFFFFF) {
729 duprintf("Invalid hook entry %u %u\n",
733 if (newinfo
->underflow
[i
] == 0xFFFFFFFF) {
734 duprintf("Invalid underflow %u %u\n",
740 if (!mark_source_chains(newinfo
, valid_hooks
, entry0
))
743 /* Finally, each sanity check must pass */
745 ret
= IP6T_ENTRY_ITERATE(entry0
, newinfo
->size
,
746 check_entry
, name
, size
, &i
);
749 IP6T_ENTRY_ITERATE(entry0
, newinfo
->size
,
754 /* And one copy for every other CPU */
755 for_each_possible_cpu(i
) {
756 if (newinfo
->entries
[i
] && newinfo
->entries
[i
] != entry0
)
757 memcpy(newinfo
->entries
[i
], entry0
, newinfo
->size
);
765 add_entry_to_counter(const struct ip6t_entry
*e
,
766 struct xt_counters total
[],
769 ADD_COUNTER(total
[*i
], e
->counters
.bcnt
, e
->counters
.pcnt
);
776 set_entry_to_counter(const struct ip6t_entry
*e
,
777 struct ip6t_counters total
[],
780 SET_COUNTER(total
[*i
], e
->counters
.bcnt
, e
->counters
.pcnt
);
787 get_counters(const struct xt_table_info
*t
,
788 struct xt_counters counters
[])
794 /* Instead of clearing (by a previous call to memset())
795 * the counters and using adds, we set the counters
796 * with data used by 'current' CPU
798 * Bottom half has to be disabled to prevent deadlock
799 * if new softirq were to run and call ipt_do_table
802 curcpu
= smp_processor_id();
805 IP6T_ENTRY_ITERATE(t
->entries
[curcpu
],
807 set_entry_to_counter
,
811 for_each_possible_cpu(cpu
) {
816 IP6T_ENTRY_ITERATE(t
->entries
[cpu
],
818 add_entry_to_counter
,
821 xt_info_wrunlock(cpu
);
826 static inline struct xt_counters
*alloc_counters(struct xt_table
*table
)
828 unsigned int countersize
;
829 struct xt_counters
*counters
;
830 struct xt_table_info
*private = table
->private;
832 /* We need atomic snapshot of counters: rest doesn't change
833 (other than comefrom, which userspace doesn't care
835 countersize
= sizeof(struct xt_counters
) * private->number
;
836 counters
= vmalloc(countersize
);
838 if (counters
== NULL
)
839 return ERR_PTR(-ENOMEM
);
841 get_counters(private, counters
);
847 copy_entries_to_user(unsigned int total_size
,
848 struct xt_table
*table
,
849 void __user
*userptr
)
851 unsigned int off
, num
;
852 struct ip6t_entry
*e
;
853 struct xt_counters
*counters
;
854 struct xt_table_info
*private = table
->private;
858 counters
= alloc_counters(table
);
859 if (IS_ERR(counters
))
860 return PTR_ERR(counters
);
862 /* choose the copy that is on ourc node/cpu */
863 loc_cpu_entry
= private->entries
[raw_smp_processor_id()];
864 if (copy_to_user(userptr
, loc_cpu_entry
, total_size
) != 0) {
869 /* FIXME: use iterator macros --RR */
870 /* ... then go back and fix counters and names */
871 for (off
= 0, num
= 0; off
< total_size
; off
+= e
->next_offset
, num
++){
873 struct ip6t_entry_match
*m
;
874 struct ip6t_entry_target
*t
;
876 e
= (struct ip6t_entry
*)(loc_cpu_entry
+ off
);
877 if (copy_to_user(userptr
+ off
878 + offsetof(struct ip6t_entry
, counters
),
880 sizeof(counters
[num
])) != 0) {
885 for (i
= sizeof(struct ip6t_entry
);
886 i
< e
->target_offset
;
887 i
+= m
->u
.match_size
) {
890 if (copy_to_user(userptr
+ off
+ i
891 + offsetof(struct ip6t_entry_match
,
893 m
->u
.kernel
.match
->name
,
894 strlen(m
->u
.kernel
.match
->name
)+1)
901 t
= ip6t_get_target(e
);
902 if (copy_to_user(userptr
+ off
+ e
->target_offset
903 + offsetof(struct ip6t_entry_target
,
905 t
->u
.kernel
.target
->name
,
906 strlen(t
->u
.kernel
.target
->name
)+1) != 0) {
918 get_entries(const struct ip6t_get_entries
*entries
,
919 struct ip6t_get_entries __user
*uptr
)
924 t
= xt_find_table_lock(AF_INET6
, entries
->name
);
925 if (t
&& !IS_ERR(t
)) {
926 struct xt_table_info
*private = t
->private;
927 duprintf("t->private->number = %u\n", private->number
);
928 if (entries
->size
== private->size
)
929 ret
= copy_entries_to_user(private->size
,
930 t
, uptr
->entrytable
);
932 duprintf("get_entries: I've got %u not %u!\n",
933 private->size
, entries
->size
);
939 ret
= t
? PTR_ERR(t
) : -ENOENT
;
945 do_replace(void __user
*user
, unsigned int len
)
948 struct ip6t_replace tmp
;
950 struct xt_table_info
*newinfo
, *oldinfo
;
951 struct xt_counters
*counters
;
952 void *loc_cpu_entry
, *loc_cpu_old_entry
;
954 if (copy_from_user(&tmp
, user
, sizeof(tmp
)) != 0)
958 if (tmp
.num_counters
>= INT_MAX
/ sizeof(struct xt_counters
))
961 newinfo
= xt_alloc_table_info(tmp
.size
);
965 /* choose the copy that is on our node/cpu */
966 loc_cpu_entry
= newinfo
->entries
[raw_smp_processor_id()];
967 if (copy_from_user(loc_cpu_entry
, user
+ sizeof(tmp
),
973 counters
= vmalloc(tmp
.num_counters
* sizeof(struct xt_counters
));
979 ret
= translate_table(tmp
.name
, tmp
.valid_hooks
,
980 newinfo
, loc_cpu_entry
, tmp
.size
, tmp
.num_entries
,
981 tmp
.hook_entry
, tmp
.underflow
);
983 goto free_newinfo_counters
;
985 duprintf("ip_tables: Translated table\n");
987 t
= try_then_request_module(xt_find_table_lock(AF_INET6
, tmp
.name
),
988 "ip6table_%s", tmp
.name
);
989 if (!t
|| IS_ERR(t
)) {
990 ret
= t
? PTR_ERR(t
) : -ENOENT
;
991 goto free_newinfo_counters_untrans
;
995 if (tmp
.valid_hooks
!= t
->valid_hooks
) {
996 duprintf("Valid hook crap: %08X vs %08X\n",
997 tmp
.valid_hooks
, t
->valid_hooks
);
1002 oldinfo
= xt_replace_table(t
, tmp
.num_counters
, newinfo
, &ret
);
1006 /* Update module usage count based on number of rules */
1007 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1008 oldinfo
->number
, oldinfo
->initial_entries
, newinfo
->number
);
1009 if ((oldinfo
->number
> oldinfo
->initial_entries
) ||
1010 (newinfo
->number
<= oldinfo
->initial_entries
))
1012 if ((oldinfo
->number
> oldinfo
->initial_entries
) &&
1013 (newinfo
->number
<= oldinfo
->initial_entries
))
1016 /* Get the old counters, and synchronize with replace */
1017 get_counters(oldinfo
, counters
);
1019 /* Decrease module usage counts and free resource */
1020 loc_cpu_old_entry
= oldinfo
->entries
[raw_smp_processor_id()];
1021 IP6T_ENTRY_ITERATE(loc_cpu_old_entry
, oldinfo
->size
, cleanup_entry
,NULL
);
1022 xt_free_table_info(oldinfo
);
1023 if (copy_to_user(tmp
.counters
, counters
,
1024 sizeof(struct xt_counters
) * tmp
.num_counters
) != 0)
1033 free_newinfo_counters_untrans
:
1034 IP6T_ENTRY_ITERATE(loc_cpu_entry
, newinfo
->size
, cleanup_entry
,NULL
);
1035 free_newinfo_counters
:
1038 xt_free_table_info(newinfo
);
1042 /* We're lazy, and add to the first CPU; overflow works its fey magic
1043 * and everything is OK. */
1045 add_counter_to_entry(struct ip6t_entry
*e
,
1046 const struct xt_counters addme
[],
1049 ADD_COUNTER(e
->counters
, addme
[*i
].bcnt
, addme
[*i
].pcnt
);
1056 do_add_counters(void __user
*user
, unsigned int len
)
1058 unsigned int i
, curcpu
;
1059 struct xt_counters_info tmp
, *paddc
;
1060 struct xt_table_info
*private;
1063 void *loc_cpu_entry
;
1065 if (copy_from_user(&tmp
, user
, sizeof(tmp
)) != 0)
1068 if (len
!= sizeof(tmp
) + tmp
.num_counters
*sizeof(struct xt_counters
))
1071 paddc
= vmalloc(len
);
1075 if (copy_from_user(paddc
, user
, len
) != 0) {
1080 t
= xt_find_table_lock(AF_INET6
, tmp
.name
);
1081 if (!t
|| IS_ERR(t
)) {
1082 ret
= t
? PTR_ERR(t
) : -ENOENT
;
1088 private = t
->private;
1089 if (private->number
!= tmp
.num_counters
) {
1091 goto unlock_up_free
;
1095 /* Choose the copy that is on our node */
1096 curcpu
= smp_processor_id();
1097 xt_info_wrlock(curcpu
);
1098 loc_cpu_entry
= private->entries
[curcpu
];
1099 IP6T_ENTRY_ITERATE(loc_cpu_entry
,
1101 add_counter_to_entry
,
1104 xt_info_wrunlock(curcpu
);
1117 do_ip6t_set_ctl(struct sock
*sk
, int cmd
, void __user
*user
, unsigned int len
)
1121 if (!capable(CAP_NET_ADMIN
))
1125 case IP6T_SO_SET_REPLACE
:
1126 ret
= do_replace(user
, len
);
1129 case IP6T_SO_SET_ADD_COUNTERS
:
1130 ret
= do_add_counters(user
, len
);
1134 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd
);
1142 do_ip6t_get_ctl(struct sock
*sk
, int cmd
, void __user
*user
, int *len
)
1146 if (!capable(CAP_NET_ADMIN
))
1150 case IP6T_SO_GET_INFO
: {
1151 char name
[IP6T_TABLE_MAXNAMELEN
];
1154 if (*len
!= sizeof(struct ip6t_getinfo
)) {
1155 duprintf("length %u != %u\n", *len
,
1156 sizeof(struct ip6t_getinfo
));
1161 if (copy_from_user(name
, user
, sizeof(name
)) != 0) {
1165 name
[IP6T_TABLE_MAXNAMELEN
-1] = '\0';
1167 t
= try_then_request_module(xt_find_table_lock(AF_INET6
, name
),
1168 "ip6table_%s", name
);
1169 if (t
&& !IS_ERR(t
)) {
1170 struct ip6t_getinfo info
;
1171 struct xt_table_info
*private = t
->private;
1173 info
.valid_hooks
= t
->valid_hooks
;
1174 memcpy(info
.hook_entry
, private->hook_entry
,
1175 sizeof(info
.hook_entry
));
1176 memcpy(info
.underflow
, private->underflow
,
1177 sizeof(info
.underflow
));
1178 info
.num_entries
= private->number
;
1179 info
.size
= private->size
;
1180 memcpy(info
.name
, name
, sizeof(info
.name
));
1182 if (copy_to_user(user
, &info
, *len
) != 0)
1189 ret
= t
? PTR_ERR(t
) : -ENOENT
;
1193 case IP6T_SO_GET_ENTRIES
: {
1194 struct ip6t_get_entries get
;
1196 if (*len
< sizeof(get
)) {
1197 duprintf("get_entries: %u < %u\n", *len
, sizeof(get
));
1199 } else if (copy_from_user(&get
, user
, sizeof(get
)) != 0) {
1201 } else if (*len
!= sizeof(struct ip6t_get_entries
) + get
.size
) {
1202 duprintf("get_entries: %u != %u\n", *len
,
1203 sizeof(struct ip6t_get_entries
) + get
.size
);
1206 ret
= get_entries(&get
, user
);
1210 case IP6T_SO_GET_REVISION_MATCH
:
1211 case IP6T_SO_GET_REVISION_TARGET
: {
1212 struct ip6t_get_revision rev
;
1215 if (*len
!= sizeof(rev
)) {
1219 if (copy_from_user(&rev
, user
, sizeof(rev
)) != 0) {
1224 if (cmd
== IP6T_SO_GET_REVISION_TARGET
)
1229 try_then_request_module(xt_find_revision(AF_INET6
, rev
.name
,
1232 "ip6t_%s", rev
.name
);
1237 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd
);
1244 int ip6t_register_table(struct xt_table
*table
,
1245 const struct ip6t_replace
*repl
)
1248 struct xt_table_info
*newinfo
;
1249 struct xt_table_info bootstrap
1250 = { 0, 0, 0, { 0 }, { 0 }, { } };
1251 void *loc_cpu_entry
;
1253 newinfo
= xt_alloc_table_info(repl
->size
);
1257 /* choose the copy on our node/cpu */
1258 loc_cpu_entry
= newinfo
->entries
[raw_smp_processor_id()];
1259 memcpy(loc_cpu_entry
, repl
->entries
, repl
->size
);
1261 ret
= translate_table(table
->name
, table
->valid_hooks
,
1262 newinfo
, loc_cpu_entry
, repl
->size
,
1267 xt_free_table_info(newinfo
);
1271 ret
= xt_register_table(table
, &bootstrap
, newinfo
);
1273 xt_free_table_info(newinfo
);
1280 void ip6t_unregister_table(struct xt_table
*table
)
1282 struct xt_table_info
*private;
1283 void *loc_cpu_entry
;
1285 private = xt_unregister_table(table
);
1287 /* Decrease module usage counts and free resources */
1288 loc_cpu_entry
= private->entries
[raw_smp_processor_id()];
1289 IP6T_ENTRY_ITERATE(loc_cpu_entry
, private->size
, cleanup_entry
, NULL
);
1290 xt_free_table_info(private);
1293 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1295 icmp6_type_code_match(u_int8_t test_type
, u_int8_t min_code
, u_int8_t max_code
,
1296 u_int8_t type
, u_int8_t code
,
1299 return (type
== test_type
&& code
>= min_code
&& code
<= max_code
)
1304 icmp6_match(const struct sk_buff
*skb
,
1305 const struct net_device
*in
,
1306 const struct net_device
*out
,
1307 const struct xt_match
*match
,
1308 const void *matchinfo
,
1310 unsigned int protoff
,
1313 struct icmp6hdr _icmp
, *ic
;
1314 const struct ip6t_icmp
*icmpinfo
= matchinfo
;
1316 /* Must not be a fragment. */
1320 ic
= skb_header_pointer(skb
, protoff
, sizeof(_icmp
), &_icmp
);
1322 /* We've been asked to examine this packet, and we
1323 can't. Hence, no choice but to drop. */
1324 duprintf("Dropping evil ICMP tinygram.\n");
1329 return icmp6_type_code_match(icmpinfo
->type
,
1332 ic
->icmp6_type
, ic
->icmp6_code
,
1333 !!(icmpinfo
->invflags
&IP6T_ICMP_INV
));
1336 /* Called when user tries to insert an entry of this type. */
1338 icmp6_checkentry(const char *tablename
,
1340 const struct xt_match
*match
,
1342 unsigned int hook_mask
)
1344 const struct ip6t_icmp
*icmpinfo
= matchinfo
;
1346 /* Must specify no unknown invflags */
1347 return !(icmpinfo
->invflags
& ~IP6T_ICMP_INV
);
1350 /* The built-in targets: standard (NULL) and error. */
1351 static struct xt_target ip6t_standard_target
= {
1352 .name
= IP6T_STANDARD_TARGET
,
1353 .targetsize
= sizeof(int),
1357 static struct xt_target ip6t_error_target
= {
1358 .name
= IP6T_ERROR_TARGET
,
1359 .target
= ip6t_error
,
1360 .targetsize
= IP6T_FUNCTION_MAXNAMELEN
,
1364 static struct nf_sockopt_ops ip6t_sockopts
= {
1366 .set_optmin
= IP6T_BASE_CTL
,
1367 .set_optmax
= IP6T_SO_SET_MAX
+1,
1368 .set
= do_ip6t_set_ctl
,
1369 .get_optmin
= IP6T_BASE_CTL
,
1370 .get_optmax
= IP6T_SO_GET_MAX
+1,
1371 .get
= do_ip6t_get_ctl
,
1374 static struct xt_match icmp6_matchstruct
= {
1376 .match
= &icmp6_match
,
1377 .matchsize
= sizeof(struct ip6t_icmp
),
1378 .checkentry
= icmp6_checkentry
,
1379 .proto
= IPPROTO_ICMPV6
,
1383 static int __init
ip6_tables_init(void)
1387 ret
= xt_proto_init(AF_INET6
);
1391 /* Noone else will be downing sem now, so we won't sleep */
1392 ret
= xt_register_target(&ip6t_standard_target
);
1395 ret
= xt_register_target(&ip6t_error_target
);
1398 ret
= xt_register_match(&icmp6_matchstruct
);
1402 /* Register setsockopt */
1403 ret
= nf_register_sockopt(&ip6t_sockopts
);
1407 printk("ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1411 xt_unregister_match(&icmp6_matchstruct
);
1413 xt_unregister_target(&ip6t_error_target
);
1415 xt_unregister_target(&ip6t_standard_target
);
1417 xt_proto_fini(AF_INET6
);
1422 static void __exit
ip6_tables_fini(void)
1424 nf_unregister_sockopt(&ip6t_sockopts
);
1425 xt_unregister_match(&icmp6_matchstruct
);
1426 xt_unregister_target(&ip6t_error_target
);
1427 xt_unregister_target(&ip6t_standard_target
);
1428 xt_proto_fini(AF_INET6
);
1432 * find the offset to specified header or the protocol number of last header
1433 * if target < 0. "last header" is transport protocol header, ESP, or
1436 * If target header is found, its offset is set in *offset and return protocol
1437 * number. Otherwise, return -1.
1439 * If the first fragment doesn't contain the final protocol header or
1440 * NEXTHDR_NONE it is considered invalid.
1442 * Note that non-1st fragment is special case that "the protocol number
1443 * of last header" is "next header" field in Fragment header. In this case,
1444 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1448 int ipv6_find_hdr(const struct sk_buff
*skb
, unsigned int *offset
,
1449 int target
, unsigned short *fragoff
)
1451 unsigned int start
= skb_network_offset(skb
) + sizeof(struct ipv6hdr
);
1452 u8 nexthdr
= ipv6_hdr(skb
)->nexthdr
;
1453 unsigned int len
= skb
->len
- start
;
1458 while (nexthdr
!= target
) {
1459 struct ipv6_opt_hdr _hdr
, *hp
;
1460 unsigned int hdrlen
;
1462 if ((!ipv6_ext_hdr(nexthdr
)) || nexthdr
== NEXTHDR_NONE
) {
1468 hp
= skb_header_pointer(skb
, start
, sizeof(_hdr
), &_hdr
);
1471 if (nexthdr
== NEXTHDR_FRAGMENT
) {
1472 unsigned short _frag_off
;
1474 fp
= skb_header_pointer(skb
,
1475 start
+offsetof(struct frag_hdr
,
1482 _frag_off
= ntohs(*fp
) & ~0x7;
1485 ((!ipv6_ext_hdr(hp
->nexthdr
)) ||
1486 hp
->nexthdr
== NEXTHDR_NONE
)) {
1488 *fragoff
= _frag_off
;
1494 } else if (nexthdr
== NEXTHDR_AUTH
)
1495 hdrlen
= (hp
->hdrlen
+ 2) << 2;
1497 hdrlen
= ipv6_optlen(hp
);
1499 nexthdr
= hp
->nexthdr
;
1508 EXPORT_SYMBOL(ip6t_register_table
);
1509 EXPORT_SYMBOL(ip6t_unregister_table
);
1510 EXPORT_SYMBOL(ip6t_do_table
);
1511 EXPORT_SYMBOL(ip6t_ext_hdr
);
1512 EXPORT_SYMBOL(ipv6_find_hdr
);
1514 module_init(ip6_tables_init
);
1515 module_exit(ip6_tables_fini
);