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