added 2.6.29.6 aldebaran kernel
[nao-ulib.git] / kernel / 2.6.29.6-aldebaran-rt / net / ipv6 / netfilter / ip6_tables.c
blob286a5b9014708fb24ee863ed3532278c21683670
1 /*
2 * Packet matching code.
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/capability.h>
13 #include <linux/in.h>
14 #include <linux/skbuff.h>
15 #include <linux/kmod.h>
16 #include <linux/vmalloc.h>
17 #include <linux/netdevice.h>
18 #include <linux/module.h>
19 #include <linux/poison.h>
20 #include <linux/icmpv6.h>
21 #include <net/ipv6.h>
22 #include <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 size_t i;
103 unsigned long ret;
104 const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
106 #define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
108 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
109 &ip6info->src), IP6T_INV_SRCIP)
110 || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
111 &ip6info->dst), IP6T_INV_DSTIP)) {
112 dprintf("Source or dest mismatch.\n");
114 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
115 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
116 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
117 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
118 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
119 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
120 return false;
123 /* Look for ifname matches; this should unroll nicely. */
124 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
125 ret |= (((const unsigned long *)indev)[i]
126 ^ ((const unsigned long *)ip6info->iniface)[i])
127 & ((const unsigned long *)ip6info->iniface_mask)[i];
130 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
131 dprintf("VIA in mismatch (%s vs %s).%s\n",
132 indev, ip6info->iniface,
133 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
134 return false;
137 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
138 ret |= (((const unsigned long *)outdev)[i]
139 ^ ((const unsigned long *)ip6info->outiface)[i])
140 & ((const unsigned long *)ip6info->outiface_mask)[i];
143 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
144 dprintf("VIA out mismatch (%s vs %s).%s\n",
145 outdev, ip6info->outiface,
146 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
147 return false;
150 /* ... might want to do something with class and flowlabel here ... */
152 /* look for the desired protocol header */
153 if((ip6info->flags & IP6T_F_PROTO)) {
154 int protohdr;
155 unsigned short _frag_off;
157 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
158 if (protohdr < 0) {
159 if (_frag_off == 0)
160 *hotdrop = true;
161 return false;
163 *fragoff = _frag_off;
165 dprintf("Packet protocol %hi ?= %s%hi.\n",
166 protohdr,
167 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
168 ip6info->proto);
170 if (ip6info->proto == protohdr) {
171 if(ip6info->invflags & IP6T_INV_PROTO) {
172 return false;
174 return true;
177 /* We need match for the '-p all', too! */
178 if ((ip6info->proto != 0) &&
179 !(ip6info->invflags & IP6T_INV_PROTO))
180 return false;
182 return true;
185 /* should be ip6 safe */
186 static bool
187 ip6_checkentry(const struct ip6t_ip6 *ipv6)
189 if (ipv6->flags & ~IP6T_F_MASK) {
190 duprintf("Unknown flag bits set: %08X\n",
191 ipv6->flags & ~IP6T_F_MASK);
192 return false;
194 if (ipv6->invflags & ~IP6T_INV_MASK) {
195 duprintf("Unknown invflag bits set: %08X\n",
196 ipv6->invflags & ~IP6T_INV_MASK);
197 return false;
199 return true;
202 static unsigned int
203 ip6t_error(struct sk_buff *skb, const struct xt_target_param *par)
205 if (net_ratelimit())
206 printk("ip6_tables: error: `%s'\n",
207 (const char *)par->targinfo);
209 return NF_DROP;
212 /* Performance critical - called for every packet */
213 static inline bool
214 do_match(struct ip6t_entry_match *m, const struct sk_buff *skb,
215 struct xt_match_param *par)
217 par->match = m->u.kernel.match;
218 par->matchinfo = m->data;
220 /* Stop iteration if it doesn't match */
221 if (!m->u.kernel.match->match(skb, par))
222 return true;
223 else
224 return false;
227 static inline struct ip6t_entry *
228 get_entry(void *base, unsigned int offset)
230 return (struct ip6t_entry *)(base + offset);
233 /* All zeroes == unconditional rule. */
234 /* Mildly perf critical (only if packet tracing is on) */
235 static inline int
236 unconditional(const struct ip6t_ip6 *ipv6)
238 unsigned int i;
240 for (i = 0; i < sizeof(*ipv6); i++)
241 if (((char *)ipv6)[i])
242 break;
244 return (i == sizeof(*ipv6));
247 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
248 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
249 /* This cries for unification! */
250 static const char *const hooknames[] = {
251 [NF_INET_PRE_ROUTING] = "PREROUTING",
252 [NF_INET_LOCAL_IN] = "INPUT",
253 [NF_INET_FORWARD] = "FORWARD",
254 [NF_INET_LOCAL_OUT] = "OUTPUT",
255 [NF_INET_POST_ROUTING] = "POSTROUTING",
258 enum nf_ip_trace_comments {
259 NF_IP6_TRACE_COMMENT_RULE,
260 NF_IP6_TRACE_COMMENT_RETURN,
261 NF_IP6_TRACE_COMMENT_POLICY,
264 static const char *const comments[] = {
265 [NF_IP6_TRACE_COMMENT_RULE] = "rule",
266 [NF_IP6_TRACE_COMMENT_RETURN] = "return",
267 [NF_IP6_TRACE_COMMENT_POLICY] = "policy",
270 static struct nf_loginfo trace_loginfo = {
271 .type = NF_LOG_TYPE_LOG,
272 .u = {
273 .log = {
274 .level = 4,
275 .logflags = NF_LOG_MASK,
280 /* Mildly perf critical (only if packet tracing is on) */
281 static inline int
282 get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
283 char *hookname, char **chainname,
284 char **comment, unsigned int *rulenum)
286 struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
288 if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
289 /* Head of user chain: ERROR target with chainname */
290 *chainname = t->target.data;
291 (*rulenum) = 0;
292 } else if (s == e) {
293 (*rulenum)++;
295 if (s->target_offset == sizeof(struct ip6t_entry)
296 && strcmp(t->target.u.kernel.target->name,
297 IP6T_STANDARD_TARGET) == 0
298 && t->verdict < 0
299 && unconditional(&s->ipv6)) {
300 /* Tail of chains: STANDARD target (return/policy) */
301 *comment = *chainname == hookname
302 ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
303 : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
305 return 1;
306 } else
307 (*rulenum)++;
309 return 0;
312 static void trace_packet(struct sk_buff *skb,
313 unsigned int hook,
314 const struct net_device *in,
315 const struct net_device *out,
316 const char *tablename,
317 struct xt_table_info *private,
318 struct ip6t_entry *e)
320 void *table_base;
321 const struct ip6t_entry *root;
322 char *hookname, *chainname, *comment;
323 unsigned int rulenum = 0;
325 table_base = (void *)private->entries[smp_processor_id()];
326 root = get_entry(table_base, private->hook_entry[hook]);
328 hookname = chainname = (char *)hooknames[hook];
329 comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
331 IP6T_ENTRY_ITERATE(root,
332 private->size - private->hook_entry[hook],
333 get_chainname_rulenum,
334 e, hookname, &chainname, &comment, &rulenum);
336 nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
337 "TRACE: %s:%s:%s:%u ",
338 tablename, chainname, comment, rulenum);
340 #endif
342 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
343 unsigned int
344 ip6t_do_table(struct sk_buff *skb,
345 unsigned int hook,
346 const struct net_device *in,
347 const struct net_device *out,
348 struct xt_table *table)
350 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
351 bool hotdrop = false;
352 /* Initializing verdict to NF_DROP keeps gcc happy. */
353 unsigned int verdict = NF_DROP;
354 const char *indev, *outdev;
355 void *table_base;
356 struct ip6t_entry *e, *back;
357 struct xt_table_info *private;
358 struct xt_match_param mtpar;
359 struct xt_target_param tgpar;
361 /* Initialization */
362 indev = in ? in->name : nulldevname;
363 outdev = out ? out->name : nulldevname;
364 /* We handle fragments by dealing with the first fragment as
365 * if it was a normal packet. All other fragments are treated
366 * normally, except that they will NEVER match rules that ask
367 * things we don't know, ie. tcp syn flag or ports). If the
368 * rule is also a fragment-specific rule, non-fragments won't
369 * match it. */
370 mtpar.hotdrop = &hotdrop;
371 mtpar.in = tgpar.in = in;
372 mtpar.out = tgpar.out = out;
373 mtpar.family = tgpar.family = NFPROTO_IPV6;
374 tgpar.hooknum = hook;
376 read_lock_bh(&table->lock);
377 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
378 private = table->private;
379 table_base = (void *)private->entries[raw_smp_processor_id()];
380 e = get_entry(table_base, private->hook_entry[hook]);
382 /* For return from builtin chain */
383 back = get_entry(table_base, private->underflow[hook]);
385 do {
386 IP_NF_ASSERT(e);
387 IP_NF_ASSERT(back);
388 if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
389 &mtpar.thoff, &mtpar.fragoff, &hotdrop)) {
390 struct ip6t_entry_target *t;
392 if (IP6T_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0)
393 goto no_match;
395 ADD_COUNTER(e->counters,
396 ntohs(ipv6_hdr(skb)->payload_len) +
397 sizeof(struct ipv6hdr), 1);
399 t = ip6t_get_target(e);
400 IP_NF_ASSERT(t->u.kernel.target);
402 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
403 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
404 /* The packet is traced: log it */
405 if (unlikely(skb->nf_trace))
406 trace_packet(skb, hook, in, out,
407 table->name, private, e);
408 #endif
409 /* Standard target? */
410 if (!t->u.kernel.target->target) {
411 int v;
413 v = ((struct ip6t_standard_target *)t)->verdict;
414 if (v < 0) {
415 /* Pop from stack? */
416 if (v != IP6T_RETURN) {
417 verdict = (unsigned)(-v) - 1;
418 break;
420 e = back;
421 back = get_entry(table_base,
422 back->comefrom);
423 continue;
425 if (table_base + v != (void *)e + e->next_offset
426 && !(e->ipv6.flags & IP6T_F_GOTO)) {
427 /* Save old back ptr in next entry */
428 struct ip6t_entry *next
429 = (void *)e + e->next_offset;
430 next->comefrom
431 = (void *)back - table_base;
432 /* set back pointer to next entry */
433 back = next;
436 e = get_entry(table_base, v);
437 } else {
438 /* Targets which reenter must return
439 abs. verdicts */
440 tgpar.target = t->u.kernel.target;
441 tgpar.targinfo = t->data;
443 #ifdef CONFIG_NETFILTER_DEBUG
444 ((struct ip6t_entry *)table_base)->comefrom
445 = 0xeeeeeeec;
446 #endif
447 verdict = t->u.kernel.target->target(skb,
448 &tgpar);
450 #ifdef CONFIG_NETFILTER_DEBUG
451 if (((struct ip6t_entry *)table_base)->comefrom
452 != 0xeeeeeeec
453 && verdict == IP6T_CONTINUE) {
454 printk("Target %s reentered!\n",
455 t->u.kernel.target->name);
456 verdict = NF_DROP;
458 ((struct ip6t_entry *)table_base)->comefrom
459 = 0x57acc001;
460 #endif
461 if (verdict == IP6T_CONTINUE)
462 e = (void *)e + e->next_offset;
463 else
464 /* Verdict */
465 break;
467 } else {
469 no_match:
470 e = (void *)e + e->next_offset;
472 } while (!hotdrop);
474 #ifdef CONFIG_NETFILTER_DEBUG
475 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
476 #endif
477 read_unlock_bh(&table->lock);
479 #ifdef DEBUG_ALLOW_ALL
480 return NF_ACCEPT;
481 #else
482 if (hotdrop)
483 return NF_DROP;
484 else return verdict;
485 #endif
488 /* Figures out from what hook each rule can be called: returns 0 if
489 there are loops. Puts hook bitmask in comefrom. */
490 static int
491 mark_source_chains(struct xt_table_info *newinfo,
492 unsigned int valid_hooks, void *entry0)
494 unsigned int hook;
496 /* No recursion; use packet counter to save back ptrs (reset
497 to 0 as we leave), and comefrom to save source hook bitmask */
498 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
499 unsigned int pos = newinfo->hook_entry[hook];
500 struct ip6t_entry *e = (struct ip6t_entry *)(entry0 + pos);
502 if (!(valid_hooks & (1 << hook)))
503 continue;
505 /* Set initial back pointer. */
506 e->counters.pcnt = pos;
508 for (;;) {
509 struct ip6t_standard_target *t
510 = (void *)ip6t_get_target(e);
511 int visited = e->comefrom & (1 << hook);
513 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
514 printk("iptables: loop hook %u pos %u %08X.\n",
515 hook, pos, e->comefrom);
516 return 0;
518 e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
520 /* Unconditional return/END. */
521 if ((e->target_offset == sizeof(struct ip6t_entry)
522 && (strcmp(t->target.u.user.name,
523 IP6T_STANDARD_TARGET) == 0)
524 && t->verdict < 0
525 && unconditional(&e->ipv6)) || visited) {
526 unsigned int oldpos, size;
528 if ((strcmp(t->target.u.user.name,
529 IP6T_STANDARD_TARGET) == 0) &&
530 t->verdict < -NF_MAX_VERDICT - 1) {
531 duprintf("mark_source_chains: bad "
532 "negative verdict (%i)\n",
533 t->verdict);
534 return 0;
537 /* Return: backtrack through the last
538 big jump. */
539 do {
540 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
541 #ifdef DEBUG_IP_FIREWALL_USER
542 if (e->comefrom
543 & (1 << NF_INET_NUMHOOKS)) {
544 duprintf("Back unset "
545 "on hook %u "
546 "rule %u\n",
547 hook, pos);
549 #endif
550 oldpos = pos;
551 pos = e->counters.pcnt;
552 e->counters.pcnt = 0;
554 /* We're at the start. */
555 if (pos == oldpos)
556 goto next;
558 e = (struct ip6t_entry *)
559 (entry0 + pos);
560 } while (oldpos == pos + e->next_offset);
562 /* Move along one */
563 size = e->next_offset;
564 e = (struct ip6t_entry *)
565 (entry0 + pos + size);
566 e->counters.pcnt = pos;
567 pos += size;
568 } else {
569 int newpos = t->verdict;
571 if (strcmp(t->target.u.user.name,
572 IP6T_STANDARD_TARGET) == 0
573 && newpos >= 0) {
574 if (newpos > newinfo->size -
575 sizeof(struct ip6t_entry)) {
576 duprintf("mark_source_chains: "
577 "bad verdict (%i)\n",
578 newpos);
579 return 0;
581 /* This a jump; chase it. */
582 duprintf("Jump rule %u -> %u\n",
583 pos, newpos);
584 } else {
585 /* ... this is a fallthru */
586 newpos = pos + e->next_offset;
588 e = (struct ip6t_entry *)
589 (entry0 + newpos);
590 e->counters.pcnt = pos;
591 pos = newpos;
594 next:
595 duprintf("Finished chain %u\n", hook);
597 return 1;
600 static int
601 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
603 struct xt_mtdtor_param par;
605 if (i && (*i)-- == 0)
606 return 1;
608 par.match = m->u.kernel.match;
609 par.matchinfo = m->data;
610 par.family = NFPROTO_IPV6;
611 if (par.match->destroy != NULL)
612 par.match->destroy(&par);
613 module_put(par.match->me);
614 return 0;
617 static int
618 check_entry(struct ip6t_entry *e, const char *name)
620 struct ip6t_entry_target *t;
622 if (!ip6_checkentry(&e->ipv6)) {
623 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
624 return -EINVAL;
627 if (e->target_offset + sizeof(struct ip6t_entry_target) >
628 e->next_offset)
629 return -EINVAL;
631 t = ip6t_get_target(e);
632 if (e->target_offset + t->u.target_size > e->next_offset)
633 return -EINVAL;
635 return 0;
638 static int check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par,
639 unsigned int *i)
641 const struct ip6t_ip6 *ipv6 = par->entryinfo;
642 int ret;
644 par->match = m->u.kernel.match;
645 par->matchinfo = m->data;
647 ret = xt_check_match(par, m->u.match_size - sizeof(*m),
648 ipv6->proto, ipv6->invflags & IP6T_INV_PROTO);
649 if (ret < 0) {
650 duprintf("ip_tables: check failed for `%s'.\n",
651 par.match->name);
652 return ret;
654 ++*i;
655 return 0;
658 static int
659 find_check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par,
660 unsigned int *i)
662 struct xt_match *match;
663 int ret;
665 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
666 m->u.user.revision),
667 "ip6t_%s", m->u.user.name);
668 if (IS_ERR(match) || !match) {
669 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
670 return match ? PTR_ERR(match) : -ENOENT;
672 m->u.kernel.match = match;
674 ret = check_match(m, par, i);
675 if (ret)
676 goto err;
678 return 0;
679 err:
680 module_put(m->u.kernel.match->me);
681 return ret;
684 static int check_target(struct ip6t_entry *e, const char *name)
686 struct ip6t_entry_target *t = ip6t_get_target(e);
687 struct xt_tgchk_param par = {
688 .table = name,
689 .entryinfo = e,
690 .target = t->u.kernel.target,
691 .targinfo = t->data,
692 .hook_mask = e->comefrom,
693 .family = NFPROTO_IPV6,
695 int ret;
697 t = ip6t_get_target(e);
698 ret = xt_check_target(&par, t->u.target_size - sizeof(*t),
699 e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
700 if (ret < 0) {
701 duprintf("ip_tables: check failed for `%s'.\n",
702 t->u.kernel.target->name);
703 return ret;
705 return 0;
708 static int
709 find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
710 unsigned int *i)
712 struct ip6t_entry_target *t;
713 struct xt_target *target;
714 int ret;
715 unsigned int j;
716 struct xt_mtchk_param mtpar;
718 ret = check_entry(e, name);
719 if (ret)
720 return ret;
722 j = 0;
723 mtpar.table = name;
724 mtpar.entryinfo = &e->ipv6;
725 mtpar.hook_mask = e->comefrom;
726 mtpar.family = NFPROTO_IPV6;
727 ret = IP6T_MATCH_ITERATE(e, find_check_match, &mtpar, &j);
728 if (ret != 0)
729 goto cleanup_matches;
731 t = ip6t_get_target(e);
732 target = try_then_request_module(xt_find_target(AF_INET6,
733 t->u.user.name,
734 t->u.user.revision),
735 "ip6t_%s", t->u.user.name);
736 if (IS_ERR(target) || !target) {
737 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
738 ret = target ? PTR_ERR(target) : -ENOENT;
739 goto cleanup_matches;
741 t->u.kernel.target = target;
743 ret = check_target(e, name);
744 if (ret)
745 goto err;
747 (*i)++;
748 return 0;
749 err:
750 module_put(t->u.kernel.target->me);
751 cleanup_matches:
752 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
753 return ret;
756 static int
757 check_entry_size_and_hooks(struct ip6t_entry *e,
758 struct xt_table_info *newinfo,
759 unsigned char *base,
760 unsigned char *limit,
761 const unsigned int *hook_entries,
762 const unsigned int *underflows,
763 unsigned int *i)
765 unsigned int h;
767 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
768 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
769 duprintf("Bad offset %p\n", e);
770 return -EINVAL;
773 if (e->next_offset
774 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
775 duprintf("checking: element %p size %u\n",
776 e, e->next_offset);
777 return -EINVAL;
780 /* Check hooks & underflows */
781 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
782 if ((unsigned char *)e - base == hook_entries[h])
783 newinfo->hook_entry[h] = hook_entries[h];
784 if ((unsigned char *)e - base == underflows[h])
785 newinfo->underflow[h] = underflows[h];
788 /* FIXME: underflows must be unconditional, standard verdicts
789 < 0 (not IP6T_RETURN). --RR */
791 /* Clear counters and comefrom */
792 e->counters = ((struct xt_counters) { 0, 0 });
793 e->comefrom = 0;
795 (*i)++;
796 return 0;
799 static int
800 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
802 struct xt_tgdtor_param par;
803 struct ip6t_entry_target *t;
805 if (i && (*i)-- == 0)
806 return 1;
808 /* Cleanup all matches */
809 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
810 t = ip6t_get_target(e);
812 par.target = t->u.kernel.target;
813 par.targinfo = t->data;
814 par.family = NFPROTO_IPV6;
815 if (par.target->destroy != NULL)
816 par.target->destroy(&par);
817 module_put(par.target->me);
818 return 0;
821 /* Checks and translates the user-supplied table segment (held in
822 newinfo) */
823 static int
824 translate_table(const char *name,
825 unsigned int valid_hooks,
826 struct xt_table_info *newinfo,
827 void *entry0,
828 unsigned int size,
829 unsigned int number,
830 const unsigned int *hook_entries,
831 const unsigned int *underflows)
833 unsigned int i;
834 int ret;
836 newinfo->size = size;
837 newinfo->number = number;
839 /* Init all hooks to impossible value. */
840 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
841 newinfo->hook_entry[i] = 0xFFFFFFFF;
842 newinfo->underflow[i] = 0xFFFFFFFF;
845 duprintf("translate_table: size %u\n", newinfo->size);
846 i = 0;
847 /* Walk through entries, checking offsets. */
848 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
849 check_entry_size_and_hooks,
850 newinfo,
851 entry0,
852 entry0 + size,
853 hook_entries, underflows, &i);
854 if (ret != 0)
855 return ret;
857 if (i != number) {
858 duprintf("translate_table: %u not %u entries\n",
859 i, number);
860 return -EINVAL;
863 /* Check hooks all assigned */
864 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
865 /* Only hooks which are valid */
866 if (!(valid_hooks & (1 << i)))
867 continue;
868 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
869 duprintf("Invalid hook entry %u %u\n",
870 i, hook_entries[i]);
871 return -EINVAL;
873 if (newinfo->underflow[i] == 0xFFFFFFFF) {
874 duprintf("Invalid underflow %u %u\n",
875 i, underflows[i]);
876 return -EINVAL;
880 if (!mark_source_chains(newinfo, valid_hooks, entry0))
881 return -ELOOP;
883 /* Finally, each sanity check must pass */
884 i = 0;
885 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
886 find_check_entry, name, size, &i);
888 if (ret != 0) {
889 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
890 cleanup_entry, &i);
891 return ret;
894 /* And one copy for every other CPU */
895 for_each_possible_cpu(i) {
896 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
897 memcpy(newinfo->entries[i], entry0, newinfo->size);
900 return ret;
903 /* Gets counters. */
904 static inline int
905 add_entry_to_counter(const struct ip6t_entry *e,
906 struct xt_counters total[],
907 unsigned int *i)
909 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
911 (*i)++;
912 return 0;
915 static inline int
916 set_entry_to_counter(const struct ip6t_entry *e,
917 struct ip6t_counters total[],
918 unsigned int *i)
920 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
922 (*i)++;
923 return 0;
926 static void
927 get_counters(const struct xt_table_info *t,
928 struct xt_counters counters[])
930 unsigned int cpu;
931 unsigned int i;
932 unsigned int curcpu;
934 /* Instead of clearing (by a previous call to memset())
935 * the counters and using adds, we set the counters
936 * with data used by 'current' CPU
937 * We dont care about preemption here.
939 curcpu = raw_smp_processor_id();
941 i = 0;
942 IP6T_ENTRY_ITERATE(t->entries[curcpu],
943 t->size,
944 set_entry_to_counter,
945 counters,
946 &i);
948 for_each_possible_cpu(cpu) {
949 if (cpu == curcpu)
950 continue;
951 i = 0;
952 IP6T_ENTRY_ITERATE(t->entries[cpu],
953 t->size,
954 add_entry_to_counter,
955 counters,
956 &i);
960 static struct xt_counters *alloc_counters(struct xt_table *table)
962 unsigned int countersize;
963 struct xt_counters *counters;
964 const struct xt_table_info *private = table->private;
966 /* We need atomic snapshot of counters: rest doesn't change
967 (other than comefrom, which userspace doesn't care
968 about). */
969 countersize = sizeof(struct xt_counters) * private->number;
970 counters = vmalloc_node(countersize, numa_node_id());
972 if (counters == NULL)
973 return ERR_PTR(-ENOMEM);
975 /* First, sum counters... */
976 write_lock_bh(&table->lock);
977 get_counters(private, counters);
978 write_unlock_bh(&table->lock);
980 return counters;
983 static int
984 copy_entries_to_user(unsigned int total_size,
985 struct xt_table *table,
986 void __user *userptr)
988 unsigned int off, num;
989 struct ip6t_entry *e;
990 struct xt_counters *counters;
991 const struct xt_table_info *private = table->private;
992 int ret = 0;
993 const void *loc_cpu_entry;
995 counters = alloc_counters(table);
996 if (IS_ERR(counters))
997 return PTR_ERR(counters);
999 /* choose the copy that is on our node/cpu, ...
1000 * This choice is lazy (because current thread is
1001 * allowed to migrate to another cpu)
1003 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1004 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1005 ret = -EFAULT;
1006 goto free_counters;
1009 /* FIXME: use iterator macros --RR */
1010 /* ... then go back and fix counters and names */
1011 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1012 unsigned int i;
1013 const struct ip6t_entry_match *m;
1014 const struct ip6t_entry_target *t;
1016 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1017 if (copy_to_user(userptr + off
1018 + offsetof(struct ip6t_entry, counters),
1019 &counters[num],
1020 sizeof(counters[num])) != 0) {
1021 ret = -EFAULT;
1022 goto free_counters;
1025 for (i = sizeof(struct ip6t_entry);
1026 i < e->target_offset;
1027 i += m->u.match_size) {
1028 m = (void *)e + i;
1030 if (copy_to_user(userptr + off + i
1031 + offsetof(struct ip6t_entry_match,
1032 u.user.name),
1033 m->u.kernel.match->name,
1034 strlen(m->u.kernel.match->name)+1)
1035 != 0) {
1036 ret = -EFAULT;
1037 goto free_counters;
1041 t = ip6t_get_target(e);
1042 if (copy_to_user(userptr + off + e->target_offset
1043 + offsetof(struct ip6t_entry_target,
1044 u.user.name),
1045 t->u.kernel.target->name,
1046 strlen(t->u.kernel.target->name)+1) != 0) {
1047 ret = -EFAULT;
1048 goto free_counters;
1052 free_counters:
1053 vfree(counters);
1054 return ret;
1057 #ifdef CONFIG_COMPAT
1058 static void compat_standard_from_user(void *dst, void *src)
1060 int v = *(compat_int_t *)src;
1062 if (v > 0)
1063 v += xt_compat_calc_jump(AF_INET6, v);
1064 memcpy(dst, &v, sizeof(v));
1067 static int compat_standard_to_user(void __user *dst, void *src)
1069 compat_int_t cv = *(int *)src;
1071 if (cv > 0)
1072 cv -= xt_compat_calc_jump(AF_INET6, cv);
1073 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1076 static inline int
1077 compat_calc_match(struct ip6t_entry_match *m, int *size)
1079 *size += xt_compat_match_offset(m->u.kernel.match);
1080 return 0;
1083 static int compat_calc_entry(struct ip6t_entry *e,
1084 const struct xt_table_info *info,
1085 void *base, struct xt_table_info *newinfo)
1087 struct ip6t_entry_target *t;
1088 unsigned int entry_offset;
1089 int off, i, ret;
1091 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1092 entry_offset = (void *)e - base;
1093 IP6T_MATCH_ITERATE(e, compat_calc_match, &off);
1094 t = ip6t_get_target(e);
1095 off += xt_compat_target_offset(t->u.kernel.target);
1096 newinfo->size -= off;
1097 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1098 if (ret)
1099 return ret;
1101 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1102 if (info->hook_entry[i] &&
1103 (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1104 newinfo->hook_entry[i] -= off;
1105 if (info->underflow[i] &&
1106 (e < (struct ip6t_entry *)(base + info->underflow[i])))
1107 newinfo->underflow[i] -= off;
1109 return 0;
1112 static int compat_table_info(const struct xt_table_info *info,
1113 struct xt_table_info *newinfo)
1115 void *loc_cpu_entry;
1117 if (!newinfo || !info)
1118 return -EINVAL;
1120 /* we dont care about newinfo->entries[] */
1121 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1122 newinfo->initial_entries = 0;
1123 loc_cpu_entry = info->entries[raw_smp_processor_id()];
1124 return IP6T_ENTRY_ITERATE(loc_cpu_entry, info->size,
1125 compat_calc_entry, info, loc_cpu_entry,
1126 newinfo);
1128 #endif
1130 static int get_info(struct net *net, void __user *user, int *len, int compat)
1132 char name[IP6T_TABLE_MAXNAMELEN];
1133 struct xt_table *t;
1134 int ret;
1136 if (*len != sizeof(struct ip6t_getinfo)) {
1137 duprintf("length %u != %zu\n", *len,
1138 sizeof(struct ip6t_getinfo));
1139 return -EINVAL;
1142 if (copy_from_user(name, user, sizeof(name)) != 0)
1143 return -EFAULT;
1145 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1146 #ifdef CONFIG_COMPAT
1147 if (compat)
1148 xt_compat_lock(AF_INET6);
1149 #endif
1150 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1151 "ip6table_%s", name);
1152 if (t && !IS_ERR(t)) {
1153 struct ip6t_getinfo info;
1154 const struct xt_table_info *private = t->private;
1156 #ifdef CONFIG_COMPAT
1157 if (compat) {
1158 struct xt_table_info tmp;
1159 ret = compat_table_info(private, &tmp);
1160 xt_compat_flush_offsets(AF_INET6);
1161 private = &tmp;
1163 #endif
1164 info.valid_hooks = t->valid_hooks;
1165 memcpy(info.hook_entry, private->hook_entry,
1166 sizeof(info.hook_entry));
1167 memcpy(info.underflow, private->underflow,
1168 sizeof(info.underflow));
1169 info.num_entries = private->number;
1170 info.size = private->size;
1171 strcpy(info.name, name);
1173 if (copy_to_user(user, &info, *len) != 0)
1174 ret = -EFAULT;
1175 else
1176 ret = 0;
1178 xt_table_unlock(t);
1179 module_put(t->me);
1180 } else
1181 ret = t ? PTR_ERR(t) : -ENOENT;
1182 #ifdef CONFIG_COMPAT
1183 if (compat)
1184 xt_compat_unlock(AF_INET6);
1185 #endif
1186 return ret;
1189 static int
1190 get_entries(struct net *net, struct ip6t_get_entries __user *uptr, int *len)
1192 int ret;
1193 struct ip6t_get_entries get;
1194 struct xt_table *t;
1196 if (*len < sizeof(get)) {
1197 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
1198 return -EINVAL;
1200 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1201 return -EFAULT;
1202 if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1203 duprintf("get_entries: %u != %zu\n",
1204 *len, sizeof(get) + get.size);
1205 return -EINVAL;
1208 t = xt_find_table_lock(net, AF_INET6, get.name);
1209 if (t && !IS_ERR(t)) {
1210 struct xt_table_info *private = t->private;
1211 duprintf("t->private->number = %u\n", private->number);
1212 if (get.size == private->size)
1213 ret = copy_entries_to_user(private->size,
1214 t, uptr->entrytable);
1215 else {
1216 duprintf("get_entries: I've got %u not %u!\n",
1217 private->size, get.size);
1218 ret = -EAGAIN;
1220 module_put(t->me);
1221 xt_table_unlock(t);
1222 } else
1223 ret = t ? PTR_ERR(t) : -ENOENT;
1225 return ret;
1228 static int
1229 __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
1230 struct xt_table_info *newinfo, unsigned int num_counters,
1231 void __user *counters_ptr)
1233 int ret;
1234 struct xt_table *t;
1235 struct xt_table_info *oldinfo;
1236 struct xt_counters *counters;
1237 const void *loc_cpu_old_entry;
1239 ret = 0;
1240 counters = vmalloc_node(num_counters * sizeof(struct xt_counters),
1241 numa_node_id());
1242 if (!counters) {
1243 ret = -ENOMEM;
1244 goto out;
1247 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1248 "ip6table_%s", name);
1249 if (!t || IS_ERR(t)) {
1250 ret = t ? PTR_ERR(t) : -ENOENT;
1251 goto free_newinfo_counters_untrans;
1254 /* You lied! */
1255 if (valid_hooks != t->valid_hooks) {
1256 duprintf("Valid hook crap: %08X vs %08X\n",
1257 valid_hooks, t->valid_hooks);
1258 ret = -EINVAL;
1259 goto put_module;
1262 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1263 if (!oldinfo)
1264 goto put_module;
1266 /* Update module usage count based on number of rules */
1267 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1268 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1269 if ((oldinfo->number > oldinfo->initial_entries) ||
1270 (newinfo->number <= oldinfo->initial_entries))
1271 module_put(t->me);
1272 if ((oldinfo->number > oldinfo->initial_entries) &&
1273 (newinfo->number <= oldinfo->initial_entries))
1274 module_put(t->me);
1276 /* Get the old counters. */
1277 get_counters(oldinfo, counters);
1278 /* Decrease module usage counts and free resource */
1279 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1280 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
1281 NULL);
1282 xt_free_table_info(oldinfo);
1283 if (copy_to_user(counters_ptr, counters,
1284 sizeof(struct xt_counters) * num_counters) != 0)
1285 ret = -EFAULT;
1286 vfree(counters);
1287 xt_table_unlock(t);
1288 return ret;
1290 put_module:
1291 module_put(t->me);
1292 xt_table_unlock(t);
1293 free_newinfo_counters_untrans:
1294 vfree(counters);
1295 out:
1296 return ret;
1299 static int
1300 do_replace(struct net *net, void __user *user, unsigned int len)
1302 int ret;
1303 struct ip6t_replace tmp;
1304 struct xt_table_info *newinfo;
1305 void *loc_cpu_entry;
1307 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1308 return -EFAULT;
1310 /* overflow check */
1311 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1312 return -ENOMEM;
1314 newinfo = xt_alloc_table_info(tmp.size);
1315 if (!newinfo)
1316 return -ENOMEM;
1318 /* choose the copy that is on our node/cpu */
1319 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1320 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1321 tmp.size) != 0) {
1322 ret = -EFAULT;
1323 goto free_newinfo;
1326 ret = translate_table(tmp.name, tmp.valid_hooks,
1327 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1328 tmp.hook_entry, tmp.underflow);
1329 if (ret != 0)
1330 goto free_newinfo;
1332 duprintf("ip_tables: Translated table\n");
1334 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1335 tmp.num_counters, tmp.counters);
1336 if (ret)
1337 goto free_newinfo_untrans;
1338 return 0;
1340 free_newinfo_untrans:
1341 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
1342 free_newinfo:
1343 xt_free_table_info(newinfo);
1344 return ret;
1347 /* We're lazy, and add to the first CPU; overflow works its fey magic
1348 * and everything is OK. */
1349 static inline int
1350 add_counter_to_entry(struct ip6t_entry *e,
1351 const struct xt_counters addme[],
1352 unsigned int *i)
1354 #if 0
1355 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1357 (long unsigned int)e->counters.pcnt,
1358 (long unsigned int)e->counters.bcnt,
1359 (long unsigned int)addme[*i].pcnt,
1360 (long unsigned int)addme[*i].bcnt);
1361 #endif
1363 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1365 (*i)++;
1366 return 0;
1369 static int
1370 do_add_counters(struct net *net, void __user *user, unsigned int len,
1371 int compat)
1373 unsigned int i;
1374 struct xt_counters_info tmp;
1375 struct xt_counters *paddc;
1376 unsigned int num_counters;
1377 char *name;
1378 int size;
1379 void *ptmp;
1380 struct xt_table *t;
1381 const struct xt_table_info *private;
1382 int ret = 0;
1383 const void *loc_cpu_entry;
1384 #ifdef CONFIG_COMPAT
1385 struct compat_xt_counters_info compat_tmp;
1387 if (compat) {
1388 ptmp = &compat_tmp;
1389 size = sizeof(struct compat_xt_counters_info);
1390 } else
1391 #endif
1393 ptmp = &tmp;
1394 size = sizeof(struct xt_counters_info);
1397 if (copy_from_user(ptmp, user, size) != 0)
1398 return -EFAULT;
1400 #ifdef CONFIG_COMPAT
1401 if (compat) {
1402 num_counters = compat_tmp.num_counters;
1403 name = compat_tmp.name;
1404 } else
1405 #endif
1407 num_counters = tmp.num_counters;
1408 name = tmp.name;
1411 if (len != size + num_counters * sizeof(struct xt_counters))
1412 return -EINVAL;
1414 paddc = vmalloc_node(len - size, numa_node_id());
1415 if (!paddc)
1416 return -ENOMEM;
1418 if (copy_from_user(paddc, user + size, len - size) != 0) {
1419 ret = -EFAULT;
1420 goto free;
1423 t = xt_find_table_lock(net, AF_INET6, name);
1424 if (!t || IS_ERR(t)) {
1425 ret = t ? PTR_ERR(t) : -ENOENT;
1426 goto free;
1429 write_lock_bh(&t->lock);
1430 private = t->private;
1431 if (private->number != num_counters) {
1432 ret = -EINVAL;
1433 goto unlock_up_free;
1436 i = 0;
1437 /* Choose the copy that is on our node */
1438 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1439 IP6T_ENTRY_ITERATE(loc_cpu_entry,
1440 private->size,
1441 add_counter_to_entry,
1442 paddc,
1443 &i);
1444 unlock_up_free:
1445 write_unlock_bh(&t->lock);
1446 xt_table_unlock(t);
1447 module_put(t->me);
1448 free:
1449 vfree(paddc);
1451 return ret;
1454 #ifdef CONFIG_COMPAT
1455 struct compat_ip6t_replace {
1456 char name[IP6T_TABLE_MAXNAMELEN];
1457 u32 valid_hooks;
1458 u32 num_entries;
1459 u32 size;
1460 u32 hook_entry[NF_INET_NUMHOOKS];
1461 u32 underflow[NF_INET_NUMHOOKS];
1462 u32 num_counters;
1463 compat_uptr_t counters; /* struct ip6t_counters * */
1464 struct compat_ip6t_entry entries[0];
1467 static int
1468 compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
1469 unsigned int *size, struct xt_counters *counters,
1470 unsigned int *i)
1472 struct ip6t_entry_target *t;
1473 struct compat_ip6t_entry __user *ce;
1474 u_int16_t target_offset, next_offset;
1475 compat_uint_t origsize;
1476 int ret;
1478 ret = -EFAULT;
1479 origsize = *size;
1480 ce = (struct compat_ip6t_entry __user *)*dstptr;
1481 if (copy_to_user(ce, e, sizeof(struct ip6t_entry)))
1482 goto out;
1484 if (copy_to_user(&ce->counters, &counters[*i], sizeof(counters[*i])))
1485 goto out;
1487 *dstptr += sizeof(struct compat_ip6t_entry);
1488 *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1490 ret = IP6T_MATCH_ITERATE(e, xt_compat_match_to_user, dstptr, size);
1491 target_offset = e->target_offset - (origsize - *size);
1492 if (ret)
1493 goto out;
1494 t = ip6t_get_target(e);
1495 ret = xt_compat_target_to_user(t, dstptr, size);
1496 if (ret)
1497 goto out;
1498 ret = -EFAULT;
1499 next_offset = e->next_offset - (origsize - *size);
1500 if (put_user(target_offset, &ce->target_offset))
1501 goto out;
1502 if (put_user(next_offset, &ce->next_offset))
1503 goto out;
1505 (*i)++;
1506 return 0;
1507 out:
1508 return ret;
1511 static int
1512 compat_find_calc_match(struct ip6t_entry_match *m,
1513 const char *name,
1514 const struct ip6t_ip6 *ipv6,
1515 unsigned int hookmask,
1516 int *size, unsigned int *i)
1518 struct xt_match *match;
1520 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
1521 m->u.user.revision),
1522 "ip6t_%s", m->u.user.name);
1523 if (IS_ERR(match) || !match) {
1524 duprintf("compat_check_calc_match: `%s' not found\n",
1525 m->u.user.name);
1526 return match ? PTR_ERR(match) : -ENOENT;
1528 m->u.kernel.match = match;
1529 *size += xt_compat_match_offset(match);
1531 (*i)++;
1532 return 0;
1535 static int
1536 compat_release_match(struct ip6t_entry_match *m, unsigned int *i)
1538 if (i && (*i)-- == 0)
1539 return 1;
1541 module_put(m->u.kernel.match->me);
1542 return 0;
1545 static int
1546 compat_release_entry(struct compat_ip6t_entry *e, unsigned int *i)
1548 struct ip6t_entry_target *t;
1550 if (i && (*i)-- == 0)
1551 return 1;
1553 /* Cleanup all matches */
1554 COMPAT_IP6T_MATCH_ITERATE(e, compat_release_match, NULL);
1555 t = compat_ip6t_get_target(e);
1556 module_put(t->u.kernel.target->me);
1557 return 0;
1560 static int
1561 check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1562 struct xt_table_info *newinfo,
1563 unsigned int *size,
1564 unsigned char *base,
1565 unsigned char *limit,
1566 unsigned int *hook_entries,
1567 unsigned int *underflows,
1568 unsigned int *i,
1569 const char *name)
1571 struct ip6t_entry_target *t;
1572 struct xt_target *target;
1573 unsigned int entry_offset;
1574 unsigned int j;
1575 int ret, off, h;
1577 duprintf("check_compat_entry_size_and_hooks %p\n", e);
1578 if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0
1579 || (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
1580 duprintf("Bad offset %p, limit = %p\n", e, limit);
1581 return -EINVAL;
1584 if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1585 sizeof(struct compat_xt_entry_target)) {
1586 duprintf("checking: element %p size %u\n",
1587 e, e->next_offset);
1588 return -EINVAL;
1591 /* For purposes of check_entry casting the compat entry is fine */
1592 ret = check_entry((struct ip6t_entry *)e, name);
1593 if (ret)
1594 return ret;
1596 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1597 entry_offset = (void *)e - (void *)base;
1598 j = 0;
1599 ret = COMPAT_IP6T_MATCH_ITERATE(e, compat_find_calc_match, name,
1600 &e->ipv6, e->comefrom, &off, &j);
1601 if (ret != 0)
1602 goto release_matches;
1604 t = compat_ip6t_get_target(e);
1605 target = try_then_request_module(xt_find_target(AF_INET6,
1606 t->u.user.name,
1607 t->u.user.revision),
1608 "ip6t_%s", t->u.user.name);
1609 if (IS_ERR(target) || !target) {
1610 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1611 t->u.user.name);
1612 ret = target ? PTR_ERR(target) : -ENOENT;
1613 goto release_matches;
1615 t->u.kernel.target = target;
1617 off += xt_compat_target_offset(target);
1618 *size += off;
1619 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1620 if (ret)
1621 goto out;
1623 /* Check hooks & underflows */
1624 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1625 if ((unsigned char *)e - base == hook_entries[h])
1626 newinfo->hook_entry[h] = hook_entries[h];
1627 if ((unsigned char *)e - base == underflows[h])
1628 newinfo->underflow[h] = underflows[h];
1631 /* Clear counters and comefrom */
1632 memset(&e->counters, 0, sizeof(e->counters));
1633 e->comefrom = 0;
1635 (*i)++;
1636 return 0;
1638 out:
1639 module_put(t->u.kernel.target->me);
1640 release_matches:
1641 IP6T_MATCH_ITERATE(e, compat_release_match, &j);
1642 return ret;
1645 static int
1646 compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1647 unsigned int *size, const char *name,
1648 struct xt_table_info *newinfo, unsigned char *base)
1650 struct ip6t_entry_target *t;
1651 struct xt_target *target;
1652 struct ip6t_entry *de;
1653 unsigned int origsize;
1654 int ret, h;
1656 ret = 0;
1657 origsize = *size;
1658 de = (struct ip6t_entry *)*dstptr;
1659 memcpy(de, e, sizeof(struct ip6t_entry));
1660 memcpy(&de->counters, &e->counters, sizeof(e->counters));
1662 *dstptr += sizeof(struct ip6t_entry);
1663 *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1665 ret = COMPAT_IP6T_MATCH_ITERATE(e, xt_compat_match_from_user,
1666 dstptr, size);
1667 if (ret)
1668 return ret;
1669 de->target_offset = e->target_offset - (origsize - *size);
1670 t = compat_ip6t_get_target(e);
1671 target = t->u.kernel.target;
1672 xt_compat_target_from_user(t, dstptr, size);
1674 de->next_offset = e->next_offset - (origsize - *size);
1675 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1676 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1677 newinfo->hook_entry[h] -= origsize - *size;
1678 if ((unsigned char *)de - base < newinfo->underflow[h])
1679 newinfo->underflow[h] -= origsize - *size;
1681 return ret;
1684 static int compat_check_entry(struct ip6t_entry *e, const char *name,
1685 unsigned int *i)
1687 unsigned int j;
1688 int ret;
1689 struct xt_mtchk_param mtpar;
1691 j = 0;
1692 mtpar.table = name;
1693 mtpar.entryinfo = &e->ipv6;
1694 mtpar.hook_mask = e->comefrom;
1695 mtpar.family = NFPROTO_IPV6;
1696 ret = IP6T_MATCH_ITERATE(e, check_match, &mtpar, &j);
1697 if (ret)
1698 goto cleanup_matches;
1700 ret = check_target(e, name);
1701 if (ret)
1702 goto cleanup_matches;
1704 (*i)++;
1705 return 0;
1707 cleanup_matches:
1708 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
1709 return ret;
1712 static int
1713 translate_compat_table(const char *name,
1714 unsigned int valid_hooks,
1715 struct xt_table_info **pinfo,
1716 void **pentry0,
1717 unsigned int total_size,
1718 unsigned int number,
1719 unsigned int *hook_entries,
1720 unsigned int *underflows)
1722 unsigned int i, j;
1723 struct xt_table_info *newinfo, *info;
1724 void *pos, *entry0, *entry1;
1725 unsigned int size;
1726 int ret;
1728 info = *pinfo;
1729 entry0 = *pentry0;
1730 size = total_size;
1731 info->number = number;
1733 /* Init all hooks to impossible value. */
1734 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1735 info->hook_entry[i] = 0xFFFFFFFF;
1736 info->underflow[i] = 0xFFFFFFFF;
1739 duprintf("translate_compat_table: size %u\n", info->size);
1740 j = 0;
1741 xt_compat_lock(AF_INET6);
1742 /* Walk through entries, checking offsets. */
1743 ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1744 check_compat_entry_size_and_hooks,
1745 info, &size, entry0,
1746 entry0 + total_size,
1747 hook_entries, underflows, &j, name);
1748 if (ret != 0)
1749 goto out_unlock;
1751 ret = -EINVAL;
1752 if (j != number) {
1753 duprintf("translate_compat_table: %u not %u entries\n",
1754 j, number);
1755 goto out_unlock;
1758 /* Check hooks all assigned */
1759 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1760 /* Only hooks which are valid */
1761 if (!(valid_hooks & (1 << i)))
1762 continue;
1763 if (info->hook_entry[i] == 0xFFFFFFFF) {
1764 duprintf("Invalid hook entry %u %u\n",
1765 i, hook_entries[i]);
1766 goto out_unlock;
1768 if (info->underflow[i] == 0xFFFFFFFF) {
1769 duprintf("Invalid underflow %u %u\n",
1770 i, underflows[i]);
1771 goto out_unlock;
1775 ret = -ENOMEM;
1776 newinfo = xt_alloc_table_info(size);
1777 if (!newinfo)
1778 goto out_unlock;
1780 newinfo->number = number;
1781 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1782 newinfo->hook_entry[i] = info->hook_entry[i];
1783 newinfo->underflow[i] = info->underflow[i];
1785 entry1 = newinfo->entries[raw_smp_processor_id()];
1786 pos = entry1;
1787 size = total_size;
1788 ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1789 compat_copy_entry_from_user,
1790 &pos, &size, name, newinfo, entry1);
1791 xt_compat_flush_offsets(AF_INET6);
1792 xt_compat_unlock(AF_INET6);
1793 if (ret)
1794 goto free_newinfo;
1796 ret = -ELOOP;
1797 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1798 goto free_newinfo;
1800 i = 0;
1801 ret = IP6T_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
1802 name, &i);
1803 if (ret) {
1804 j -= i;
1805 COMPAT_IP6T_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
1806 compat_release_entry, &j);
1807 IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i);
1808 xt_free_table_info(newinfo);
1809 return ret;
1812 /* And one copy for every other CPU */
1813 for_each_possible_cpu(i)
1814 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1815 memcpy(newinfo->entries[i], entry1, newinfo->size);
1817 *pinfo = newinfo;
1818 *pentry0 = entry1;
1819 xt_free_table_info(info);
1820 return 0;
1822 free_newinfo:
1823 xt_free_table_info(newinfo);
1824 out:
1825 COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size, compat_release_entry, &j);
1826 return ret;
1827 out_unlock:
1828 xt_compat_flush_offsets(AF_INET6);
1829 xt_compat_unlock(AF_INET6);
1830 goto out;
1833 static int
1834 compat_do_replace(struct net *net, void __user *user, unsigned int len)
1836 int ret;
1837 struct compat_ip6t_replace tmp;
1838 struct xt_table_info *newinfo;
1839 void *loc_cpu_entry;
1841 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1842 return -EFAULT;
1844 /* overflow check */
1845 if (tmp.size >= INT_MAX / num_possible_cpus())
1846 return -ENOMEM;
1847 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1848 return -ENOMEM;
1850 newinfo = xt_alloc_table_info(tmp.size);
1851 if (!newinfo)
1852 return -ENOMEM;
1854 /* choose the copy that is on our node/cpu */
1855 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1856 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1857 tmp.size) != 0) {
1858 ret = -EFAULT;
1859 goto free_newinfo;
1862 ret = translate_compat_table(tmp.name, tmp.valid_hooks,
1863 &newinfo, &loc_cpu_entry, tmp.size,
1864 tmp.num_entries, tmp.hook_entry,
1865 tmp.underflow);
1866 if (ret != 0)
1867 goto free_newinfo;
1869 duprintf("compat_do_replace: Translated table\n");
1871 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1872 tmp.num_counters, compat_ptr(tmp.counters));
1873 if (ret)
1874 goto free_newinfo_untrans;
1875 return 0;
1877 free_newinfo_untrans:
1878 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
1879 free_newinfo:
1880 xt_free_table_info(newinfo);
1881 return ret;
1884 static int
1885 compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1886 unsigned int len)
1888 int ret;
1890 if (!capable(CAP_NET_ADMIN))
1891 return -EPERM;
1893 switch (cmd) {
1894 case IP6T_SO_SET_REPLACE:
1895 ret = compat_do_replace(sock_net(sk), user, len);
1896 break;
1898 case IP6T_SO_SET_ADD_COUNTERS:
1899 ret = do_add_counters(sock_net(sk), user, len, 1);
1900 break;
1902 default:
1903 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1904 ret = -EINVAL;
1907 return ret;
1910 struct compat_ip6t_get_entries {
1911 char name[IP6T_TABLE_MAXNAMELEN];
1912 compat_uint_t size;
1913 struct compat_ip6t_entry entrytable[0];
1916 static int
1917 compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1918 void __user *userptr)
1920 struct xt_counters *counters;
1921 const struct xt_table_info *private = table->private;
1922 void __user *pos;
1923 unsigned int size;
1924 int ret = 0;
1925 const void *loc_cpu_entry;
1926 unsigned int i = 0;
1928 counters = alloc_counters(table);
1929 if (IS_ERR(counters))
1930 return PTR_ERR(counters);
1932 /* choose the copy that is on our node/cpu, ...
1933 * This choice is lazy (because current thread is
1934 * allowed to migrate to another cpu)
1936 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1937 pos = userptr;
1938 size = total_size;
1939 ret = IP6T_ENTRY_ITERATE(loc_cpu_entry, total_size,
1940 compat_copy_entry_to_user,
1941 &pos, &size, counters, &i);
1943 vfree(counters);
1944 return ret;
1947 static int
1948 compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1949 int *len)
1951 int ret;
1952 struct compat_ip6t_get_entries get;
1953 struct xt_table *t;
1955 if (*len < sizeof(get)) {
1956 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
1957 return -EINVAL;
1960 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1961 return -EFAULT;
1963 if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
1964 duprintf("compat_get_entries: %u != %zu\n",
1965 *len, sizeof(get) + get.size);
1966 return -EINVAL;
1969 xt_compat_lock(AF_INET6);
1970 t = xt_find_table_lock(net, AF_INET6, get.name);
1971 if (t && !IS_ERR(t)) {
1972 const struct xt_table_info *private = t->private;
1973 struct xt_table_info info;
1974 duprintf("t->private->number = %u\n", private->number);
1975 ret = compat_table_info(private, &info);
1976 if (!ret && get.size == info.size) {
1977 ret = compat_copy_entries_to_user(private->size,
1978 t, uptr->entrytable);
1979 } else if (!ret) {
1980 duprintf("compat_get_entries: I've got %u not %u!\n",
1981 private->size, get.size);
1982 ret = -EAGAIN;
1984 xt_compat_flush_offsets(AF_INET6);
1985 module_put(t->me);
1986 xt_table_unlock(t);
1987 } else
1988 ret = t ? PTR_ERR(t) : -ENOENT;
1990 xt_compat_unlock(AF_INET6);
1991 return ret;
1994 static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1996 static int
1997 compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1999 int ret;
2001 if (!capable(CAP_NET_ADMIN))
2002 return -EPERM;
2004 switch (cmd) {
2005 case IP6T_SO_GET_INFO:
2006 ret = get_info(sock_net(sk), user, len, 1);
2007 break;
2008 case IP6T_SO_GET_ENTRIES:
2009 ret = compat_get_entries(sock_net(sk), user, len);
2010 break;
2011 default:
2012 ret = do_ip6t_get_ctl(sk, cmd, user, len);
2014 return ret;
2016 #endif
2018 static int
2019 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2021 int ret;
2023 if (!capable(CAP_NET_ADMIN))
2024 return -EPERM;
2026 switch (cmd) {
2027 case IP6T_SO_SET_REPLACE:
2028 ret = do_replace(sock_net(sk), user, len);
2029 break;
2031 case IP6T_SO_SET_ADD_COUNTERS:
2032 ret = do_add_counters(sock_net(sk), user, len, 0);
2033 break;
2035 default:
2036 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
2037 ret = -EINVAL;
2040 return ret;
2043 static int
2044 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2046 int ret;
2048 if (!capable(CAP_NET_ADMIN))
2049 return -EPERM;
2051 switch (cmd) {
2052 case IP6T_SO_GET_INFO:
2053 ret = get_info(sock_net(sk), user, len, 0);
2054 break;
2056 case IP6T_SO_GET_ENTRIES:
2057 ret = get_entries(sock_net(sk), user, len);
2058 break;
2060 case IP6T_SO_GET_REVISION_MATCH:
2061 case IP6T_SO_GET_REVISION_TARGET: {
2062 struct ip6t_get_revision rev;
2063 int target;
2065 if (*len != sizeof(rev)) {
2066 ret = -EINVAL;
2067 break;
2069 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2070 ret = -EFAULT;
2071 break;
2074 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2075 target = 1;
2076 else
2077 target = 0;
2079 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2080 rev.revision,
2081 target, &ret),
2082 "ip6t_%s", rev.name);
2083 break;
2086 default:
2087 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2088 ret = -EINVAL;
2091 return ret;
2094 struct xt_table *ip6t_register_table(struct net *net, struct xt_table *table,
2095 const struct ip6t_replace *repl)
2097 int ret;
2098 struct xt_table_info *newinfo;
2099 struct xt_table_info bootstrap
2100 = { 0, 0, 0, { 0 }, { 0 }, { } };
2101 void *loc_cpu_entry;
2102 struct xt_table *new_table;
2104 newinfo = xt_alloc_table_info(repl->size);
2105 if (!newinfo) {
2106 ret = -ENOMEM;
2107 goto out;
2110 /* choose the copy on our node/cpu, but dont care about preemption */
2111 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2112 memcpy(loc_cpu_entry, repl->entries, repl->size);
2114 ret = translate_table(table->name, table->valid_hooks,
2115 newinfo, loc_cpu_entry, repl->size,
2116 repl->num_entries,
2117 repl->hook_entry,
2118 repl->underflow);
2119 if (ret != 0)
2120 goto out_free;
2122 new_table = xt_register_table(net, table, &bootstrap, newinfo);
2123 if (IS_ERR(new_table)) {
2124 ret = PTR_ERR(new_table);
2125 goto out_free;
2127 return new_table;
2129 out_free:
2130 xt_free_table_info(newinfo);
2131 out:
2132 return ERR_PTR(ret);
2135 void ip6t_unregister_table(struct xt_table *table)
2137 struct xt_table_info *private;
2138 void *loc_cpu_entry;
2139 struct module *table_owner = table->me;
2141 private = xt_unregister_table(table);
2143 /* Decrease module usage counts and free resources */
2144 loc_cpu_entry = private->entries[raw_smp_processor_id()];
2145 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
2146 if (private->number > private->initial_entries)
2147 module_put(table_owner);
2148 xt_free_table_info(private);
2151 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
2152 static inline bool
2153 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2154 u_int8_t type, u_int8_t code,
2155 bool invert)
2157 return (type == test_type && code >= min_code && code <= max_code)
2158 ^ invert;
2161 static bool
2162 icmp6_match(const struct sk_buff *skb, const struct xt_match_param *par)
2164 const struct icmp6hdr *ic;
2165 struct icmp6hdr _icmph;
2166 const struct ip6t_icmp *icmpinfo = par->matchinfo;
2168 /* Must not be a fragment. */
2169 if (par->fragoff != 0)
2170 return false;
2172 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
2173 if (ic == NULL) {
2174 /* We've been asked to examine this packet, and we
2175 * can't. Hence, no choice but to drop.
2177 duprintf("Dropping evil ICMP tinygram.\n");
2178 *par->hotdrop = true;
2179 return false;
2182 return icmp6_type_code_match(icmpinfo->type,
2183 icmpinfo->code[0],
2184 icmpinfo->code[1],
2185 ic->icmp6_type, ic->icmp6_code,
2186 !!(icmpinfo->invflags&IP6T_ICMP_INV));
2189 /* Called when user tries to insert an entry of this type. */
2190 static bool icmp6_checkentry(const struct xt_mtchk_param *par)
2192 const struct ip6t_icmp *icmpinfo = par->matchinfo;
2194 /* Must specify no unknown invflags */
2195 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
2198 /* The built-in targets: standard (NULL) and error. */
2199 static struct xt_target ip6t_standard_target __read_mostly = {
2200 .name = IP6T_STANDARD_TARGET,
2201 .targetsize = sizeof(int),
2202 .family = AF_INET6,
2203 #ifdef CONFIG_COMPAT
2204 .compatsize = sizeof(compat_int_t),
2205 .compat_from_user = compat_standard_from_user,
2206 .compat_to_user = compat_standard_to_user,
2207 #endif
2210 static struct xt_target ip6t_error_target __read_mostly = {
2211 .name = IP6T_ERROR_TARGET,
2212 .target = ip6t_error,
2213 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
2214 .family = AF_INET6,
2217 static struct nf_sockopt_ops ip6t_sockopts = {
2218 .pf = PF_INET6,
2219 .set_optmin = IP6T_BASE_CTL,
2220 .set_optmax = IP6T_SO_SET_MAX+1,
2221 .set = do_ip6t_set_ctl,
2222 #ifdef CONFIG_COMPAT
2223 .compat_set = compat_do_ip6t_set_ctl,
2224 #endif
2225 .get_optmin = IP6T_BASE_CTL,
2226 .get_optmax = IP6T_SO_GET_MAX+1,
2227 .get = do_ip6t_get_ctl,
2228 #ifdef CONFIG_COMPAT
2229 .compat_get = compat_do_ip6t_get_ctl,
2230 #endif
2231 .owner = THIS_MODULE,
2234 static struct xt_match icmp6_matchstruct __read_mostly = {
2235 .name = "icmp6",
2236 .match = icmp6_match,
2237 .matchsize = sizeof(struct ip6t_icmp),
2238 .checkentry = icmp6_checkentry,
2239 .proto = IPPROTO_ICMPV6,
2240 .family = AF_INET6,
2243 static int __net_init ip6_tables_net_init(struct net *net)
2245 return xt_proto_init(net, AF_INET6);
2248 static void __net_exit ip6_tables_net_exit(struct net *net)
2250 xt_proto_fini(net, AF_INET6);
2253 static struct pernet_operations ip6_tables_net_ops = {
2254 .init = ip6_tables_net_init,
2255 .exit = ip6_tables_net_exit,
2258 static int __init ip6_tables_init(void)
2260 int ret;
2262 ret = register_pernet_subsys(&ip6_tables_net_ops);
2263 if (ret < 0)
2264 goto err1;
2266 /* Noone else will be downing sem now, so we won't sleep */
2267 ret = xt_register_target(&ip6t_standard_target);
2268 if (ret < 0)
2269 goto err2;
2270 ret = xt_register_target(&ip6t_error_target);
2271 if (ret < 0)
2272 goto err3;
2273 ret = xt_register_match(&icmp6_matchstruct);
2274 if (ret < 0)
2275 goto err4;
2277 /* Register setsockopt */
2278 ret = nf_register_sockopt(&ip6t_sockopts);
2279 if (ret < 0)
2280 goto err5;
2282 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
2283 return 0;
2285 err5:
2286 xt_unregister_match(&icmp6_matchstruct);
2287 err4:
2288 xt_unregister_target(&ip6t_error_target);
2289 err3:
2290 xt_unregister_target(&ip6t_standard_target);
2291 err2:
2292 unregister_pernet_subsys(&ip6_tables_net_ops);
2293 err1:
2294 return ret;
2297 static void __exit ip6_tables_fini(void)
2299 nf_unregister_sockopt(&ip6t_sockopts);
2301 xt_unregister_match(&icmp6_matchstruct);
2302 xt_unregister_target(&ip6t_error_target);
2303 xt_unregister_target(&ip6t_standard_target);
2305 unregister_pernet_subsys(&ip6_tables_net_ops);
2309 * find the offset to specified header or the protocol number of last header
2310 * if target < 0. "last header" is transport protocol header, ESP, or
2311 * "No next header".
2313 * If target header is found, its offset is set in *offset and return protocol
2314 * number. Otherwise, return -1.
2316 * If the first fragment doesn't contain the final protocol header or
2317 * NEXTHDR_NONE it is considered invalid.
2319 * Note that non-1st fragment is special case that "the protocol number
2320 * of last header" is "next header" field in Fragment header. In this case,
2321 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
2322 * isn't NULL.
2325 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2326 int target, unsigned short *fragoff)
2328 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
2329 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
2330 unsigned int len = skb->len - start;
2332 if (fragoff)
2333 *fragoff = 0;
2335 while (nexthdr != target) {
2336 struct ipv6_opt_hdr _hdr, *hp;
2337 unsigned int hdrlen;
2339 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2340 if (target < 0)
2341 break;
2342 return -ENOENT;
2345 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2346 if (hp == NULL)
2347 return -EBADMSG;
2348 if (nexthdr == NEXTHDR_FRAGMENT) {
2349 unsigned short _frag_off;
2350 __be16 *fp;
2351 fp = skb_header_pointer(skb,
2352 start+offsetof(struct frag_hdr,
2353 frag_off),
2354 sizeof(_frag_off),
2355 &_frag_off);
2356 if (fp == NULL)
2357 return -EBADMSG;
2359 _frag_off = ntohs(*fp) & ~0x7;
2360 if (_frag_off) {
2361 if (target < 0 &&
2362 ((!ipv6_ext_hdr(hp->nexthdr)) ||
2363 hp->nexthdr == NEXTHDR_NONE)) {
2364 if (fragoff)
2365 *fragoff = _frag_off;
2366 return hp->nexthdr;
2368 return -ENOENT;
2370 hdrlen = 8;
2371 } else if (nexthdr == NEXTHDR_AUTH)
2372 hdrlen = (hp->hdrlen + 2) << 2;
2373 else
2374 hdrlen = ipv6_optlen(hp);
2376 nexthdr = hp->nexthdr;
2377 len -= hdrlen;
2378 start += hdrlen;
2381 *offset = start;
2382 return nexthdr;
2385 EXPORT_SYMBOL(ip6t_register_table);
2386 EXPORT_SYMBOL(ip6t_unregister_table);
2387 EXPORT_SYMBOL(ip6t_do_table);
2388 EXPORT_SYMBOL(ip6t_ext_hdr);
2389 EXPORT_SYMBOL(ipv6_find_hdr);
2391 module_init(ip6_tables_init);
2392 module_exit(ip6_tables_fini);