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