RT-AC66 3.0.0.4.374.130 core
[tomato.git] / release / src-rt-6.x / linux / linux-2.6 / net / ipv6 / netfilter / ip6_tables.c
blobdcdb456865933c1e46dadf06e7e62964dfdd28f5
1 /*
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>
13 #include <linux/in.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>
21 #include <net/ipv6.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)
43 #else
44 #define dprintf(format, args...)
45 #endif
47 #ifdef DEBUG_IP_FIREWALL_USER
48 #define duprintf(format, args...) printk(format , ## args)
49 #else
50 #define duprintf(format, args...)
51 #endif
53 #ifdef CONFIG_NETFILTER_DEBUG
54 #define IP_NF_ASSERT(x) \
55 do { \
56 if (!(x)) \
57 printk("IP_NF_ASSERT: %s:%s:%u\n", \
58 __FUNCTION__, __FILE__, __LINE__); \
59 } while(0)
60 #else
61 #define IP_NF_ASSERT(x)
62 #endif
64 #if 0
65 /* All the better to debug you with... */
66 #define static
67 #define inline
68 #endif
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. */
79 #if 0
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)
83 #endif
85 /* Check for an extension */
86 int
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. */
99 static inline int
100 ip6_packet_match(const struct sk_buff *skb,
101 const char *indev,
102 const char *outdev,
103 const struct ip6t_ip6 *ip6info,
104 unsigned int *protoff,
105 int *fragoff, int *hotdrop)
107 unsigned long ret;
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)" : "");*/
124 return 0;
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)":"");
133 return 0;
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)":"");
142 return 0;
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)) {
149 int protohdr;
150 unsigned short _frag_off;
152 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
153 if (protohdr < 0) {
154 if (_frag_off == 0)
155 *hotdrop = 1;
156 return 0;
158 *fragoff = _frag_off;
160 dprintf("Packet protocol %hi ?= %s%hi.\n",
161 protohdr,
162 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
163 ip6info->proto);
165 if (ip6info->proto == protohdr) {
166 if(ip6info->invflags & IP6T_INV_PROTO) {
167 return 0;
169 return 1;
172 /* We need match for the '-p all', too! */
173 if ((ip6info->proto != 0) &&
174 !(ip6info->invflags & IP6T_INV_PROTO))
175 return 0;
177 return 1;
180 /* should be ip6 safe */
181 static inline int
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);
187 return 0;
189 if (ipv6->invflags & ~IP6T_INV_MASK) {
190 duprintf("Unknown invflag bits set: %08X\n",
191 ipv6->invflags & ~IP6T_INV_MASK);
192 return 0;
194 return 1;
197 static unsigned int
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)
205 if (net_ratelimit())
206 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
208 return NF_DROP;
211 static inline
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,
216 int offset,
217 unsigned int protoff,
218 int *hotdrop)
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))
223 return 1;
224 else
225 return 0;
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. */
235 unsigned int
236 ip6t_do_table(struct sk_buff *skb,
237 unsigned int hook,
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))));
243 int offset = 0;
244 unsigned int protoff = 0;
245 int hotdrop = 0;
246 /* Initializing verdict to NF_DROP keeps gcc happy. */
247 unsigned int verdict = NF_DROP;
248 const char *indev, *outdev;
249 void *table_base;
250 struct ip6t_entry *e, *back;
251 struct xt_table_info *private;
253 /* Initialization */
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
261 * match it. */
263 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
265 xt_info_rdlock_bh();
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]);
274 do {
275 IP_NF_ASSERT(e);
276 IP_NF_ASSERT(back);
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,
282 skb, in, out,
283 offset, protoff, &hotdrop) != 0)
284 goto no_match;
286 ADD_COUNTER(e->counters,
287 ntohs(ipv6_hdr(skb)->payload_len)
288 + IPV6_HDR_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) {
295 int v;
297 v = ((struct ip6t_standard_target *)t)->verdict;
298 if (v < 0) {
299 /* Pop from stack? */
300 if (v != IP6T_RETURN) {
301 verdict = (unsigned)(-v) - 1;
302 break;
304 e = back;
305 back = get_entry(table_base,
306 back->comefrom);
307 continue;
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;
314 next->comefrom
315 = (void *)back - table_base;
316 /* set back pointer to next entry */
317 back = next;
320 e = get_entry(table_base, v);
321 } else {
322 /* Targets which reenter must return
323 abs. verdicts */
324 #ifdef CONFIG_NETFILTER_DEBUG
325 ((struct ip6t_entry *)table_base)->comefrom
326 = 0xeeeeeeec;
327 #endif
328 verdict = t->u.kernel.target->target(skb,
329 in, out,
330 hook,
331 t->u.kernel.target,
332 t->data);
334 #ifdef CONFIG_NETFILTER_DEBUG
335 if (((struct ip6t_entry *)table_base)->comefrom
336 != 0xeeeeeeec
337 && verdict == IP6T_CONTINUE) {
338 printk("Target %s reentered!\n",
339 t->u.kernel.target->name);
340 verdict = NF_DROP;
342 ((struct ip6t_entry *)table_base)->comefrom
343 = 0x57acc001;
344 #endif
345 if (verdict == IP6T_CONTINUE)
346 e = (void *)e + e->next_offset;
347 else if (verdict == IP6T_RETURN) { // added -- zzz
348 e = back;
349 back = get_entry(table_base, back->comefrom);
350 continue;
352 else
353 /* Verdict */
354 break;
356 } else {
358 no_match:
359 e = (void *)e + e->next_offset;
361 } while (!hotdrop);
363 #ifdef CONFIG_NETFILTER_DEBUG
364 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
365 #endif
366 xt_info_rdunlock_bh();
368 #ifdef DEBUG_ALLOW_ALL
369 return NF_ACCEPT;
370 #else
371 if (hotdrop)
372 return NF_DROP;
373 else return verdict;
374 #endif
377 /* All zeroes == unconditional rule. */
378 static inline int
379 unconditional(const struct ip6t_ip6 *ipv6)
381 unsigned int i;
383 for (i = 0; i < sizeof(*ipv6); i++)
384 if (((char *)ipv6)[i])
385 break;
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. */
392 static int
393 mark_source_chains(struct xt_table_info *newinfo,
394 unsigned int valid_hooks, void *entry0)
396 unsigned int hook;
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];
402 struct ip6t_entry *e
403 = (struct ip6t_entry *)(entry0 + pos);
404 int visited = e->comefrom & (1 << hook);
406 if (!(valid_hooks & (1 << hook)))
407 continue;
409 /* Set initial back pointer. */
410 e->counters.pcnt = pos;
412 for (;;) {
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);
419 return 0;
421 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)
428 && t->verdict < 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",
437 t->verdict);
438 return 0;
441 /* Return: backtrack through the last
442 big jump. */
443 do {
444 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
445 #ifdef DEBUG_IP_FIREWALL_USER
446 if (e->comefrom
447 & (1 << NF_IP6_NUMHOOKS)) {
448 duprintf("Back unset "
449 "on hook %u "
450 "rule %u\n",
451 hook, pos);
453 #endif
454 oldpos = pos;
455 pos = e->counters.pcnt;
456 e->counters.pcnt = 0;
458 /* We're at the start. */
459 if (pos == oldpos)
460 goto next;
462 e = (struct ip6t_entry *)
463 (entry0 + pos);
464 } while (oldpos == pos + e->next_offset);
466 /* Move along one */
467 size = e->next_offset;
468 e = (struct ip6t_entry *)
469 (entry0 + pos + size);
470 e->counters.pcnt = pos;
471 pos += size;
472 } else {
473 int newpos = t->verdict;
475 if (strcmp(t->target.u.user.name,
476 IP6T_STANDARD_TARGET) == 0
477 && newpos >= 0) {
478 if (newpos > newinfo->size -
479 sizeof(struct ip6t_entry)) {
480 duprintf("mark_source_chains: "
481 "bad verdict (%i)\n",
482 newpos);
483 return 0;
485 /* This a jump; chase it. */
486 duprintf("Jump rule %u -> %u\n",
487 pos, newpos);
488 } else {
489 /* ... this is a fallthru */
490 newpos = pos + e->next_offset;
492 e = (struct ip6t_entry *)
493 (entry0 + newpos);
494 e->counters.pcnt = pos;
495 pos = newpos;
498 next:
499 duprintf("Finished chain %u\n", hook);
501 return 1;
504 static inline int
505 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
507 if (i && (*i)-- == 0)
508 return 1;
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);
513 return 0;
516 static inline int
517 check_match(struct ip6t_entry_match *m,
518 const char *name,
519 const struct ip6t_ip6 *ipv6,
520 unsigned int hookmask,
521 unsigned int *i)
523 struct xt_match *match;
524 int ret;
526 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
527 m->u.user.revision),
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);
538 if (ret)
539 goto err;
541 if (m->u.kernel.match->checkentry
542 && !m->u.kernel.match->checkentry(name, ipv6, match, m->data,
543 hookmask)) {
544 duprintf("ip_tables: check failed for `%s'.\n",
545 m->u.kernel.match->name);
546 ret = -EINVAL;
547 goto err;
550 (*i)++;
551 return 0;
552 err:
553 module_put(m->u.kernel.match->me);
554 return ret;
557 static struct xt_target ip6t_standard_target;
559 static inline int
560 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
561 unsigned int *i)
563 struct ip6t_entry_target *t;
564 struct xt_target *target;
565 int ret;
566 unsigned int j;
568 if (!ip6_checkentry(&e->ipv6)) {
569 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
570 return -EINVAL;
573 if (e->target_offset + sizeof(struct ip6t_entry_target) >
574 e->next_offset)
575 return -EINVAL;
577 j = 0;
578 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
579 if (ret != 0)
580 goto cleanup_matches;
582 t = ip6t_get_target(e);
583 ret = -EINVAL;
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,
587 t->u.user.name,
588 t->u.user.revision),
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);
600 if (ret)
601 goto err;
603 if (t->u.kernel.target->checkentry
604 && !t->u.kernel.target->checkentry(name, e, target, t->data,
605 e->comefrom)) {
606 duprintf("ip_tables: check failed for `%s'.\n",
607 t->u.kernel.target->name);
608 ret = -EINVAL;
609 goto err;
612 (*i)++;
613 return 0;
614 err:
615 module_put(t->u.kernel.target->me);
616 cleanup_matches:
617 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
618 return ret;
621 static inline int
622 check_entry_size_and_hooks(struct ip6t_entry *e,
623 struct xt_table_info *newinfo,
624 unsigned char *base,
625 unsigned char *limit,
626 const unsigned int *hook_entries,
627 const unsigned int *underflows,
628 unsigned int *i)
630 unsigned int h;
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);
635 return -EINVAL;
638 if (e->next_offset
639 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
640 duprintf("checking: element %p size %u\n",
641 e, e->next_offset);
642 return -EINVAL;
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 });
658 e->comefrom = 0;
660 (*i)++;
661 return 0;
664 static inline int
665 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
667 struct ip6t_entry_target *t;
669 if (i && (*i)-- == 0)
670 return 1;
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);
678 return 0;
681 /* Checks and translates the user-supplied table segment (held in
682 newinfo) */
683 static int
684 translate_table(const char *name,
685 unsigned int valid_hooks,
686 struct xt_table_info *newinfo,
687 void *entry0,
688 unsigned int size,
689 unsigned int number,
690 const unsigned int *hook_entries,
691 const unsigned int *underflows)
693 unsigned int i;
694 int ret;
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);
706 i = 0;
707 /* Walk through entries, checking offsets. */
708 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
709 check_entry_size_and_hooks,
710 newinfo,
711 entry0,
712 entry0 + size,
713 hook_entries, underflows, &i);
714 if (ret != 0)
715 return ret;
717 if (i != number) {
718 duprintf("translate_table: %u not %u entries\n",
719 i, number);
720 return -EINVAL;
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)))
727 continue;
728 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
729 duprintf("Invalid hook entry %u %u\n",
730 i, hook_entries[i]);
731 return -EINVAL;
733 if (newinfo->underflow[i] == 0xFFFFFFFF) {
734 duprintf("Invalid underflow %u %u\n",
735 i, underflows[i]);
736 return -EINVAL;
740 if (!mark_source_chains(newinfo, valid_hooks, entry0))
741 return -ELOOP;
743 /* Finally, each sanity check must pass */
744 i = 0;
745 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
746 check_entry, name, size, &i);
748 if (ret != 0) {
749 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
750 cleanup_entry, &i);
751 return ret;
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);
760 return 0;
763 /* Gets counters. */
764 static inline int
765 add_entry_to_counter(const struct ip6t_entry *e,
766 struct xt_counters total[],
767 unsigned int *i)
769 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
771 (*i)++;
772 return 0;
775 static inline int
776 set_entry_to_counter(const struct ip6t_entry *e,
777 struct ip6t_counters total[],
778 unsigned int *i)
780 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
782 (*i)++;
783 return 0;
786 static void
787 get_counters(const struct xt_table_info *t,
788 struct xt_counters counters[])
790 unsigned int cpu;
791 unsigned int i;
792 unsigned int curcpu;
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
801 local_bh_disable();
802 curcpu = smp_processor_id();
804 i = 0;
805 IP6T_ENTRY_ITERATE(t->entries[curcpu],
806 t->size,
807 set_entry_to_counter,
808 counters,
809 &i);
811 for_each_possible_cpu(cpu) {
812 if (cpu == curcpu)
813 continue;
814 i = 0;
815 xt_info_wrlock(cpu);
816 IP6T_ENTRY_ITERATE(t->entries[cpu],
817 t->size,
818 add_entry_to_counter,
819 counters,
820 &i);
821 xt_info_wrunlock(cpu);
823 local_bh_enable();
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
834 about). */
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);
843 return counters;
846 static int
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;
855 int ret = 0;
856 void *loc_cpu_entry;
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) {
865 ret = -EFAULT;
866 goto free_counters;
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++){
872 unsigned int i;
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),
879 &counters[num],
880 sizeof(counters[num])) != 0) {
881 ret = -EFAULT;
882 goto free_counters;
885 for (i = sizeof(struct ip6t_entry);
886 i < e->target_offset;
887 i += m->u.match_size) {
888 m = (void *)e + i;
890 if (copy_to_user(userptr + off + i
891 + offsetof(struct ip6t_entry_match,
892 u.user.name),
893 m->u.kernel.match->name,
894 strlen(m->u.kernel.match->name)+1)
895 != 0) {
896 ret = -EFAULT;
897 goto free_counters;
901 t = ip6t_get_target(e);
902 if (copy_to_user(userptr + off + e->target_offset
903 + offsetof(struct ip6t_entry_target,
904 u.user.name),
905 t->u.kernel.target->name,
906 strlen(t->u.kernel.target->name)+1) != 0) {
907 ret = -EFAULT;
908 goto free_counters;
912 free_counters:
913 vfree(counters);
914 return ret;
917 static int
918 get_entries(const struct ip6t_get_entries *entries,
919 struct ip6t_get_entries __user *uptr)
921 int ret;
922 struct xt_table *t;
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);
931 else {
932 duprintf("get_entries: I've got %u not %u!\n",
933 private->size, entries->size);
934 ret = -EINVAL;
936 module_put(t->me);
937 xt_table_unlock(t);
938 } else
939 ret = t ? PTR_ERR(t) : -ENOENT;
941 return ret;
944 static int
945 do_replace(void __user *user, unsigned int len)
947 int ret;
948 struct ip6t_replace tmp;
949 struct xt_table *t;
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)
955 return -EFAULT;
957 /* overflow check */
958 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
959 return -ENOMEM;
961 newinfo = xt_alloc_table_info(tmp.size);
962 if (!newinfo)
963 return -ENOMEM;
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),
968 tmp.size) != 0) {
969 ret = -EFAULT;
970 goto free_newinfo;
973 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
974 if (!counters) {
975 ret = -ENOMEM;
976 goto free_newinfo;
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);
982 if (ret != 0)
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;
994 /* You lied! */
995 if (tmp.valid_hooks != t->valid_hooks) {
996 duprintf("Valid hook crap: %08X vs %08X\n",
997 tmp.valid_hooks, t->valid_hooks);
998 ret = -EINVAL;
999 goto put_module;
1002 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1003 if (!oldinfo)
1004 goto put_module;
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))
1011 module_put(t->me);
1012 if ((oldinfo->number > oldinfo->initial_entries) &&
1013 (newinfo->number <= oldinfo->initial_entries))
1014 module_put(t->me);
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)
1025 ret = -EFAULT;
1026 vfree(counters);
1027 xt_table_unlock(t);
1028 return ret;
1030 put_module:
1031 module_put(t->me);
1032 xt_table_unlock(t);
1033 free_newinfo_counters_untrans:
1034 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1035 free_newinfo_counters:
1036 vfree(counters);
1037 free_newinfo:
1038 xt_free_table_info(newinfo);
1039 return ret;
1042 /* We're lazy, and add to the first CPU; overflow works its fey magic
1043 * and everything is OK. */
1044 static int
1045 add_counter_to_entry(struct ip6t_entry *e,
1046 const struct xt_counters addme[],
1047 unsigned int *i)
1049 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1051 (*i)++;
1052 return 0;
1055 static int
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;
1061 struct xt_table *t;
1062 int ret = 0;
1063 void *loc_cpu_entry;
1065 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1066 return -EFAULT;
1068 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1069 return -EINVAL;
1071 paddc = vmalloc(len);
1072 if (!paddc)
1073 return -ENOMEM;
1075 if (copy_from_user(paddc, user, len) != 0) {
1076 ret = -EFAULT;
1077 goto free;
1080 t = xt_find_table_lock(AF_INET6, tmp.name);
1081 if (!t || IS_ERR(t)) {
1082 ret = t ? PTR_ERR(t) : -ENOENT;
1083 goto free;
1087 local_bh_disable();
1088 private = t->private;
1089 if (private->number != tmp.num_counters) {
1090 ret = -EINVAL;
1091 goto unlock_up_free;
1094 i = 0;
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,
1100 private->size,
1101 add_counter_to_entry,
1102 paddc->counters,
1103 &i);
1104 xt_info_wrunlock(curcpu);
1106 unlock_up_free:
1107 local_bh_enable();
1108 xt_table_unlock(t);
1109 module_put(t->me);
1110 free:
1111 vfree(paddc);
1113 return ret;
1116 static int
1117 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1119 int ret;
1121 if (!capable(CAP_NET_ADMIN))
1122 return -EPERM;
1124 switch (cmd) {
1125 case IP6T_SO_SET_REPLACE:
1126 ret = do_replace(user, len);
1127 break;
1129 case IP6T_SO_SET_ADD_COUNTERS:
1130 ret = do_add_counters(user, len);
1131 break;
1133 default:
1134 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1135 ret = -EINVAL;
1138 return ret;
1141 static int
1142 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1144 int ret;
1146 if (!capable(CAP_NET_ADMIN))
1147 return -EPERM;
1149 switch (cmd) {
1150 case IP6T_SO_GET_INFO: {
1151 char name[IP6T_TABLE_MAXNAMELEN];
1152 struct xt_table *t;
1154 if (*len != sizeof(struct ip6t_getinfo)) {
1155 duprintf("length %u != %u\n", *len,
1156 sizeof(struct ip6t_getinfo));
1157 ret = -EINVAL;
1158 break;
1161 if (copy_from_user(name, user, sizeof(name)) != 0) {
1162 ret = -EFAULT;
1163 break;
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)
1183 ret = -EFAULT;
1184 else
1185 ret = 0;
1186 xt_table_unlock(t);
1187 module_put(t->me);
1188 } else
1189 ret = t ? PTR_ERR(t) : -ENOENT;
1191 break;
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));
1198 ret = -EINVAL;
1199 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1200 ret = -EFAULT;
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);
1204 ret = -EINVAL;
1205 } else
1206 ret = get_entries(&get, user);
1207 break;
1210 case IP6T_SO_GET_REVISION_MATCH:
1211 case IP6T_SO_GET_REVISION_TARGET: {
1212 struct ip6t_get_revision rev;
1213 int target;
1215 if (*len != sizeof(rev)) {
1216 ret = -EINVAL;
1217 break;
1219 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1220 ret = -EFAULT;
1221 break;
1224 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1225 target = 1;
1226 else
1227 target = 0;
1229 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1230 rev.revision,
1231 target, &ret),
1232 "ip6t_%s", rev.name);
1233 break;
1236 default:
1237 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1238 ret = -EINVAL;
1241 return ret;
1244 int ip6t_register_table(struct xt_table *table,
1245 const struct ip6t_replace *repl)
1247 int ret;
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);
1254 if (!newinfo)
1255 return -ENOMEM;
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,
1263 repl->num_entries,
1264 repl->hook_entry,
1265 repl->underflow);
1266 if (ret != 0) {
1267 xt_free_table_info(newinfo);
1268 return ret;
1271 ret = xt_register_table(table, &bootstrap, newinfo);
1272 if (ret != 0) {
1273 xt_free_table_info(newinfo);
1274 return ret;
1277 return 0;
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 */
1294 static inline int
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,
1297 int invert)
1299 return (type == test_type && code >= min_code && code <= max_code)
1300 ^ invert;
1303 static int
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,
1309 int offset,
1310 unsigned int protoff,
1311 int *hotdrop)
1313 struct icmp6hdr _icmp, *ic;
1314 const struct ip6t_icmp *icmpinfo = matchinfo;
1316 /* Must not be a fragment. */
1317 if (offset)
1318 return 0;
1320 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1321 if (ic == NULL) {
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");
1325 *hotdrop = 1;
1326 return 0;
1329 return icmp6_type_code_match(icmpinfo->type,
1330 icmpinfo->code[0],
1331 icmpinfo->code[1],
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. */
1337 static int
1338 icmp6_checkentry(const char *tablename,
1339 const void *entry,
1340 const struct xt_match *match,
1341 void *matchinfo,
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),
1354 .family = AF_INET6,
1357 static struct xt_target ip6t_error_target = {
1358 .name = IP6T_ERROR_TARGET,
1359 .target = ip6t_error,
1360 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
1361 .family = AF_INET6,
1364 static struct nf_sockopt_ops ip6t_sockopts = {
1365 .pf = PF_INET6,
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 = {
1375 .name = "icmp6",
1376 .match = &icmp6_match,
1377 .matchsize = sizeof(struct ip6t_icmp),
1378 .checkentry = icmp6_checkentry,
1379 .proto = IPPROTO_ICMPV6,
1380 .family = AF_INET6,
1383 static int __init ip6_tables_init(void)
1385 int ret;
1387 ret = xt_proto_init(AF_INET6);
1388 if (ret < 0)
1389 goto err1;
1391 /* Noone else will be downing sem now, so we won't sleep */
1392 ret = xt_register_target(&ip6t_standard_target);
1393 if (ret < 0)
1394 goto err2;
1395 ret = xt_register_target(&ip6t_error_target);
1396 if (ret < 0)
1397 goto err3;
1398 ret = xt_register_match(&icmp6_matchstruct);
1399 if (ret < 0)
1400 goto err4;
1402 /* Register setsockopt */
1403 ret = nf_register_sockopt(&ip6t_sockopts);
1404 if (ret < 0)
1405 goto err5;
1407 printk("ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1408 return 0;
1410 err5:
1411 xt_unregister_match(&icmp6_matchstruct);
1412 err4:
1413 xt_unregister_target(&ip6t_error_target);
1414 err3:
1415 xt_unregister_target(&ip6t_standard_target);
1416 err2:
1417 xt_proto_fini(AF_INET6);
1418 err1:
1419 return ret;
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
1434 * "No next header".
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
1445 * isn't NULL.
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;
1455 if (fragoff)
1456 *fragoff = 0;
1458 while (nexthdr != target) {
1459 struct ipv6_opt_hdr _hdr, *hp;
1460 unsigned int hdrlen;
1462 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1463 if (target < 0)
1464 break;
1465 return -ENOENT;
1468 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1469 if (hp == NULL)
1470 return -EBADMSG;
1471 if (nexthdr == NEXTHDR_FRAGMENT) {
1472 unsigned short _frag_off;
1473 __be16 *fp;
1474 fp = skb_header_pointer(skb,
1475 start+offsetof(struct frag_hdr,
1476 frag_off),
1477 sizeof(_frag_off),
1478 &_frag_off);
1479 if (fp == NULL)
1480 return -EBADMSG;
1482 _frag_off = ntohs(*fp) & ~0x7;
1483 if (_frag_off) {
1484 if (target < 0 &&
1485 ((!ipv6_ext_hdr(hp->nexthdr)) ||
1486 hp->nexthdr == NEXTHDR_NONE)) {
1487 if (fragoff)
1488 *fragoff = _frag_off;
1489 return hp->nexthdr;
1491 return -ENOENT;
1493 hdrlen = 8;
1494 } else if (nexthdr == NEXTHDR_AUTH)
1495 hdrlen = (hp->hdrlen + 2) << 2;
1496 else
1497 hdrlen = ipv6_optlen(hp);
1499 nexthdr = hp->nexthdr;
1500 len -= hdrlen;
1501 start += hdrlen;
1504 *offset = start;
1505 return 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);