irq_work: Use per cpu atomics instead of regular atomics
[linux-2.6.git] / net / ipv6 / netfilter / ip6_tables.c
blob455582384eced0b9242a40ea523ac31c8d6e08f4
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>
32 #include "../../netfilter/xt_repldata.h"
34 MODULE_LICENSE("GPL");
35 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
36 MODULE_DESCRIPTION("IPv6 packet filter");
38 /*#define DEBUG_IP_FIREWALL*/
39 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
40 /*#define DEBUG_IP_FIREWALL_USER*/
42 #ifdef DEBUG_IP_FIREWALL
43 #define dprintf(format, args...) pr_info(format , ## args)
44 #else
45 #define dprintf(format, args...)
46 #endif
48 #ifdef DEBUG_IP_FIREWALL_USER
49 #define duprintf(format, args...) pr_info(format , ## args)
50 #else
51 #define duprintf(format, args...)
52 #endif
54 #ifdef CONFIG_NETFILTER_DEBUG
55 #define IP_NF_ASSERT(x) WARN_ON(!(x))
56 #else
57 #define IP_NF_ASSERT(x)
58 #endif
60 #if 0
61 /* All the better to debug you with... */
62 #define static
63 #define inline
64 #endif
66 void *ip6t_alloc_initial_table(const struct xt_table *info)
68 return xt_alloc_initial_table(ip6t, IP6T);
70 EXPORT_SYMBOL_GPL(ip6t_alloc_initial_table);
73 We keep a set of rules for each CPU, so we can avoid write-locking
74 them in the softirq when updating the counters and therefore
75 only need to read-lock in the softirq; doing a write_lock_bh() in user
76 context stops packets coming through and allows user context to read
77 the counters or update the rules.
79 Hence the start of any table is given by get_table() below. */
81 /* Check for an extension */
82 int
83 ip6t_ext_hdr(u8 nexthdr)
85 return (nexthdr == IPPROTO_HOPOPTS) ||
86 (nexthdr == IPPROTO_ROUTING) ||
87 (nexthdr == IPPROTO_FRAGMENT) ||
88 (nexthdr == IPPROTO_ESP) ||
89 (nexthdr == IPPROTO_AH) ||
90 (nexthdr == IPPROTO_NONE) ||
91 (nexthdr == IPPROTO_DSTOPTS);
94 /* Returns whether matches rule or not. */
95 /* Performance critical - called for every packet */
96 static inline bool
97 ip6_packet_match(const struct sk_buff *skb,
98 const char *indev,
99 const char *outdev,
100 const struct ip6t_ip6 *ip6info,
101 unsigned int *protoff,
102 int *fragoff, bool *hotdrop)
104 unsigned long ret;
105 const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
107 #define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
109 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
110 &ip6info->src), IP6T_INV_SRCIP) ||
111 FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
112 &ip6info->dst), IP6T_INV_DSTIP)) {
113 dprintf("Source or dest mismatch.\n");
115 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
116 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
117 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
118 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
119 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
120 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
121 return false;
124 ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask);
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 ret = ifname_compare_aligned(outdev, ip6info->outiface, ip6info->outiface_mask);
135 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
136 dprintf("VIA out mismatch (%s vs %s).%s\n",
137 outdev, ip6info->outiface,
138 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
139 return false;
142 /* ... might want to do something with class and flowlabel here ... */
144 /* look for the desired protocol header */
145 if((ip6info->flags & IP6T_F_PROTO)) {
146 int protohdr;
147 unsigned short _frag_off;
149 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
150 if (protohdr < 0) {
151 if (_frag_off == 0)
152 *hotdrop = true;
153 return false;
155 *fragoff = _frag_off;
157 dprintf("Packet protocol %hi ?= %s%hi.\n",
158 protohdr,
159 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
160 ip6info->proto);
162 if (ip6info->proto == protohdr) {
163 if(ip6info->invflags & IP6T_INV_PROTO) {
164 return false;
166 return true;
169 /* We need match for the '-p all', too! */
170 if ((ip6info->proto != 0) &&
171 !(ip6info->invflags & IP6T_INV_PROTO))
172 return false;
174 return true;
177 /* should be ip6 safe */
178 static bool
179 ip6_checkentry(const struct ip6t_ip6 *ipv6)
181 if (ipv6->flags & ~IP6T_F_MASK) {
182 duprintf("Unknown flag bits set: %08X\n",
183 ipv6->flags & ~IP6T_F_MASK);
184 return false;
186 if (ipv6->invflags & ~IP6T_INV_MASK) {
187 duprintf("Unknown invflag bits set: %08X\n",
188 ipv6->invflags & ~IP6T_INV_MASK);
189 return false;
191 return true;
194 static unsigned int
195 ip6t_error(struct sk_buff *skb, const struct xt_action_param *par)
197 if (net_ratelimit())
198 pr_info("error: `%s'\n", (const char *)par->targinfo);
200 return NF_DROP;
203 static inline struct ip6t_entry *
204 get_entry(const void *base, unsigned int offset)
206 return (struct ip6t_entry *)(base + offset);
209 /* All zeroes == unconditional rule. */
210 /* Mildly perf critical (only if packet tracing is on) */
211 static inline bool unconditional(const struct ip6t_ip6 *ipv6)
213 static const struct ip6t_ip6 uncond;
215 return memcmp(ipv6, &uncond, sizeof(uncond)) == 0;
218 static inline const struct xt_entry_target *
219 ip6t_get_target_c(const struct ip6t_entry *e)
221 return ip6t_get_target((struct ip6t_entry *)e);
224 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
225 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
226 /* This cries for unification! */
227 static const char *const hooknames[] = {
228 [NF_INET_PRE_ROUTING] = "PREROUTING",
229 [NF_INET_LOCAL_IN] = "INPUT",
230 [NF_INET_FORWARD] = "FORWARD",
231 [NF_INET_LOCAL_OUT] = "OUTPUT",
232 [NF_INET_POST_ROUTING] = "POSTROUTING",
235 enum nf_ip_trace_comments {
236 NF_IP6_TRACE_COMMENT_RULE,
237 NF_IP6_TRACE_COMMENT_RETURN,
238 NF_IP6_TRACE_COMMENT_POLICY,
241 static const char *const comments[] = {
242 [NF_IP6_TRACE_COMMENT_RULE] = "rule",
243 [NF_IP6_TRACE_COMMENT_RETURN] = "return",
244 [NF_IP6_TRACE_COMMENT_POLICY] = "policy",
247 static struct nf_loginfo trace_loginfo = {
248 .type = NF_LOG_TYPE_LOG,
249 .u = {
250 .log = {
251 .level = 4,
252 .logflags = NF_LOG_MASK,
257 /* Mildly perf critical (only if packet tracing is on) */
258 static inline int
259 get_chainname_rulenum(const struct ip6t_entry *s, const struct ip6t_entry *e,
260 const char *hookname, const char **chainname,
261 const char **comment, unsigned int *rulenum)
263 const struct xt_standard_target *t = (void *)ip6t_get_target_c(s);
265 if (strcmp(t->target.u.kernel.target->name, XT_ERROR_TARGET) == 0) {
266 /* Head of user chain: ERROR target with chainname */
267 *chainname = t->target.data;
268 (*rulenum) = 0;
269 } else if (s == e) {
270 (*rulenum)++;
272 if (s->target_offset == sizeof(struct ip6t_entry) &&
273 strcmp(t->target.u.kernel.target->name,
274 XT_STANDARD_TARGET) == 0 &&
275 t->verdict < 0 &&
276 unconditional(&s->ipv6)) {
277 /* Tail of chains: STANDARD target (return/policy) */
278 *comment = *chainname == hookname
279 ? comments[NF_IP6_TRACE_COMMENT_POLICY]
280 : comments[NF_IP6_TRACE_COMMENT_RETURN];
282 return 1;
283 } else
284 (*rulenum)++;
286 return 0;
289 static void trace_packet(const struct sk_buff *skb,
290 unsigned int hook,
291 const struct net_device *in,
292 const struct net_device *out,
293 const char *tablename,
294 const struct xt_table_info *private,
295 const struct ip6t_entry *e)
297 const void *table_base;
298 const struct ip6t_entry *root;
299 const char *hookname, *chainname, *comment;
300 const struct ip6t_entry *iter;
301 unsigned int rulenum = 0;
303 table_base = private->entries[smp_processor_id()];
304 root = get_entry(table_base, private->hook_entry[hook]);
306 hookname = chainname = hooknames[hook];
307 comment = comments[NF_IP6_TRACE_COMMENT_RULE];
309 xt_entry_foreach(iter, root, private->size - private->hook_entry[hook])
310 if (get_chainname_rulenum(iter, e, hookname,
311 &chainname, &comment, &rulenum) != 0)
312 break;
314 nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
315 "TRACE: %s:%s:%s:%u ",
316 tablename, chainname, comment, rulenum);
318 #endif
320 static inline __pure struct ip6t_entry *
321 ip6t_next_entry(const struct ip6t_entry *entry)
323 return (void *)entry + entry->next_offset;
326 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
327 unsigned int
328 ip6t_do_table(struct sk_buff *skb,
329 unsigned int hook,
330 const struct net_device *in,
331 const struct net_device *out,
332 struct xt_table *table)
334 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
335 /* Initializing verdict to NF_DROP keeps gcc happy. */
336 unsigned int verdict = NF_DROP;
337 const char *indev, *outdev;
338 const void *table_base;
339 struct ip6t_entry *e, **jumpstack;
340 unsigned int *stackptr, origptr, cpu;
341 const struct xt_table_info *private;
342 struct xt_action_param acpar;
344 /* Initialization */
345 indev = in ? in->name : nulldevname;
346 outdev = out ? out->name : nulldevname;
347 /* We handle fragments by dealing with the first fragment as
348 * if it was a normal packet. All other fragments are treated
349 * normally, except that they will NEVER match rules that ask
350 * things we don't know, ie. tcp syn flag or ports). If the
351 * rule is also a fragment-specific rule, non-fragments won't
352 * match it. */
353 acpar.hotdrop = false;
354 acpar.in = in;
355 acpar.out = out;
356 acpar.family = NFPROTO_IPV6;
357 acpar.hooknum = hook;
359 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
361 xt_info_rdlock_bh();
362 private = table->private;
363 cpu = smp_processor_id();
364 table_base = private->entries[cpu];
365 jumpstack = (struct ip6t_entry **)private->jumpstack[cpu];
366 stackptr = per_cpu_ptr(private->stackptr, cpu);
367 origptr = *stackptr;
369 e = get_entry(table_base, private->hook_entry[hook]);
371 do {
372 const struct xt_entry_target *t;
373 const struct xt_entry_match *ematch;
375 IP_NF_ASSERT(e);
376 if (!ip6_packet_match(skb, indev, outdev, &e->ipv6,
377 &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) {
378 no_match:
379 e = ip6t_next_entry(e);
380 continue;
383 xt_ematch_foreach(ematch, e) {
384 acpar.match = ematch->u.kernel.match;
385 acpar.matchinfo = ematch->data;
386 if (!acpar.match->match(skb, &acpar))
387 goto no_match;
390 ADD_COUNTER(e->counters, skb->len, 1);
392 t = ip6t_get_target_c(e);
393 IP_NF_ASSERT(t->u.kernel.target);
395 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
396 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
397 /* The packet is traced: log it */
398 if (unlikely(skb->nf_trace))
399 trace_packet(skb, hook, in, out,
400 table->name, private, e);
401 #endif
402 /* Standard target? */
403 if (!t->u.kernel.target->target) {
404 int v;
406 v = ((struct xt_standard_target *)t)->verdict;
407 if (v < 0) {
408 /* Pop from stack? */
409 if (v != XT_RETURN) {
410 verdict = (unsigned)(-v) - 1;
411 break;
413 if (*stackptr == 0)
414 e = get_entry(table_base,
415 private->underflow[hook]);
416 else
417 e = ip6t_next_entry(jumpstack[--*stackptr]);
418 continue;
420 if (table_base + v != ip6t_next_entry(e) &&
421 !(e->ipv6.flags & IP6T_F_GOTO)) {
422 if (*stackptr >= private->stacksize) {
423 verdict = NF_DROP;
424 break;
426 jumpstack[(*stackptr)++] = e;
429 e = get_entry(table_base, v);
430 continue;
433 acpar.target = t->u.kernel.target;
434 acpar.targinfo = t->data;
436 verdict = t->u.kernel.target->target(skb, &acpar);
437 if (verdict == XT_CONTINUE)
438 e = ip6t_next_entry(e);
439 else
440 /* Verdict */
441 break;
442 } while (!acpar.hotdrop);
444 xt_info_rdunlock_bh();
445 *stackptr = origptr;
447 #ifdef DEBUG_ALLOW_ALL
448 return NF_ACCEPT;
449 #else
450 if (acpar.hotdrop)
451 return NF_DROP;
452 else return verdict;
453 #endif
456 /* Figures out from what hook each rule can be called: returns 0 if
457 there are loops. Puts hook bitmask in comefrom. */
458 static int
459 mark_source_chains(const struct xt_table_info *newinfo,
460 unsigned int valid_hooks, void *entry0)
462 unsigned int hook;
464 /* No recursion; use packet counter to save back ptrs (reset
465 to 0 as we leave), and comefrom to save source hook bitmask */
466 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
467 unsigned int pos = newinfo->hook_entry[hook];
468 struct ip6t_entry *e = (struct ip6t_entry *)(entry0 + pos);
470 if (!(valid_hooks & (1 << hook)))
471 continue;
473 /* Set initial back pointer. */
474 e->counters.pcnt = pos;
476 for (;;) {
477 const struct xt_standard_target *t
478 = (void *)ip6t_get_target_c(e);
479 int visited = e->comefrom & (1 << hook);
481 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
482 pr_err("iptables: loop hook %u pos %u %08X.\n",
483 hook, pos, e->comefrom);
484 return 0;
486 e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
488 /* Unconditional return/END. */
489 if ((e->target_offset == sizeof(struct ip6t_entry) &&
490 (strcmp(t->target.u.user.name,
491 XT_STANDARD_TARGET) == 0) &&
492 t->verdict < 0 &&
493 unconditional(&e->ipv6)) || visited) {
494 unsigned int oldpos, size;
496 if ((strcmp(t->target.u.user.name,
497 XT_STANDARD_TARGET) == 0) &&
498 t->verdict < -NF_MAX_VERDICT - 1) {
499 duprintf("mark_source_chains: bad "
500 "negative verdict (%i)\n",
501 t->verdict);
502 return 0;
505 /* Return: backtrack through the last
506 big jump. */
507 do {
508 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
509 #ifdef DEBUG_IP_FIREWALL_USER
510 if (e->comefrom
511 & (1 << NF_INET_NUMHOOKS)) {
512 duprintf("Back unset "
513 "on hook %u "
514 "rule %u\n",
515 hook, pos);
517 #endif
518 oldpos = pos;
519 pos = e->counters.pcnt;
520 e->counters.pcnt = 0;
522 /* We're at the start. */
523 if (pos == oldpos)
524 goto next;
526 e = (struct ip6t_entry *)
527 (entry0 + pos);
528 } while (oldpos == pos + e->next_offset);
530 /* Move along one */
531 size = e->next_offset;
532 e = (struct ip6t_entry *)
533 (entry0 + pos + size);
534 e->counters.pcnt = pos;
535 pos += size;
536 } else {
537 int newpos = t->verdict;
539 if (strcmp(t->target.u.user.name,
540 XT_STANDARD_TARGET) == 0 &&
541 newpos >= 0) {
542 if (newpos > newinfo->size -
543 sizeof(struct ip6t_entry)) {
544 duprintf("mark_source_chains: "
545 "bad verdict (%i)\n",
546 newpos);
547 return 0;
549 /* This a jump; chase it. */
550 duprintf("Jump rule %u -> %u\n",
551 pos, newpos);
552 } else {
553 /* ... this is a fallthru */
554 newpos = pos + e->next_offset;
556 e = (struct ip6t_entry *)
557 (entry0 + newpos);
558 e->counters.pcnt = pos;
559 pos = newpos;
562 next:
563 duprintf("Finished chain %u\n", hook);
565 return 1;
568 static void cleanup_match(struct xt_entry_match *m, struct net *net)
570 struct xt_mtdtor_param par;
572 par.net = net;
573 par.match = m->u.kernel.match;
574 par.matchinfo = m->data;
575 par.family = NFPROTO_IPV6;
576 if (par.match->destroy != NULL)
577 par.match->destroy(&par);
578 module_put(par.match->me);
581 static int
582 check_entry(const struct ip6t_entry *e, const char *name)
584 const struct xt_entry_target *t;
586 if (!ip6_checkentry(&e->ipv6)) {
587 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
588 return -EINVAL;
591 if (e->target_offset + sizeof(struct xt_entry_target) >
592 e->next_offset)
593 return -EINVAL;
595 t = ip6t_get_target_c(e);
596 if (e->target_offset + t->u.target_size > e->next_offset)
597 return -EINVAL;
599 return 0;
602 static int check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
604 const struct ip6t_ip6 *ipv6 = par->entryinfo;
605 int ret;
607 par->match = m->u.kernel.match;
608 par->matchinfo = m->data;
610 ret = xt_check_match(par, m->u.match_size - sizeof(*m),
611 ipv6->proto, ipv6->invflags & IP6T_INV_PROTO);
612 if (ret < 0) {
613 duprintf("ip_tables: check failed for `%s'.\n",
614 par.match->name);
615 return ret;
617 return 0;
620 static int
621 find_check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
623 struct xt_match *match;
624 int ret;
626 match = xt_request_find_match(NFPROTO_IPV6, m->u.user.name,
627 m->u.user.revision);
628 if (IS_ERR(match)) {
629 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
630 return PTR_ERR(match);
632 m->u.kernel.match = match;
634 ret = check_match(m, par);
635 if (ret)
636 goto err;
638 return 0;
639 err:
640 module_put(m->u.kernel.match->me);
641 return ret;
644 static int check_target(struct ip6t_entry *e, struct net *net, const char *name)
646 struct xt_entry_target *t = ip6t_get_target(e);
647 struct xt_tgchk_param par = {
648 .net = net,
649 .table = name,
650 .entryinfo = e,
651 .target = t->u.kernel.target,
652 .targinfo = t->data,
653 .hook_mask = e->comefrom,
654 .family = NFPROTO_IPV6,
656 int ret;
658 t = ip6t_get_target(e);
659 ret = xt_check_target(&par, t->u.target_size - sizeof(*t),
660 e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
661 if (ret < 0) {
662 duprintf("ip_tables: check failed for `%s'.\n",
663 t->u.kernel.target->name);
664 return ret;
666 return 0;
669 static int
670 find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
671 unsigned int size)
673 struct xt_entry_target *t;
674 struct xt_target *target;
675 int ret;
676 unsigned int j;
677 struct xt_mtchk_param mtpar;
678 struct xt_entry_match *ematch;
680 ret = check_entry(e, name);
681 if (ret)
682 return ret;
684 j = 0;
685 mtpar.net = net;
686 mtpar.table = name;
687 mtpar.entryinfo = &e->ipv6;
688 mtpar.hook_mask = e->comefrom;
689 mtpar.family = NFPROTO_IPV6;
690 xt_ematch_foreach(ematch, e) {
691 ret = find_check_match(ematch, &mtpar);
692 if (ret != 0)
693 goto cleanup_matches;
694 ++j;
697 t = ip6t_get_target(e);
698 target = xt_request_find_target(NFPROTO_IPV6, t->u.user.name,
699 t->u.user.revision);
700 if (IS_ERR(target)) {
701 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
702 ret = PTR_ERR(target);
703 goto cleanup_matches;
705 t->u.kernel.target = target;
707 ret = check_target(e, net, name);
708 if (ret)
709 goto err;
710 return 0;
711 err:
712 module_put(t->u.kernel.target->me);
713 cleanup_matches:
714 xt_ematch_foreach(ematch, e) {
715 if (j-- == 0)
716 break;
717 cleanup_match(ematch, net);
719 return ret;
722 static bool check_underflow(const struct ip6t_entry *e)
724 const struct xt_entry_target *t;
725 unsigned int verdict;
727 if (!unconditional(&e->ipv6))
728 return false;
729 t = ip6t_get_target_c(e);
730 if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
731 return false;
732 verdict = ((struct xt_standard_target *)t)->verdict;
733 verdict = -verdict - 1;
734 return verdict == NF_DROP || verdict == NF_ACCEPT;
737 static int
738 check_entry_size_and_hooks(struct ip6t_entry *e,
739 struct xt_table_info *newinfo,
740 const unsigned char *base,
741 const unsigned char *limit,
742 const unsigned int *hook_entries,
743 const unsigned int *underflows,
744 unsigned int valid_hooks)
746 unsigned int h;
748 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
749 (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
750 duprintf("Bad offset %p\n", e);
751 return -EINVAL;
754 if (e->next_offset
755 < sizeof(struct ip6t_entry) + sizeof(struct xt_entry_target)) {
756 duprintf("checking: element %p size %u\n",
757 e, e->next_offset);
758 return -EINVAL;
761 /* Check hooks & underflows */
762 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
763 if (!(valid_hooks & (1 << h)))
764 continue;
765 if ((unsigned char *)e - base == hook_entries[h])
766 newinfo->hook_entry[h] = hook_entries[h];
767 if ((unsigned char *)e - base == underflows[h]) {
768 if (!check_underflow(e)) {
769 pr_err("Underflows must be unconditional and "
770 "use the STANDARD target with "
771 "ACCEPT/DROP\n");
772 return -EINVAL;
774 newinfo->underflow[h] = underflows[h];
778 /* Clear counters and comefrom */
779 e->counters = ((struct xt_counters) { 0, 0 });
780 e->comefrom = 0;
781 return 0;
784 static void cleanup_entry(struct ip6t_entry *e, struct net *net)
786 struct xt_tgdtor_param par;
787 struct xt_entry_target *t;
788 struct xt_entry_match *ematch;
790 /* Cleanup all matches */
791 xt_ematch_foreach(ematch, e)
792 cleanup_match(ematch, net);
793 t = ip6t_get_target(e);
795 par.net = net;
796 par.target = t->u.kernel.target;
797 par.targinfo = t->data;
798 par.family = NFPROTO_IPV6;
799 if (par.target->destroy != NULL)
800 par.target->destroy(&par);
801 module_put(par.target->me);
804 /* Checks and translates the user-supplied table segment (held in
805 newinfo) */
806 static int
807 translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
808 const struct ip6t_replace *repl)
810 struct ip6t_entry *iter;
811 unsigned int i;
812 int ret = 0;
814 newinfo->size = repl->size;
815 newinfo->number = repl->num_entries;
817 /* Init all hooks to impossible value. */
818 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
819 newinfo->hook_entry[i] = 0xFFFFFFFF;
820 newinfo->underflow[i] = 0xFFFFFFFF;
823 duprintf("translate_table: size %u\n", newinfo->size);
824 i = 0;
825 /* Walk through entries, checking offsets. */
826 xt_entry_foreach(iter, entry0, newinfo->size) {
827 ret = check_entry_size_and_hooks(iter, newinfo, entry0,
828 entry0 + repl->size,
829 repl->hook_entry,
830 repl->underflow,
831 repl->valid_hooks);
832 if (ret != 0)
833 return ret;
834 ++i;
835 if (strcmp(ip6t_get_target(iter)->u.user.name,
836 XT_ERROR_TARGET) == 0)
837 ++newinfo->stacksize;
840 if (i != repl->num_entries) {
841 duprintf("translate_table: %u not %u entries\n",
842 i, repl->num_entries);
843 return -EINVAL;
846 /* Check hooks all assigned */
847 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
848 /* Only hooks which are valid */
849 if (!(repl->valid_hooks & (1 << i)))
850 continue;
851 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
852 duprintf("Invalid hook entry %u %u\n",
853 i, repl->hook_entry[i]);
854 return -EINVAL;
856 if (newinfo->underflow[i] == 0xFFFFFFFF) {
857 duprintf("Invalid underflow %u %u\n",
858 i, repl->underflow[i]);
859 return -EINVAL;
863 if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
864 return -ELOOP;
866 /* Finally, each sanity check must pass */
867 i = 0;
868 xt_entry_foreach(iter, entry0, newinfo->size) {
869 ret = find_check_entry(iter, net, repl->name, repl->size);
870 if (ret != 0)
871 break;
872 ++i;
875 if (ret != 0) {
876 xt_entry_foreach(iter, entry0, newinfo->size) {
877 if (i-- == 0)
878 break;
879 cleanup_entry(iter, net);
881 return ret;
884 /* And one copy for every other CPU */
885 for_each_possible_cpu(i) {
886 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
887 memcpy(newinfo->entries[i], entry0, newinfo->size);
890 return ret;
893 static void
894 get_counters(const struct xt_table_info *t,
895 struct xt_counters counters[])
897 struct ip6t_entry *iter;
898 unsigned int cpu;
899 unsigned int i;
900 unsigned int curcpu = get_cpu();
902 /* Instead of clearing (by a previous call to memset())
903 * the counters and using adds, we set the counters
904 * with data used by 'current' CPU
906 * Bottom half has to be disabled to prevent deadlock
907 * if new softirq were to run and call ipt_do_table
909 local_bh_disable();
910 i = 0;
911 xt_entry_foreach(iter, t->entries[curcpu], t->size) {
912 SET_COUNTER(counters[i], iter->counters.bcnt,
913 iter->counters.pcnt);
914 ++i;
916 local_bh_enable();
917 /* Processing counters from other cpus, we can let bottom half enabled,
918 * (preemption is disabled)
921 for_each_possible_cpu(cpu) {
922 if (cpu == curcpu)
923 continue;
924 i = 0;
925 local_bh_disable();
926 xt_info_wrlock(cpu);
927 xt_entry_foreach(iter, t->entries[cpu], t->size) {
928 ADD_COUNTER(counters[i], iter->counters.bcnt,
929 iter->counters.pcnt);
930 ++i;
932 xt_info_wrunlock(cpu);
933 local_bh_enable();
935 put_cpu();
938 static struct xt_counters *alloc_counters(const struct xt_table *table)
940 unsigned int countersize;
941 struct xt_counters *counters;
942 const struct xt_table_info *private = table->private;
944 /* We need atomic snapshot of counters: rest doesn't change
945 (other than comefrom, which userspace doesn't care
946 about). */
947 countersize = sizeof(struct xt_counters) * private->number;
948 counters = vmalloc(countersize);
950 if (counters == NULL)
951 return ERR_PTR(-ENOMEM);
953 get_counters(private, counters);
955 return counters;
958 static int
959 copy_entries_to_user(unsigned int total_size,
960 const struct xt_table *table,
961 void __user *userptr)
963 unsigned int off, num;
964 const struct ip6t_entry *e;
965 struct xt_counters *counters;
966 const struct xt_table_info *private = table->private;
967 int ret = 0;
968 const void *loc_cpu_entry;
970 counters = alloc_counters(table);
971 if (IS_ERR(counters))
972 return PTR_ERR(counters);
974 /* choose the copy that is on our node/cpu, ...
975 * This choice is lazy (because current thread is
976 * allowed to migrate to another cpu)
978 loc_cpu_entry = private->entries[raw_smp_processor_id()];
979 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
980 ret = -EFAULT;
981 goto free_counters;
984 /* FIXME: use iterator macros --RR */
985 /* ... then go back and fix counters and names */
986 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
987 unsigned int i;
988 const struct xt_entry_match *m;
989 const struct xt_entry_target *t;
991 e = (struct ip6t_entry *)(loc_cpu_entry + off);
992 if (copy_to_user(userptr + off
993 + offsetof(struct ip6t_entry, counters),
994 &counters[num],
995 sizeof(counters[num])) != 0) {
996 ret = -EFAULT;
997 goto free_counters;
1000 for (i = sizeof(struct ip6t_entry);
1001 i < e->target_offset;
1002 i += m->u.match_size) {
1003 m = (void *)e + i;
1005 if (copy_to_user(userptr + off + i
1006 + offsetof(struct xt_entry_match,
1007 u.user.name),
1008 m->u.kernel.match->name,
1009 strlen(m->u.kernel.match->name)+1)
1010 != 0) {
1011 ret = -EFAULT;
1012 goto free_counters;
1016 t = ip6t_get_target_c(e);
1017 if (copy_to_user(userptr + off + e->target_offset
1018 + offsetof(struct xt_entry_target,
1019 u.user.name),
1020 t->u.kernel.target->name,
1021 strlen(t->u.kernel.target->name)+1) != 0) {
1022 ret = -EFAULT;
1023 goto free_counters;
1027 free_counters:
1028 vfree(counters);
1029 return ret;
1032 #ifdef CONFIG_COMPAT
1033 static void compat_standard_from_user(void *dst, const void *src)
1035 int v = *(compat_int_t *)src;
1037 if (v > 0)
1038 v += xt_compat_calc_jump(AF_INET6, v);
1039 memcpy(dst, &v, sizeof(v));
1042 static int compat_standard_to_user(void __user *dst, const void *src)
1044 compat_int_t cv = *(int *)src;
1046 if (cv > 0)
1047 cv -= xt_compat_calc_jump(AF_INET6, cv);
1048 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1051 static int compat_calc_entry(const struct ip6t_entry *e,
1052 const struct xt_table_info *info,
1053 const void *base, struct xt_table_info *newinfo)
1055 const struct xt_entry_match *ematch;
1056 const struct xt_entry_target *t;
1057 unsigned int entry_offset;
1058 int off, i, ret;
1060 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1061 entry_offset = (void *)e - base;
1062 xt_ematch_foreach(ematch, e)
1063 off += xt_compat_match_offset(ematch->u.kernel.match);
1064 t = ip6t_get_target_c(e);
1065 off += xt_compat_target_offset(t->u.kernel.target);
1066 newinfo->size -= off;
1067 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1068 if (ret)
1069 return ret;
1071 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1072 if (info->hook_entry[i] &&
1073 (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1074 newinfo->hook_entry[i] -= off;
1075 if (info->underflow[i] &&
1076 (e < (struct ip6t_entry *)(base + info->underflow[i])))
1077 newinfo->underflow[i] -= off;
1079 return 0;
1082 static int compat_table_info(const struct xt_table_info *info,
1083 struct xt_table_info *newinfo)
1085 struct ip6t_entry *iter;
1086 void *loc_cpu_entry;
1087 int ret;
1089 if (!newinfo || !info)
1090 return -EINVAL;
1092 /* we dont care about newinfo->entries[] */
1093 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1094 newinfo->initial_entries = 0;
1095 loc_cpu_entry = info->entries[raw_smp_processor_id()];
1096 xt_entry_foreach(iter, loc_cpu_entry, info->size) {
1097 ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
1098 if (ret != 0)
1099 return ret;
1101 return 0;
1103 #endif
1105 static int get_info(struct net *net, void __user *user,
1106 const int *len, int compat)
1108 char name[XT_TABLE_MAXNAMELEN];
1109 struct xt_table *t;
1110 int ret;
1112 if (*len != sizeof(struct ip6t_getinfo)) {
1113 duprintf("length %u != %zu\n", *len,
1114 sizeof(struct ip6t_getinfo));
1115 return -EINVAL;
1118 if (copy_from_user(name, user, sizeof(name)) != 0)
1119 return -EFAULT;
1121 name[XT_TABLE_MAXNAMELEN-1] = '\0';
1122 #ifdef CONFIG_COMPAT
1123 if (compat)
1124 xt_compat_lock(AF_INET6);
1125 #endif
1126 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1127 "ip6table_%s", name);
1128 if (t && !IS_ERR(t)) {
1129 struct ip6t_getinfo info;
1130 const struct xt_table_info *private = t->private;
1131 #ifdef CONFIG_COMPAT
1132 struct xt_table_info tmp;
1134 if (compat) {
1135 ret = compat_table_info(private, &tmp);
1136 xt_compat_flush_offsets(AF_INET6);
1137 private = &tmp;
1139 #endif
1140 memset(&info, 0, sizeof(info));
1141 info.valid_hooks = t->valid_hooks;
1142 memcpy(info.hook_entry, private->hook_entry,
1143 sizeof(info.hook_entry));
1144 memcpy(info.underflow, private->underflow,
1145 sizeof(info.underflow));
1146 info.num_entries = private->number;
1147 info.size = private->size;
1148 strcpy(info.name, name);
1150 if (copy_to_user(user, &info, *len) != 0)
1151 ret = -EFAULT;
1152 else
1153 ret = 0;
1155 xt_table_unlock(t);
1156 module_put(t->me);
1157 } else
1158 ret = t ? PTR_ERR(t) : -ENOENT;
1159 #ifdef CONFIG_COMPAT
1160 if (compat)
1161 xt_compat_unlock(AF_INET6);
1162 #endif
1163 return ret;
1166 static int
1167 get_entries(struct net *net, struct ip6t_get_entries __user *uptr,
1168 const int *len)
1170 int ret;
1171 struct ip6t_get_entries get;
1172 struct xt_table *t;
1174 if (*len < sizeof(get)) {
1175 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
1176 return -EINVAL;
1178 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1179 return -EFAULT;
1180 if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1181 duprintf("get_entries: %u != %zu\n",
1182 *len, sizeof(get) + get.size);
1183 return -EINVAL;
1186 t = xt_find_table_lock(net, AF_INET6, get.name);
1187 if (t && !IS_ERR(t)) {
1188 struct xt_table_info *private = t->private;
1189 duprintf("t->private->number = %u\n", private->number);
1190 if (get.size == private->size)
1191 ret = copy_entries_to_user(private->size,
1192 t, uptr->entrytable);
1193 else {
1194 duprintf("get_entries: I've got %u not %u!\n",
1195 private->size, get.size);
1196 ret = -EAGAIN;
1198 module_put(t->me);
1199 xt_table_unlock(t);
1200 } else
1201 ret = t ? PTR_ERR(t) : -ENOENT;
1203 return ret;
1206 static int
1207 __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
1208 struct xt_table_info *newinfo, unsigned int num_counters,
1209 void __user *counters_ptr)
1211 int ret;
1212 struct xt_table *t;
1213 struct xt_table_info *oldinfo;
1214 struct xt_counters *counters;
1215 const void *loc_cpu_old_entry;
1216 struct ip6t_entry *iter;
1218 ret = 0;
1219 counters = vmalloc(num_counters * sizeof(struct xt_counters));
1220 if (!counters) {
1221 ret = -ENOMEM;
1222 goto out;
1225 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1226 "ip6table_%s", name);
1227 if (!t || IS_ERR(t)) {
1228 ret = t ? PTR_ERR(t) : -ENOENT;
1229 goto free_newinfo_counters_untrans;
1232 /* You lied! */
1233 if (valid_hooks != t->valid_hooks) {
1234 duprintf("Valid hook crap: %08X vs %08X\n",
1235 valid_hooks, t->valid_hooks);
1236 ret = -EINVAL;
1237 goto put_module;
1240 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1241 if (!oldinfo)
1242 goto put_module;
1244 /* Update module usage count based on number of rules */
1245 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1246 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1247 if ((oldinfo->number > oldinfo->initial_entries) ||
1248 (newinfo->number <= oldinfo->initial_entries))
1249 module_put(t->me);
1250 if ((oldinfo->number > oldinfo->initial_entries) &&
1251 (newinfo->number <= oldinfo->initial_entries))
1252 module_put(t->me);
1254 /* Get the old counters, and synchronize with replace */
1255 get_counters(oldinfo, counters);
1257 /* Decrease module usage counts and free resource */
1258 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1259 xt_entry_foreach(iter, loc_cpu_old_entry, oldinfo->size)
1260 cleanup_entry(iter, net);
1262 xt_free_table_info(oldinfo);
1263 if (copy_to_user(counters_ptr, counters,
1264 sizeof(struct xt_counters) * num_counters) != 0)
1265 ret = -EFAULT;
1266 vfree(counters);
1267 xt_table_unlock(t);
1268 return ret;
1270 put_module:
1271 module_put(t->me);
1272 xt_table_unlock(t);
1273 free_newinfo_counters_untrans:
1274 vfree(counters);
1275 out:
1276 return ret;
1279 static int
1280 do_replace(struct net *net, const void __user *user, unsigned int len)
1282 int ret;
1283 struct ip6t_replace tmp;
1284 struct xt_table_info *newinfo;
1285 void *loc_cpu_entry;
1286 struct ip6t_entry *iter;
1288 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1289 return -EFAULT;
1291 /* overflow check */
1292 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1293 return -ENOMEM;
1295 newinfo = xt_alloc_table_info(tmp.size);
1296 if (!newinfo)
1297 return -ENOMEM;
1299 /* choose the copy that is on our node/cpu */
1300 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1301 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1302 tmp.size) != 0) {
1303 ret = -EFAULT;
1304 goto free_newinfo;
1307 ret = translate_table(net, newinfo, loc_cpu_entry, &tmp);
1308 if (ret != 0)
1309 goto free_newinfo;
1311 duprintf("ip_tables: Translated table\n");
1313 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1314 tmp.num_counters, tmp.counters);
1315 if (ret)
1316 goto free_newinfo_untrans;
1317 return 0;
1319 free_newinfo_untrans:
1320 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
1321 cleanup_entry(iter, net);
1322 free_newinfo:
1323 xt_free_table_info(newinfo);
1324 return ret;
1327 static int
1328 do_add_counters(struct net *net, const void __user *user, unsigned int len,
1329 int compat)
1331 unsigned int i, curcpu;
1332 struct xt_counters_info tmp;
1333 struct xt_counters *paddc;
1334 unsigned int num_counters;
1335 char *name;
1336 int size;
1337 void *ptmp;
1338 struct xt_table *t;
1339 const struct xt_table_info *private;
1340 int ret = 0;
1341 const void *loc_cpu_entry;
1342 struct ip6t_entry *iter;
1343 #ifdef CONFIG_COMPAT
1344 struct compat_xt_counters_info compat_tmp;
1346 if (compat) {
1347 ptmp = &compat_tmp;
1348 size = sizeof(struct compat_xt_counters_info);
1349 } else
1350 #endif
1352 ptmp = &tmp;
1353 size = sizeof(struct xt_counters_info);
1356 if (copy_from_user(ptmp, user, size) != 0)
1357 return -EFAULT;
1359 #ifdef CONFIG_COMPAT
1360 if (compat) {
1361 num_counters = compat_tmp.num_counters;
1362 name = compat_tmp.name;
1363 } else
1364 #endif
1366 num_counters = tmp.num_counters;
1367 name = tmp.name;
1370 if (len != size + num_counters * sizeof(struct xt_counters))
1371 return -EINVAL;
1373 paddc = vmalloc(len - size);
1374 if (!paddc)
1375 return -ENOMEM;
1377 if (copy_from_user(paddc, user + size, len - size) != 0) {
1378 ret = -EFAULT;
1379 goto free;
1382 t = xt_find_table_lock(net, AF_INET6, name);
1383 if (!t || IS_ERR(t)) {
1384 ret = t ? PTR_ERR(t) : -ENOENT;
1385 goto free;
1389 local_bh_disable();
1390 private = t->private;
1391 if (private->number != num_counters) {
1392 ret = -EINVAL;
1393 goto unlock_up_free;
1396 i = 0;
1397 /* Choose the copy that is on our node */
1398 curcpu = smp_processor_id();
1399 xt_info_wrlock(curcpu);
1400 loc_cpu_entry = private->entries[curcpu];
1401 xt_entry_foreach(iter, loc_cpu_entry, private->size) {
1402 ADD_COUNTER(iter->counters, paddc[i].bcnt, paddc[i].pcnt);
1403 ++i;
1405 xt_info_wrunlock(curcpu);
1407 unlock_up_free:
1408 local_bh_enable();
1409 xt_table_unlock(t);
1410 module_put(t->me);
1411 free:
1412 vfree(paddc);
1414 return ret;
1417 #ifdef CONFIG_COMPAT
1418 struct compat_ip6t_replace {
1419 char name[XT_TABLE_MAXNAMELEN];
1420 u32 valid_hooks;
1421 u32 num_entries;
1422 u32 size;
1423 u32 hook_entry[NF_INET_NUMHOOKS];
1424 u32 underflow[NF_INET_NUMHOOKS];
1425 u32 num_counters;
1426 compat_uptr_t counters; /* struct xt_counters * */
1427 struct compat_ip6t_entry entries[0];
1430 static int
1431 compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
1432 unsigned int *size, struct xt_counters *counters,
1433 unsigned int i)
1435 struct xt_entry_target *t;
1436 struct compat_ip6t_entry __user *ce;
1437 u_int16_t target_offset, next_offset;
1438 compat_uint_t origsize;
1439 const struct xt_entry_match *ematch;
1440 int ret = 0;
1442 origsize = *size;
1443 ce = (struct compat_ip6t_entry __user *)*dstptr;
1444 if (copy_to_user(ce, e, sizeof(struct ip6t_entry)) != 0 ||
1445 copy_to_user(&ce->counters, &counters[i],
1446 sizeof(counters[i])) != 0)
1447 return -EFAULT;
1449 *dstptr += sizeof(struct compat_ip6t_entry);
1450 *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1452 xt_ematch_foreach(ematch, e) {
1453 ret = xt_compat_match_to_user(ematch, dstptr, size);
1454 if (ret != 0)
1455 return ret;
1457 target_offset = e->target_offset - (origsize - *size);
1458 t = ip6t_get_target(e);
1459 ret = xt_compat_target_to_user(t, dstptr, size);
1460 if (ret)
1461 return ret;
1462 next_offset = e->next_offset - (origsize - *size);
1463 if (put_user(target_offset, &ce->target_offset) != 0 ||
1464 put_user(next_offset, &ce->next_offset) != 0)
1465 return -EFAULT;
1466 return 0;
1469 static int
1470 compat_find_calc_match(struct xt_entry_match *m,
1471 const char *name,
1472 const struct ip6t_ip6 *ipv6,
1473 unsigned int hookmask,
1474 int *size)
1476 struct xt_match *match;
1478 match = xt_request_find_match(NFPROTO_IPV6, m->u.user.name,
1479 m->u.user.revision);
1480 if (IS_ERR(match)) {
1481 duprintf("compat_check_calc_match: `%s' not found\n",
1482 m->u.user.name);
1483 return PTR_ERR(match);
1485 m->u.kernel.match = match;
1486 *size += xt_compat_match_offset(match);
1487 return 0;
1490 static void compat_release_entry(struct compat_ip6t_entry *e)
1492 struct xt_entry_target *t;
1493 struct xt_entry_match *ematch;
1495 /* Cleanup all matches */
1496 xt_ematch_foreach(ematch, e)
1497 module_put(ematch->u.kernel.match->me);
1498 t = compat_ip6t_get_target(e);
1499 module_put(t->u.kernel.target->me);
1502 static int
1503 check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1504 struct xt_table_info *newinfo,
1505 unsigned int *size,
1506 const unsigned char *base,
1507 const unsigned char *limit,
1508 const unsigned int *hook_entries,
1509 const unsigned int *underflows,
1510 const char *name)
1512 struct xt_entry_match *ematch;
1513 struct xt_entry_target *t;
1514 struct xt_target *target;
1515 unsigned int entry_offset;
1516 unsigned int j;
1517 int ret, off, h;
1519 duprintf("check_compat_entry_size_and_hooks %p\n", e);
1520 if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
1521 (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
1522 duprintf("Bad offset %p, limit = %p\n", e, limit);
1523 return -EINVAL;
1526 if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1527 sizeof(struct compat_xt_entry_target)) {
1528 duprintf("checking: element %p size %u\n",
1529 e, e->next_offset);
1530 return -EINVAL;
1533 /* For purposes of check_entry casting the compat entry is fine */
1534 ret = check_entry((struct ip6t_entry *)e, name);
1535 if (ret)
1536 return ret;
1538 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1539 entry_offset = (void *)e - (void *)base;
1540 j = 0;
1541 xt_ematch_foreach(ematch, e) {
1542 ret = compat_find_calc_match(ematch, name,
1543 &e->ipv6, e->comefrom, &off);
1544 if (ret != 0)
1545 goto release_matches;
1546 ++j;
1549 t = compat_ip6t_get_target(e);
1550 target = xt_request_find_target(NFPROTO_IPV6, t->u.user.name,
1551 t->u.user.revision);
1552 if (IS_ERR(target)) {
1553 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1554 t->u.user.name);
1555 ret = PTR_ERR(target);
1556 goto release_matches;
1558 t->u.kernel.target = target;
1560 off += xt_compat_target_offset(target);
1561 *size += off;
1562 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1563 if (ret)
1564 goto out;
1566 /* Check hooks & underflows */
1567 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1568 if ((unsigned char *)e - base == hook_entries[h])
1569 newinfo->hook_entry[h] = hook_entries[h];
1570 if ((unsigned char *)e - base == underflows[h])
1571 newinfo->underflow[h] = underflows[h];
1574 /* Clear counters and comefrom */
1575 memset(&e->counters, 0, sizeof(e->counters));
1576 e->comefrom = 0;
1577 return 0;
1579 out:
1580 module_put(t->u.kernel.target->me);
1581 release_matches:
1582 xt_ematch_foreach(ematch, e) {
1583 if (j-- == 0)
1584 break;
1585 module_put(ematch->u.kernel.match->me);
1587 return ret;
1590 static int
1591 compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1592 unsigned int *size, const char *name,
1593 struct xt_table_info *newinfo, unsigned char *base)
1595 struct xt_entry_target *t;
1596 struct xt_target *target;
1597 struct ip6t_entry *de;
1598 unsigned int origsize;
1599 int ret, h;
1600 struct xt_entry_match *ematch;
1602 ret = 0;
1603 origsize = *size;
1604 de = (struct ip6t_entry *)*dstptr;
1605 memcpy(de, e, sizeof(struct ip6t_entry));
1606 memcpy(&de->counters, &e->counters, sizeof(e->counters));
1608 *dstptr += sizeof(struct ip6t_entry);
1609 *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1611 xt_ematch_foreach(ematch, e) {
1612 ret = xt_compat_match_from_user(ematch, dstptr, size);
1613 if (ret != 0)
1614 return ret;
1616 de->target_offset = e->target_offset - (origsize - *size);
1617 t = compat_ip6t_get_target(e);
1618 target = t->u.kernel.target;
1619 xt_compat_target_from_user(t, dstptr, size);
1621 de->next_offset = e->next_offset - (origsize - *size);
1622 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1623 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1624 newinfo->hook_entry[h] -= origsize - *size;
1625 if ((unsigned char *)de - base < newinfo->underflow[h])
1626 newinfo->underflow[h] -= origsize - *size;
1628 return ret;
1631 static int compat_check_entry(struct ip6t_entry *e, struct net *net,
1632 const char *name)
1634 unsigned int j;
1635 int ret = 0;
1636 struct xt_mtchk_param mtpar;
1637 struct xt_entry_match *ematch;
1639 j = 0;
1640 mtpar.net = net;
1641 mtpar.table = name;
1642 mtpar.entryinfo = &e->ipv6;
1643 mtpar.hook_mask = e->comefrom;
1644 mtpar.family = NFPROTO_IPV6;
1645 xt_ematch_foreach(ematch, e) {
1646 ret = check_match(ematch, &mtpar);
1647 if (ret != 0)
1648 goto cleanup_matches;
1649 ++j;
1652 ret = check_target(e, net, name);
1653 if (ret)
1654 goto cleanup_matches;
1655 return 0;
1657 cleanup_matches:
1658 xt_ematch_foreach(ematch, e) {
1659 if (j-- == 0)
1660 break;
1661 cleanup_match(ematch, net);
1663 return ret;
1666 static int
1667 translate_compat_table(struct net *net,
1668 const char *name,
1669 unsigned int valid_hooks,
1670 struct xt_table_info **pinfo,
1671 void **pentry0,
1672 unsigned int total_size,
1673 unsigned int number,
1674 unsigned int *hook_entries,
1675 unsigned int *underflows)
1677 unsigned int i, j;
1678 struct xt_table_info *newinfo, *info;
1679 void *pos, *entry0, *entry1;
1680 struct compat_ip6t_entry *iter0;
1681 struct ip6t_entry *iter1;
1682 unsigned int size;
1683 int ret = 0;
1685 info = *pinfo;
1686 entry0 = *pentry0;
1687 size = total_size;
1688 info->number = number;
1690 /* Init all hooks to impossible value. */
1691 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1692 info->hook_entry[i] = 0xFFFFFFFF;
1693 info->underflow[i] = 0xFFFFFFFF;
1696 duprintf("translate_compat_table: size %u\n", info->size);
1697 j = 0;
1698 xt_compat_lock(AF_INET6);
1699 /* Walk through entries, checking offsets. */
1700 xt_entry_foreach(iter0, entry0, total_size) {
1701 ret = check_compat_entry_size_and_hooks(iter0, info, &size,
1702 entry0,
1703 entry0 + total_size,
1704 hook_entries,
1705 underflows,
1706 name);
1707 if (ret != 0)
1708 goto out_unlock;
1709 ++j;
1712 ret = -EINVAL;
1713 if (j != number) {
1714 duprintf("translate_compat_table: %u not %u entries\n",
1715 j, number);
1716 goto out_unlock;
1719 /* Check hooks all assigned */
1720 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1721 /* Only hooks which are valid */
1722 if (!(valid_hooks & (1 << i)))
1723 continue;
1724 if (info->hook_entry[i] == 0xFFFFFFFF) {
1725 duprintf("Invalid hook entry %u %u\n",
1726 i, hook_entries[i]);
1727 goto out_unlock;
1729 if (info->underflow[i] == 0xFFFFFFFF) {
1730 duprintf("Invalid underflow %u %u\n",
1731 i, underflows[i]);
1732 goto out_unlock;
1736 ret = -ENOMEM;
1737 newinfo = xt_alloc_table_info(size);
1738 if (!newinfo)
1739 goto out_unlock;
1741 newinfo->number = number;
1742 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1743 newinfo->hook_entry[i] = info->hook_entry[i];
1744 newinfo->underflow[i] = info->underflow[i];
1746 entry1 = newinfo->entries[raw_smp_processor_id()];
1747 pos = entry1;
1748 size = total_size;
1749 xt_entry_foreach(iter0, entry0, total_size) {
1750 ret = compat_copy_entry_from_user(iter0, &pos, &size,
1751 name, newinfo, entry1);
1752 if (ret != 0)
1753 break;
1755 xt_compat_flush_offsets(AF_INET6);
1756 xt_compat_unlock(AF_INET6);
1757 if (ret)
1758 goto free_newinfo;
1760 ret = -ELOOP;
1761 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1762 goto free_newinfo;
1764 i = 0;
1765 xt_entry_foreach(iter1, entry1, newinfo->size) {
1766 ret = compat_check_entry(iter1, net, name);
1767 if (ret != 0)
1768 break;
1769 ++i;
1770 if (strcmp(ip6t_get_target(iter1)->u.user.name,
1771 XT_ERROR_TARGET) == 0)
1772 ++newinfo->stacksize;
1774 if (ret) {
1776 * The first i matches need cleanup_entry (calls ->destroy)
1777 * because they had called ->check already. The other j-i
1778 * entries need only release.
1780 int skip = i;
1781 j -= i;
1782 xt_entry_foreach(iter0, entry0, newinfo->size) {
1783 if (skip-- > 0)
1784 continue;
1785 if (j-- == 0)
1786 break;
1787 compat_release_entry(iter0);
1789 xt_entry_foreach(iter1, entry1, newinfo->size) {
1790 if (i-- == 0)
1791 break;
1792 cleanup_entry(iter1, net);
1794 xt_free_table_info(newinfo);
1795 return ret;
1798 /* And one copy for every other CPU */
1799 for_each_possible_cpu(i)
1800 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1801 memcpy(newinfo->entries[i], entry1, newinfo->size);
1803 *pinfo = newinfo;
1804 *pentry0 = entry1;
1805 xt_free_table_info(info);
1806 return 0;
1808 free_newinfo:
1809 xt_free_table_info(newinfo);
1810 out:
1811 xt_entry_foreach(iter0, entry0, total_size) {
1812 if (j-- == 0)
1813 break;
1814 compat_release_entry(iter0);
1816 return ret;
1817 out_unlock:
1818 xt_compat_flush_offsets(AF_INET6);
1819 xt_compat_unlock(AF_INET6);
1820 goto out;
1823 static int
1824 compat_do_replace(struct net *net, void __user *user, unsigned int len)
1826 int ret;
1827 struct compat_ip6t_replace tmp;
1828 struct xt_table_info *newinfo;
1829 void *loc_cpu_entry;
1830 struct ip6t_entry *iter;
1832 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1833 return -EFAULT;
1835 /* overflow check */
1836 if (tmp.size >= INT_MAX / num_possible_cpus())
1837 return -ENOMEM;
1838 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1839 return -ENOMEM;
1841 newinfo = xt_alloc_table_info(tmp.size);
1842 if (!newinfo)
1843 return -ENOMEM;
1845 /* choose the copy that is on our node/cpu */
1846 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1847 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1848 tmp.size) != 0) {
1849 ret = -EFAULT;
1850 goto free_newinfo;
1853 ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
1854 &newinfo, &loc_cpu_entry, tmp.size,
1855 tmp.num_entries, tmp.hook_entry,
1856 tmp.underflow);
1857 if (ret != 0)
1858 goto free_newinfo;
1860 duprintf("compat_do_replace: Translated table\n");
1862 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1863 tmp.num_counters, compat_ptr(tmp.counters));
1864 if (ret)
1865 goto free_newinfo_untrans;
1866 return 0;
1868 free_newinfo_untrans:
1869 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
1870 cleanup_entry(iter, net);
1871 free_newinfo:
1872 xt_free_table_info(newinfo);
1873 return ret;
1876 static int
1877 compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1878 unsigned int len)
1880 int ret;
1882 if (!capable(CAP_NET_ADMIN))
1883 return -EPERM;
1885 switch (cmd) {
1886 case IP6T_SO_SET_REPLACE:
1887 ret = compat_do_replace(sock_net(sk), user, len);
1888 break;
1890 case IP6T_SO_SET_ADD_COUNTERS:
1891 ret = do_add_counters(sock_net(sk), user, len, 1);
1892 break;
1894 default:
1895 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1896 ret = -EINVAL;
1899 return ret;
1902 struct compat_ip6t_get_entries {
1903 char name[XT_TABLE_MAXNAMELEN];
1904 compat_uint_t size;
1905 struct compat_ip6t_entry entrytable[0];
1908 static int
1909 compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1910 void __user *userptr)
1912 struct xt_counters *counters;
1913 const struct xt_table_info *private = table->private;
1914 void __user *pos;
1915 unsigned int size;
1916 int ret = 0;
1917 const void *loc_cpu_entry;
1918 unsigned int i = 0;
1919 struct ip6t_entry *iter;
1921 counters = alloc_counters(table);
1922 if (IS_ERR(counters))
1923 return PTR_ERR(counters);
1925 /* choose the copy that is on our node/cpu, ...
1926 * This choice is lazy (because current thread is
1927 * allowed to migrate to another cpu)
1929 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1930 pos = userptr;
1931 size = total_size;
1932 xt_entry_foreach(iter, loc_cpu_entry, total_size) {
1933 ret = compat_copy_entry_to_user(iter, &pos,
1934 &size, counters, i++);
1935 if (ret != 0)
1936 break;
1939 vfree(counters);
1940 return ret;
1943 static int
1944 compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1945 int *len)
1947 int ret;
1948 struct compat_ip6t_get_entries get;
1949 struct xt_table *t;
1951 if (*len < sizeof(get)) {
1952 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
1953 return -EINVAL;
1956 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1957 return -EFAULT;
1959 if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
1960 duprintf("compat_get_entries: %u != %zu\n",
1961 *len, sizeof(get) + get.size);
1962 return -EINVAL;
1965 xt_compat_lock(AF_INET6);
1966 t = xt_find_table_lock(net, AF_INET6, get.name);
1967 if (t && !IS_ERR(t)) {
1968 const struct xt_table_info *private = t->private;
1969 struct xt_table_info info;
1970 duprintf("t->private->number = %u\n", private->number);
1971 ret = compat_table_info(private, &info);
1972 if (!ret && get.size == info.size) {
1973 ret = compat_copy_entries_to_user(private->size,
1974 t, uptr->entrytable);
1975 } else if (!ret) {
1976 duprintf("compat_get_entries: I've got %u not %u!\n",
1977 private->size, get.size);
1978 ret = -EAGAIN;
1980 xt_compat_flush_offsets(AF_INET6);
1981 module_put(t->me);
1982 xt_table_unlock(t);
1983 } else
1984 ret = t ? PTR_ERR(t) : -ENOENT;
1986 xt_compat_unlock(AF_INET6);
1987 return ret;
1990 static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1992 static int
1993 compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1995 int ret;
1997 if (!capable(CAP_NET_ADMIN))
1998 return -EPERM;
2000 switch (cmd) {
2001 case IP6T_SO_GET_INFO:
2002 ret = get_info(sock_net(sk), user, len, 1);
2003 break;
2004 case IP6T_SO_GET_ENTRIES:
2005 ret = compat_get_entries(sock_net(sk), user, len);
2006 break;
2007 default:
2008 ret = do_ip6t_get_ctl(sk, cmd, user, len);
2010 return ret;
2012 #endif
2014 static int
2015 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2017 int ret;
2019 if (!capable(CAP_NET_ADMIN))
2020 return -EPERM;
2022 switch (cmd) {
2023 case IP6T_SO_SET_REPLACE:
2024 ret = do_replace(sock_net(sk), user, len);
2025 break;
2027 case IP6T_SO_SET_ADD_COUNTERS:
2028 ret = do_add_counters(sock_net(sk), user, len, 0);
2029 break;
2031 default:
2032 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
2033 ret = -EINVAL;
2036 return ret;
2039 static int
2040 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2042 int ret;
2044 if (!capable(CAP_NET_ADMIN))
2045 return -EPERM;
2047 switch (cmd) {
2048 case IP6T_SO_GET_INFO:
2049 ret = get_info(sock_net(sk), user, len, 0);
2050 break;
2052 case IP6T_SO_GET_ENTRIES:
2053 ret = get_entries(sock_net(sk), user, len);
2054 break;
2056 case IP6T_SO_GET_REVISION_MATCH:
2057 case IP6T_SO_GET_REVISION_TARGET: {
2058 struct xt_get_revision rev;
2059 int target;
2061 if (*len != sizeof(rev)) {
2062 ret = -EINVAL;
2063 break;
2065 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2066 ret = -EFAULT;
2067 break;
2070 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2071 target = 1;
2072 else
2073 target = 0;
2075 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2076 rev.revision,
2077 target, &ret),
2078 "ip6t_%s", rev.name);
2079 break;
2082 default:
2083 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2084 ret = -EINVAL;
2087 return ret;
2090 struct xt_table *ip6t_register_table(struct net *net,
2091 const struct xt_table *table,
2092 const struct ip6t_replace *repl)
2094 int ret;
2095 struct xt_table_info *newinfo;
2096 struct xt_table_info bootstrap = {0};
2097 void *loc_cpu_entry;
2098 struct xt_table *new_table;
2100 newinfo = xt_alloc_table_info(repl->size);
2101 if (!newinfo) {
2102 ret = -ENOMEM;
2103 goto out;
2106 /* choose the copy on our node/cpu, but dont care about preemption */
2107 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2108 memcpy(loc_cpu_entry, repl->entries, repl->size);
2110 ret = translate_table(net, newinfo, loc_cpu_entry, repl);
2111 if (ret != 0)
2112 goto out_free;
2114 new_table = xt_register_table(net, table, &bootstrap, newinfo);
2115 if (IS_ERR(new_table)) {
2116 ret = PTR_ERR(new_table);
2117 goto out_free;
2119 return new_table;
2121 out_free:
2122 xt_free_table_info(newinfo);
2123 out:
2124 return ERR_PTR(ret);
2127 void ip6t_unregister_table(struct net *net, struct xt_table *table)
2129 struct xt_table_info *private;
2130 void *loc_cpu_entry;
2131 struct module *table_owner = table->me;
2132 struct ip6t_entry *iter;
2134 private = xt_unregister_table(table);
2136 /* Decrease module usage counts and free resources */
2137 loc_cpu_entry = private->entries[raw_smp_processor_id()];
2138 xt_entry_foreach(iter, loc_cpu_entry, private->size)
2139 cleanup_entry(iter, net);
2140 if (private->number > private->initial_entries)
2141 module_put(table_owner);
2142 xt_free_table_info(private);
2145 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
2146 static inline bool
2147 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2148 u_int8_t type, u_int8_t code,
2149 bool invert)
2151 return (type == test_type && code >= min_code && code <= max_code)
2152 ^ invert;
2155 static bool
2156 icmp6_match(const struct sk_buff *skb, struct xt_action_param *par)
2158 const struct icmp6hdr *ic;
2159 struct icmp6hdr _icmph;
2160 const struct ip6t_icmp *icmpinfo = par->matchinfo;
2162 /* Must not be a fragment. */
2163 if (par->fragoff != 0)
2164 return false;
2166 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
2167 if (ic == NULL) {
2168 /* We've been asked to examine this packet, and we
2169 * can't. Hence, no choice but to drop.
2171 duprintf("Dropping evil ICMP tinygram.\n");
2172 par->hotdrop = true;
2173 return false;
2176 return icmp6_type_code_match(icmpinfo->type,
2177 icmpinfo->code[0],
2178 icmpinfo->code[1],
2179 ic->icmp6_type, ic->icmp6_code,
2180 !!(icmpinfo->invflags&IP6T_ICMP_INV));
2183 /* Called when user tries to insert an entry of this type. */
2184 static int icmp6_checkentry(const struct xt_mtchk_param *par)
2186 const struct ip6t_icmp *icmpinfo = par->matchinfo;
2188 /* Must specify no unknown invflags */
2189 return (icmpinfo->invflags & ~IP6T_ICMP_INV) ? -EINVAL : 0;
2192 /* The built-in targets: standard (NULL) and error. */
2193 static struct xt_target ip6t_builtin_tg[] __read_mostly = {
2195 .name = XT_STANDARD_TARGET,
2196 .targetsize = sizeof(int),
2197 .family = NFPROTO_IPV6,
2198 #ifdef CONFIG_COMPAT
2199 .compatsize = sizeof(compat_int_t),
2200 .compat_from_user = compat_standard_from_user,
2201 .compat_to_user = compat_standard_to_user,
2202 #endif
2205 .name = XT_ERROR_TARGET,
2206 .target = ip6t_error,
2207 .targetsize = XT_FUNCTION_MAXNAMELEN,
2208 .family = NFPROTO_IPV6,
2212 static struct nf_sockopt_ops ip6t_sockopts = {
2213 .pf = PF_INET6,
2214 .set_optmin = IP6T_BASE_CTL,
2215 .set_optmax = IP6T_SO_SET_MAX+1,
2216 .set = do_ip6t_set_ctl,
2217 #ifdef CONFIG_COMPAT
2218 .compat_set = compat_do_ip6t_set_ctl,
2219 #endif
2220 .get_optmin = IP6T_BASE_CTL,
2221 .get_optmax = IP6T_SO_GET_MAX+1,
2222 .get = do_ip6t_get_ctl,
2223 #ifdef CONFIG_COMPAT
2224 .compat_get = compat_do_ip6t_get_ctl,
2225 #endif
2226 .owner = THIS_MODULE,
2229 static struct xt_match ip6t_builtin_mt[] __read_mostly = {
2231 .name = "icmp6",
2232 .match = icmp6_match,
2233 .matchsize = sizeof(struct ip6t_icmp),
2234 .checkentry = icmp6_checkentry,
2235 .proto = IPPROTO_ICMPV6,
2236 .family = NFPROTO_IPV6,
2240 static int __net_init ip6_tables_net_init(struct net *net)
2242 return xt_proto_init(net, NFPROTO_IPV6);
2245 static void __net_exit ip6_tables_net_exit(struct net *net)
2247 xt_proto_fini(net, NFPROTO_IPV6);
2250 static struct pernet_operations ip6_tables_net_ops = {
2251 .init = ip6_tables_net_init,
2252 .exit = ip6_tables_net_exit,
2255 static int __init ip6_tables_init(void)
2257 int ret;
2259 ret = register_pernet_subsys(&ip6_tables_net_ops);
2260 if (ret < 0)
2261 goto err1;
2263 /* Noone else will be downing sem now, so we won't sleep */
2264 ret = xt_register_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
2265 if (ret < 0)
2266 goto err2;
2267 ret = xt_register_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
2268 if (ret < 0)
2269 goto err4;
2271 /* Register setsockopt */
2272 ret = nf_register_sockopt(&ip6t_sockopts);
2273 if (ret < 0)
2274 goto err5;
2276 pr_info("(C) 2000-2006 Netfilter Core Team\n");
2277 return 0;
2279 err5:
2280 xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
2281 err4:
2282 xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
2283 err2:
2284 unregister_pernet_subsys(&ip6_tables_net_ops);
2285 err1:
2286 return ret;
2289 static void __exit ip6_tables_fini(void)
2291 nf_unregister_sockopt(&ip6t_sockopts);
2293 xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
2294 xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
2295 unregister_pernet_subsys(&ip6_tables_net_ops);
2299 * find the offset to specified header or the protocol number of last header
2300 * if target < 0. "last header" is transport protocol header, ESP, or
2301 * "No next header".
2303 * If target header is found, its offset is set in *offset and return protocol
2304 * number. Otherwise, return -1.
2306 * If the first fragment doesn't contain the final protocol header or
2307 * NEXTHDR_NONE it is considered invalid.
2309 * Note that non-1st fragment is special case that "the protocol number
2310 * of last header" is "next header" field in Fragment header. In this case,
2311 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
2312 * isn't NULL.
2315 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2316 int target, unsigned short *fragoff)
2318 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
2319 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
2320 unsigned int len = skb->len - start;
2322 if (fragoff)
2323 *fragoff = 0;
2325 while (nexthdr != target) {
2326 struct ipv6_opt_hdr _hdr, *hp;
2327 unsigned int hdrlen;
2329 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2330 if (target < 0)
2331 break;
2332 return -ENOENT;
2335 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2336 if (hp == NULL)
2337 return -EBADMSG;
2338 if (nexthdr == NEXTHDR_FRAGMENT) {
2339 unsigned short _frag_off;
2340 __be16 *fp;
2341 fp = skb_header_pointer(skb,
2342 start+offsetof(struct frag_hdr,
2343 frag_off),
2344 sizeof(_frag_off),
2345 &_frag_off);
2346 if (fp == NULL)
2347 return -EBADMSG;
2349 _frag_off = ntohs(*fp) & ~0x7;
2350 if (_frag_off) {
2351 if (target < 0 &&
2352 ((!ipv6_ext_hdr(hp->nexthdr)) ||
2353 hp->nexthdr == NEXTHDR_NONE)) {
2354 if (fragoff)
2355 *fragoff = _frag_off;
2356 return hp->nexthdr;
2358 return -ENOENT;
2360 hdrlen = 8;
2361 } else if (nexthdr == NEXTHDR_AUTH)
2362 hdrlen = (hp->hdrlen + 2) << 2;
2363 else
2364 hdrlen = ipv6_optlen(hp);
2366 nexthdr = hp->nexthdr;
2367 len -= hdrlen;
2368 start += hdrlen;
2371 *offset = start;
2372 return nexthdr;
2375 EXPORT_SYMBOL(ip6t_register_table);
2376 EXPORT_SYMBOL(ip6t_unregister_table);
2377 EXPORT_SYMBOL(ip6t_do_table);
2378 EXPORT_SYMBOL(ip6t_ext_hdr);
2379 EXPORT_SYMBOL(ipv6_find_hdr);
2381 module_init(ip6_tables_init);
2382 module_exit(ip6_tables_fini);