Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[linux-ginger.git] / net / ipv6 / netfilter / ip6_tables.c
blobcc9f8ef303fd2896fd4083f4320c4ade2e3aa7b0
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.
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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 <net/compat.h>
23 #include <asm/uaccess.h>
24 #include <linux/mutex.h>
25 #include <linux/proc_fs.h>
26 #include <linux/err.h>
27 #include <linux/cpumask.h>
29 #include <linux/netfilter_ipv6/ip6_tables.h>
30 #include <linux/netfilter/x_tables.h>
31 #include <net/netfilter/nf_log.h>
33 MODULE_LICENSE("GPL");
34 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
35 MODULE_DESCRIPTION("IPv6 packet filter");
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 __func__, __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 /* Check for an extension */
80 int
81 ip6t_ext_hdr(u8 nexthdr)
83 return ( (nexthdr == IPPROTO_HOPOPTS) ||
84 (nexthdr == IPPROTO_ROUTING) ||
85 (nexthdr == IPPROTO_FRAGMENT) ||
86 (nexthdr == IPPROTO_ESP) ||
87 (nexthdr == IPPROTO_AH) ||
88 (nexthdr == IPPROTO_NONE) ||
89 (nexthdr == IPPROTO_DSTOPTS) );
92 /* Returns whether matches rule or not. */
93 /* Performance critical - called for every packet */
94 static inline bool
95 ip6_packet_match(const struct sk_buff *skb,
96 const char *indev,
97 const char *outdev,
98 const struct ip6t_ip6 *ip6info,
99 unsigned int *protoff,
100 int *fragoff, bool *hotdrop)
102 unsigned long ret;
103 const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
105 #define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
107 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
108 &ip6info->src), IP6T_INV_SRCIP)
109 || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
110 &ip6info->dst), IP6T_INV_DSTIP)) {
111 dprintf("Source or dest mismatch.\n");
113 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
114 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
115 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
116 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
117 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
118 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
119 return false;
122 ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask);
124 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
125 dprintf("VIA in mismatch (%s vs %s).%s\n",
126 indev, ip6info->iniface,
127 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
128 return false;
131 ret = ifname_compare_aligned(outdev, ip6info->outiface, ip6info->outiface_mask);
133 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
134 dprintf("VIA out mismatch (%s vs %s).%s\n",
135 outdev, ip6info->outiface,
136 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
137 return false;
140 /* ... might want to do something with class and flowlabel here ... */
142 /* look for the desired protocol header */
143 if((ip6info->flags & IP6T_F_PROTO)) {
144 int protohdr;
145 unsigned short _frag_off;
147 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
148 if (protohdr < 0) {
149 if (_frag_off == 0)
150 *hotdrop = true;
151 return false;
153 *fragoff = _frag_off;
155 dprintf("Packet protocol %hi ?= %s%hi.\n",
156 protohdr,
157 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
158 ip6info->proto);
160 if (ip6info->proto == protohdr) {
161 if(ip6info->invflags & IP6T_INV_PROTO) {
162 return false;
164 return true;
167 /* We need match for the '-p all', too! */
168 if ((ip6info->proto != 0) &&
169 !(ip6info->invflags & IP6T_INV_PROTO))
170 return false;
172 return true;
175 /* should be ip6 safe */
176 static bool
177 ip6_checkentry(const struct ip6t_ip6 *ipv6)
179 if (ipv6->flags & ~IP6T_F_MASK) {
180 duprintf("Unknown flag bits set: %08X\n",
181 ipv6->flags & ~IP6T_F_MASK);
182 return false;
184 if (ipv6->invflags & ~IP6T_INV_MASK) {
185 duprintf("Unknown invflag bits set: %08X\n",
186 ipv6->invflags & ~IP6T_INV_MASK);
187 return false;
189 return true;
192 static unsigned int
193 ip6t_error(struct sk_buff *skb, const struct xt_target_param *par)
195 if (net_ratelimit())
196 printk("ip6_tables: error: `%s'\n",
197 (const char *)par->targinfo);
199 return NF_DROP;
202 /* Performance critical - called for every packet */
203 static inline bool
204 do_match(struct ip6t_entry_match *m, const struct sk_buff *skb,
205 struct xt_match_param *par)
207 par->match = m->u.kernel.match;
208 par->matchinfo = m->data;
210 /* Stop iteration if it doesn't match */
211 if (!m->u.kernel.match->match(skb, par))
212 return true;
213 else
214 return false;
217 static inline struct ip6t_entry *
218 get_entry(void *base, unsigned int offset)
220 return (struct ip6t_entry *)(base + offset);
223 /* All zeroes == unconditional rule. */
224 /* Mildly perf critical (only if packet tracing is on) */
225 static inline bool unconditional(const struct ip6t_ip6 *ipv6)
227 static const struct ip6t_ip6 uncond;
229 return memcmp(ipv6, &uncond, sizeof(uncond)) == 0;
232 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
233 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
234 /* This cries for unification! */
235 static const char *const hooknames[] = {
236 [NF_INET_PRE_ROUTING] = "PREROUTING",
237 [NF_INET_LOCAL_IN] = "INPUT",
238 [NF_INET_FORWARD] = "FORWARD",
239 [NF_INET_LOCAL_OUT] = "OUTPUT",
240 [NF_INET_POST_ROUTING] = "POSTROUTING",
243 enum nf_ip_trace_comments {
244 NF_IP6_TRACE_COMMENT_RULE,
245 NF_IP6_TRACE_COMMENT_RETURN,
246 NF_IP6_TRACE_COMMENT_POLICY,
249 static const char *const comments[] = {
250 [NF_IP6_TRACE_COMMENT_RULE] = "rule",
251 [NF_IP6_TRACE_COMMENT_RETURN] = "return",
252 [NF_IP6_TRACE_COMMENT_POLICY] = "policy",
255 static struct nf_loginfo trace_loginfo = {
256 .type = NF_LOG_TYPE_LOG,
257 .u = {
258 .log = {
259 .level = 4,
260 .logflags = NF_LOG_MASK,
265 /* Mildly perf critical (only if packet tracing is on) */
266 static inline int
267 get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
268 const char *hookname, const char **chainname,
269 const char **comment, unsigned int *rulenum)
271 struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
273 if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
274 /* Head of user chain: ERROR target with chainname */
275 *chainname = t->target.data;
276 (*rulenum) = 0;
277 } else if (s == e) {
278 (*rulenum)++;
280 if (s->target_offset == sizeof(struct ip6t_entry)
281 && strcmp(t->target.u.kernel.target->name,
282 IP6T_STANDARD_TARGET) == 0
283 && t->verdict < 0
284 && unconditional(&s->ipv6)) {
285 /* Tail of chains: STANDARD target (return/policy) */
286 *comment = *chainname == hookname
287 ? comments[NF_IP6_TRACE_COMMENT_POLICY]
288 : comments[NF_IP6_TRACE_COMMENT_RETURN];
290 return 1;
291 } else
292 (*rulenum)++;
294 return 0;
297 static void trace_packet(struct sk_buff *skb,
298 unsigned int hook,
299 const struct net_device *in,
300 const struct net_device *out,
301 const char *tablename,
302 struct xt_table_info *private,
303 struct ip6t_entry *e)
305 void *table_base;
306 const struct ip6t_entry *root;
307 const char *hookname, *chainname, *comment;
308 unsigned int rulenum = 0;
310 table_base = private->entries[smp_processor_id()];
311 root = get_entry(table_base, private->hook_entry[hook]);
313 hookname = chainname = hooknames[hook];
314 comment = comments[NF_IP6_TRACE_COMMENT_RULE];
316 IP6T_ENTRY_ITERATE(root,
317 private->size - private->hook_entry[hook],
318 get_chainname_rulenum,
319 e, hookname, &chainname, &comment, &rulenum);
321 nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
322 "TRACE: %s:%s:%s:%u ",
323 tablename, chainname, comment, rulenum);
325 #endif
327 static inline __pure struct ip6t_entry *
328 ip6t_next_entry(const struct ip6t_entry *entry)
330 return (void *)entry + entry->next_offset;
333 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
334 unsigned int
335 ip6t_do_table(struct sk_buff *skb,
336 unsigned int hook,
337 const struct net_device *in,
338 const struct net_device *out,
339 struct xt_table *table)
341 #define tb_comefrom ((struct ip6t_entry *)table_base)->comefrom
343 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
344 bool hotdrop = false;
345 /* Initializing verdict to NF_DROP keeps gcc happy. */
346 unsigned int verdict = NF_DROP;
347 const char *indev, *outdev;
348 void *table_base;
349 struct ip6t_entry *e, *back;
350 struct xt_table_info *private;
351 struct xt_match_param mtpar;
352 struct xt_target_param tgpar;
354 /* Initialization */
355 indev = in ? in->name : nulldevname;
356 outdev = out ? out->name : nulldevname;
357 /* We handle fragments by dealing with the first fragment as
358 * if it was a normal packet. All other fragments are treated
359 * normally, except that they will NEVER match rules that ask
360 * things we don't know, ie. tcp syn flag or ports). If the
361 * rule is also a fragment-specific rule, non-fragments won't
362 * match it. */
363 mtpar.hotdrop = &hotdrop;
364 mtpar.in = tgpar.in = in;
365 mtpar.out = tgpar.out = out;
366 mtpar.family = tgpar.family = NFPROTO_IPV6;
367 mtpar.hooknum = tgpar.hooknum = hook;
369 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
371 xt_info_rdlock_bh();
372 private = table->private;
373 table_base = 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 struct ip6t_entry_target *t;
383 IP_NF_ASSERT(e);
384 IP_NF_ASSERT(back);
385 if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
386 &mtpar.thoff, &mtpar.fragoff, &hotdrop) ||
387 IP6T_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0) {
388 e = ip6t_next_entry(e);
389 continue;
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, back->comefrom);
419 continue;
421 if (table_base + v != ip6t_next_entry(e)
422 && !(e->ipv6.flags & IP6T_F_GOTO)) {
423 /* Save old back ptr in next entry */
424 struct ip6t_entry *next = ip6t_next_entry(e);
425 next->comefrom = (void *)back - table_base;
426 /* set back pointer to next entry */
427 back = next;
430 e = get_entry(table_base, v);
431 continue;
434 /* Targets which reenter must return
435 abs. verdicts */
436 tgpar.target = t->u.kernel.target;
437 tgpar.targinfo = t->data;
439 #ifdef CONFIG_NETFILTER_DEBUG
440 tb_comefrom = 0xeeeeeeec;
441 #endif
442 verdict = t->u.kernel.target->target(skb, &tgpar);
444 #ifdef CONFIG_NETFILTER_DEBUG
445 if (tb_comefrom != 0xeeeeeeec && verdict == IP6T_CONTINUE) {
446 printk("Target %s reentered!\n",
447 t->u.kernel.target->name);
448 verdict = NF_DROP;
450 tb_comefrom = 0x57acc001;
451 #endif
452 if (verdict == IP6T_CONTINUE)
453 e = ip6t_next_entry(e);
454 else
455 /* Verdict */
456 break;
457 } while (!hotdrop);
459 #ifdef CONFIG_NETFILTER_DEBUG
460 tb_comefrom = NETFILTER_LINK_POISON;
461 #endif
462 xt_info_rdunlock_bh();
464 #ifdef DEBUG_ALLOW_ALL
465 return NF_ACCEPT;
466 #else
467 if (hotdrop)
468 return NF_DROP;
469 else return verdict;
470 #endif
472 #undef tb_comefrom
475 /* Figures out from what hook each rule can be called: returns 0 if
476 there are loops. Puts hook bitmask in comefrom. */
477 static int
478 mark_source_chains(struct xt_table_info *newinfo,
479 unsigned int valid_hooks, void *entry0)
481 unsigned int hook;
483 /* No recursion; use packet counter to save back ptrs (reset
484 to 0 as we leave), and comefrom to save source hook bitmask */
485 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
486 unsigned int pos = newinfo->hook_entry[hook];
487 struct ip6t_entry *e = (struct ip6t_entry *)(entry0 + pos);
489 if (!(valid_hooks & (1 << hook)))
490 continue;
492 /* Set initial back pointer. */
493 e->counters.pcnt = pos;
495 for (;;) {
496 struct ip6t_standard_target *t
497 = (void *)ip6t_get_target(e);
498 int visited = e->comefrom & (1 << hook);
500 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
501 printk("iptables: loop hook %u pos %u %08X.\n",
502 hook, pos, e->comefrom);
503 return 0;
505 e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
507 /* Unconditional return/END. */
508 if ((e->target_offset == sizeof(struct ip6t_entry)
509 && (strcmp(t->target.u.user.name,
510 IP6T_STANDARD_TARGET) == 0)
511 && t->verdict < 0
512 && unconditional(&e->ipv6)) || visited) {
513 unsigned int oldpos, size;
515 if ((strcmp(t->target.u.user.name,
516 IP6T_STANDARD_TARGET) == 0) &&
517 t->verdict < -NF_MAX_VERDICT - 1) {
518 duprintf("mark_source_chains: bad "
519 "negative verdict (%i)\n",
520 t->verdict);
521 return 0;
524 /* Return: backtrack through the last
525 big jump. */
526 do {
527 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
528 #ifdef DEBUG_IP_FIREWALL_USER
529 if (e->comefrom
530 & (1 << NF_INET_NUMHOOKS)) {
531 duprintf("Back unset "
532 "on hook %u "
533 "rule %u\n",
534 hook, pos);
536 #endif
537 oldpos = pos;
538 pos = e->counters.pcnt;
539 e->counters.pcnt = 0;
541 /* We're at the start. */
542 if (pos == oldpos)
543 goto next;
545 e = (struct ip6t_entry *)
546 (entry0 + pos);
547 } while (oldpos == pos + e->next_offset);
549 /* Move along one */
550 size = e->next_offset;
551 e = (struct ip6t_entry *)
552 (entry0 + pos + size);
553 e->counters.pcnt = pos;
554 pos += size;
555 } else {
556 int newpos = t->verdict;
558 if (strcmp(t->target.u.user.name,
559 IP6T_STANDARD_TARGET) == 0
560 && newpos >= 0) {
561 if (newpos > newinfo->size -
562 sizeof(struct ip6t_entry)) {
563 duprintf("mark_source_chains: "
564 "bad verdict (%i)\n",
565 newpos);
566 return 0;
568 /* This a jump; chase it. */
569 duprintf("Jump rule %u -> %u\n",
570 pos, newpos);
571 } else {
572 /* ... this is a fallthru */
573 newpos = pos + e->next_offset;
575 e = (struct ip6t_entry *)
576 (entry0 + newpos);
577 e->counters.pcnt = pos;
578 pos = newpos;
581 next:
582 duprintf("Finished chain %u\n", hook);
584 return 1;
587 static int
588 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
590 struct xt_mtdtor_param par;
592 if (i && (*i)-- == 0)
593 return 1;
595 par.match = m->u.kernel.match;
596 par.matchinfo = m->data;
597 par.family = NFPROTO_IPV6;
598 if (par.match->destroy != NULL)
599 par.match->destroy(&par);
600 module_put(par.match->me);
601 return 0;
604 static int
605 check_entry(struct ip6t_entry *e, const char *name)
607 struct ip6t_entry_target *t;
609 if (!ip6_checkentry(&e->ipv6)) {
610 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
611 return -EINVAL;
614 if (e->target_offset + sizeof(struct ip6t_entry_target) >
615 e->next_offset)
616 return -EINVAL;
618 t = ip6t_get_target(e);
619 if (e->target_offset + t->u.target_size > e->next_offset)
620 return -EINVAL;
622 return 0;
625 static int check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par,
626 unsigned int *i)
628 const struct ip6t_ip6 *ipv6 = par->entryinfo;
629 int ret;
631 par->match = m->u.kernel.match;
632 par->matchinfo = m->data;
634 ret = xt_check_match(par, m->u.match_size - sizeof(*m),
635 ipv6->proto, ipv6->invflags & IP6T_INV_PROTO);
636 if (ret < 0) {
637 duprintf("ip_tables: check failed for `%s'.\n",
638 par.match->name);
639 return ret;
641 ++*i;
642 return 0;
645 static int
646 find_check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par,
647 unsigned int *i)
649 struct xt_match *match;
650 int ret;
652 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
653 m->u.user.revision),
654 "ip6t_%s", m->u.user.name);
655 if (IS_ERR(match) || !match) {
656 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
657 return match ? PTR_ERR(match) : -ENOENT;
659 m->u.kernel.match = match;
661 ret = check_match(m, par, i);
662 if (ret)
663 goto err;
665 return 0;
666 err:
667 module_put(m->u.kernel.match->me);
668 return ret;
671 static int check_target(struct ip6t_entry *e, const char *name)
673 struct ip6t_entry_target *t = ip6t_get_target(e);
674 struct xt_tgchk_param par = {
675 .table = name,
676 .entryinfo = e,
677 .target = t->u.kernel.target,
678 .targinfo = t->data,
679 .hook_mask = e->comefrom,
680 .family = NFPROTO_IPV6,
682 int ret;
684 t = ip6t_get_target(e);
685 ret = xt_check_target(&par, t->u.target_size - sizeof(*t),
686 e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
687 if (ret < 0) {
688 duprintf("ip_tables: check failed for `%s'.\n",
689 t->u.kernel.target->name);
690 return ret;
692 return 0;
695 static int
696 find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
697 unsigned int *i)
699 struct ip6t_entry_target *t;
700 struct xt_target *target;
701 int ret;
702 unsigned int j;
703 struct xt_mtchk_param mtpar;
705 ret = check_entry(e, name);
706 if (ret)
707 return ret;
709 j = 0;
710 mtpar.table = name;
711 mtpar.entryinfo = &e->ipv6;
712 mtpar.hook_mask = e->comefrom;
713 mtpar.family = NFPROTO_IPV6;
714 ret = IP6T_MATCH_ITERATE(e, find_check_match, &mtpar, &j);
715 if (ret != 0)
716 goto cleanup_matches;
718 t = ip6t_get_target(e);
719 target = try_then_request_module(xt_find_target(AF_INET6,
720 t->u.user.name,
721 t->u.user.revision),
722 "ip6t_%s", t->u.user.name);
723 if (IS_ERR(target) || !target) {
724 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
725 ret = target ? PTR_ERR(target) : -ENOENT;
726 goto cleanup_matches;
728 t->u.kernel.target = target;
730 ret = check_target(e, name);
731 if (ret)
732 goto err;
734 (*i)++;
735 return 0;
736 err:
737 module_put(t->u.kernel.target->me);
738 cleanup_matches:
739 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
740 return ret;
743 static bool check_underflow(struct ip6t_entry *e)
745 const struct ip6t_entry_target *t;
746 unsigned int verdict;
748 if (!unconditional(&e->ipv6))
749 return false;
750 t = ip6t_get_target(e);
751 if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
752 return false;
753 verdict = ((struct ip6t_standard_target *)t)->verdict;
754 verdict = -verdict - 1;
755 return verdict == NF_DROP || verdict == NF_ACCEPT;
758 static int
759 check_entry_size_and_hooks(struct ip6t_entry *e,
760 struct xt_table_info *newinfo,
761 unsigned char *base,
762 unsigned char *limit,
763 const unsigned int *hook_entries,
764 const unsigned int *underflows,
765 unsigned int valid_hooks,
766 unsigned int *i)
768 unsigned int h;
770 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
771 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
772 duprintf("Bad offset %p\n", e);
773 return -EINVAL;
776 if (e->next_offset
777 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
778 duprintf("checking: element %p size %u\n",
779 e, e->next_offset);
780 return -EINVAL;
783 /* Check hooks & underflows */
784 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
785 if (!(valid_hooks & (1 << h)))
786 continue;
787 if ((unsigned char *)e - base == hook_entries[h])
788 newinfo->hook_entry[h] = hook_entries[h];
789 if ((unsigned char *)e - base == underflows[h]) {
790 if (!check_underflow(e)) {
791 pr_err("Underflows must be unconditional and "
792 "use the STANDARD target with "
793 "ACCEPT/DROP\n");
794 return -EINVAL;
796 newinfo->underflow[h] = underflows[h];
800 /* Clear counters and comefrom */
801 e->counters = ((struct xt_counters) { 0, 0 });
802 e->comefrom = 0;
804 (*i)++;
805 return 0;
808 static int
809 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
811 struct xt_tgdtor_param par;
812 struct ip6t_entry_target *t;
814 if (i && (*i)-- == 0)
815 return 1;
817 /* Cleanup all matches */
818 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
819 t = ip6t_get_target(e);
821 par.target = t->u.kernel.target;
822 par.targinfo = t->data;
823 par.family = NFPROTO_IPV6;
824 if (par.target->destroy != NULL)
825 par.target->destroy(&par);
826 module_put(par.target->me);
827 return 0;
830 /* Checks and translates the user-supplied table segment (held in
831 newinfo) */
832 static int
833 translate_table(const char *name,
834 unsigned int valid_hooks,
835 struct xt_table_info *newinfo,
836 void *entry0,
837 unsigned int size,
838 unsigned int number,
839 const unsigned int *hook_entries,
840 const unsigned int *underflows)
842 unsigned int i;
843 int ret;
845 newinfo->size = size;
846 newinfo->number = number;
848 /* Init all hooks to impossible value. */
849 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
850 newinfo->hook_entry[i] = 0xFFFFFFFF;
851 newinfo->underflow[i] = 0xFFFFFFFF;
854 duprintf("translate_table: size %u\n", newinfo->size);
855 i = 0;
856 /* Walk through entries, checking offsets. */
857 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
858 check_entry_size_and_hooks,
859 newinfo,
860 entry0,
861 entry0 + size,
862 hook_entries, underflows, valid_hooks, &i);
863 if (ret != 0)
864 return ret;
866 if (i != number) {
867 duprintf("translate_table: %u not %u entries\n",
868 i, number);
869 return -EINVAL;
872 /* Check hooks all assigned */
873 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
874 /* Only hooks which are valid */
875 if (!(valid_hooks & (1 << i)))
876 continue;
877 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
878 duprintf("Invalid hook entry %u %u\n",
879 i, hook_entries[i]);
880 return -EINVAL;
882 if (newinfo->underflow[i] == 0xFFFFFFFF) {
883 duprintf("Invalid underflow %u %u\n",
884 i, underflows[i]);
885 return -EINVAL;
889 if (!mark_source_chains(newinfo, valid_hooks, entry0))
890 return -ELOOP;
892 /* Finally, each sanity check must pass */
893 i = 0;
894 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
895 find_check_entry, name, size, &i);
897 if (ret != 0) {
898 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
899 cleanup_entry, &i);
900 return ret;
903 /* And one copy for every other CPU */
904 for_each_possible_cpu(i) {
905 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
906 memcpy(newinfo->entries[i], entry0, newinfo->size);
909 return ret;
912 /* Gets counters. */
913 static inline int
914 add_entry_to_counter(const struct ip6t_entry *e,
915 struct xt_counters total[],
916 unsigned int *i)
918 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
920 (*i)++;
921 return 0;
924 static inline int
925 set_entry_to_counter(const struct ip6t_entry *e,
926 struct ip6t_counters total[],
927 unsigned int *i)
929 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
931 (*i)++;
932 return 0;
935 static void
936 get_counters(const struct xt_table_info *t,
937 struct xt_counters counters[])
939 unsigned int cpu;
940 unsigned int i;
941 unsigned int curcpu;
943 /* Instead of clearing (by a previous call to memset())
944 * the counters and using adds, we set the counters
945 * with data used by 'current' CPU
947 * Bottom half has to be disabled to prevent deadlock
948 * if new softirq were to run and call ipt_do_table
950 local_bh_disable();
951 curcpu = smp_processor_id();
953 i = 0;
954 IP6T_ENTRY_ITERATE(t->entries[curcpu],
955 t->size,
956 set_entry_to_counter,
957 counters,
958 &i);
960 for_each_possible_cpu(cpu) {
961 if (cpu == curcpu)
962 continue;
963 i = 0;
964 xt_info_wrlock(cpu);
965 IP6T_ENTRY_ITERATE(t->entries[cpu],
966 t->size,
967 add_entry_to_counter,
968 counters,
969 &i);
970 xt_info_wrunlock(cpu);
972 local_bh_enable();
975 static struct xt_counters *alloc_counters(struct xt_table *table)
977 unsigned int countersize;
978 struct xt_counters *counters;
979 struct xt_table_info *private = table->private;
981 /* We need atomic snapshot of counters: rest doesn't change
982 (other than comefrom, which userspace doesn't care
983 about). */
984 countersize = sizeof(struct xt_counters) * private->number;
985 counters = vmalloc_node(countersize, numa_node_id());
987 if (counters == NULL)
988 return ERR_PTR(-ENOMEM);
990 get_counters(private, counters);
992 return counters;
995 static int
996 copy_entries_to_user(unsigned int total_size,
997 struct xt_table *table,
998 void __user *userptr)
1000 unsigned int off, num;
1001 struct ip6t_entry *e;
1002 struct xt_counters *counters;
1003 const struct xt_table_info *private = table->private;
1004 int ret = 0;
1005 const void *loc_cpu_entry;
1007 counters = alloc_counters(table);
1008 if (IS_ERR(counters))
1009 return PTR_ERR(counters);
1011 /* choose the copy that is on our node/cpu, ...
1012 * This choice is lazy (because current thread is
1013 * allowed to migrate to another cpu)
1015 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1016 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1017 ret = -EFAULT;
1018 goto free_counters;
1021 /* FIXME: use iterator macros --RR */
1022 /* ... then go back and fix counters and names */
1023 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1024 unsigned int i;
1025 const struct ip6t_entry_match *m;
1026 const struct ip6t_entry_target *t;
1028 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1029 if (copy_to_user(userptr + off
1030 + offsetof(struct ip6t_entry, counters),
1031 &counters[num],
1032 sizeof(counters[num])) != 0) {
1033 ret = -EFAULT;
1034 goto free_counters;
1037 for (i = sizeof(struct ip6t_entry);
1038 i < e->target_offset;
1039 i += m->u.match_size) {
1040 m = (void *)e + i;
1042 if (copy_to_user(userptr + off + i
1043 + offsetof(struct ip6t_entry_match,
1044 u.user.name),
1045 m->u.kernel.match->name,
1046 strlen(m->u.kernel.match->name)+1)
1047 != 0) {
1048 ret = -EFAULT;
1049 goto free_counters;
1053 t = ip6t_get_target(e);
1054 if (copy_to_user(userptr + off + e->target_offset
1055 + offsetof(struct ip6t_entry_target,
1056 u.user.name),
1057 t->u.kernel.target->name,
1058 strlen(t->u.kernel.target->name)+1) != 0) {
1059 ret = -EFAULT;
1060 goto free_counters;
1064 free_counters:
1065 vfree(counters);
1066 return ret;
1069 #ifdef CONFIG_COMPAT
1070 static void compat_standard_from_user(void *dst, void *src)
1072 int v = *(compat_int_t *)src;
1074 if (v > 0)
1075 v += xt_compat_calc_jump(AF_INET6, v);
1076 memcpy(dst, &v, sizeof(v));
1079 static int compat_standard_to_user(void __user *dst, void *src)
1081 compat_int_t cv = *(int *)src;
1083 if (cv > 0)
1084 cv -= xt_compat_calc_jump(AF_INET6, cv);
1085 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1088 static inline int
1089 compat_calc_match(struct ip6t_entry_match *m, int *size)
1091 *size += xt_compat_match_offset(m->u.kernel.match);
1092 return 0;
1095 static int compat_calc_entry(struct ip6t_entry *e,
1096 const struct xt_table_info *info,
1097 void *base, struct xt_table_info *newinfo)
1099 struct ip6t_entry_target *t;
1100 unsigned int entry_offset;
1101 int off, i, ret;
1103 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1104 entry_offset = (void *)e - base;
1105 IP6T_MATCH_ITERATE(e, compat_calc_match, &off);
1106 t = ip6t_get_target(e);
1107 off += xt_compat_target_offset(t->u.kernel.target);
1108 newinfo->size -= off;
1109 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1110 if (ret)
1111 return ret;
1113 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1114 if (info->hook_entry[i] &&
1115 (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1116 newinfo->hook_entry[i] -= off;
1117 if (info->underflow[i] &&
1118 (e < (struct ip6t_entry *)(base + info->underflow[i])))
1119 newinfo->underflow[i] -= off;
1121 return 0;
1124 static int compat_table_info(const struct xt_table_info *info,
1125 struct xt_table_info *newinfo)
1127 void *loc_cpu_entry;
1129 if (!newinfo || !info)
1130 return -EINVAL;
1132 /* we dont care about newinfo->entries[] */
1133 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1134 newinfo->initial_entries = 0;
1135 loc_cpu_entry = info->entries[raw_smp_processor_id()];
1136 return IP6T_ENTRY_ITERATE(loc_cpu_entry, info->size,
1137 compat_calc_entry, info, loc_cpu_entry,
1138 newinfo);
1140 #endif
1142 static int get_info(struct net *net, void __user *user, int *len, int compat)
1144 char name[IP6T_TABLE_MAXNAMELEN];
1145 struct xt_table *t;
1146 int ret;
1148 if (*len != sizeof(struct ip6t_getinfo)) {
1149 duprintf("length %u != %zu\n", *len,
1150 sizeof(struct ip6t_getinfo));
1151 return -EINVAL;
1154 if (copy_from_user(name, user, sizeof(name)) != 0)
1155 return -EFAULT;
1157 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1158 #ifdef CONFIG_COMPAT
1159 if (compat)
1160 xt_compat_lock(AF_INET6);
1161 #endif
1162 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1163 "ip6table_%s", name);
1164 if (t && !IS_ERR(t)) {
1165 struct ip6t_getinfo info;
1166 const struct xt_table_info *private = t->private;
1168 #ifdef CONFIG_COMPAT
1169 if (compat) {
1170 struct xt_table_info tmp;
1171 ret = compat_table_info(private, &tmp);
1172 xt_compat_flush_offsets(AF_INET6);
1173 private = &tmp;
1175 #endif
1176 info.valid_hooks = t->valid_hooks;
1177 memcpy(info.hook_entry, private->hook_entry,
1178 sizeof(info.hook_entry));
1179 memcpy(info.underflow, private->underflow,
1180 sizeof(info.underflow));
1181 info.num_entries = private->number;
1182 info.size = private->size;
1183 strcpy(info.name, name);
1185 if (copy_to_user(user, &info, *len) != 0)
1186 ret = -EFAULT;
1187 else
1188 ret = 0;
1190 xt_table_unlock(t);
1191 module_put(t->me);
1192 } else
1193 ret = t ? PTR_ERR(t) : -ENOENT;
1194 #ifdef CONFIG_COMPAT
1195 if (compat)
1196 xt_compat_unlock(AF_INET6);
1197 #endif
1198 return ret;
1201 static int
1202 get_entries(struct net *net, struct ip6t_get_entries __user *uptr, int *len)
1204 int ret;
1205 struct ip6t_get_entries get;
1206 struct xt_table *t;
1208 if (*len < sizeof(get)) {
1209 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
1210 return -EINVAL;
1212 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1213 return -EFAULT;
1214 if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1215 duprintf("get_entries: %u != %zu\n",
1216 *len, sizeof(get) + get.size);
1217 return -EINVAL;
1220 t = xt_find_table_lock(net, AF_INET6, get.name);
1221 if (t && !IS_ERR(t)) {
1222 struct xt_table_info *private = t->private;
1223 duprintf("t->private->number = %u\n", private->number);
1224 if (get.size == private->size)
1225 ret = copy_entries_to_user(private->size,
1226 t, uptr->entrytable);
1227 else {
1228 duprintf("get_entries: I've got %u not %u!\n",
1229 private->size, get.size);
1230 ret = -EAGAIN;
1232 module_put(t->me);
1233 xt_table_unlock(t);
1234 } else
1235 ret = t ? PTR_ERR(t) : -ENOENT;
1237 return ret;
1240 static int
1241 __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
1242 struct xt_table_info *newinfo, unsigned int num_counters,
1243 void __user *counters_ptr)
1245 int ret;
1246 struct xt_table *t;
1247 struct xt_table_info *oldinfo;
1248 struct xt_counters *counters;
1249 const void *loc_cpu_old_entry;
1251 ret = 0;
1252 counters = vmalloc_node(num_counters * sizeof(struct xt_counters),
1253 numa_node_id());
1254 if (!counters) {
1255 ret = -ENOMEM;
1256 goto out;
1259 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1260 "ip6table_%s", name);
1261 if (!t || IS_ERR(t)) {
1262 ret = t ? PTR_ERR(t) : -ENOENT;
1263 goto free_newinfo_counters_untrans;
1266 /* You lied! */
1267 if (valid_hooks != t->valid_hooks) {
1268 duprintf("Valid hook crap: %08X vs %08X\n",
1269 valid_hooks, t->valid_hooks);
1270 ret = -EINVAL;
1271 goto put_module;
1274 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1275 if (!oldinfo)
1276 goto put_module;
1278 /* Update module usage count based on number of rules */
1279 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1280 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1281 if ((oldinfo->number > oldinfo->initial_entries) ||
1282 (newinfo->number <= oldinfo->initial_entries))
1283 module_put(t->me);
1284 if ((oldinfo->number > oldinfo->initial_entries) &&
1285 (newinfo->number <= oldinfo->initial_entries))
1286 module_put(t->me);
1288 /* Get the old counters, and synchronize with replace */
1289 get_counters(oldinfo, counters);
1291 /* Decrease module usage counts and free resource */
1292 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1293 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
1294 NULL);
1295 xt_free_table_info(oldinfo);
1296 if (copy_to_user(counters_ptr, counters,
1297 sizeof(struct xt_counters) * num_counters) != 0)
1298 ret = -EFAULT;
1299 vfree(counters);
1300 xt_table_unlock(t);
1301 return ret;
1303 put_module:
1304 module_put(t->me);
1305 xt_table_unlock(t);
1306 free_newinfo_counters_untrans:
1307 vfree(counters);
1308 out:
1309 return ret;
1312 static int
1313 do_replace(struct net *net, void __user *user, unsigned int len)
1315 int ret;
1316 struct ip6t_replace tmp;
1317 struct xt_table_info *newinfo;
1318 void *loc_cpu_entry;
1320 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1321 return -EFAULT;
1323 /* overflow check */
1324 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1325 return -ENOMEM;
1327 newinfo = xt_alloc_table_info(tmp.size);
1328 if (!newinfo)
1329 return -ENOMEM;
1331 /* choose the copy that is on our node/cpu */
1332 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1333 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1334 tmp.size) != 0) {
1335 ret = -EFAULT;
1336 goto free_newinfo;
1339 ret = translate_table(tmp.name, tmp.valid_hooks,
1340 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1341 tmp.hook_entry, tmp.underflow);
1342 if (ret != 0)
1343 goto free_newinfo;
1345 duprintf("ip_tables: Translated table\n");
1347 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1348 tmp.num_counters, tmp.counters);
1349 if (ret)
1350 goto free_newinfo_untrans;
1351 return 0;
1353 free_newinfo_untrans:
1354 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
1355 free_newinfo:
1356 xt_free_table_info(newinfo);
1357 return ret;
1360 /* We're lazy, and add to the first CPU; overflow works its fey magic
1361 * and everything is OK. */
1362 static int
1363 add_counter_to_entry(struct ip6t_entry *e,
1364 const struct xt_counters addme[],
1365 unsigned int *i)
1367 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1369 (*i)++;
1370 return 0;
1373 static int
1374 do_add_counters(struct net *net, void __user *user, unsigned int len,
1375 int compat)
1377 unsigned int i, curcpu;
1378 struct xt_counters_info tmp;
1379 struct xt_counters *paddc;
1380 unsigned int num_counters;
1381 char *name;
1382 int size;
1383 void *ptmp;
1384 struct xt_table *t;
1385 const struct xt_table_info *private;
1386 int ret = 0;
1387 const void *loc_cpu_entry;
1388 #ifdef CONFIG_COMPAT
1389 struct compat_xt_counters_info compat_tmp;
1391 if (compat) {
1392 ptmp = &compat_tmp;
1393 size = sizeof(struct compat_xt_counters_info);
1394 } else
1395 #endif
1397 ptmp = &tmp;
1398 size = sizeof(struct xt_counters_info);
1401 if (copy_from_user(ptmp, user, size) != 0)
1402 return -EFAULT;
1404 #ifdef CONFIG_COMPAT
1405 if (compat) {
1406 num_counters = compat_tmp.num_counters;
1407 name = compat_tmp.name;
1408 } else
1409 #endif
1411 num_counters = tmp.num_counters;
1412 name = tmp.name;
1415 if (len != size + num_counters * sizeof(struct xt_counters))
1416 return -EINVAL;
1418 paddc = vmalloc_node(len - size, numa_node_id());
1419 if (!paddc)
1420 return -ENOMEM;
1422 if (copy_from_user(paddc, user + size, len - size) != 0) {
1423 ret = -EFAULT;
1424 goto free;
1427 t = xt_find_table_lock(net, AF_INET6, name);
1428 if (!t || IS_ERR(t)) {
1429 ret = t ? PTR_ERR(t) : -ENOENT;
1430 goto free;
1434 local_bh_disable();
1435 private = t->private;
1436 if (private->number != num_counters) {
1437 ret = -EINVAL;
1438 goto unlock_up_free;
1441 i = 0;
1442 /* Choose the copy that is on our node */
1443 curcpu = smp_processor_id();
1444 xt_info_wrlock(curcpu);
1445 loc_cpu_entry = private->entries[curcpu];
1446 IP6T_ENTRY_ITERATE(loc_cpu_entry,
1447 private->size,
1448 add_counter_to_entry,
1449 paddc,
1450 &i);
1451 xt_info_wrunlock(curcpu);
1453 unlock_up_free:
1454 local_bh_enable();
1455 xt_table_unlock(t);
1456 module_put(t->me);
1457 free:
1458 vfree(paddc);
1460 return ret;
1463 #ifdef CONFIG_COMPAT
1464 struct compat_ip6t_replace {
1465 char name[IP6T_TABLE_MAXNAMELEN];
1466 u32 valid_hooks;
1467 u32 num_entries;
1468 u32 size;
1469 u32 hook_entry[NF_INET_NUMHOOKS];
1470 u32 underflow[NF_INET_NUMHOOKS];
1471 u32 num_counters;
1472 compat_uptr_t counters; /* struct ip6t_counters * */
1473 struct compat_ip6t_entry entries[0];
1476 static int
1477 compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
1478 unsigned int *size, struct xt_counters *counters,
1479 unsigned int *i)
1481 struct ip6t_entry_target *t;
1482 struct compat_ip6t_entry __user *ce;
1483 u_int16_t target_offset, next_offset;
1484 compat_uint_t origsize;
1485 int ret;
1487 ret = -EFAULT;
1488 origsize = *size;
1489 ce = (struct compat_ip6t_entry __user *)*dstptr;
1490 if (copy_to_user(ce, e, sizeof(struct ip6t_entry)))
1491 goto out;
1493 if (copy_to_user(&ce->counters, &counters[*i], sizeof(counters[*i])))
1494 goto out;
1496 *dstptr += sizeof(struct compat_ip6t_entry);
1497 *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1499 ret = IP6T_MATCH_ITERATE(e, xt_compat_match_to_user, dstptr, size);
1500 target_offset = e->target_offset - (origsize - *size);
1501 if (ret)
1502 goto out;
1503 t = ip6t_get_target(e);
1504 ret = xt_compat_target_to_user(t, dstptr, size);
1505 if (ret)
1506 goto out;
1507 ret = -EFAULT;
1508 next_offset = e->next_offset - (origsize - *size);
1509 if (put_user(target_offset, &ce->target_offset))
1510 goto out;
1511 if (put_user(next_offset, &ce->next_offset))
1512 goto out;
1514 (*i)++;
1515 return 0;
1516 out:
1517 return ret;
1520 static int
1521 compat_find_calc_match(struct ip6t_entry_match *m,
1522 const char *name,
1523 const struct ip6t_ip6 *ipv6,
1524 unsigned int hookmask,
1525 int *size, unsigned int *i)
1527 struct xt_match *match;
1529 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
1530 m->u.user.revision),
1531 "ip6t_%s", m->u.user.name);
1532 if (IS_ERR(match) || !match) {
1533 duprintf("compat_check_calc_match: `%s' not found\n",
1534 m->u.user.name);
1535 return match ? PTR_ERR(match) : -ENOENT;
1537 m->u.kernel.match = match;
1538 *size += xt_compat_match_offset(match);
1540 (*i)++;
1541 return 0;
1544 static int
1545 compat_release_match(struct ip6t_entry_match *m, unsigned int *i)
1547 if (i && (*i)-- == 0)
1548 return 1;
1550 module_put(m->u.kernel.match->me);
1551 return 0;
1554 static int
1555 compat_release_entry(struct compat_ip6t_entry *e, unsigned int *i)
1557 struct ip6t_entry_target *t;
1559 if (i && (*i)-- == 0)
1560 return 1;
1562 /* Cleanup all matches */
1563 COMPAT_IP6T_MATCH_ITERATE(e, compat_release_match, NULL);
1564 t = compat_ip6t_get_target(e);
1565 module_put(t->u.kernel.target->me);
1566 return 0;
1569 static int
1570 check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1571 struct xt_table_info *newinfo,
1572 unsigned int *size,
1573 unsigned char *base,
1574 unsigned char *limit,
1575 unsigned int *hook_entries,
1576 unsigned int *underflows,
1577 unsigned int *i,
1578 const char *name)
1580 struct ip6t_entry_target *t;
1581 struct xt_target *target;
1582 unsigned int entry_offset;
1583 unsigned int j;
1584 int ret, off, h;
1586 duprintf("check_compat_entry_size_and_hooks %p\n", e);
1587 if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0
1588 || (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
1589 duprintf("Bad offset %p, limit = %p\n", e, limit);
1590 return -EINVAL;
1593 if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1594 sizeof(struct compat_xt_entry_target)) {
1595 duprintf("checking: element %p size %u\n",
1596 e, e->next_offset);
1597 return -EINVAL;
1600 /* For purposes of check_entry casting the compat entry is fine */
1601 ret = check_entry((struct ip6t_entry *)e, name);
1602 if (ret)
1603 return ret;
1605 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1606 entry_offset = (void *)e - (void *)base;
1607 j = 0;
1608 ret = COMPAT_IP6T_MATCH_ITERATE(e, compat_find_calc_match, name,
1609 &e->ipv6, e->comefrom, &off, &j);
1610 if (ret != 0)
1611 goto release_matches;
1613 t = compat_ip6t_get_target(e);
1614 target = try_then_request_module(xt_find_target(AF_INET6,
1615 t->u.user.name,
1616 t->u.user.revision),
1617 "ip6t_%s", t->u.user.name);
1618 if (IS_ERR(target) || !target) {
1619 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1620 t->u.user.name);
1621 ret = target ? PTR_ERR(target) : -ENOENT;
1622 goto release_matches;
1624 t->u.kernel.target = target;
1626 off += xt_compat_target_offset(target);
1627 *size += off;
1628 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1629 if (ret)
1630 goto out;
1632 /* Check hooks & underflows */
1633 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1634 if ((unsigned char *)e - base == hook_entries[h])
1635 newinfo->hook_entry[h] = hook_entries[h];
1636 if ((unsigned char *)e - base == underflows[h])
1637 newinfo->underflow[h] = underflows[h];
1640 /* Clear counters and comefrom */
1641 memset(&e->counters, 0, sizeof(e->counters));
1642 e->comefrom = 0;
1644 (*i)++;
1645 return 0;
1647 out:
1648 module_put(t->u.kernel.target->me);
1649 release_matches:
1650 IP6T_MATCH_ITERATE(e, compat_release_match, &j);
1651 return ret;
1654 static int
1655 compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1656 unsigned int *size, const char *name,
1657 struct xt_table_info *newinfo, unsigned char *base)
1659 struct ip6t_entry_target *t;
1660 struct xt_target *target;
1661 struct ip6t_entry *de;
1662 unsigned int origsize;
1663 int ret, h;
1665 ret = 0;
1666 origsize = *size;
1667 de = (struct ip6t_entry *)*dstptr;
1668 memcpy(de, e, sizeof(struct ip6t_entry));
1669 memcpy(&de->counters, &e->counters, sizeof(e->counters));
1671 *dstptr += sizeof(struct ip6t_entry);
1672 *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1674 ret = COMPAT_IP6T_MATCH_ITERATE(e, xt_compat_match_from_user,
1675 dstptr, size);
1676 if (ret)
1677 return ret;
1678 de->target_offset = e->target_offset - (origsize - *size);
1679 t = compat_ip6t_get_target(e);
1680 target = t->u.kernel.target;
1681 xt_compat_target_from_user(t, dstptr, size);
1683 de->next_offset = e->next_offset - (origsize - *size);
1684 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1685 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1686 newinfo->hook_entry[h] -= origsize - *size;
1687 if ((unsigned char *)de - base < newinfo->underflow[h])
1688 newinfo->underflow[h] -= origsize - *size;
1690 return ret;
1693 static int compat_check_entry(struct ip6t_entry *e, const char *name,
1694 unsigned int *i)
1696 unsigned int j;
1697 int ret;
1698 struct xt_mtchk_param mtpar;
1700 j = 0;
1701 mtpar.table = name;
1702 mtpar.entryinfo = &e->ipv6;
1703 mtpar.hook_mask = e->comefrom;
1704 mtpar.family = NFPROTO_IPV6;
1705 ret = IP6T_MATCH_ITERATE(e, check_match, &mtpar, &j);
1706 if (ret)
1707 goto cleanup_matches;
1709 ret = check_target(e, name);
1710 if (ret)
1711 goto cleanup_matches;
1713 (*i)++;
1714 return 0;
1716 cleanup_matches:
1717 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
1718 return ret;
1721 static int
1722 translate_compat_table(const char *name,
1723 unsigned int valid_hooks,
1724 struct xt_table_info **pinfo,
1725 void **pentry0,
1726 unsigned int total_size,
1727 unsigned int number,
1728 unsigned int *hook_entries,
1729 unsigned int *underflows)
1731 unsigned int i, j;
1732 struct xt_table_info *newinfo, *info;
1733 void *pos, *entry0, *entry1;
1734 unsigned int size;
1735 int ret;
1737 info = *pinfo;
1738 entry0 = *pentry0;
1739 size = total_size;
1740 info->number = number;
1742 /* Init all hooks to impossible value. */
1743 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1744 info->hook_entry[i] = 0xFFFFFFFF;
1745 info->underflow[i] = 0xFFFFFFFF;
1748 duprintf("translate_compat_table: size %u\n", info->size);
1749 j = 0;
1750 xt_compat_lock(AF_INET6);
1751 /* Walk through entries, checking offsets. */
1752 ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1753 check_compat_entry_size_and_hooks,
1754 info, &size, entry0,
1755 entry0 + total_size,
1756 hook_entries, underflows, &j, name);
1757 if (ret != 0)
1758 goto out_unlock;
1760 ret = -EINVAL;
1761 if (j != number) {
1762 duprintf("translate_compat_table: %u not %u entries\n",
1763 j, number);
1764 goto out_unlock;
1767 /* Check hooks all assigned */
1768 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1769 /* Only hooks which are valid */
1770 if (!(valid_hooks & (1 << i)))
1771 continue;
1772 if (info->hook_entry[i] == 0xFFFFFFFF) {
1773 duprintf("Invalid hook entry %u %u\n",
1774 i, hook_entries[i]);
1775 goto out_unlock;
1777 if (info->underflow[i] == 0xFFFFFFFF) {
1778 duprintf("Invalid underflow %u %u\n",
1779 i, underflows[i]);
1780 goto out_unlock;
1784 ret = -ENOMEM;
1785 newinfo = xt_alloc_table_info(size);
1786 if (!newinfo)
1787 goto out_unlock;
1789 newinfo->number = number;
1790 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1791 newinfo->hook_entry[i] = info->hook_entry[i];
1792 newinfo->underflow[i] = info->underflow[i];
1794 entry1 = newinfo->entries[raw_smp_processor_id()];
1795 pos = entry1;
1796 size = total_size;
1797 ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1798 compat_copy_entry_from_user,
1799 &pos, &size, name, newinfo, entry1);
1800 xt_compat_flush_offsets(AF_INET6);
1801 xt_compat_unlock(AF_INET6);
1802 if (ret)
1803 goto free_newinfo;
1805 ret = -ELOOP;
1806 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1807 goto free_newinfo;
1809 i = 0;
1810 ret = IP6T_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
1811 name, &i);
1812 if (ret) {
1813 j -= i;
1814 COMPAT_IP6T_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
1815 compat_release_entry, &j);
1816 IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i);
1817 xt_free_table_info(newinfo);
1818 return ret;
1821 /* And one copy for every other CPU */
1822 for_each_possible_cpu(i)
1823 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1824 memcpy(newinfo->entries[i], entry1, newinfo->size);
1826 *pinfo = newinfo;
1827 *pentry0 = entry1;
1828 xt_free_table_info(info);
1829 return 0;
1831 free_newinfo:
1832 xt_free_table_info(newinfo);
1833 out:
1834 COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size, compat_release_entry, &j);
1835 return ret;
1836 out_unlock:
1837 xt_compat_flush_offsets(AF_INET6);
1838 xt_compat_unlock(AF_INET6);
1839 goto out;
1842 static int
1843 compat_do_replace(struct net *net, void __user *user, unsigned int len)
1845 int ret;
1846 struct compat_ip6t_replace tmp;
1847 struct xt_table_info *newinfo;
1848 void *loc_cpu_entry;
1850 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1851 return -EFAULT;
1853 /* overflow check */
1854 if (tmp.size >= INT_MAX / num_possible_cpus())
1855 return -ENOMEM;
1856 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1857 return -ENOMEM;
1859 newinfo = xt_alloc_table_info(tmp.size);
1860 if (!newinfo)
1861 return -ENOMEM;
1863 /* choose the copy that is on our node/cpu */
1864 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1865 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1866 tmp.size) != 0) {
1867 ret = -EFAULT;
1868 goto free_newinfo;
1871 ret = translate_compat_table(tmp.name, tmp.valid_hooks,
1872 &newinfo, &loc_cpu_entry, tmp.size,
1873 tmp.num_entries, tmp.hook_entry,
1874 tmp.underflow);
1875 if (ret != 0)
1876 goto free_newinfo;
1878 duprintf("compat_do_replace: Translated table\n");
1880 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1881 tmp.num_counters, compat_ptr(tmp.counters));
1882 if (ret)
1883 goto free_newinfo_untrans;
1884 return 0;
1886 free_newinfo_untrans:
1887 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
1888 free_newinfo:
1889 xt_free_table_info(newinfo);
1890 return ret;
1893 static int
1894 compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1895 unsigned int len)
1897 int ret;
1899 if (!capable(CAP_NET_ADMIN))
1900 return -EPERM;
1902 switch (cmd) {
1903 case IP6T_SO_SET_REPLACE:
1904 ret = compat_do_replace(sock_net(sk), user, len);
1905 break;
1907 case IP6T_SO_SET_ADD_COUNTERS:
1908 ret = do_add_counters(sock_net(sk), user, len, 1);
1909 break;
1911 default:
1912 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1913 ret = -EINVAL;
1916 return ret;
1919 struct compat_ip6t_get_entries {
1920 char name[IP6T_TABLE_MAXNAMELEN];
1921 compat_uint_t size;
1922 struct compat_ip6t_entry entrytable[0];
1925 static int
1926 compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1927 void __user *userptr)
1929 struct xt_counters *counters;
1930 const struct xt_table_info *private = table->private;
1931 void __user *pos;
1932 unsigned int size;
1933 int ret = 0;
1934 const void *loc_cpu_entry;
1935 unsigned int i = 0;
1937 counters = alloc_counters(table);
1938 if (IS_ERR(counters))
1939 return PTR_ERR(counters);
1941 /* choose the copy that is on our node/cpu, ...
1942 * This choice is lazy (because current thread is
1943 * allowed to migrate to another cpu)
1945 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1946 pos = userptr;
1947 size = total_size;
1948 ret = IP6T_ENTRY_ITERATE(loc_cpu_entry, total_size,
1949 compat_copy_entry_to_user,
1950 &pos, &size, counters, &i);
1952 vfree(counters);
1953 return ret;
1956 static int
1957 compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1958 int *len)
1960 int ret;
1961 struct compat_ip6t_get_entries get;
1962 struct xt_table *t;
1964 if (*len < sizeof(get)) {
1965 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
1966 return -EINVAL;
1969 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1970 return -EFAULT;
1972 if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
1973 duprintf("compat_get_entries: %u != %zu\n",
1974 *len, sizeof(get) + get.size);
1975 return -EINVAL;
1978 xt_compat_lock(AF_INET6);
1979 t = xt_find_table_lock(net, AF_INET6, get.name);
1980 if (t && !IS_ERR(t)) {
1981 const struct xt_table_info *private = t->private;
1982 struct xt_table_info info;
1983 duprintf("t->private->number = %u\n", private->number);
1984 ret = compat_table_info(private, &info);
1985 if (!ret && get.size == info.size) {
1986 ret = compat_copy_entries_to_user(private->size,
1987 t, uptr->entrytable);
1988 } else if (!ret) {
1989 duprintf("compat_get_entries: I've got %u not %u!\n",
1990 private->size, get.size);
1991 ret = -EAGAIN;
1993 xt_compat_flush_offsets(AF_INET6);
1994 module_put(t->me);
1995 xt_table_unlock(t);
1996 } else
1997 ret = t ? PTR_ERR(t) : -ENOENT;
1999 xt_compat_unlock(AF_INET6);
2000 return ret;
2003 static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
2005 static int
2006 compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2008 int ret;
2010 if (!capable(CAP_NET_ADMIN))
2011 return -EPERM;
2013 switch (cmd) {
2014 case IP6T_SO_GET_INFO:
2015 ret = get_info(sock_net(sk), user, len, 1);
2016 break;
2017 case IP6T_SO_GET_ENTRIES:
2018 ret = compat_get_entries(sock_net(sk), user, len);
2019 break;
2020 default:
2021 ret = do_ip6t_get_ctl(sk, cmd, user, len);
2023 return ret;
2025 #endif
2027 static int
2028 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2030 int ret;
2032 if (!capable(CAP_NET_ADMIN))
2033 return -EPERM;
2035 switch (cmd) {
2036 case IP6T_SO_SET_REPLACE:
2037 ret = do_replace(sock_net(sk), user, len);
2038 break;
2040 case IP6T_SO_SET_ADD_COUNTERS:
2041 ret = do_add_counters(sock_net(sk), user, len, 0);
2042 break;
2044 default:
2045 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
2046 ret = -EINVAL;
2049 return ret;
2052 static int
2053 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2055 int ret;
2057 if (!capable(CAP_NET_ADMIN))
2058 return -EPERM;
2060 switch (cmd) {
2061 case IP6T_SO_GET_INFO:
2062 ret = get_info(sock_net(sk), user, len, 0);
2063 break;
2065 case IP6T_SO_GET_ENTRIES:
2066 ret = get_entries(sock_net(sk), user, len);
2067 break;
2069 case IP6T_SO_GET_REVISION_MATCH:
2070 case IP6T_SO_GET_REVISION_TARGET: {
2071 struct ip6t_get_revision rev;
2072 int target;
2074 if (*len != sizeof(rev)) {
2075 ret = -EINVAL;
2076 break;
2078 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2079 ret = -EFAULT;
2080 break;
2083 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2084 target = 1;
2085 else
2086 target = 0;
2088 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2089 rev.revision,
2090 target, &ret),
2091 "ip6t_%s", rev.name);
2092 break;
2095 default:
2096 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2097 ret = -EINVAL;
2100 return ret;
2103 struct xt_table *ip6t_register_table(struct net *net,
2104 const struct xt_table *table,
2105 const struct ip6t_replace *repl)
2107 int ret;
2108 struct xt_table_info *newinfo;
2109 struct xt_table_info bootstrap
2110 = { 0, 0, 0, { 0 }, { 0 }, { } };
2111 void *loc_cpu_entry;
2112 struct xt_table *new_table;
2114 newinfo = xt_alloc_table_info(repl->size);
2115 if (!newinfo) {
2116 ret = -ENOMEM;
2117 goto out;
2120 /* choose the copy on our node/cpu, but dont care about preemption */
2121 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2122 memcpy(loc_cpu_entry, repl->entries, repl->size);
2124 ret = translate_table(table->name, table->valid_hooks,
2125 newinfo, loc_cpu_entry, repl->size,
2126 repl->num_entries,
2127 repl->hook_entry,
2128 repl->underflow);
2129 if (ret != 0)
2130 goto out_free;
2132 new_table = xt_register_table(net, table, &bootstrap, newinfo);
2133 if (IS_ERR(new_table)) {
2134 ret = PTR_ERR(new_table);
2135 goto out_free;
2137 return new_table;
2139 out_free:
2140 xt_free_table_info(newinfo);
2141 out:
2142 return ERR_PTR(ret);
2145 void ip6t_unregister_table(struct xt_table *table)
2147 struct xt_table_info *private;
2148 void *loc_cpu_entry;
2149 struct module *table_owner = table->me;
2151 private = xt_unregister_table(table);
2153 /* Decrease module usage counts and free resources */
2154 loc_cpu_entry = private->entries[raw_smp_processor_id()];
2155 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
2156 if (private->number > private->initial_entries)
2157 module_put(table_owner);
2158 xt_free_table_info(private);
2161 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
2162 static inline bool
2163 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2164 u_int8_t type, u_int8_t code,
2165 bool invert)
2167 return (type == test_type && code >= min_code && code <= max_code)
2168 ^ invert;
2171 static bool
2172 icmp6_match(const struct sk_buff *skb, const struct xt_match_param *par)
2174 const struct icmp6hdr *ic;
2175 struct icmp6hdr _icmph;
2176 const struct ip6t_icmp *icmpinfo = par->matchinfo;
2178 /* Must not be a fragment. */
2179 if (par->fragoff != 0)
2180 return false;
2182 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
2183 if (ic == NULL) {
2184 /* We've been asked to examine this packet, and we
2185 * can't. Hence, no choice but to drop.
2187 duprintf("Dropping evil ICMP tinygram.\n");
2188 *par->hotdrop = true;
2189 return false;
2192 return icmp6_type_code_match(icmpinfo->type,
2193 icmpinfo->code[0],
2194 icmpinfo->code[1],
2195 ic->icmp6_type, ic->icmp6_code,
2196 !!(icmpinfo->invflags&IP6T_ICMP_INV));
2199 /* Called when user tries to insert an entry of this type. */
2200 static bool icmp6_checkentry(const struct xt_mtchk_param *par)
2202 const struct ip6t_icmp *icmpinfo = par->matchinfo;
2204 /* Must specify no unknown invflags */
2205 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
2208 /* The built-in targets: standard (NULL) and error. */
2209 static struct xt_target ip6t_standard_target __read_mostly = {
2210 .name = IP6T_STANDARD_TARGET,
2211 .targetsize = sizeof(int),
2212 .family = NFPROTO_IPV6,
2213 #ifdef CONFIG_COMPAT
2214 .compatsize = sizeof(compat_int_t),
2215 .compat_from_user = compat_standard_from_user,
2216 .compat_to_user = compat_standard_to_user,
2217 #endif
2220 static struct xt_target ip6t_error_target __read_mostly = {
2221 .name = IP6T_ERROR_TARGET,
2222 .target = ip6t_error,
2223 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
2224 .family = NFPROTO_IPV6,
2227 static struct nf_sockopt_ops ip6t_sockopts = {
2228 .pf = PF_INET6,
2229 .set_optmin = IP6T_BASE_CTL,
2230 .set_optmax = IP6T_SO_SET_MAX+1,
2231 .set = do_ip6t_set_ctl,
2232 #ifdef CONFIG_COMPAT
2233 .compat_set = compat_do_ip6t_set_ctl,
2234 #endif
2235 .get_optmin = IP6T_BASE_CTL,
2236 .get_optmax = IP6T_SO_GET_MAX+1,
2237 .get = do_ip6t_get_ctl,
2238 #ifdef CONFIG_COMPAT
2239 .compat_get = compat_do_ip6t_get_ctl,
2240 #endif
2241 .owner = THIS_MODULE,
2244 static struct xt_match icmp6_matchstruct __read_mostly = {
2245 .name = "icmp6",
2246 .match = icmp6_match,
2247 .matchsize = sizeof(struct ip6t_icmp),
2248 .checkentry = icmp6_checkentry,
2249 .proto = IPPROTO_ICMPV6,
2250 .family = NFPROTO_IPV6,
2253 static int __net_init ip6_tables_net_init(struct net *net)
2255 return xt_proto_init(net, NFPROTO_IPV6);
2258 static void __net_exit ip6_tables_net_exit(struct net *net)
2260 xt_proto_fini(net, NFPROTO_IPV6);
2263 static struct pernet_operations ip6_tables_net_ops = {
2264 .init = ip6_tables_net_init,
2265 .exit = ip6_tables_net_exit,
2268 static int __init ip6_tables_init(void)
2270 int ret;
2272 ret = register_pernet_subsys(&ip6_tables_net_ops);
2273 if (ret < 0)
2274 goto err1;
2276 /* Noone else will be downing sem now, so we won't sleep */
2277 ret = xt_register_target(&ip6t_standard_target);
2278 if (ret < 0)
2279 goto err2;
2280 ret = xt_register_target(&ip6t_error_target);
2281 if (ret < 0)
2282 goto err3;
2283 ret = xt_register_match(&icmp6_matchstruct);
2284 if (ret < 0)
2285 goto err4;
2287 /* Register setsockopt */
2288 ret = nf_register_sockopt(&ip6t_sockopts);
2289 if (ret < 0)
2290 goto err5;
2292 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
2293 return 0;
2295 err5:
2296 xt_unregister_match(&icmp6_matchstruct);
2297 err4:
2298 xt_unregister_target(&ip6t_error_target);
2299 err3:
2300 xt_unregister_target(&ip6t_standard_target);
2301 err2:
2302 unregister_pernet_subsys(&ip6_tables_net_ops);
2303 err1:
2304 return ret;
2307 static void __exit ip6_tables_fini(void)
2309 nf_unregister_sockopt(&ip6t_sockopts);
2311 xt_unregister_match(&icmp6_matchstruct);
2312 xt_unregister_target(&ip6t_error_target);
2313 xt_unregister_target(&ip6t_standard_target);
2315 unregister_pernet_subsys(&ip6_tables_net_ops);
2319 * find the offset to specified header or the protocol number of last header
2320 * if target < 0. "last header" is transport protocol header, ESP, or
2321 * "No next header".
2323 * If target header is found, its offset is set in *offset and return protocol
2324 * number. Otherwise, return -1.
2326 * If the first fragment doesn't contain the final protocol header or
2327 * NEXTHDR_NONE it is considered invalid.
2329 * Note that non-1st fragment is special case that "the protocol number
2330 * of last header" is "next header" field in Fragment header. In this case,
2331 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
2332 * isn't NULL.
2335 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2336 int target, unsigned short *fragoff)
2338 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
2339 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
2340 unsigned int len = skb->len - start;
2342 if (fragoff)
2343 *fragoff = 0;
2345 while (nexthdr != target) {
2346 struct ipv6_opt_hdr _hdr, *hp;
2347 unsigned int hdrlen;
2349 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2350 if (target < 0)
2351 break;
2352 return -ENOENT;
2355 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2356 if (hp == NULL)
2357 return -EBADMSG;
2358 if (nexthdr == NEXTHDR_FRAGMENT) {
2359 unsigned short _frag_off;
2360 __be16 *fp;
2361 fp = skb_header_pointer(skb,
2362 start+offsetof(struct frag_hdr,
2363 frag_off),
2364 sizeof(_frag_off),
2365 &_frag_off);
2366 if (fp == NULL)
2367 return -EBADMSG;
2369 _frag_off = ntohs(*fp) & ~0x7;
2370 if (_frag_off) {
2371 if (target < 0 &&
2372 ((!ipv6_ext_hdr(hp->nexthdr)) ||
2373 hp->nexthdr == NEXTHDR_NONE)) {
2374 if (fragoff)
2375 *fragoff = _frag_off;
2376 return hp->nexthdr;
2378 return -ENOENT;
2380 hdrlen = 8;
2381 } else if (nexthdr == NEXTHDR_AUTH)
2382 hdrlen = (hp->hdrlen + 2) << 2;
2383 else
2384 hdrlen = ipv6_optlen(hp);
2386 nexthdr = hp->nexthdr;
2387 len -= hdrlen;
2388 start += hdrlen;
2391 *offset = start;
2392 return nexthdr;
2395 EXPORT_SYMBOL(ip6t_register_table);
2396 EXPORT_SYMBOL(ip6t_unregister_table);
2397 EXPORT_SYMBOL(ip6t_do_table);
2398 EXPORT_SYMBOL(ip6t_ext_hdr);
2399 EXPORT_SYMBOL(ipv6_find_hdr);
2401 module_init(ip6_tables_init);
2402 module_exit(ip6_tables_fini);