netfilter: xtables: move extension arguments into compound structure (2/6)
[safe/jmp/linux-2.6] / net / ipv6 / netfilter / ip6_tables.c
1 /*
2  * Packet matching code.
3  *
4  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5  * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
6  *
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.
10  */
11
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>
28
29 #include <linux/netfilter_ipv6/ip6_tables.h>
30 #include <linux/netfilter/x_tables.h>
31 #include <net/netfilter/nf_log.h>
32
33 MODULE_LICENSE("GPL");
34 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
35 MODULE_DESCRIPTION("IPv6 packet filter");
36
37 /*#define DEBUG_IP_FIREWALL*/
38 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
39 /*#define DEBUG_IP_FIREWALL_USER*/
40
41 #ifdef DEBUG_IP_FIREWALL
42 #define dprintf(format, args...)  printk(format , ## args)
43 #else
44 #define dprintf(format, args...)
45 #endif
46
47 #ifdef DEBUG_IP_FIREWALL_USER
48 #define duprintf(format, args...) printk(format , ## args)
49 #else
50 #define duprintf(format, args...)
51 #endif
52
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
63
64 #if 0
65 /* All the better to debug you with... */
66 #define static
67 #define inline
68 #endif
69
70 /*
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.
76
77    Hence the start of any table is given by get_table() below.  */
78
79 /* Check for an extension */
80 int
81 ip6t_ext_hdr(u8 nexthdr)
82 {
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) );
90 }
91
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)
101 {
102         size_t i;
103         unsigned long ret;
104         const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
105
106 #define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
107
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");
113 /*
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;
121         }
122
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];
128         }
129
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;
135         }
136
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];
141         }
142
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;
148         }
149
150 /* ... might want to do something with class and flowlabel here ... */
151
152         /* look for the desired protocol header */
153         if((ip6info->flags & IP6T_F_PROTO)) {
154                 int protohdr;
155                 unsigned short _frag_off;
156
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;
162                 }
163                 *fragoff = _frag_off;
164
165                 dprintf("Packet protocol %hi ?= %s%hi.\n",
166                                 protohdr,
167                                 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
168                                 ip6info->proto);
169
170                 if (ip6info->proto == protohdr) {
171                         if(ip6info->invflags & IP6T_INV_PROTO) {
172                                 return false;
173                         }
174                         return true;
175                 }
176
177                 /* We need match for the '-p all', too! */
178                 if ((ip6info->proto != 0) &&
179                         !(ip6info->invflags & IP6T_INV_PROTO))
180                         return false;
181         }
182         return true;
183 }
184
185 /* should be ip6 safe */
186 static bool
187 ip6_checkentry(const struct ip6t_ip6 *ipv6)
188 {
189         if (ipv6->flags & ~IP6T_F_MASK) {
190                 duprintf("Unknown flag bits set: %08X\n",
191                          ipv6->flags & ~IP6T_F_MASK);
192                 return false;
193         }
194         if (ipv6->invflags & ~IP6T_INV_MASK) {
195                 duprintf("Unknown invflag bits set: %08X\n",
196                          ipv6->invflags & ~IP6T_INV_MASK);
197                 return false;
198         }
199         return true;
200 }
201
202 static unsigned int
203 ip6t_error(struct sk_buff *skb,
204           const struct net_device *in,
205           const struct net_device *out,
206           unsigned int hooknum,
207           const struct xt_target *target,
208           const void *targinfo)
209 {
210         if (net_ratelimit())
211                 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
212
213         return NF_DROP;
214 }
215
216 /* Performance critical - called for every packet */
217 static inline bool
218 do_match(struct ip6t_entry_match *m, const struct sk_buff *skb,
219          struct xt_match_param *par)
220 {
221         par->match     = m->u.kernel.match;
222         par->matchinfo = m->data;
223
224         /* Stop iteration if it doesn't match */
225         if (!m->u.kernel.match->match(skb, par))
226                 return true;
227         else
228                 return false;
229 }
230
231 static inline struct ip6t_entry *
232 get_entry(void *base, unsigned int offset)
233 {
234         return (struct ip6t_entry *)(base + offset);
235 }
236
237 /* All zeroes == unconditional rule. */
238 /* Mildly perf critical (only if packet tracing is on) */
239 static inline int
240 unconditional(const struct ip6t_ip6 *ipv6)
241 {
242         unsigned int i;
243
244         for (i = 0; i < sizeof(*ipv6); i++)
245                 if (((char *)ipv6)[i])
246                         break;
247
248         return (i == sizeof(*ipv6));
249 }
250
251 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
252     defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
253 /* This cries for unification! */
254 static const char *const hooknames[] = {
255         [NF_INET_PRE_ROUTING]           = "PREROUTING",
256         [NF_INET_LOCAL_IN]              = "INPUT",
257         [NF_INET_FORWARD]               = "FORWARD",
258         [NF_INET_LOCAL_OUT]             = "OUTPUT",
259         [NF_INET_POST_ROUTING]          = "POSTROUTING",
260 };
261
262 enum nf_ip_trace_comments {
263         NF_IP6_TRACE_COMMENT_RULE,
264         NF_IP6_TRACE_COMMENT_RETURN,
265         NF_IP6_TRACE_COMMENT_POLICY,
266 };
267
268 static const char *const comments[] = {
269         [NF_IP6_TRACE_COMMENT_RULE]     = "rule",
270         [NF_IP6_TRACE_COMMENT_RETURN]   = "return",
271         [NF_IP6_TRACE_COMMENT_POLICY]   = "policy",
272 };
273
274 static struct nf_loginfo trace_loginfo = {
275         .type = NF_LOG_TYPE_LOG,
276         .u = {
277                 .log = {
278                         .level = 4,
279                         .logflags = NF_LOG_MASK,
280                 },
281         },
282 };
283
284 /* Mildly perf critical (only if packet tracing is on) */
285 static inline int
286 get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
287                       char *hookname, char **chainname,
288                       char **comment, unsigned int *rulenum)
289 {
290         struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
291
292         if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
293                 /* Head of user chain: ERROR target with chainname */
294                 *chainname = t->target.data;
295                 (*rulenum) = 0;
296         } else if (s == e) {
297                 (*rulenum)++;
298
299                 if (s->target_offset == sizeof(struct ip6t_entry)
300                    && strcmp(t->target.u.kernel.target->name,
301                              IP6T_STANDARD_TARGET) == 0
302                    && t->verdict < 0
303                    && unconditional(&s->ipv6)) {
304                         /* Tail of chains: STANDARD target (return/policy) */
305                         *comment = *chainname == hookname
306                                 ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
307                                 : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
308                 }
309                 return 1;
310         } else
311                 (*rulenum)++;
312
313         return 0;
314 }
315
316 static void trace_packet(struct sk_buff *skb,
317                          unsigned int hook,
318                          const struct net_device *in,
319                          const struct net_device *out,
320                          const char *tablename,
321                          struct xt_table_info *private,
322                          struct ip6t_entry *e)
323 {
324         void *table_base;
325         const struct ip6t_entry *root;
326         char *hookname, *chainname, *comment;
327         unsigned int rulenum = 0;
328
329         table_base = (void *)private->entries[smp_processor_id()];
330         root = get_entry(table_base, private->hook_entry[hook]);
331
332         hookname = chainname = (char *)hooknames[hook];
333         comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
334
335         IP6T_ENTRY_ITERATE(root,
336                            private->size - private->hook_entry[hook],
337                            get_chainname_rulenum,
338                            e, hookname, &chainname, &comment, &rulenum);
339
340         nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
341                       "TRACE: %s:%s:%s:%u ",
342                       tablename, chainname, comment, rulenum);
343 }
344 #endif
345
346 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
347 unsigned int
348 ip6t_do_table(struct sk_buff *skb,
349               unsigned int hook,
350               const struct net_device *in,
351               const struct net_device *out,
352               struct xt_table *table)
353 {
354         static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
355         bool hotdrop = false;
356         /* Initializing verdict to NF_DROP keeps gcc happy. */
357         unsigned int verdict = NF_DROP;
358         const char *indev, *outdev;
359         void *table_base;
360         struct ip6t_entry *e, *back;
361         struct xt_table_info *private;
362         struct xt_match_param mtpar;
363
364         /* Initialization */
365         indev = in ? in->name : nulldevname;
366         outdev = out ? out->name : nulldevname;
367         /* We handle fragments by dealing with the first fragment as
368          * if it was a normal packet.  All other fragments are treated
369          * normally, except that they will NEVER match rules that ask
370          * things we don't know, ie. tcp syn flag or ports).  If the
371          * rule is also a fragment-specific rule, non-fragments won't
372          * match it. */
373         mtpar.hotdrop = &hotdrop;
374         mtpar.in      = in;
375         mtpar.out     = out;
376
377         read_lock_bh(&table->lock);
378         IP_NF_ASSERT(table->valid_hooks & (1 << hook));
379         private = table->private;
380         table_base = (void *)private->entries[smp_processor_id()];
381         e = get_entry(table_base, private->hook_entry[hook]);
382
383         /* For return from builtin chain */
384         back = get_entry(table_base, private->underflow[hook]);
385
386         do {
387                 IP_NF_ASSERT(e);
388                 IP_NF_ASSERT(back);
389                 if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
390                         &mtpar.thoff, &mtpar.fragoff, &hotdrop)) {
391                         struct ip6t_entry_target *t;
392
393                         if (IP6T_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0)
394                                 goto no_match;
395
396                         ADD_COUNTER(e->counters,
397                                     ntohs(ipv6_hdr(skb)->payload_len) +
398                                     sizeof(struct ipv6hdr), 1);
399
400                         t = ip6t_get_target(e);
401                         IP_NF_ASSERT(t->u.kernel.target);
402
403 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
404     defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
405                         /* The packet is traced: log it */
406                         if (unlikely(skb->nf_trace))
407                                 trace_packet(skb, hook, in, out,
408                                              table->name, private, e);
409 #endif
410                         /* Standard target? */
411                         if (!t->u.kernel.target->target) {
412                                 int v;
413
414                                 v = ((struct ip6t_standard_target *)t)->verdict;
415                                 if (v < 0) {
416                                         /* Pop from stack? */
417                                         if (v != IP6T_RETURN) {
418                                                 verdict = (unsigned)(-v) - 1;
419                                                 break;
420                                         }
421                                         e = back;
422                                         back = get_entry(table_base,
423                                                          back->comefrom);
424                                         continue;
425                                 }
426                                 if (table_base + v != (void *)e + e->next_offset
427                                     && !(e->ipv6.flags & IP6T_F_GOTO)) {
428                                         /* Save old back ptr in next entry */
429                                         struct ip6t_entry *next
430                                                 = (void *)e + e->next_offset;
431                                         next->comefrom
432                                                 = (void *)back - table_base;
433                                         /* set back pointer to next entry */
434                                         back = next;
435                                 }
436
437                                 e = get_entry(table_base, v);
438                         } else {
439                                 /* Targets which reenter must return
440                                    abs. verdicts */
441 #ifdef CONFIG_NETFILTER_DEBUG
442                                 ((struct ip6t_entry *)table_base)->comefrom
443                                         = 0xeeeeeeec;
444 #endif
445                                 verdict = t->u.kernel.target->target(skb,
446                                                                      in, out,
447                                                                      hook,
448                                                                      t->u.kernel.target,
449                                                                      t->data);
450
451 #ifdef CONFIG_NETFILTER_DEBUG
452                                 if (((struct ip6t_entry *)table_base)->comefrom
453                                     != 0xeeeeeeec
454                                     && verdict == IP6T_CONTINUE) {
455                                         printk("Target %s reentered!\n",
456                                                t->u.kernel.target->name);
457                                         verdict = NF_DROP;
458                                 }
459                                 ((struct ip6t_entry *)table_base)->comefrom
460                                         = 0x57acc001;
461 #endif
462                                 if (verdict == IP6T_CONTINUE)
463                                         e = (void *)e + e->next_offset;
464                                 else
465                                         /* Verdict */
466                                         break;
467                         }
468                 } else {
469
470                 no_match:
471                         e = (void *)e + e->next_offset;
472                 }
473         } while (!hotdrop);
474
475 #ifdef CONFIG_NETFILTER_DEBUG
476         ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
477 #endif
478         read_unlock_bh(&table->lock);
479
480 #ifdef DEBUG_ALLOW_ALL
481         return NF_ACCEPT;
482 #else
483         if (hotdrop)
484                 return NF_DROP;
485         else return verdict;
486 #endif
487 }
488
489 /* Figures out from what hook each rule can be called: returns 0 if
490    there are loops.  Puts hook bitmask in comefrom. */
491 static int
492 mark_source_chains(struct xt_table_info *newinfo,
493                    unsigned int valid_hooks, void *entry0)
494 {
495         unsigned int hook;
496
497         /* No recursion; use packet counter to save back ptrs (reset
498            to 0 as we leave), and comefrom to save source hook bitmask */
499         for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
500                 unsigned int pos = newinfo->hook_entry[hook];
501                 struct ip6t_entry *e = (struct ip6t_entry *)(entry0 + pos);
502
503                 if (!(valid_hooks & (1 << hook)))
504                         continue;
505
506                 /* Set initial back pointer. */
507                 e->counters.pcnt = pos;
508
509                 for (;;) {
510                         struct ip6t_standard_target *t
511                                 = (void *)ip6t_get_target(e);
512                         int visited = e->comefrom & (1 << hook);
513
514                         if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
515                                 printk("iptables: loop hook %u pos %u %08X.\n",
516                                        hook, pos, e->comefrom);
517                                 return 0;
518                         }
519                         e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
520
521                         /* Unconditional return/END. */
522                         if ((e->target_offset == sizeof(struct ip6t_entry)
523                             && (strcmp(t->target.u.user.name,
524                                        IP6T_STANDARD_TARGET) == 0)
525                             && t->verdict < 0
526                             && unconditional(&e->ipv6)) || visited) {
527                                 unsigned int oldpos, size;
528
529                                 if (t->verdict < -NF_MAX_VERDICT - 1) {
530                                         duprintf("mark_source_chains: bad "
531                                                 "negative verdict (%i)\n",
532                                                                 t->verdict);
533                                         return 0;
534                                 }
535
536                                 /* Return: backtrack through the last
537                                    big jump. */
538                                 do {
539                                         e->comefrom ^= (1<<NF_INET_NUMHOOKS);
540 #ifdef DEBUG_IP_FIREWALL_USER
541                                         if (e->comefrom
542                                             & (1 << NF_INET_NUMHOOKS)) {
543                                                 duprintf("Back unset "
544                                                          "on hook %u "
545                                                          "rule %u\n",
546                                                          hook, pos);
547                                         }
548 #endif
549                                         oldpos = pos;
550                                         pos = e->counters.pcnt;
551                                         e->counters.pcnt = 0;
552
553                                         /* We're at the start. */
554                                         if (pos == oldpos)
555                                                 goto next;
556
557                                         e = (struct ip6t_entry *)
558                                                 (entry0 + pos);
559                                 } while (oldpos == pos + e->next_offset);
560
561                                 /* Move along one */
562                                 size = e->next_offset;
563                                 e = (struct ip6t_entry *)
564                                         (entry0 + pos + size);
565                                 e->counters.pcnt = pos;
566                                 pos += size;
567                         } else {
568                                 int newpos = t->verdict;
569
570                                 if (strcmp(t->target.u.user.name,
571                                            IP6T_STANDARD_TARGET) == 0
572                                     && newpos >= 0) {
573                                         if (newpos > newinfo->size -
574                                                 sizeof(struct ip6t_entry)) {
575                                                 duprintf("mark_source_chains: "
576                                                         "bad verdict (%i)\n",
577                                                                 newpos);
578                                                 return 0;
579                                         }
580                                         /* This a jump; chase it. */
581                                         duprintf("Jump rule %u -> %u\n",
582                                                  pos, newpos);
583                                 } else {
584                                         /* ... this is a fallthru */
585                                         newpos = pos + e->next_offset;
586                                 }
587                                 e = (struct ip6t_entry *)
588                                         (entry0 + newpos);
589                                 e->counters.pcnt = pos;
590                                 pos = newpos;
591                         }
592                 }
593                 next:
594                 duprintf("Finished chain %u\n", hook);
595         }
596         return 1;
597 }
598
599 static int
600 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
601 {
602         if (i && (*i)-- == 0)
603                 return 1;
604
605         if (m->u.kernel.match->destroy)
606                 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
607         module_put(m->u.kernel.match->me);
608         return 0;
609 }
610
611 static int
612 check_entry(struct ip6t_entry *e, const char *name)
613 {
614         struct ip6t_entry_target *t;
615
616         if (!ip6_checkentry(&e->ipv6)) {
617                 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
618                 return -EINVAL;
619         }
620
621         if (e->target_offset + sizeof(struct ip6t_entry_target) >
622             e->next_offset)
623                 return -EINVAL;
624
625         t = ip6t_get_target(e);
626         if (e->target_offset + t->u.target_size > e->next_offset)
627                 return -EINVAL;
628
629         return 0;
630 }
631
632 static int check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par,
633                        unsigned int *i)
634 {
635         const struct ip6t_ip6 *ipv6 = par->entryinfo;
636         int ret;
637
638         par->match     = m->u.kernel.match;
639         par->matchinfo = m->data;
640
641         ret = xt_check_match(par, NFPROTO_IPV6, m->u.match_size - sizeof(*m),
642                              ipv6->proto, ipv6->invflags & IP6T_INV_PROTO);
643         if (ret < 0) {
644                 duprintf("ip_tables: check failed for `%s'.\n",
645                          par.match->name);
646                 return ret;
647         }
648         ++*i;
649         return 0;
650 }
651
652 static int
653 find_check_match(struct ip6t_entry_match *m, struct xt_mtchk_param *par,
654                  unsigned int *i)
655 {
656         struct xt_match *match;
657         int ret;
658
659         match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
660                                                       m->u.user.revision),
661                                         "ip6t_%s", m->u.user.name);
662         if (IS_ERR(match) || !match) {
663                 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
664                 return match ? PTR_ERR(match) : -ENOENT;
665         }
666         m->u.kernel.match = match;
667
668         ret = check_match(m, par, i);
669         if (ret)
670                 goto err;
671
672         return 0;
673 err:
674         module_put(m->u.kernel.match->me);
675         return ret;
676 }
677
678 static int check_target(struct ip6t_entry *e, const char *name)
679 {
680         struct ip6t_entry_target *t;
681         struct xt_target *target;
682         int ret;
683
684         t = ip6t_get_target(e);
685         target = t->u.kernel.target;
686         ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
687                               name, e->comefrom, e->ipv6.proto,
688                               e->ipv6.invflags & IP6T_INV_PROTO, e, t->data);
689         if (ret < 0) {
690                 duprintf("ip_tables: check failed for `%s'.\n",
691                          t->u.kernel.target->name);
692                 return ret;
693         }
694         return 0;
695 }
696
697 static int
698 find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
699                  unsigned int *i)
700 {
701         struct ip6t_entry_target *t;
702         struct xt_target *target;
703         int ret;
704         unsigned int j;
705         struct xt_mtchk_param mtpar;
706
707         ret = check_entry(e, name);
708         if (ret)
709                 return ret;
710
711         j = 0;
712         mtpar.table     = name;
713         mtpar.entryinfo = &e->ipv6;
714         mtpar.hook_mask = e->comefrom;
715         ret = IP6T_MATCH_ITERATE(e, find_check_match, &mtpar, &j);
716         if (ret != 0)
717                 goto cleanup_matches;
718
719         t = ip6t_get_target(e);
720         target = try_then_request_module(xt_find_target(AF_INET6,
721                                                         t->u.user.name,
722                                                         t->u.user.revision),
723                                          "ip6t_%s", t->u.user.name);
724         if (IS_ERR(target) || !target) {
725                 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
726                 ret = target ? PTR_ERR(target) : -ENOENT;
727                 goto cleanup_matches;
728         }
729         t->u.kernel.target = target;
730
731         ret = check_target(e, name);
732         if (ret)
733                 goto err;
734
735         (*i)++;
736         return 0;
737  err:
738         module_put(t->u.kernel.target->me);
739  cleanup_matches:
740         IP6T_MATCH_ITERATE(e, cleanup_match, &j);
741         return ret;
742 }
743
744 static int
745 check_entry_size_and_hooks(struct ip6t_entry *e,
746                            struct xt_table_info *newinfo,
747                            unsigned char *base,
748                            unsigned char *limit,
749                            const unsigned int *hook_entries,
750                            const unsigned int *underflows,
751                            unsigned int *i)
752 {
753         unsigned int h;
754
755         if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
756             || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
757                 duprintf("Bad offset %p\n", e);
758                 return -EINVAL;
759         }
760
761         if (e->next_offset
762             < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
763                 duprintf("checking: element %p size %u\n",
764                          e, e->next_offset);
765                 return -EINVAL;
766         }
767
768         /* Check hooks & underflows */
769         for (h = 0; h < NF_INET_NUMHOOKS; h++) {
770                 if ((unsigned char *)e - base == hook_entries[h])
771                         newinfo->hook_entry[h] = hook_entries[h];
772                 if ((unsigned char *)e - base == underflows[h])
773                         newinfo->underflow[h] = underflows[h];
774         }
775
776         /* FIXME: underflows must be unconditional, standard verdicts
777            < 0 (not IP6T_RETURN). --RR */
778
779         /* Clear counters and comefrom */
780         e->counters = ((struct xt_counters) { 0, 0 });
781         e->comefrom = 0;
782
783         (*i)++;
784         return 0;
785 }
786
787 static int
788 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
789 {
790         struct ip6t_entry_target *t;
791
792         if (i && (*i)-- == 0)
793                 return 1;
794
795         /* Cleanup all matches */
796         IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
797         t = ip6t_get_target(e);
798         if (t->u.kernel.target->destroy)
799                 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
800         module_put(t->u.kernel.target->me);
801         return 0;
802 }
803
804 /* Checks and translates the user-supplied table segment (held in
805    newinfo) */
806 static int
807 translate_table(const char *name,
808                 unsigned int valid_hooks,
809                 struct xt_table_info *newinfo,
810                 void *entry0,
811                 unsigned int size,
812                 unsigned int number,
813                 const unsigned int *hook_entries,
814                 const unsigned int *underflows)
815 {
816         unsigned int i;
817         int ret;
818
819         newinfo->size = size;
820         newinfo->number = number;
821
822         /* Init all hooks to impossible value. */
823         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
824                 newinfo->hook_entry[i] = 0xFFFFFFFF;
825                 newinfo->underflow[i] = 0xFFFFFFFF;
826         }
827
828         duprintf("translate_table: size %u\n", newinfo->size);
829         i = 0;
830         /* Walk through entries, checking offsets. */
831         ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
832                                 check_entry_size_and_hooks,
833                                 newinfo,
834                                 entry0,
835                                 entry0 + size,
836                                 hook_entries, underflows, &i);
837         if (ret != 0)
838                 return ret;
839
840         if (i != number) {
841                 duprintf("translate_table: %u not %u entries\n",
842                          i, number);
843                 return -EINVAL;
844         }
845
846         /* Check hooks all assigned */
847         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
848                 /* Only hooks which are valid */
849                 if (!(valid_hooks & (1 << i)))
850                         continue;
851                 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
852                         duprintf("Invalid hook entry %u %u\n",
853                                  i, hook_entries[i]);
854                         return -EINVAL;
855                 }
856                 if (newinfo->underflow[i] == 0xFFFFFFFF) {
857                         duprintf("Invalid underflow %u %u\n",
858                                  i, underflows[i]);
859                         return -EINVAL;
860                 }
861         }
862
863         if (!mark_source_chains(newinfo, valid_hooks, entry0))
864                 return -ELOOP;
865
866         /* Finally, each sanity check must pass */
867         i = 0;
868         ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
869                                 find_check_entry, name, size, &i);
870
871         if (ret != 0) {
872                 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
873                                    cleanup_entry, &i);
874                 return ret;
875         }
876
877         /* And one copy for every other CPU */
878         for_each_possible_cpu(i) {
879                 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
880                         memcpy(newinfo->entries[i], entry0, newinfo->size);
881         }
882
883         return ret;
884 }
885
886 /* Gets counters. */
887 static inline int
888 add_entry_to_counter(const struct ip6t_entry *e,
889                      struct xt_counters total[],
890                      unsigned int *i)
891 {
892         ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
893
894         (*i)++;
895         return 0;
896 }
897
898 static inline int
899 set_entry_to_counter(const struct ip6t_entry *e,
900                      struct ip6t_counters total[],
901                      unsigned int *i)
902 {
903         SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
904
905         (*i)++;
906         return 0;
907 }
908
909 static void
910 get_counters(const struct xt_table_info *t,
911              struct xt_counters counters[])
912 {
913         unsigned int cpu;
914         unsigned int i;
915         unsigned int curcpu;
916
917         /* Instead of clearing (by a previous call to memset())
918          * the counters and using adds, we set the counters
919          * with data used by 'current' CPU
920          * We dont care about preemption here.
921          */
922         curcpu = raw_smp_processor_id();
923
924         i = 0;
925         IP6T_ENTRY_ITERATE(t->entries[curcpu],
926                            t->size,
927                            set_entry_to_counter,
928                            counters,
929                            &i);
930
931         for_each_possible_cpu(cpu) {
932                 if (cpu == curcpu)
933                         continue;
934                 i = 0;
935                 IP6T_ENTRY_ITERATE(t->entries[cpu],
936                                   t->size,
937                                   add_entry_to_counter,
938                                   counters,
939                                   &i);
940         }
941 }
942
943 static struct xt_counters *alloc_counters(struct xt_table *table)
944 {
945         unsigned int countersize;
946         struct xt_counters *counters;
947         const struct xt_table_info *private = table->private;
948
949         /* We need atomic snapshot of counters: rest doesn't change
950            (other than comefrom, which userspace doesn't care
951            about). */
952         countersize = sizeof(struct xt_counters) * private->number;
953         counters = vmalloc_node(countersize, numa_node_id());
954
955         if (counters == NULL)
956                 return ERR_PTR(-ENOMEM);
957
958         /* First, sum counters... */
959         write_lock_bh(&table->lock);
960         get_counters(private, counters);
961         write_unlock_bh(&table->lock);
962
963         return counters;
964 }
965
966 static int
967 copy_entries_to_user(unsigned int total_size,
968                      struct xt_table *table,
969                      void __user *userptr)
970 {
971         unsigned int off, num;
972         struct ip6t_entry *e;
973         struct xt_counters *counters;
974         const struct xt_table_info *private = table->private;
975         int ret = 0;
976         const void *loc_cpu_entry;
977
978         counters = alloc_counters(table);
979         if (IS_ERR(counters))
980                 return PTR_ERR(counters);
981
982         /* choose the copy that is on our node/cpu, ...
983          * This choice is lazy (because current thread is
984          * allowed to migrate to another cpu)
985          */
986         loc_cpu_entry = private->entries[raw_smp_processor_id()];
987         if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
988                 ret = -EFAULT;
989                 goto free_counters;
990         }
991
992         /* FIXME: use iterator macros --RR */
993         /* ... then go back and fix counters and names */
994         for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
995                 unsigned int i;
996                 const struct ip6t_entry_match *m;
997                 const struct ip6t_entry_target *t;
998
999                 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1000                 if (copy_to_user(userptr + off
1001                                  + offsetof(struct ip6t_entry, counters),
1002                                  &counters[num],
1003                                  sizeof(counters[num])) != 0) {
1004                         ret = -EFAULT;
1005                         goto free_counters;
1006                 }
1007
1008                 for (i = sizeof(struct ip6t_entry);
1009                      i < e->target_offset;
1010                      i += m->u.match_size) {
1011                         m = (void *)e + i;
1012
1013                         if (copy_to_user(userptr + off + i
1014                                          + offsetof(struct ip6t_entry_match,
1015                                                     u.user.name),
1016                                          m->u.kernel.match->name,
1017                                          strlen(m->u.kernel.match->name)+1)
1018                             != 0) {
1019                                 ret = -EFAULT;
1020                                 goto free_counters;
1021                         }
1022                 }
1023
1024                 t = ip6t_get_target(e);
1025                 if (copy_to_user(userptr + off + e->target_offset
1026                                  + offsetof(struct ip6t_entry_target,
1027                                             u.user.name),
1028                                  t->u.kernel.target->name,
1029                                  strlen(t->u.kernel.target->name)+1) != 0) {
1030                         ret = -EFAULT;
1031                         goto free_counters;
1032                 }
1033         }
1034
1035  free_counters:
1036         vfree(counters);
1037         return ret;
1038 }
1039
1040 #ifdef CONFIG_COMPAT
1041 static void compat_standard_from_user(void *dst, void *src)
1042 {
1043         int v = *(compat_int_t *)src;
1044
1045         if (v > 0)
1046                 v += xt_compat_calc_jump(AF_INET6, v);
1047         memcpy(dst, &v, sizeof(v));
1048 }
1049
1050 static int compat_standard_to_user(void __user *dst, void *src)
1051 {
1052         compat_int_t cv = *(int *)src;
1053
1054         if (cv > 0)
1055                 cv -= xt_compat_calc_jump(AF_INET6, cv);
1056         return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1057 }
1058
1059 static inline int
1060 compat_calc_match(struct ip6t_entry_match *m, int *size)
1061 {
1062         *size += xt_compat_match_offset(m->u.kernel.match);
1063         return 0;
1064 }
1065
1066 static int compat_calc_entry(struct ip6t_entry *e,
1067                              const struct xt_table_info *info,
1068                              void *base, struct xt_table_info *newinfo)
1069 {
1070         struct ip6t_entry_target *t;
1071         unsigned int entry_offset;
1072         int off, i, ret;
1073
1074         off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1075         entry_offset = (void *)e - base;
1076         IP6T_MATCH_ITERATE(e, compat_calc_match, &off);
1077         t = ip6t_get_target(e);
1078         off += xt_compat_target_offset(t->u.kernel.target);
1079         newinfo->size -= off;
1080         ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1081         if (ret)
1082                 return ret;
1083
1084         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1085                 if (info->hook_entry[i] &&
1086                     (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1087                         newinfo->hook_entry[i] -= off;
1088                 if (info->underflow[i] &&
1089                     (e < (struct ip6t_entry *)(base + info->underflow[i])))
1090                         newinfo->underflow[i] -= off;
1091         }
1092         return 0;
1093 }
1094
1095 static int compat_table_info(const struct xt_table_info *info,
1096                              struct xt_table_info *newinfo)
1097 {
1098         void *loc_cpu_entry;
1099
1100         if (!newinfo || !info)
1101                 return -EINVAL;
1102
1103         /* we dont care about newinfo->entries[] */
1104         memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1105         newinfo->initial_entries = 0;
1106         loc_cpu_entry = info->entries[raw_smp_processor_id()];
1107         return IP6T_ENTRY_ITERATE(loc_cpu_entry, info->size,
1108                                   compat_calc_entry, info, loc_cpu_entry,
1109                                   newinfo);
1110 }
1111 #endif
1112
1113 static int get_info(struct net *net, void __user *user, int *len, int compat)
1114 {
1115         char name[IP6T_TABLE_MAXNAMELEN];
1116         struct xt_table *t;
1117         int ret;
1118
1119         if (*len != sizeof(struct ip6t_getinfo)) {
1120                 duprintf("length %u != %zu\n", *len,
1121                          sizeof(struct ip6t_getinfo));
1122                 return -EINVAL;
1123         }
1124
1125         if (copy_from_user(name, user, sizeof(name)) != 0)
1126                 return -EFAULT;
1127
1128         name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1129 #ifdef CONFIG_COMPAT
1130         if (compat)
1131                 xt_compat_lock(AF_INET6);
1132 #endif
1133         t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1134                                     "ip6table_%s", name);
1135         if (t && !IS_ERR(t)) {
1136                 struct ip6t_getinfo info;
1137                 const struct xt_table_info *private = t->private;
1138
1139 #ifdef CONFIG_COMPAT
1140                 if (compat) {
1141                         struct xt_table_info tmp;
1142                         ret = compat_table_info(private, &tmp);
1143                         xt_compat_flush_offsets(AF_INET6);
1144                         private = &tmp;
1145                 }
1146 #endif
1147                 info.valid_hooks = t->valid_hooks;
1148                 memcpy(info.hook_entry, private->hook_entry,
1149                        sizeof(info.hook_entry));
1150                 memcpy(info.underflow, private->underflow,
1151                        sizeof(info.underflow));
1152                 info.num_entries = private->number;
1153                 info.size = private->size;
1154                 strcpy(info.name, name);
1155
1156                 if (copy_to_user(user, &info, *len) != 0)
1157                         ret = -EFAULT;
1158                 else
1159                         ret = 0;
1160
1161                 xt_table_unlock(t);
1162                 module_put(t->me);
1163         } else
1164                 ret = t ? PTR_ERR(t) : -ENOENT;
1165 #ifdef CONFIG_COMPAT
1166         if (compat)
1167                 xt_compat_unlock(AF_INET6);
1168 #endif
1169         return ret;
1170 }
1171
1172 static int
1173 get_entries(struct net *net, struct ip6t_get_entries __user *uptr, int *len)
1174 {
1175         int ret;
1176         struct ip6t_get_entries get;
1177         struct xt_table *t;
1178
1179         if (*len < sizeof(get)) {
1180                 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
1181                 return -EINVAL;
1182         }
1183         if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1184                 return -EFAULT;
1185         if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1186                 duprintf("get_entries: %u != %zu\n",
1187                          *len, sizeof(get) + get.size);
1188                 return -EINVAL;
1189         }
1190
1191         t = xt_find_table_lock(net, AF_INET6, get.name);
1192         if (t && !IS_ERR(t)) {
1193                 struct xt_table_info *private = t->private;
1194                 duprintf("t->private->number = %u\n", private->number);
1195                 if (get.size == private->size)
1196                         ret = copy_entries_to_user(private->size,
1197                                                    t, uptr->entrytable);
1198                 else {
1199                         duprintf("get_entries: I've got %u not %u!\n",
1200                                  private->size, get.size);
1201                         ret = -EAGAIN;
1202                 }
1203                 module_put(t->me);
1204                 xt_table_unlock(t);
1205         } else
1206                 ret = t ? PTR_ERR(t) : -ENOENT;
1207
1208         return ret;
1209 }
1210
1211 static int
1212 __do_replace(struct net *net, const char *name, unsigned int valid_hooks,
1213              struct xt_table_info *newinfo, unsigned int num_counters,
1214              void __user *counters_ptr)
1215 {
1216         int ret;
1217         struct xt_table *t;
1218         struct xt_table_info *oldinfo;
1219         struct xt_counters *counters;
1220         const void *loc_cpu_old_entry;
1221
1222         ret = 0;
1223         counters = vmalloc_node(num_counters * sizeof(struct xt_counters),
1224                                 numa_node_id());
1225         if (!counters) {
1226                 ret = -ENOMEM;
1227                 goto out;
1228         }
1229
1230         t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
1231                                     "ip6table_%s", name);
1232         if (!t || IS_ERR(t)) {
1233                 ret = t ? PTR_ERR(t) : -ENOENT;
1234                 goto free_newinfo_counters_untrans;
1235         }
1236
1237         /* You lied! */
1238         if (valid_hooks != t->valid_hooks) {
1239                 duprintf("Valid hook crap: %08X vs %08X\n",
1240                          valid_hooks, t->valid_hooks);
1241                 ret = -EINVAL;
1242                 goto put_module;
1243         }
1244
1245         oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1246         if (!oldinfo)
1247                 goto put_module;
1248
1249         /* Update module usage count based on number of rules */
1250         duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1251                 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1252         if ((oldinfo->number > oldinfo->initial_entries) ||
1253             (newinfo->number <= oldinfo->initial_entries))
1254                 module_put(t->me);
1255         if ((oldinfo->number > oldinfo->initial_entries) &&
1256             (newinfo->number <= oldinfo->initial_entries))
1257                 module_put(t->me);
1258
1259         /* Get the old counters. */
1260         get_counters(oldinfo, counters);
1261         /* Decrease module usage counts and free resource */
1262         loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1263         IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
1264                            NULL);
1265         xt_free_table_info(oldinfo);
1266         if (copy_to_user(counters_ptr, counters,
1267                          sizeof(struct xt_counters) * num_counters) != 0)
1268                 ret = -EFAULT;
1269         vfree(counters);
1270         xt_table_unlock(t);
1271         return ret;
1272
1273  put_module:
1274         module_put(t->me);
1275         xt_table_unlock(t);
1276  free_newinfo_counters_untrans:
1277         vfree(counters);
1278  out:
1279         return ret;
1280 }
1281
1282 static int
1283 do_replace(struct net *net, void __user *user, unsigned int len)
1284 {
1285         int ret;
1286         struct ip6t_replace tmp;
1287         struct xt_table_info *newinfo;
1288         void *loc_cpu_entry;
1289
1290         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1291                 return -EFAULT;
1292
1293         /* overflow check */
1294         if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1295                 return -ENOMEM;
1296
1297         newinfo = xt_alloc_table_info(tmp.size);
1298         if (!newinfo)
1299                 return -ENOMEM;
1300
1301         /* choose the copy that is on our node/cpu */
1302         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1303         if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1304                            tmp.size) != 0) {
1305                 ret = -EFAULT;
1306                 goto free_newinfo;
1307         }
1308
1309         ret = translate_table(tmp.name, tmp.valid_hooks,
1310                               newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1311                               tmp.hook_entry, tmp.underflow);
1312         if (ret != 0)
1313                 goto free_newinfo;
1314
1315         duprintf("ip_tables: Translated table\n");
1316
1317         ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1318                            tmp.num_counters, tmp.counters);
1319         if (ret)
1320                 goto free_newinfo_untrans;
1321         return 0;
1322
1323  free_newinfo_untrans:
1324         IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
1325  free_newinfo:
1326         xt_free_table_info(newinfo);
1327         return ret;
1328 }
1329
1330 /* We're lazy, and add to the first CPU; overflow works its fey magic
1331  * and everything is OK. */
1332 static inline int
1333 add_counter_to_entry(struct ip6t_entry *e,
1334                      const struct xt_counters addme[],
1335                      unsigned int *i)
1336 {
1337 #if 0
1338         duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1339                  *i,
1340                  (long unsigned int)e->counters.pcnt,
1341                  (long unsigned int)e->counters.bcnt,
1342                  (long unsigned int)addme[*i].pcnt,
1343                  (long unsigned int)addme[*i].bcnt);
1344 #endif
1345
1346         ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1347
1348         (*i)++;
1349         return 0;
1350 }
1351
1352 static int
1353 do_add_counters(struct net *net, void __user *user, unsigned int len,
1354                 int compat)
1355 {
1356         unsigned int i;
1357         struct xt_counters_info tmp;
1358         struct xt_counters *paddc;
1359         unsigned int num_counters;
1360         char *name;
1361         int size;
1362         void *ptmp;
1363         struct xt_table *t;
1364         const struct xt_table_info *private;
1365         int ret = 0;
1366         const void *loc_cpu_entry;
1367 #ifdef CONFIG_COMPAT
1368         struct compat_xt_counters_info compat_tmp;
1369
1370         if (compat) {
1371                 ptmp = &compat_tmp;
1372                 size = sizeof(struct compat_xt_counters_info);
1373         } else
1374 #endif
1375         {
1376                 ptmp = &tmp;
1377                 size = sizeof(struct xt_counters_info);
1378         }
1379
1380         if (copy_from_user(ptmp, user, size) != 0)
1381                 return -EFAULT;
1382
1383 #ifdef CONFIG_COMPAT
1384         if (compat) {
1385                 num_counters = compat_tmp.num_counters;
1386                 name = compat_tmp.name;
1387         } else
1388 #endif
1389         {
1390                 num_counters = tmp.num_counters;
1391                 name = tmp.name;
1392         }
1393
1394         if (len != size + num_counters * sizeof(struct xt_counters))
1395                 return -EINVAL;
1396
1397         paddc = vmalloc_node(len - size, numa_node_id());
1398         if (!paddc)
1399                 return -ENOMEM;
1400
1401         if (copy_from_user(paddc, user + size, len - size) != 0) {
1402                 ret = -EFAULT;
1403                 goto free;
1404         }
1405
1406         t = xt_find_table_lock(net, AF_INET6, name);
1407         if (!t || IS_ERR(t)) {
1408                 ret = t ? PTR_ERR(t) : -ENOENT;
1409                 goto free;
1410         }
1411
1412         write_lock_bh(&t->lock);
1413         private = t->private;
1414         if (private->number != num_counters) {
1415                 ret = -EINVAL;
1416                 goto unlock_up_free;
1417         }
1418
1419         i = 0;
1420         /* Choose the copy that is on our node */
1421         loc_cpu_entry = private->entries[raw_smp_processor_id()];
1422         IP6T_ENTRY_ITERATE(loc_cpu_entry,
1423                           private->size,
1424                           add_counter_to_entry,
1425                           paddc,
1426                           &i);
1427  unlock_up_free:
1428         write_unlock_bh(&t->lock);
1429         xt_table_unlock(t);
1430         module_put(t->me);
1431  free:
1432         vfree(paddc);
1433
1434         return ret;
1435 }
1436
1437 #ifdef CONFIG_COMPAT
1438 struct compat_ip6t_replace {
1439         char                    name[IP6T_TABLE_MAXNAMELEN];
1440         u32                     valid_hooks;
1441         u32                     num_entries;
1442         u32                     size;
1443         u32                     hook_entry[NF_INET_NUMHOOKS];
1444         u32                     underflow[NF_INET_NUMHOOKS];
1445         u32                     num_counters;
1446         compat_uptr_t           counters;       /* struct ip6t_counters * */
1447         struct compat_ip6t_entry entries[0];
1448 };
1449
1450 static int
1451 compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
1452                           unsigned int *size, struct xt_counters *counters,
1453                           unsigned int *i)
1454 {
1455         struct ip6t_entry_target *t;
1456         struct compat_ip6t_entry __user *ce;
1457         u_int16_t target_offset, next_offset;
1458         compat_uint_t origsize;
1459         int ret;
1460
1461         ret = -EFAULT;
1462         origsize = *size;
1463         ce = (struct compat_ip6t_entry __user *)*dstptr;
1464         if (copy_to_user(ce, e, sizeof(struct ip6t_entry)))
1465                 goto out;
1466
1467         if (copy_to_user(&ce->counters, &counters[*i], sizeof(counters[*i])))
1468                 goto out;
1469
1470         *dstptr += sizeof(struct compat_ip6t_entry);
1471         *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1472
1473         ret = IP6T_MATCH_ITERATE(e, xt_compat_match_to_user, dstptr, size);
1474         target_offset = e->target_offset - (origsize - *size);
1475         if (ret)
1476                 goto out;
1477         t = ip6t_get_target(e);
1478         ret = xt_compat_target_to_user(t, dstptr, size);
1479         if (ret)
1480                 goto out;
1481         ret = -EFAULT;
1482         next_offset = e->next_offset - (origsize - *size);
1483         if (put_user(target_offset, &ce->target_offset))
1484                 goto out;
1485         if (put_user(next_offset, &ce->next_offset))
1486                 goto out;
1487
1488         (*i)++;
1489         return 0;
1490 out:
1491         return ret;
1492 }
1493
1494 static int
1495 compat_find_calc_match(struct ip6t_entry_match *m,
1496                        const char *name,
1497                        const struct ip6t_ip6 *ipv6,
1498                        unsigned int hookmask,
1499                        int *size, unsigned int *i)
1500 {
1501         struct xt_match *match;
1502
1503         match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
1504                                                       m->u.user.revision),
1505                                         "ip6t_%s", m->u.user.name);
1506         if (IS_ERR(match) || !match) {
1507                 duprintf("compat_check_calc_match: `%s' not found\n",
1508                          m->u.user.name);
1509                 return match ? PTR_ERR(match) : -ENOENT;
1510         }
1511         m->u.kernel.match = match;
1512         *size += xt_compat_match_offset(match);
1513
1514         (*i)++;
1515         return 0;
1516 }
1517
1518 static int
1519 compat_release_match(struct ip6t_entry_match *m, unsigned int *i)
1520 {
1521         if (i && (*i)-- == 0)
1522                 return 1;
1523
1524         module_put(m->u.kernel.match->me);
1525         return 0;
1526 }
1527
1528 static int
1529 compat_release_entry(struct compat_ip6t_entry *e, unsigned int *i)
1530 {
1531         struct ip6t_entry_target *t;
1532
1533         if (i && (*i)-- == 0)
1534                 return 1;
1535
1536         /* Cleanup all matches */
1537         COMPAT_IP6T_MATCH_ITERATE(e, compat_release_match, NULL);
1538         t = compat_ip6t_get_target(e);
1539         module_put(t->u.kernel.target->me);
1540         return 0;
1541 }
1542
1543 static int
1544 check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1545                                   struct xt_table_info *newinfo,
1546                                   unsigned int *size,
1547                                   unsigned char *base,
1548                                   unsigned char *limit,
1549                                   unsigned int *hook_entries,
1550                                   unsigned int *underflows,
1551                                   unsigned int *i,
1552                                   const char *name)
1553 {
1554         struct ip6t_entry_target *t;
1555         struct xt_target *target;
1556         unsigned int entry_offset;
1557         unsigned int j;
1558         int ret, off, h;
1559
1560         duprintf("check_compat_entry_size_and_hooks %p\n", e);
1561         if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0
1562             || (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
1563                 duprintf("Bad offset %p, limit = %p\n", e, limit);
1564                 return -EINVAL;
1565         }
1566
1567         if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1568                              sizeof(struct compat_xt_entry_target)) {
1569                 duprintf("checking: element %p size %u\n",
1570                          e, e->next_offset);
1571                 return -EINVAL;
1572         }
1573
1574         /* For purposes of check_entry casting the compat entry is fine */
1575         ret = check_entry((struct ip6t_entry *)e, name);
1576         if (ret)
1577                 return ret;
1578
1579         off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1580         entry_offset = (void *)e - (void *)base;
1581         j = 0;
1582         ret = COMPAT_IP6T_MATCH_ITERATE(e, compat_find_calc_match, name,
1583                                         &e->ipv6, e->comefrom, &off, &j);
1584         if (ret != 0)
1585                 goto release_matches;
1586
1587         t = compat_ip6t_get_target(e);
1588         target = try_then_request_module(xt_find_target(AF_INET6,
1589                                                         t->u.user.name,
1590                                                         t->u.user.revision),
1591                                          "ip6t_%s", t->u.user.name);
1592         if (IS_ERR(target) || !target) {
1593                 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1594                          t->u.user.name);
1595                 ret = target ? PTR_ERR(target) : -ENOENT;
1596                 goto release_matches;
1597         }
1598         t->u.kernel.target = target;
1599
1600         off += xt_compat_target_offset(target);
1601         *size += off;
1602         ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1603         if (ret)
1604                 goto out;
1605
1606         /* Check hooks & underflows */
1607         for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1608                 if ((unsigned char *)e - base == hook_entries[h])
1609                         newinfo->hook_entry[h] = hook_entries[h];
1610                 if ((unsigned char *)e - base == underflows[h])
1611                         newinfo->underflow[h] = underflows[h];
1612         }
1613
1614         /* Clear counters and comefrom */
1615         memset(&e->counters, 0, sizeof(e->counters));
1616         e->comefrom = 0;
1617
1618         (*i)++;
1619         return 0;
1620
1621 out:
1622         module_put(t->u.kernel.target->me);
1623 release_matches:
1624         IP6T_MATCH_ITERATE(e, compat_release_match, &j);
1625         return ret;
1626 }
1627
1628 static int
1629 compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1630                             unsigned int *size, const char *name,
1631                             struct xt_table_info *newinfo, unsigned char *base)
1632 {
1633         struct ip6t_entry_target *t;
1634         struct xt_target *target;
1635         struct ip6t_entry *de;
1636         unsigned int origsize;
1637         int ret, h;
1638
1639         ret = 0;
1640         origsize = *size;
1641         de = (struct ip6t_entry *)*dstptr;
1642         memcpy(de, e, sizeof(struct ip6t_entry));
1643         memcpy(&de->counters, &e->counters, sizeof(e->counters));
1644
1645         *dstptr += sizeof(struct ip6t_entry);
1646         *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1647
1648         ret = COMPAT_IP6T_MATCH_ITERATE(e, xt_compat_match_from_user,
1649                                         dstptr, size);
1650         if (ret)
1651                 return ret;
1652         de->target_offset = e->target_offset - (origsize - *size);
1653         t = compat_ip6t_get_target(e);
1654         target = t->u.kernel.target;
1655         xt_compat_target_from_user(t, dstptr, size);
1656
1657         de->next_offset = e->next_offset - (origsize - *size);
1658         for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1659                 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1660                         newinfo->hook_entry[h] -= origsize - *size;
1661                 if ((unsigned char *)de - base < newinfo->underflow[h])
1662                         newinfo->underflow[h] -= origsize - *size;
1663         }
1664         return ret;
1665 }
1666
1667 static int compat_check_entry(struct ip6t_entry *e, const char *name,
1668                                      unsigned int *i)
1669 {
1670         unsigned int j;
1671         int ret;
1672         struct xt_mtchk_param mtpar;
1673
1674         j = 0;
1675         mtpar.table     = name;
1676         mtpar.entryinfo = &e->ipv6;
1677         mtpar.hook_mask = e->comefrom;
1678         ret = IP6T_MATCH_ITERATE(e, check_match, &mtpar, &j);
1679         if (ret)
1680                 goto cleanup_matches;
1681
1682         ret = check_target(e, name);
1683         if (ret)
1684                 goto cleanup_matches;
1685
1686         (*i)++;
1687         return 0;
1688
1689  cleanup_matches:
1690         IP6T_MATCH_ITERATE(e, cleanup_match, &j);
1691         return ret;
1692 }
1693
1694 static int
1695 translate_compat_table(const char *name,
1696                        unsigned int valid_hooks,
1697                        struct xt_table_info **pinfo,
1698                        void **pentry0,
1699                        unsigned int total_size,
1700                        unsigned int number,
1701                        unsigned int *hook_entries,
1702                        unsigned int *underflows)
1703 {
1704         unsigned int i, j;
1705         struct xt_table_info *newinfo, *info;
1706         void *pos, *entry0, *entry1;
1707         unsigned int size;
1708         int ret;
1709
1710         info = *pinfo;
1711         entry0 = *pentry0;
1712         size = total_size;
1713         info->number = number;
1714
1715         /* Init all hooks to impossible value. */
1716         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1717                 info->hook_entry[i] = 0xFFFFFFFF;
1718                 info->underflow[i] = 0xFFFFFFFF;
1719         }
1720
1721         duprintf("translate_compat_table: size %u\n", info->size);
1722         j = 0;
1723         xt_compat_lock(AF_INET6);
1724         /* Walk through entries, checking offsets. */
1725         ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1726                                         check_compat_entry_size_and_hooks,
1727                                         info, &size, entry0,
1728                                         entry0 + total_size,
1729                                         hook_entries, underflows, &j, name);
1730         if (ret != 0)
1731                 goto out_unlock;
1732
1733         ret = -EINVAL;
1734         if (j != number) {
1735                 duprintf("translate_compat_table: %u not %u entries\n",
1736                          j, number);
1737                 goto out_unlock;
1738         }
1739
1740         /* Check hooks all assigned */
1741         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1742                 /* Only hooks which are valid */
1743                 if (!(valid_hooks & (1 << i)))
1744                         continue;
1745                 if (info->hook_entry[i] == 0xFFFFFFFF) {
1746                         duprintf("Invalid hook entry %u %u\n",
1747                                  i, hook_entries[i]);
1748                         goto out_unlock;
1749                 }
1750                 if (info->underflow[i] == 0xFFFFFFFF) {
1751                         duprintf("Invalid underflow %u %u\n",
1752                                  i, underflows[i]);
1753                         goto out_unlock;
1754                 }
1755         }
1756
1757         ret = -ENOMEM;
1758         newinfo = xt_alloc_table_info(size);
1759         if (!newinfo)
1760                 goto out_unlock;
1761
1762         newinfo->number = number;
1763         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1764                 newinfo->hook_entry[i] = info->hook_entry[i];
1765                 newinfo->underflow[i] = info->underflow[i];
1766         }
1767         entry1 = newinfo->entries[raw_smp_processor_id()];
1768         pos = entry1;
1769         size = total_size;
1770         ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1771                                         compat_copy_entry_from_user,
1772                                         &pos, &size, name, newinfo, entry1);
1773         xt_compat_flush_offsets(AF_INET6);
1774         xt_compat_unlock(AF_INET6);
1775         if (ret)
1776                 goto free_newinfo;
1777
1778         ret = -ELOOP;
1779         if (!mark_source_chains(newinfo, valid_hooks, entry1))
1780                 goto free_newinfo;
1781
1782         i = 0;
1783         ret = IP6T_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
1784                                  name, &i);
1785         if (ret) {
1786                 j -= i;
1787                 COMPAT_IP6T_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
1788                                                    compat_release_entry, &j);
1789                 IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i);
1790                 xt_free_table_info(newinfo);
1791                 return ret;
1792         }
1793
1794         /* And one copy for every other CPU */
1795         for_each_possible_cpu(i)
1796                 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1797                         memcpy(newinfo->entries[i], entry1, newinfo->size);
1798
1799         *pinfo = newinfo;
1800         *pentry0 = entry1;
1801         xt_free_table_info(info);
1802         return 0;
1803
1804 free_newinfo:
1805         xt_free_table_info(newinfo);
1806 out:
1807         COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size, compat_release_entry, &j);
1808         return ret;
1809 out_unlock:
1810         xt_compat_flush_offsets(AF_INET6);
1811         xt_compat_unlock(AF_INET6);
1812         goto out;
1813 }
1814
1815 static int
1816 compat_do_replace(struct net *net, void __user *user, unsigned int len)
1817 {
1818         int ret;
1819         struct compat_ip6t_replace tmp;
1820         struct xt_table_info *newinfo;
1821         void *loc_cpu_entry;
1822
1823         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1824                 return -EFAULT;
1825
1826         /* overflow check */
1827         if (tmp.size >= INT_MAX / num_possible_cpus())
1828                 return -ENOMEM;
1829         if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1830                 return -ENOMEM;
1831
1832         newinfo = xt_alloc_table_info(tmp.size);
1833         if (!newinfo)
1834                 return -ENOMEM;
1835
1836         /* choose the copy that is on our node/cpu */
1837         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1838         if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1839                            tmp.size) != 0) {
1840                 ret = -EFAULT;
1841                 goto free_newinfo;
1842         }
1843
1844         ret = translate_compat_table(tmp.name, tmp.valid_hooks,
1845                                      &newinfo, &loc_cpu_entry, tmp.size,
1846                                      tmp.num_entries, tmp.hook_entry,
1847                                      tmp.underflow);
1848         if (ret != 0)
1849                 goto free_newinfo;
1850
1851         duprintf("compat_do_replace: Translated table\n");
1852
1853         ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
1854                            tmp.num_counters, compat_ptr(tmp.counters));
1855         if (ret)
1856                 goto free_newinfo_untrans;
1857         return 0;
1858
1859  free_newinfo_untrans:
1860         IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
1861  free_newinfo:
1862         xt_free_table_info(newinfo);
1863         return ret;
1864 }
1865
1866 static int
1867 compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1868                        unsigned int len)
1869 {
1870         int ret;
1871
1872         if (!capable(CAP_NET_ADMIN))
1873                 return -EPERM;
1874
1875         switch (cmd) {
1876         case IP6T_SO_SET_REPLACE:
1877                 ret = compat_do_replace(sock_net(sk), user, len);
1878                 break;
1879
1880         case IP6T_SO_SET_ADD_COUNTERS:
1881                 ret = do_add_counters(sock_net(sk), user, len, 1);
1882                 break;
1883
1884         default:
1885                 duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1886                 ret = -EINVAL;
1887         }
1888
1889         return ret;
1890 }
1891
1892 struct compat_ip6t_get_entries {
1893         char name[IP6T_TABLE_MAXNAMELEN];
1894         compat_uint_t size;
1895         struct compat_ip6t_entry entrytable[0];
1896 };
1897
1898 static int
1899 compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1900                             void __user *userptr)
1901 {
1902         struct xt_counters *counters;
1903         const struct xt_table_info *private = table->private;
1904         void __user *pos;
1905         unsigned int size;
1906         int ret = 0;
1907         const void *loc_cpu_entry;
1908         unsigned int i = 0;
1909
1910         counters = alloc_counters(table);
1911         if (IS_ERR(counters))
1912                 return PTR_ERR(counters);
1913
1914         /* choose the copy that is on our node/cpu, ...
1915          * This choice is lazy (because current thread is
1916          * allowed to migrate to another cpu)
1917          */
1918         loc_cpu_entry = private->entries[raw_smp_processor_id()];
1919         pos = userptr;
1920         size = total_size;
1921         ret = IP6T_ENTRY_ITERATE(loc_cpu_entry, total_size,
1922                                  compat_copy_entry_to_user,
1923                                  &pos, &size, counters, &i);
1924
1925         vfree(counters);
1926         return ret;
1927 }
1928
1929 static int
1930 compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1931                    int *len)
1932 {
1933         int ret;
1934         struct compat_ip6t_get_entries get;
1935         struct xt_table *t;
1936
1937         if (*len < sizeof(get)) {
1938                 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
1939                 return -EINVAL;
1940         }
1941
1942         if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1943                 return -EFAULT;
1944
1945         if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
1946                 duprintf("compat_get_entries: %u != %zu\n",
1947                          *len, sizeof(get) + get.size);
1948                 return -EINVAL;
1949         }
1950
1951         xt_compat_lock(AF_INET6);
1952         t = xt_find_table_lock(net, AF_INET6, get.name);
1953         if (t && !IS_ERR(t)) {
1954                 const struct xt_table_info *private = t->private;
1955                 struct xt_table_info info;
1956                 duprintf("t->private->number = %u\n", private->number);
1957                 ret = compat_table_info(private, &info);
1958                 if (!ret && get.size == info.size) {
1959                         ret = compat_copy_entries_to_user(private->size,
1960                                                           t, uptr->entrytable);
1961                 } else if (!ret) {
1962                         duprintf("compat_get_entries: I've got %u not %u!\n",
1963                                  private->size, get.size);
1964                         ret = -EAGAIN;
1965                 }
1966                 xt_compat_flush_offsets(AF_INET6);
1967                 module_put(t->me);
1968                 xt_table_unlock(t);
1969         } else
1970                 ret = t ? PTR_ERR(t) : -ENOENT;
1971
1972         xt_compat_unlock(AF_INET6);
1973         return ret;
1974 }
1975
1976 static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1977
1978 static int
1979 compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1980 {
1981         int ret;
1982
1983         if (!capable(CAP_NET_ADMIN))
1984                 return -EPERM;
1985
1986         switch (cmd) {
1987         case IP6T_SO_GET_INFO:
1988                 ret = get_info(sock_net(sk), user, len, 1);
1989                 break;
1990         case IP6T_SO_GET_ENTRIES:
1991                 ret = compat_get_entries(sock_net(sk), user, len);
1992                 break;
1993         default:
1994                 ret = do_ip6t_get_ctl(sk, cmd, user, len);
1995         }
1996         return ret;
1997 }
1998 #endif
1999
2000 static int
2001 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2002 {
2003         int ret;
2004
2005         if (!capable(CAP_NET_ADMIN))
2006                 return -EPERM;
2007
2008         switch (cmd) {
2009         case IP6T_SO_SET_REPLACE:
2010                 ret = do_replace(sock_net(sk), user, len);
2011                 break;
2012
2013         case IP6T_SO_SET_ADD_COUNTERS:
2014                 ret = do_add_counters(sock_net(sk), user, len, 0);
2015                 break;
2016
2017         default:
2018                 duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
2019                 ret = -EINVAL;
2020         }
2021
2022         return ret;
2023 }
2024
2025 static int
2026 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2027 {
2028         int ret;
2029
2030         if (!capable(CAP_NET_ADMIN))
2031                 return -EPERM;
2032
2033         switch (cmd) {
2034         case IP6T_SO_GET_INFO:
2035                 ret = get_info(sock_net(sk), user, len, 0);
2036                 break;
2037
2038         case IP6T_SO_GET_ENTRIES:
2039                 ret = get_entries(sock_net(sk), user, len);
2040                 break;
2041
2042         case IP6T_SO_GET_REVISION_MATCH:
2043         case IP6T_SO_GET_REVISION_TARGET: {
2044                 struct ip6t_get_revision rev;
2045                 int target;
2046
2047                 if (*len != sizeof(rev)) {
2048                         ret = -EINVAL;
2049                         break;
2050                 }
2051                 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2052                         ret = -EFAULT;
2053                         break;
2054                 }
2055
2056                 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2057                         target = 1;
2058                 else
2059                         target = 0;
2060
2061                 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2062                                                          rev.revision,
2063                                                          target, &ret),
2064                                         "ip6t_%s", rev.name);
2065                 break;
2066         }
2067
2068         default:
2069                 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2070                 ret = -EINVAL;
2071         }
2072
2073         return ret;
2074 }
2075
2076 struct xt_table *ip6t_register_table(struct net *net, struct xt_table *table,
2077                                      const struct ip6t_replace *repl)
2078 {
2079         int ret;
2080         struct xt_table_info *newinfo;
2081         struct xt_table_info bootstrap
2082                 = { 0, 0, 0, { 0 }, { 0 }, { } };
2083         void *loc_cpu_entry;
2084         struct xt_table *new_table;
2085
2086         newinfo = xt_alloc_table_info(repl->size);
2087         if (!newinfo) {
2088                 ret = -ENOMEM;
2089                 goto out;
2090         }
2091
2092         /* choose the copy on our node/cpu, but dont care about preemption */
2093         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2094         memcpy(loc_cpu_entry, repl->entries, repl->size);
2095
2096         ret = translate_table(table->name, table->valid_hooks,
2097                               newinfo, loc_cpu_entry, repl->size,
2098                               repl->num_entries,
2099                               repl->hook_entry,
2100                               repl->underflow);
2101         if (ret != 0)
2102                 goto out_free;
2103
2104         new_table = xt_register_table(net, table, &bootstrap, newinfo);
2105         if (IS_ERR(new_table)) {
2106                 ret = PTR_ERR(new_table);
2107                 goto out_free;
2108         }
2109         return new_table;
2110
2111 out_free:
2112         xt_free_table_info(newinfo);
2113 out:
2114         return ERR_PTR(ret);
2115 }
2116
2117 void ip6t_unregister_table(struct xt_table *table)
2118 {
2119         struct xt_table_info *private;
2120         void *loc_cpu_entry;
2121         struct module *table_owner = table->me;
2122
2123         private = xt_unregister_table(table);
2124
2125         /* Decrease module usage counts and free resources */
2126         loc_cpu_entry = private->entries[raw_smp_processor_id()];
2127         IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
2128         if (private->number > private->initial_entries)
2129                 module_put(table_owner);
2130         xt_free_table_info(private);
2131 }
2132
2133 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
2134 static inline bool
2135 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2136                      u_int8_t type, u_int8_t code,
2137                      bool invert)
2138 {
2139         return (type == test_type && code >= min_code && code <= max_code)
2140                 ^ invert;
2141 }
2142
2143 static bool
2144 icmp6_match(const struct sk_buff *skb, const struct xt_match_param *par)
2145 {
2146         const struct icmp6hdr *ic;
2147         struct icmp6hdr _icmph;
2148         const struct ip6t_icmp *icmpinfo = par->matchinfo;
2149
2150         /* Must not be a fragment. */
2151         if (par->fragoff != 0)
2152                 return false;
2153
2154         ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
2155         if (ic == NULL) {
2156                 /* We've been asked to examine this packet, and we
2157                  * can't.  Hence, no choice but to drop.
2158                  */
2159                 duprintf("Dropping evil ICMP tinygram.\n");
2160                 *par->hotdrop = true;
2161                 return false;
2162         }
2163
2164         return icmp6_type_code_match(icmpinfo->type,
2165                                      icmpinfo->code[0],
2166                                      icmpinfo->code[1],
2167                                      ic->icmp6_type, ic->icmp6_code,
2168                                      !!(icmpinfo->invflags&IP6T_ICMP_INV));
2169 }
2170
2171 /* Called when user tries to insert an entry of this type. */
2172 static bool icmp6_checkentry(const struct xt_mtchk_param *par)
2173 {
2174         const struct ip6t_icmp *icmpinfo = par->matchinfo;
2175
2176         /* Must specify no unknown invflags */
2177         return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
2178 }
2179
2180 /* The built-in targets: standard (NULL) and error. */
2181 static struct xt_target ip6t_standard_target __read_mostly = {
2182         .name           = IP6T_STANDARD_TARGET,
2183         .targetsize     = sizeof(int),
2184         .family         = AF_INET6,
2185 #ifdef CONFIG_COMPAT
2186         .compatsize     = sizeof(compat_int_t),
2187         .compat_from_user = compat_standard_from_user,
2188         .compat_to_user = compat_standard_to_user,
2189 #endif
2190 };
2191
2192 static struct xt_target ip6t_error_target __read_mostly = {
2193         .name           = IP6T_ERROR_TARGET,
2194         .target         = ip6t_error,
2195         .targetsize     = IP6T_FUNCTION_MAXNAMELEN,
2196         .family         = AF_INET6,
2197 };
2198
2199 static struct nf_sockopt_ops ip6t_sockopts = {
2200         .pf             = PF_INET6,
2201         .set_optmin     = IP6T_BASE_CTL,
2202         .set_optmax     = IP6T_SO_SET_MAX+1,
2203         .set            = do_ip6t_set_ctl,
2204 #ifdef CONFIG_COMPAT
2205         .compat_set     = compat_do_ip6t_set_ctl,
2206 #endif
2207         .get_optmin     = IP6T_BASE_CTL,
2208         .get_optmax     = IP6T_SO_GET_MAX+1,
2209         .get            = do_ip6t_get_ctl,
2210 #ifdef CONFIG_COMPAT
2211         .compat_get     = compat_do_ip6t_get_ctl,
2212 #endif
2213         .owner          = THIS_MODULE,
2214 };
2215
2216 static struct xt_match icmp6_matchstruct __read_mostly = {
2217         .name           = "icmp6",
2218         .match          = icmp6_match,
2219         .matchsize      = sizeof(struct ip6t_icmp),
2220         .checkentry     = icmp6_checkentry,
2221         .proto          = IPPROTO_ICMPV6,
2222         .family         = AF_INET6,
2223 };
2224
2225 static int __net_init ip6_tables_net_init(struct net *net)
2226 {
2227         return xt_proto_init(net, AF_INET6);
2228 }
2229
2230 static void __net_exit ip6_tables_net_exit(struct net *net)
2231 {
2232         xt_proto_fini(net, AF_INET6);
2233 }
2234
2235 static struct pernet_operations ip6_tables_net_ops = {
2236         .init = ip6_tables_net_init,
2237         .exit = ip6_tables_net_exit,
2238 };
2239
2240 static int __init ip6_tables_init(void)
2241 {
2242         int ret;
2243
2244         ret = register_pernet_subsys(&ip6_tables_net_ops);
2245         if (ret < 0)
2246                 goto err1;
2247
2248         /* Noone else will be downing sem now, so we won't sleep */
2249         ret = xt_register_target(&ip6t_standard_target);
2250         if (ret < 0)
2251                 goto err2;
2252         ret = xt_register_target(&ip6t_error_target);
2253         if (ret < 0)
2254                 goto err3;
2255         ret = xt_register_match(&icmp6_matchstruct);
2256         if (ret < 0)
2257                 goto err4;
2258
2259         /* Register setsockopt */
2260         ret = nf_register_sockopt(&ip6t_sockopts);
2261         if (ret < 0)
2262                 goto err5;
2263
2264         printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
2265         return 0;
2266
2267 err5:
2268         xt_unregister_match(&icmp6_matchstruct);
2269 err4:
2270         xt_unregister_target(&ip6t_error_target);
2271 err3:
2272         xt_unregister_target(&ip6t_standard_target);
2273 err2:
2274         unregister_pernet_subsys(&ip6_tables_net_ops);
2275 err1:
2276         return ret;
2277 }
2278
2279 static void __exit ip6_tables_fini(void)
2280 {
2281         nf_unregister_sockopt(&ip6t_sockopts);
2282
2283         xt_unregister_match(&icmp6_matchstruct);
2284         xt_unregister_target(&ip6t_error_target);
2285         xt_unregister_target(&ip6t_standard_target);
2286
2287         unregister_pernet_subsys(&ip6_tables_net_ops);
2288 }
2289
2290 /*
2291  * find the offset to specified header or the protocol number of last header
2292  * if target < 0. "last header" is transport protocol header, ESP, or
2293  * "No next header".
2294  *
2295  * If target header is found, its offset is set in *offset and return protocol
2296  * number. Otherwise, return -1.
2297  *
2298  * If the first fragment doesn't contain the final protocol header or
2299  * NEXTHDR_NONE it is considered invalid.
2300  *
2301  * Note that non-1st fragment is special case that "the protocol number
2302  * of last header" is "next header" field in Fragment header. In this case,
2303  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
2304  * isn't NULL.
2305  *
2306  */
2307 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2308                   int target, unsigned short *fragoff)
2309 {
2310         unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
2311         u8 nexthdr = ipv6_hdr(skb)->nexthdr;
2312         unsigned int len = skb->len - start;
2313
2314         if (fragoff)
2315                 *fragoff = 0;
2316
2317         while (nexthdr != target) {
2318                 struct ipv6_opt_hdr _hdr, *hp;
2319                 unsigned int hdrlen;
2320
2321                 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2322                         if (target < 0)
2323                                 break;
2324                         return -ENOENT;
2325                 }
2326
2327                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2328                 if (hp == NULL)
2329                         return -EBADMSG;
2330                 if (nexthdr == NEXTHDR_FRAGMENT) {
2331                         unsigned short _frag_off;
2332                         __be16 *fp;
2333                         fp = skb_header_pointer(skb,
2334                                                 start+offsetof(struct frag_hdr,
2335                                                                frag_off),
2336                                                 sizeof(_frag_off),
2337                                                 &_frag_off);
2338                         if (fp == NULL)
2339                                 return -EBADMSG;
2340
2341                         _frag_off = ntohs(*fp) & ~0x7;
2342                         if (_frag_off) {
2343                                 if (target < 0 &&
2344                                     ((!ipv6_ext_hdr(hp->nexthdr)) ||
2345                                      hp->nexthdr == NEXTHDR_NONE)) {
2346                                         if (fragoff)
2347                                                 *fragoff = _frag_off;
2348                                         return hp->nexthdr;
2349                                 }
2350                                 return -ENOENT;
2351                         }
2352                         hdrlen = 8;
2353                 } else if (nexthdr == NEXTHDR_AUTH)
2354                         hdrlen = (hp->hdrlen + 2) << 2;
2355                 else
2356                         hdrlen = ipv6_optlen(hp);
2357
2358                 nexthdr = hp->nexthdr;
2359                 len -= hdrlen;
2360                 start += hdrlen;
2361         }
2362
2363         *offset = start;
2364         return nexthdr;
2365 }
2366
2367 EXPORT_SYMBOL(ip6t_register_table);
2368 EXPORT_SYMBOL(ip6t_unregister_table);
2369 EXPORT_SYMBOL(ip6t_do_table);
2370 EXPORT_SYMBOL(ip6t_ext_hdr);
2371 EXPORT_SYMBOL(ipv6_find_hdr);
2372
2373 module_init(ip6_tables_init);
2374 module_exit(ip6_tables_fini);