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