[NETFILTER]: ip6_tables: move entry, match and target checks to seperate functions
[linux-2.6/kvm.git] / net / ipv6 / netfilter / ip6_tables.c
blob655c221acd1a91efc787b52fbf76e27c909fe1f6
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 DEBUG_IP_FIREWALL*/
35 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
36 /*#define DEBUG_IP_FIREWALL_USER*/
38 #ifdef DEBUG_IP_FIREWALL
39 #define dprintf(format, args...) printk(format , ## args)
40 #else
41 #define dprintf(format, args...)
42 #endif
44 #ifdef DEBUG_IP_FIREWALL_USER
45 #define duprintf(format, args...) printk(format , ## args)
46 #else
47 #define duprintf(format, args...)
48 #endif
50 #ifdef CONFIG_NETFILTER_DEBUG
51 #define IP_NF_ASSERT(x) \
52 do { \
53 if (!(x)) \
54 printk("IP_NF_ASSERT: %s:%s:%u\n", \
55 __FUNCTION__, __FILE__, __LINE__); \
56 } while(0)
57 #else
58 #define IP_NF_ASSERT(x)
59 #endif
61 #if 0
62 /* All the better to debug you with... */
63 #define static
64 #define inline
65 #endif
68 We keep a set of rules for each CPU, so we can avoid write-locking
69 them in the softirq when updating the counters and therefore
70 only need to read-lock in the softirq; doing a write_lock_bh() in user
71 context stops packets coming through and allows user context to read
72 the counters or update the rules.
74 Hence the start of any table is given by get_table() below. */
76 /* Check for an extension */
77 int
78 ip6t_ext_hdr(u8 nexthdr)
80 return ( (nexthdr == IPPROTO_HOPOPTS) ||
81 (nexthdr == IPPROTO_ROUTING) ||
82 (nexthdr == IPPROTO_FRAGMENT) ||
83 (nexthdr == IPPROTO_ESP) ||
84 (nexthdr == IPPROTO_AH) ||
85 (nexthdr == IPPROTO_NONE) ||
86 (nexthdr == IPPROTO_DSTOPTS) );
89 /* Returns whether matches rule or not. */
90 static inline bool
91 ip6_packet_match(const struct sk_buff *skb,
92 const char *indev,
93 const char *outdev,
94 const struct ip6t_ip6 *ip6info,
95 unsigned int *protoff,
96 int *fragoff, bool *hotdrop)
98 size_t i;
99 unsigned long ret;
100 const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
102 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
104 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
105 &ip6info->src), IP6T_INV_SRCIP)
106 || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
107 &ip6info->dst), IP6T_INV_DSTIP)) {
108 dprintf("Source or dest mismatch.\n");
110 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
111 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
112 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
113 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
114 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
115 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
116 return false;
119 /* Look for ifname matches; this should unroll nicely. */
120 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
121 ret |= (((const unsigned long *)indev)[i]
122 ^ ((const unsigned long *)ip6info->iniface)[i])
123 & ((const unsigned long *)ip6info->iniface_mask)[i];
126 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
127 dprintf("VIA in mismatch (%s vs %s).%s\n",
128 indev, ip6info->iniface,
129 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
130 return false;
133 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
134 ret |= (((const unsigned long *)outdev)[i]
135 ^ ((const unsigned long *)ip6info->outiface)[i])
136 & ((const unsigned long *)ip6info->outiface_mask)[i];
139 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
140 dprintf("VIA out mismatch (%s vs %s).%s\n",
141 outdev, ip6info->outiface,
142 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
143 return false;
146 /* ... might want to do something with class and flowlabel here ... */
148 /* look for the desired protocol header */
149 if((ip6info->flags & IP6T_F_PROTO)) {
150 int protohdr;
151 unsigned short _frag_off;
153 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
154 if (protohdr < 0) {
155 if (_frag_off == 0)
156 *hotdrop = true;
157 return false;
159 *fragoff = _frag_off;
161 dprintf("Packet protocol %hi ?= %s%hi.\n",
162 protohdr,
163 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
164 ip6info->proto);
166 if (ip6info->proto == protohdr) {
167 if(ip6info->invflags & IP6T_INV_PROTO) {
168 return false;
170 return true;
173 /* We need match for the '-p all', too! */
174 if ((ip6info->proto != 0) &&
175 !(ip6info->invflags & IP6T_INV_PROTO))
176 return false;
178 return true;
181 /* should be ip6 safe */
182 static inline bool
183 ip6_checkentry(const struct ip6t_ip6 *ipv6)
185 if (ipv6->flags & ~IP6T_F_MASK) {
186 duprintf("Unknown flag bits set: %08X\n",
187 ipv6->flags & ~IP6T_F_MASK);
188 return false;
190 if (ipv6->invflags & ~IP6T_INV_MASK) {
191 duprintf("Unknown invflag bits set: %08X\n",
192 ipv6->invflags & ~IP6T_INV_MASK);
193 return false;
195 return true;
198 static unsigned int
199 ip6t_error(struct sk_buff *skb,
200 const struct net_device *in,
201 const struct net_device *out,
202 unsigned int hooknum,
203 const struct xt_target *target,
204 const void *targinfo)
206 if (net_ratelimit())
207 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
209 return NF_DROP;
212 static inline
213 bool do_match(struct ip6t_entry_match *m,
214 const struct sk_buff *skb,
215 const struct net_device *in,
216 const struct net_device *out,
217 int offset,
218 unsigned int protoff,
219 bool *hotdrop)
221 /* Stop iteration if it doesn't match */
222 if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
223 offset, protoff, hotdrop))
224 return true;
225 else
226 return false;
229 static inline struct ip6t_entry *
230 get_entry(void *base, unsigned int offset)
232 return (struct ip6t_entry *)(base + offset);
235 /* All zeroes == unconditional rule. */
236 static inline int
237 unconditional(const struct ip6t_ip6 *ipv6)
239 unsigned int i;
241 for (i = 0; i < sizeof(*ipv6); i++)
242 if (((char *)ipv6)[i])
243 break;
245 return (i == sizeof(*ipv6));
248 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
249 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
250 /* This cries for unification! */
251 static const char *hooknames[] = {
252 [NF_INET_PRE_ROUTING] = "PREROUTING",
253 [NF_INET_LOCAL_IN] = "INPUT",
254 [NF_INET_FORWARD] = "FORWARD",
255 [NF_INET_LOCAL_OUT] = "OUTPUT",
256 [NF_INET_POST_ROUTING] = "POSTROUTING",
259 enum nf_ip_trace_comments {
260 NF_IP6_TRACE_COMMENT_RULE,
261 NF_IP6_TRACE_COMMENT_RETURN,
262 NF_IP6_TRACE_COMMENT_POLICY,
265 static const char *comments[] = {
266 [NF_IP6_TRACE_COMMENT_RULE] = "rule",
267 [NF_IP6_TRACE_COMMENT_RETURN] = "return",
268 [NF_IP6_TRACE_COMMENT_POLICY] = "policy",
271 static struct nf_loginfo trace_loginfo = {
272 .type = NF_LOG_TYPE_LOG,
273 .u = {
274 .log = {
275 .level = 4,
276 .logflags = NF_LOG_MASK,
281 static inline int
282 get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
283 char *hookname, char **chainname,
284 char **comment, unsigned int *rulenum)
286 struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
288 if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
289 /* Head of user chain: ERROR target with chainname */
290 *chainname = t->target.data;
291 (*rulenum) = 0;
292 } else if (s == e) {
293 (*rulenum)++;
295 if (s->target_offset == sizeof(struct ip6t_entry)
296 && strcmp(t->target.u.kernel.target->name,
297 IP6T_STANDARD_TARGET) == 0
298 && t->verdict < 0
299 && unconditional(&s->ipv6)) {
300 /* Tail of chains: STANDARD target (return/policy) */
301 *comment = *chainname == hookname
302 ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
303 : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
305 return 1;
306 } else
307 (*rulenum)++;
309 return 0;
312 static void trace_packet(struct sk_buff *skb,
313 unsigned int hook,
314 const struct net_device *in,
315 const struct net_device *out,
316 char *tablename,
317 struct xt_table_info *private,
318 struct ip6t_entry *e)
320 void *table_base;
321 struct ip6t_entry *root;
322 char *hookname, *chainname, *comment;
323 unsigned int rulenum = 0;
325 table_base = (void *)private->entries[smp_processor_id()];
326 root = get_entry(table_base, private->hook_entry[hook]);
328 hookname = chainname = (char *)hooknames[hook];
329 comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
331 IP6T_ENTRY_ITERATE(root,
332 private->size - private->hook_entry[hook],
333 get_chainname_rulenum,
334 e, hookname, &chainname, &comment, &rulenum);
336 nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
337 "TRACE: %s:%s:%s:%u ",
338 tablename, chainname, comment, rulenum);
340 #endif
342 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
343 unsigned int
344 ip6t_do_table(struct sk_buff *skb,
345 unsigned int hook,
346 const struct net_device *in,
347 const struct net_device *out,
348 struct xt_table *table)
350 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
351 int offset = 0;
352 unsigned int protoff = 0;
353 bool hotdrop = false;
354 /* Initializing verdict to NF_DROP keeps gcc happy. */
355 unsigned int verdict = NF_DROP;
356 const char *indev, *outdev;
357 void *table_base;
358 struct ip6t_entry *e, *back;
359 struct xt_table_info *private;
361 /* Initialization */
362 indev = in ? in->name : nulldevname;
363 outdev = out ? out->name : nulldevname;
364 /* We handle fragments by dealing with the first fragment as
365 * if it was a normal packet. All other fragments are treated
366 * normally, except that they will NEVER match rules that ask
367 * things we don't know, ie. tcp syn flag or ports). If the
368 * rule is also a fragment-specific rule, non-fragments won't
369 * match it. */
371 read_lock_bh(&table->lock);
372 private = table->private;
373 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
374 table_base = (void *)private->entries[smp_processor_id()];
375 e = get_entry(table_base, private->hook_entry[hook]);
377 /* For return from builtin chain */
378 back = get_entry(table_base, private->underflow[hook]);
380 do {
381 IP_NF_ASSERT(e);
382 IP_NF_ASSERT(back);
383 if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
384 &protoff, &offset, &hotdrop)) {
385 struct ip6t_entry_target *t;
387 if (IP6T_MATCH_ITERATE(e, do_match,
388 skb, in, out,
389 offset, protoff, &hotdrop) != 0)
390 goto no_match;
392 ADD_COUNTER(e->counters,
393 ntohs(ipv6_hdr(skb)->payload_len) +
394 sizeof(struct ipv6hdr), 1);
396 t = ip6t_get_target(e);
397 IP_NF_ASSERT(t->u.kernel.target);
399 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
400 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
401 /* The packet is traced: log it */
402 if (unlikely(skb->nf_trace))
403 trace_packet(skb, hook, in, out,
404 table->name, private, e);
405 #endif
406 /* Standard target? */
407 if (!t->u.kernel.target->target) {
408 int v;
410 v = ((struct ip6t_standard_target *)t)->verdict;
411 if (v < 0) {
412 /* Pop from stack? */
413 if (v != IP6T_RETURN) {
414 verdict = (unsigned)(-v) - 1;
415 break;
417 e = back;
418 back = get_entry(table_base,
419 back->comefrom);
420 continue;
422 if (table_base + v != (void *)e + e->next_offset
423 && !(e->ipv6.flags & IP6T_F_GOTO)) {
424 /* Save old back ptr in next entry */
425 struct ip6t_entry *next
426 = (void *)e + e->next_offset;
427 next->comefrom
428 = (void *)back - table_base;
429 /* set back pointer to next entry */
430 back = next;
433 e = get_entry(table_base, v);
434 } else {
435 /* Targets which reenter must return
436 abs. verdicts */
437 #ifdef CONFIG_NETFILTER_DEBUG
438 ((struct ip6t_entry *)table_base)->comefrom
439 = 0xeeeeeeec;
440 #endif
441 verdict = t->u.kernel.target->target(skb,
442 in, out,
443 hook,
444 t->u.kernel.target,
445 t->data);
447 #ifdef CONFIG_NETFILTER_DEBUG
448 if (((struct ip6t_entry *)table_base)->comefrom
449 != 0xeeeeeeec
450 && verdict == IP6T_CONTINUE) {
451 printk("Target %s reentered!\n",
452 t->u.kernel.target->name);
453 verdict = NF_DROP;
455 ((struct ip6t_entry *)table_base)->comefrom
456 = 0x57acc001;
457 #endif
458 if (verdict == IP6T_CONTINUE)
459 e = (void *)e + e->next_offset;
460 else
461 /* Verdict */
462 break;
464 } else {
466 no_match:
467 e = (void *)e + e->next_offset;
469 } while (!hotdrop);
471 #ifdef CONFIG_NETFILTER_DEBUG
472 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
473 #endif
474 read_unlock_bh(&table->lock);
476 #ifdef DEBUG_ALLOW_ALL
477 return NF_ACCEPT;
478 #else
479 if (hotdrop)
480 return NF_DROP;
481 else return verdict;
482 #endif
485 /* Figures out from what hook each rule can be called: returns 0 if
486 there are loops. Puts hook bitmask in comefrom. */
487 static int
488 mark_source_chains(struct xt_table_info *newinfo,
489 unsigned int valid_hooks, void *entry0)
491 unsigned int hook;
493 /* No recursion; use packet counter to save back ptrs (reset
494 to 0 as we leave), and comefrom to save source hook bitmask */
495 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
496 unsigned int pos = newinfo->hook_entry[hook];
497 struct ip6t_entry *e
498 = (struct ip6t_entry *)(entry0 + pos);
499 int visited = e->comefrom & (1 << hook);
501 if (!(valid_hooks & (1 << hook)))
502 continue;
504 /* Set initial back pointer. */
505 e->counters.pcnt = pos;
507 for (;;) {
508 struct ip6t_standard_target *t
509 = (void *)ip6t_get_target(e);
511 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
512 printk("iptables: loop hook %u pos %u %08X.\n",
513 hook, pos, e->comefrom);
514 return 0;
516 e->comefrom
517 |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
519 /* Unconditional return/END. */
520 if ((e->target_offset == sizeof(struct ip6t_entry)
521 && (strcmp(t->target.u.user.name,
522 IP6T_STANDARD_TARGET) == 0)
523 && t->verdict < 0
524 && unconditional(&e->ipv6)) || visited) {
525 unsigned int oldpos, size;
527 if (t->verdict < -NF_MAX_VERDICT - 1) {
528 duprintf("mark_source_chains: bad "
529 "negative verdict (%i)\n",
530 t->verdict);
531 return 0;
534 /* Return: backtrack through the last
535 big jump. */
536 do {
537 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
538 #ifdef DEBUG_IP_FIREWALL_USER
539 if (e->comefrom
540 & (1 << NF_INET_NUMHOOKS)) {
541 duprintf("Back unset "
542 "on hook %u "
543 "rule %u\n",
544 hook, pos);
546 #endif
547 oldpos = pos;
548 pos = e->counters.pcnt;
549 e->counters.pcnt = 0;
551 /* We're at the start. */
552 if (pos == oldpos)
553 goto next;
555 e = (struct ip6t_entry *)
556 (entry0 + pos);
557 } while (oldpos == pos + e->next_offset);
559 /* Move along one */
560 size = e->next_offset;
561 e = (struct ip6t_entry *)
562 (entry0 + pos + size);
563 e->counters.pcnt = pos;
564 pos += size;
565 } else {
566 int newpos = t->verdict;
568 if (strcmp(t->target.u.user.name,
569 IP6T_STANDARD_TARGET) == 0
570 && newpos >= 0) {
571 if (newpos > newinfo->size -
572 sizeof(struct ip6t_entry)) {
573 duprintf("mark_source_chains: "
574 "bad verdict (%i)\n",
575 newpos);
576 return 0;
578 /* This a jump; chase it. */
579 duprintf("Jump rule %u -> %u\n",
580 pos, newpos);
581 } else {
582 /* ... this is a fallthru */
583 newpos = pos + e->next_offset;
585 e = (struct ip6t_entry *)
586 (entry0 + newpos);
587 e->counters.pcnt = pos;
588 pos = newpos;
591 next:
592 duprintf("Finished chain %u\n", hook);
594 return 1;
597 static inline int
598 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
600 if (i && (*i)-- == 0)
601 return 1;
603 if (m->u.kernel.match->destroy)
604 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
605 module_put(m->u.kernel.match->me);
606 return 0;
609 static inline int
610 check_entry(struct ip6t_entry *e, const char *name)
612 struct ip6t_entry_target *t;
614 if (!ip6_checkentry(&e->ipv6)) {
615 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
616 return -EINVAL;
619 if (e->target_offset + sizeof(struct ip6t_entry_target) >
620 e->next_offset)
621 return -EINVAL;
623 t = ip6t_get_target(e);
624 if (e->target_offset + t->u.target_size > e->next_offset)
625 return -EINVAL;
627 return 0;
630 static inline int check_match(struct ip6t_entry_match *m, const char *name,
631 const struct ip6t_ip6 *ipv6,
632 unsigned int hookmask, unsigned int *i)
634 struct xt_match *match;
635 int ret;
637 match = m->u.kernel.match;
638 ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
639 name, hookmask, ipv6->proto,
640 ipv6->invflags & IP6T_INV_PROTO);
641 if (!ret && m->u.kernel.match->checkentry
642 && !m->u.kernel.match->checkentry(name, ipv6, match, m->data,
643 hookmask)) {
644 duprintf("ip_tables: check failed for `%s'.\n",
645 m->u.kernel.match->name);
646 ret = -EINVAL;
648 if (!ret)
649 (*i)++;
650 return ret;
653 static inline int
654 find_check_match(struct ip6t_entry_match *m,
655 const char *name,
656 const struct ip6t_ip6 *ipv6,
657 unsigned int hookmask,
658 unsigned int *i)
660 struct xt_match *match;
661 int ret;
663 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
664 m->u.user.revision),
665 "ip6t_%s", m->u.user.name);
666 if (IS_ERR(match) || !match) {
667 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
668 return match ? PTR_ERR(match) : -ENOENT;
670 m->u.kernel.match = match;
672 ret = check_match(m, name, ipv6, hookmask, i);
673 if (ret)
674 goto err;
676 return 0;
677 err:
678 module_put(m->u.kernel.match->me);
679 return ret;
682 static inline int check_target(struct ip6t_entry *e, const char *name)
684 struct ip6t_entry_target *t;
685 struct xt_target *target;
686 int ret;
688 t = ip6t_get_target(e);
689 target = t->u.kernel.target;
690 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
691 name, e->comefrom, e->ipv6.proto,
692 e->ipv6.invflags & IP6T_INV_PROTO);
693 if (!ret && t->u.kernel.target->checkentry
694 && !t->u.kernel.target->checkentry(name, e, target, t->data,
695 e->comefrom)) {
696 duprintf("ip_tables: check failed for `%s'.\n",
697 t->u.kernel.target->name);
698 ret = -EINVAL;
700 return ret;
703 static inline int
704 find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
705 unsigned int *i)
707 struct ip6t_entry_target *t;
708 struct xt_target *target;
709 int ret;
710 unsigned int j;
712 ret = check_entry(e, name);
713 if (ret)
714 return ret;
716 j = 0;
717 ret = IP6T_MATCH_ITERATE(e, find_check_match, name, &e->ipv6,
718 e->comefrom, &j);
719 if (ret != 0)
720 goto cleanup_matches;
722 t = ip6t_get_target(e);
723 target = try_then_request_module(xt_find_target(AF_INET6,
724 t->u.user.name,
725 t->u.user.revision),
726 "ip6t_%s", t->u.user.name);
727 if (IS_ERR(target) || !target) {
728 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
729 ret = target ? PTR_ERR(target) : -ENOENT;
730 goto cleanup_matches;
732 t->u.kernel.target = target;
734 ret = check_target(e, name);
735 if (ret)
736 goto err;
738 (*i)++;
739 return 0;
740 err:
741 module_put(t->u.kernel.target->me);
742 cleanup_matches:
743 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
744 return ret;
747 static inline int
748 check_entry_size_and_hooks(struct ip6t_entry *e,
749 struct xt_table_info *newinfo,
750 unsigned char *base,
751 unsigned char *limit,
752 const unsigned int *hook_entries,
753 const unsigned int *underflows,
754 unsigned int *i)
756 unsigned int h;
758 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
759 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
760 duprintf("Bad offset %p\n", e);
761 return -EINVAL;
764 if (e->next_offset
765 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
766 duprintf("checking: element %p size %u\n",
767 e, e->next_offset);
768 return -EINVAL;
771 /* Check hooks & underflows */
772 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
773 if ((unsigned char *)e - base == hook_entries[h])
774 newinfo->hook_entry[h] = hook_entries[h];
775 if ((unsigned char *)e - base == underflows[h])
776 newinfo->underflow[h] = underflows[h];
779 /* FIXME: underflows must be unconditional, standard verdicts
780 < 0 (not IP6T_RETURN). --RR */
782 /* Clear counters and comefrom */
783 e->counters = ((struct xt_counters) { 0, 0 });
784 e->comefrom = 0;
786 (*i)++;
787 return 0;
790 static inline int
791 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
793 struct ip6t_entry_target *t;
795 if (i && (*i)-- == 0)
796 return 1;
798 /* Cleanup all matches */
799 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
800 t = ip6t_get_target(e);
801 if (t->u.kernel.target->destroy)
802 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
803 module_put(t->u.kernel.target->me);
804 return 0;
807 /* Checks and translates the user-supplied table segment (held in
808 newinfo) */
809 static int
810 translate_table(const char *name,
811 unsigned int valid_hooks,
812 struct xt_table_info *newinfo,
813 void *entry0,
814 unsigned int size,
815 unsigned int number,
816 const unsigned int *hook_entries,
817 const unsigned int *underflows)
819 unsigned int i;
820 int ret;
822 newinfo->size = size;
823 newinfo->number = number;
825 /* Init all hooks to impossible value. */
826 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
827 newinfo->hook_entry[i] = 0xFFFFFFFF;
828 newinfo->underflow[i] = 0xFFFFFFFF;
831 duprintf("translate_table: size %u\n", newinfo->size);
832 i = 0;
833 /* Walk through entries, checking offsets. */
834 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
835 check_entry_size_and_hooks,
836 newinfo,
837 entry0,
838 entry0 + size,
839 hook_entries, underflows, &i);
840 if (ret != 0)
841 return ret;
843 if (i != number) {
844 duprintf("translate_table: %u not %u entries\n",
845 i, number);
846 return -EINVAL;
849 /* Check hooks all assigned */
850 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
851 /* Only hooks which are valid */
852 if (!(valid_hooks & (1 << i)))
853 continue;
854 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
855 duprintf("Invalid hook entry %u %u\n",
856 i, hook_entries[i]);
857 return -EINVAL;
859 if (newinfo->underflow[i] == 0xFFFFFFFF) {
860 duprintf("Invalid underflow %u %u\n",
861 i, underflows[i]);
862 return -EINVAL;
866 if (!mark_source_chains(newinfo, valid_hooks, entry0))
867 return -ELOOP;
869 /* Finally, each sanity check must pass */
870 i = 0;
871 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
872 find_check_entry, name, size, &i);
874 if (ret != 0) {
875 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
876 cleanup_entry, &i);
877 return ret;
880 /* And one copy for every other CPU */
881 for_each_possible_cpu(i) {
882 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
883 memcpy(newinfo->entries[i], entry0, newinfo->size);
886 return 0;
889 /* Gets counters. */
890 static inline int
891 add_entry_to_counter(const struct ip6t_entry *e,
892 struct xt_counters total[],
893 unsigned int *i)
895 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
897 (*i)++;
898 return 0;
901 static inline int
902 set_entry_to_counter(const struct ip6t_entry *e,
903 struct ip6t_counters total[],
904 unsigned int *i)
906 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
908 (*i)++;
909 return 0;
912 static void
913 get_counters(const struct xt_table_info *t,
914 struct xt_counters counters[])
916 unsigned int cpu;
917 unsigned int i;
918 unsigned int curcpu;
920 /* Instead of clearing (by a previous call to memset())
921 * the counters and using adds, we set the counters
922 * with data used by 'current' CPU
923 * We dont care about preemption here.
925 curcpu = raw_smp_processor_id();
927 i = 0;
928 IP6T_ENTRY_ITERATE(t->entries[curcpu],
929 t->size,
930 set_entry_to_counter,
931 counters,
932 &i);
934 for_each_possible_cpu(cpu) {
935 if (cpu == curcpu)
936 continue;
937 i = 0;
938 IP6T_ENTRY_ITERATE(t->entries[cpu],
939 t->size,
940 add_entry_to_counter,
941 counters,
942 &i);
946 static int
947 copy_entries_to_user(unsigned int total_size,
948 struct xt_table *table,
949 void __user *userptr)
951 unsigned int off, num, countersize;
952 struct ip6t_entry *e;
953 struct xt_counters *counters;
954 struct xt_table_info *private = table->private;
955 int ret = 0;
956 void *loc_cpu_entry;
958 /* We need atomic snapshot of counters: rest doesn't change
959 (other than comefrom, which userspace doesn't care
960 about). */
961 countersize = sizeof(struct xt_counters) * private->number;
962 counters = vmalloc(countersize);
964 if (counters == NULL)
965 return -ENOMEM;
967 /* First, sum counters... */
968 write_lock_bh(&table->lock);
969 get_counters(private, counters);
970 write_unlock_bh(&table->lock);
972 /* choose the copy that is on ourc node/cpu */
973 loc_cpu_entry = private->entries[raw_smp_processor_id()];
974 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
975 ret = -EFAULT;
976 goto free_counters;
979 /* FIXME: use iterator macros --RR */
980 /* ... then go back and fix counters and names */
981 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
982 unsigned int i;
983 struct ip6t_entry_match *m;
984 struct ip6t_entry_target *t;
986 e = (struct ip6t_entry *)(loc_cpu_entry + off);
987 if (copy_to_user(userptr + off
988 + offsetof(struct ip6t_entry, counters),
989 &counters[num],
990 sizeof(counters[num])) != 0) {
991 ret = -EFAULT;
992 goto free_counters;
995 for (i = sizeof(struct ip6t_entry);
996 i < e->target_offset;
997 i += m->u.match_size) {
998 m = (void *)e + i;
1000 if (copy_to_user(userptr + off + i
1001 + offsetof(struct ip6t_entry_match,
1002 u.user.name),
1003 m->u.kernel.match->name,
1004 strlen(m->u.kernel.match->name)+1)
1005 != 0) {
1006 ret = -EFAULT;
1007 goto free_counters;
1011 t = ip6t_get_target(e);
1012 if (copy_to_user(userptr + off + e->target_offset
1013 + offsetof(struct ip6t_entry_target,
1014 u.user.name),
1015 t->u.kernel.target->name,
1016 strlen(t->u.kernel.target->name)+1) != 0) {
1017 ret = -EFAULT;
1018 goto free_counters;
1022 free_counters:
1023 vfree(counters);
1024 return ret;
1027 static int
1028 get_entries(const struct ip6t_get_entries *entries,
1029 struct ip6t_get_entries __user *uptr)
1031 int ret;
1032 struct xt_table *t;
1034 t = xt_find_table_lock(AF_INET6, entries->name);
1035 if (t && !IS_ERR(t)) {
1036 struct xt_table_info *private = t->private;
1037 duprintf("t->private->number = %u\n", private->number);
1038 if (entries->size == private->size)
1039 ret = copy_entries_to_user(private->size,
1040 t, uptr->entrytable);
1041 else {
1042 duprintf("get_entries: I've got %u not %u!\n",
1043 private->size, entries->size);
1044 ret = -EINVAL;
1046 module_put(t->me);
1047 xt_table_unlock(t);
1048 } else
1049 ret = t ? PTR_ERR(t) : -ENOENT;
1051 return ret;
1054 static int
1055 do_replace(void __user *user, unsigned int len)
1057 int ret;
1058 struct ip6t_replace tmp;
1059 struct xt_table *t;
1060 struct xt_table_info *newinfo, *oldinfo;
1061 struct xt_counters *counters;
1062 void *loc_cpu_entry, *loc_cpu_old_entry;
1064 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1065 return -EFAULT;
1067 /* overflow check */
1068 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1069 return -ENOMEM;
1071 newinfo = xt_alloc_table_info(tmp.size);
1072 if (!newinfo)
1073 return -ENOMEM;
1075 /* choose the copy that is on our node/cpu */
1076 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1077 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1078 tmp.size) != 0) {
1079 ret = -EFAULT;
1080 goto free_newinfo;
1083 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
1084 if (!counters) {
1085 ret = -ENOMEM;
1086 goto free_newinfo;
1089 ret = translate_table(tmp.name, tmp.valid_hooks,
1090 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1091 tmp.hook_entry, tmp.underflow);
1092 if (ret != 0)
1093 goto free_newinfo_counters;
1095 duprintf("ip_tables: Translated table\n");
1097 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
1098 "ip6table_%s", tmp.name);
1099 if (!t || IS_ERR(t)) {
1100 ret = t ? PTR_ERR(t) : -ENOENT;
1101 goto free_newinfo_counters_untrans;
1104 /* You lied! */
1105 if (tmp.valid_hooks != t->valid_hooks) {
1106 duprintf("Valid hook crap: %08X vs %08X\n",
1107 tmp.valid_hooks, t->valid_hooks);
1108 ret = -EINVAL;
1109 goto put_module;
1112 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1113 if (!oldinfo)
1114 goto put_module;
1116 /* Update module usage count based on number of rules */
1117 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1118 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1119 if ((oldinfo->number > oldinfo->initial_entries) ||
1120 (newinfo->number <= oldinfo->initial_entries))
1121 module_put(t->me);
1122 if ((oldinfo->number > oldinfo->initial_entries) &&
1123 (newinfo->number <= oldinfo->initial_entries))
1124 module_put(t->me);
1126 /* Get the old counters. */
1127 get_counters(oldinfo, counters);
1128 /* Decrease module usage counts and free resource */
1129 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1130 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1131 xt_free_table_info(oldinfo);
1132 if (copy_to_user(tmp.counters, counters,
1133 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1134 ret = -EFAULT;
1135 vfree(counters);
1136 xt_table_unlock(t);
1137 return ret;
1139 put_module:
1140 module_put(t->me);
1141 xt_table_unlock(t);
1142 free_newinfo_counters_untrans:
1143 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1144 free_newinfo_counters:
1145 vfree(counters);
1146 free_newinfo:
1147 xt_free_table_info(newinfo);
1148 return ret;
1151 /* We're lazy, and add to the first CPU; overflow works its fey magic
1152 * and everything is OK. */
1153 static inline int
1154 add_counter_to_entry(struct ip6t_entry *e,
1155 const struct xt_counters addme[],
1156 unsigned int *i)
1158 #if 0
1159 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1161 (long unsigned int)e->counters.pcnt,
1162 (long unsigned int)e->counters.bcnt,
1163 (long unsigned int)addme[*i].pcnt,
1164 (long unsigned int)addme[*i].bcnt);
1165 #endif
1167 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1169 (*i)++;
1170 return 0;
1173 static int
1174 do_add_counters(void __user *user, unsigned int len)
1176 unsigned int i;
1177 struct xt_counters_info tmp, *paddc;
1178 struct xt_table_info *private;
1179 struct xt_table *t;
1180 int ret = 0;
1181 void *loc_cpu_entry;
1183 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1184 return -EFAULT;
1186 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1187 return -EINVAL;
1189 paddc = vmalloc(len);
1190 if (!paddc)
1191 return -ENOMEM;
1193 if (copy_from_user(paddc, user, len) != 0) {
1194 ret = -EFAULT;
1195 goto free;
1198 t = xt_find_table_lock(AF_INET6, tmp.name);
1199 if (!t || IS_ERR(t)) {
1200 ret = t ? PTR_ERR(t) : -ENOENT;
1201 goto free;
1204 write_lock_bh(&t->lock);
1205 private = t->private;
1206 if (private->number != tmp.num_counters) {
1207 ret = -EINVAL;
1208 goto unlock_up_free;
1211 i = 0;
1212 /* Choose the copy that is on our node */
1213 loc_cpu_entry = private->entries[smp_processor_id()];
1214 IP6T_ENTRY_ITERATE(loc_cpu_entry,
1215 private->size,
1216 add_counter_to_entry,
1217 paddc->counters,
1218 &i);
1219 unlock_up_free:
1220 write_unlock_bh(&t->lock);
1221 xt_table_unlock(t);
1222 module_put(t->me);
1223 free:
1224 vfree(paddc);
1226 return ret;
1229 static int
1230 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1232 int ret;
1234 if (!capable(CAP_NET_ADMIN))
1235 return -EPERM;
1237 switch (cmd) {
1238 case IP6T_SO_SET_REPLACE:
1239 ret = do_replace(user, len);
1240 break;
1242 case IP6T_SO_SET_ADD_COUNTERS:
1243 ret = do_add_counters(user, len);
1244 break;
1246 default:
1247 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1248 ret = -EINVAL;
1251 return ret;
1254 static int
1255 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1257 int ret;
1259 if (!capable(CAP_NET_ADMIN))
1260 return -EPERM;
1262 switch (cmd) {
1263 case IP6T_SO_GET_INFO: {
1264 char name[IP6T_TABLE_MAXNAMELEN];
1265 struct xt_table *t;
1267 if (*len != sizeof(struct ip6t_getinfo)) {
1268 duprintf("length %u != %u\n", *len,
1269 sizeof(struct ip6t_getinfo));
1270 ret = -EINVAL;
1271 break;
1274 if (copy_from_user(name, user, sizeof(name)) != 0) {
1275 ret = -EFAULT;
1276 break;
1278 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1280 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1281 "ip6table_%s", name);
1282 if (t && !IS_ERR(t)) {
1283 struct ip6t_getinfo info;
1284 struct xt_table_info *private = t->private;
1286 info.valid_hooks = t->valid_hooks;
1287 memcpy(info.hook_entry, private->hook_entry,
1288 sizeof(info.hook_entry));
1289 memcpy(info.underflow, private->underflow,
1290 sizeof(info.underflow));
1291 info.num_entries = private->number;
1292 info.size = private->size;
1293 memcpy(info.name, name, sizeof(info.name));
1295 if (copy_to_user(user, &info, *len) != 0)
1296 ret = -EFAULT;
1297 else
1298 ret = 0;
1299 xt_table_unlock(t);
1300 module_put(t->me);
1301 } else
1302 ret = t ? PTR_ERR(t) : -ENOENT;
1304 break;
1306 case IP6T_SO_GET_ENTRIES: {
1307 struct ip6t_get_entries get;
1309 if (*len < sizeof(get)) {
1310 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1311 ret = -EINVAL;
1312 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1313 ret = -EFAULT;
1314 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1315 duprintf("get_entries: %u != %u\n", *len,
1316 sizeof(struct ip6t_get_entries) + get.size);
1317 ret = -EINVAL;
1318 } else
1319 ret = get_entries(&get, user);
1320 break;
1323 case IP6T_SO_GET_REVISION_MATCH:
1324 case IP6T_SO_GET_REVISION_TARGET: {
1325 struct ip6t_get_revision rev;
1326 int target;
1328 if (*len != sizeof(rev)) {
1329 ret = -EINVAL;
1330 break;
1332 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1333 ret = -EFAULT;
1334 break;
1337 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1338 target = 1;
1339 else
1340 target = 0;
1342 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1343 rev.revision,
1344 target, &ret),
1345 "ip6t_%s", rev.name);
1346 break;
1349 default:
1350 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1351 ret = -EINVAL;
1354 return ret;
1357 int ip6t_register_table(struct xt_table *table,
1358 const struct ip6t_replace *repl)
1360 int ret;
1361 struct xt_table_info *newinfo;
1362 struct xt_table_info bootstrap
1363 = { 0, 0, 0, { 0 }, { 0 }, { } };
1364 void *loc_cpu_entry;
1366 newinfo = xt_alloc_table_info(repl->size);
1367 if (!newinfo)
1368 return -ENOMEM;
1370 /* choose the copy on our node/cpu */
1371 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1372 memcpy(loc_cpu_entry, repl->entries, repl->size);
1374 ret = translate_table(table->name, table->valid_hooks,
1375 newinfo, loc_cpu_entry, repl->size,
1376 repl->num_entries,
1377 repl->hook_entry,
1378 repl->underflow);
1379 if (ret != 0) {
1380 xt_free_table_info(newinfo);
1381 return ret;
1384 ret = xt_register_table(table, &bootstrap, newinfo);
1385 if (ret != 0) {
1386 xt_free_table_info(newinfo);
1387 return ret;
1390 return 0;
1393 void ip6t_unregister_table(struct xt_table *table)
1395 struct xt_table_info *private;
1396 void *loc_cpu_entry;
1398 private = xt_unregister_table(table);
1400 /* Decrease module usage counts and free resources */
1401 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1402 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1403 xt_free_table_info(private);
1406 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1407 static inline bool
1408 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1409 u_int8_t type, u_int8_t code,
1410 bool invert)
1412 return (type == test_type && code >= min_code && code <= max_code)
1413 ^ invert;
1416 static bool
1417 icmp6_match(const struct sk_buff *skb,
1418 const struct net_device *in,
1419 const struct net_device *out,
1420 const struct xt_match *match,
1421 const void *matchinfo,
1422 int offset,
1423 unsigned int protoff,
1424 bool *hotdrop)
1426 struct icmp6hdr _icmp, *ic;
1427 const struct ip6t_icmp *icmpinfo = matchinfo;
1429 /* Must not be a fragment. */
1430 if (offset)
1431 return false;
1433 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1434 if (ic == NULL) {
1435 /* We've been asked to examine this packet, and we
1436 can't. Hence, no choice but to drop. */
1437 duprintf("Dropping evil ICMP tinygram.\n");
1438 *hotdrop = true;
1439 return false;
1442 return icmp6_type_code_match(icmpinfo->type,
1443 icmpinfo->code[0],
1444 icmpinfo->code[1],
1445 ic->icmp6_type, ic->icmp6_code,
1446 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1449 /* Called when user tries to insert an entry of this type. */
1450 static bool
1451 icmp6_checkentry(const char *tablename,
1452 const void *entry,
1453 const struct xt_match *match,
1454 void *matchinfo,
1455 unsigned int hook_mask)
1457 const struct ip6t_icmp *icmpinfo = matchinfo;
1459 /* Must specify no unknown invflags */
1460 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1463 /* The built-in targets: standard (NULL) and error. */
1464 static struct xt_target ip6t_standard_target __read_mostly = {
1465 .name = IP6T_STANDARD_TARGET,
1466 .targetsize = sizeof(int),
1467 .family = AF_INET6,
1470 static struct xt_target ip6t_error_target __read_mostly = {
1471 .name = IP6T_ERROR_TARGET,
1472 .target = ip6t_error,
1473 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
1474 .family = AF_INET6,
1477 static struct nf_sockopt_ops ip6t_sockopts = {
1478 .pf = PF_INET6,
1479 .set_optmin = IP6T_BASE_CTL,
1480 .set_optmax = IP6T_SO_SET_MAX+1,
1481 .set = do_ip6t_set_ctl,
1482 .get_optmin = IP6T_BASE_CTL,
1483 .get_optmax = IP6T_SO_GET_MAX+1,
1484 .get = do_ip6t_get_ctl,
1485 .owner = THIS_MODULE,
1488 static struct xt_match icmp6_matchstruct __read_mostly = {
1489 .name = "icmp6",
1490 .match = &icmp6_match,
1491 .matchsize = sizeof(struct ip6t_icmp),
1492 .checkentry = icmp6_checkentry,
1493 .proto = IPPROTO_ICMPV6,
1494 .family = AF_INET6,
1497 static int __init ip6_tables_init(void)
1499 int ret;
1501 ret = xt_proto_init(AF_INET6);
1502 if (ret < 0)
1503 goto err1;
1505 /* Noone else will be downing sem now, so we won't sleep */
1506 ret = xt_register_target(&ip6t_standard_target);
1507 if (ret < 0)
1508 goto err2;
1509 ret = xt_register_target(&ip6t_error_target);
1510 if (ret < 0)
1511 goto err3;
1512 ret = xt_register_match(&icmp6_matchstruct);
1513 if (ret < 0)
1514 goto err4;
1516 /* Register setsockopt */
1517 ret = nf_register_sockopt(&ip6t_sockopts);
1518 if (ret < 0)
1519 goto err5;
1521 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1522 return 0;
1524 err5:
1525 xt_unregister_match(&icmp6_matchstruct);
1526 err4:
1527 xt_unregister_target(&ip6t_error_target);
1528 err3:
1529 xt_unregister_target(&ip6t_standard_target);
1530 err2:
1531 xt_proto_fini(AF_INET6);
1532 err1:
1533 return ret;
1536 static void __exit ip6_tables_fini(void)
1538 nf_unregister_sockopt(&ip6t_sockopts);
1539 xt_unregister_match(&icmp6_matchstruct);
1540 xt_unregister_target(&ip6t_error_target);
1541 xt_unregister_target(&ip6t_standard_target);
1542 xt_proto_fini(AF_INET6);
1546 * find the offset to specified header or the protocol number of last header
1547 * if target < 0. "last header" is transport protocol header, ESP, or
1548 * "No next header".
1550 * If target header is found, its offset is set in *offset and return protocol
1551 * number. Otherwise, return -1.
1553 * If the first fragment doesn't contain the final protocol header or
1554 * NEXTHDR_NONE it is considered invalid.
1556 * Note that non-1st fragment is special case that "the protocol number
1557 * of last header" is "next header" field in Fragment header. In this case,
1558 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1559 * isn't NULL.
1562 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1563 int target, unsigned short *fragoff)
1565 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
1566 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
1567 unsigned int len = skb->len - start;
1569 if (fragoff)
1570 *fragoff = 0;
1572 while (nexthdr != target) {
1573 struct ipv6_opt_hdr _hdr, *hp;
1574 unsigned int hdrlen;
1576 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1577 if (target < 0)
1578 break;
1579 return -ENOENT;
1582 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1583 if (hp == NULL)
1584 return -EBADMSG;
1585 if (nexthdr == NEXTHDR_FRAGMENT) {
1586 unsigned short _frag_off;
1587 __be16 *fp;
1588 fp = skb_header_pointer(skb,
1589 start+offsetof(struct frag_hdr,
1590 frag_off),
1591 sizeof(_frag_off),
1592 &_frag_off);
1593 if (fp == NULL)
1594 return -EBADMSG;
1596 _frag_off = ntohs(*fp) & ~0x7;
1597 if (_frag_off) {
1598 if (target < 0 &&
1599 ((!ipv6_ext_hdr(hp->nexthdr)) ||
1600 hp->nexthdr == NEXTHDR_NONE)) {
1601 if (fragoff)
1602 *fragoff = _frag_off;
1603 return hp->nexthdr;
1605 return -ENOENT;
1607 hdrlen = 8;
1608 } else if (nexthdr == NEXTHDR_AUTH)
1609 hdrlen = (hp->hdrlen + 2) << 2;
1610 else
1611 hdrlen = ipv6_optlen(hp);
1613 nexthdr = hp->nexthdr;
1614 len -= hdrlen;
1615 start += hdrlen;
1618 *offset = start;
1619 return nexthdr;
1622 EXPORT_SYMBOL(ip6t_register_table);
1623 EXPORT_SYMBOL(ip6t_unregister_table);
1624 EXPORT_SYMBOL(ip6t_do_table);
1625 EXPORT_SYMBOL(ip6t_ext_hdr);
1626 EXPORT_SYMBOL(ipv6_find_hdr);
1628 module_init(ip6_tables_init);
1629 module_exit(ip6_tables_fini);