[NETFILTER]: ip6_tables: resync get_entries() with ip_tables
[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 <asm/uaccess.h>
23 #include <linux/mutex.h>
24 #include <linux/proc_fs.h>
25 #include <linux/cpumask.h>
26
27 #include <linux/netfilter_ipv6/ip6_tables.h>
28 #include <linux/netfilter/x_tables.h>
29
30 MODULE_LICENSE("GPL");
31 MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
32 MODULE_DESCRIPTION("IPv6 packet filter");
33
34 /*#define DEBUG_IP_FIREWALL*/
35 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
36 /*#define DEBUG_IP_FIREWALL_USER*/
37
38 #ifdef DEBUG_IP_FIREWALL
39 #define dprintf(format, args...)  printk(format , ## args)
40 #else
41 #define dprintf(format, args...)
42 #endif
43
44 #ifdef DEBUG_IP_FIREWALL_USER
45 #define duprintf(format, args...) printk(format , ## args)
46 #else
47 #define duprintf(format, args...)
48 #endif
49
50 #ifdef CONFIG_NETFILTER_DEBUG
51 #define IP_NF_ASSERT(x)                                         \
52 do {                                                            \
53         if (!(x))                                               \
54                 printk("IP_NF_ASSERT: %s:%s:%u\n",              \
55                        __FUNCTION__, __FILE__, __LINE__);       \
56 } while(0)
57 #else
58 #define IP_NF_ASSERT(x)
59 #endif
60
61 #if 0
62 /* All the better to debug you with... */
63 #define static
64 #define inline
65 #endif
66
67 /*
68    We keep a set of rules for each CPU, so we can avoid write-locking
69    them in the softirq when updating the counters and therefore
70    only need to read-lock in the softirq; doing a write_lock_bh() in user
71    context stops packets coming through and allows user context to read
72    the counters or update the rules.
73
74    Hence the start of any table is given by get_table() below.  */
75
76 /* Check for an extension */
77 int
78 ip6t_ext_hdr(u8 nexthdr)
79 {
80         return ( (nexthdr == IPPROTO_HOPOPTS)   ||
81                  (nexthdr == IPPROTO_ROUTING)   ||
82                  (nexthdr == IPPROTO_FRAGMENT)  ||
83                  (nexthdr == IPPROTO_ESP)       ||
84                  (nexthdr == IPPROTO_AH)        ||
85                  (nexthdr == IPPROTO_NONE)      ||
86                  (nexthdr == IPPROTO_DSTOPTS) );
87 }
88
89 /* Returns whether matches rule or not. */
90 static inline bool
91 ip6_packet_match(const struct sk_buff *skb,
92                  const char *indev,
93                  const char *outdev,
94                  const struct ip6t_ip6 *ip6info,
95                  unsigned int *protoff,
96                  int *fragoff, bool *hotdrop)
97 {
98         size_t i;
99         unsigned long ret;
100         const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
101
102 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
103
104         if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
105                                        &ip6info->src), IP6T_INV_SRCIP)
106             || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
107                                           &ip6info->dst), IP6T_INV_DSTIP)) {
108                 dprintf("Source or dest mismatch.\n");
109 /*
110                 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
111                         ipinfo->smsk.s_addr, ipinfo->src.s_addr,
112                         ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
113                 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
114                         ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
115                         ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
116                 return false;
117         }
118
119         /* Look for ifname matches; this should unroll nicely. */
120         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
121                 ret |= (((const unsigned long *)indev)[i]
122                         ^ ((const unsigned long *)ip6info->iniface)[i])
123                         & ((const unsigned long *)ip6info->iniface_mask)[i];
124         }
125
126         if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
127                 dprintf("VIA in mismatch (%s vs %s).%s\n",
128                         indev, ip6info->iniface,
129                         ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
130                 return false;
131         }
132
133         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
134                 ret |= (((const unsigned long *)outdev)[i]
135                         ^ ((const unsigned long *)ip6info->outiface)[i])
136                         & ((const unsigned long *)ip6info->outiface_mask)[i];
137         }
138
139         if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
140                 dprintf("VIA out mismatch (%s vs %s).%s\n",
141                         outdev, ip6info->outiface,
142                         ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
143                 return false;
144         }
145
146 /* ... might want to do something with class and flowlabel here ... */
147
148         /* look for the desired protocol header */
149         if((ip6info->flags & IP6T_F_PROTO)) {
150                 int protohdr;
151                 unsigned short _frag_off;
152
153                 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
154                 if (protohdr < 0) {
155                         if (_frag_off == 0)
156                                 *hotdrop = true;
157                         return false;
158                 }
159                 *fragoff = _frag_off;
160
161                 dprintf("Packet protocol %hi ?= %s%hi.\n",
162                                 protohdr,
163                                 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
164                                 ip6info->proto);
165
166                 if (ip6info->proto == protohdr) {
167                         if(ip6info->invflags & IP6T_INV_PROTO) {
168                                 return false;
169                         }
170                         return true;
171                 }
172
173                 /* We need match for the '-p all', too! */
174                 if ((ip6info->proto != 0) &&
175                         !(ip6info->invflags & IP6T_INV_PROTO))
176                         return false;
177         }
178         return true;
179 }
180
181 /* should be ip6 safe */
182 static inline bool
183 ip6_checkentry(const struct ip6t_ip6 *ipv6)
184 {
185         if (ipv6->flags & ~IP6T_F_MASK) {
186                 duprintf("Unknown flag bits set: %08X\n",
187                          ipv6->flags & ~IP6T_F_MASK);
188                 return false;
189         }
190         if (ipv6->invflags & ~IP6T_INV_MASK) {
191                 duprintf("Unknown invflag bits set: %08X\n",
192                          ipv6->invflags & ~IP6T_INV_MASK);
193                 return false;
194         }
195         return true;
196 }
197
198 static unsigned int
199 ip6t_error(struct sk_buff *skb,
200           const struct net_device *in,
201           const struct net_device *out,
202           unsigned int hooknum,
203           const struct xt_target *target,
204           const void *targinfo)
205 {
206         if (net_ratelimit())
207                 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
208
209         return NF_DROP;
210 }
211
212 static inline
213 bool do_match(struct ip6t_entry_match *m,
214               const struct sk_buff *skb,
215               const struct net_device *in,
216               const struct net_device *out,
217               int offset,
218               unsigned int protoff,
219               bool *hotdrop)
220 {
221         /* Stop iteration if it doesn't match */
222         if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
223                                       offset, protoff, hotdrop))
224                 return true;
225         else
226                 return false;
227 }
228
229 static inline struct ip6t_entry *
230 get_entry(void *base, unsigned int offset)
231 {
232         return (struct ip6t_entry *)(base + offset);
233 }
234
235 /* All zeroes == unconditional rule. */
236 static inline int
237 unconditional(const struct ip6t_ip6 *ipv6)
238 {
239         unsigned int i;
240
241         for (i = 0; i < sizeof(*ipv6); i++)
242                 if (((char *)ipv6)[i])
243                         break;
244
245         return (i == sizeof(*ipv6));
246 }
247
248 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
249     defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
250 /* This cries for unification! */
251 static const char *hooknames[] = {
252         [NF_INET_PRE_ROUTING]           = "PREROUTING",
253         [NF_INET_LOCAL_IN]              = "INPUT",
254         [NF_INET_FORWARD]               = "FORWARD",
255         [NF_INET_LOCAL_OUT]             = "OUTPUT",
256         [NF_INET_POST_ROUTING]          = "POSTROUTING",
257 };
258
259 enum nf_ip_trace_comments {
260         NF_IP6_TRACE_COMMENT_RULE,
261         NF_IP6_TRACE_COMMENT_RETURN,
262         NF_IP6_TRACE_COMMENT_POLICY,
263 };
264
265 static const char *comments[] = {
266         [NF_IP6_TRACE_COMMENT_RULE]     = "rule",
267         [NF_IP6_TRACE_COMMENT_RETURN]   = "return",
268         [NF_IP6_TRACE_COMMENT_POLICY]   = "policy",
269 };
270
271 static struct nf_loginfo trace_loginfo = {
272         .type = NF_LOG_TYPE_LOG,
273         .u = {
274                 .log = {
275                         .level = 4,
276                         .logflags = NF_LOG_MASK,
277                 },
278         },
279 };
280
281 static inline int
282 get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
283                       char *hookname, char **chainname,
284                       char **comment, unsigned int *rulenum)
285 {
286         struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
287
288         if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
289                 /* Head of user chain: ERROR target with chainname */
290                 *chainname = t->target.data;
291                 (*rulenum) = 0;
292         } else if (s == e) {
293                 (*rulenum)++;
294
295                 if (s->target_offset == sizeof(struct ip6t_entry)
296                    && strcmp(t->target.u.kernel.target->name,
297                              IP6T_STANDARD_TARGET) == 0
298                    && t->verdict < 0
299                    && unconditional(&s->ipv6)) {
300                         /* Tail of chains: STANDARD target (return/policy) */
301                         *comment = *chainname == hookname
302                                 ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
303                                 : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
304                 }
305                 return 1;
306         } else
307                 (*rulenum)++;
308
309         return 0;
310 }
311
312 static void trace_packet(struct sk_buff *skb,
313                          unsigned int hook,
314                          const struct net_device *in,
315                          const struct net_device *out,
316                          char *tablename,
317                          struct xt_table_info *private,
318                          struct ip6t_entry *e)
319 {
320         void *table_base;
321         struct ip6t_entry *root;
322         char *hookname, *chainname, *comment;
323         unsigned int rulenum = 0;
324
325         table_base = (void *)private->entries[smp_processor_id()];
326         root = get_entry(table_base, private->hook_entry[hook]);
327
328         hookname = chainname = (char *)hooknames[hook];
329         comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
330
331         IP6T_ENTRY_ITERATE(root,
332                            private->size - private->hook_entry[hook],
333                            get_chainname_rulenum,
334                            e, hookname, &chainname, &comment, &rulenum);
335
336         nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
337                       "TRACE: %s:%s:%s:%u ",
338                       tablename, chainname, comment, rulenum);
339 }
340 #endif
341
342 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
343 unsigned int
344 ip6t_do_table(struct sk_buff *skb,
345               unsigned int hook,
346               const struct net_device *in,
347               const struct net_device *out,
348               struct xt_table *table)
349 {
350         static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
351         int offset = 0;
352         unsigned int protoff = 0;
353         bool hotdrop = false;
354         /* Initializing verdict to NF_DROP keeps gcc happy. */
355         unsigned int verdict = NF_DROP;
356         const char *indev, *outdev;
357         void *table_base;
358         struct ip6t_entry *e, *back;
359         struct xt_table_info *private;
360
361         /* Initialization */
362         indev = in ? in->name : nulldevname;
363         outdev = out ? out->name : nulldevname;
364         /* We handle fragments by dealing with the first fragment as
365          * if it was a normal packet.  All other fragments are treated
366          * normally, except that they will NEVER match rules that ask
367          * things we don't know, ie. tcp syn flag or ports).  If the
368          * rule is also a fragment-specific rule, non-fragments won't
369          * match it. */
370
371         read_lock_bh(&table->lock);
372         private = table->private;
373         IP_NF_ASSERT(table->valid_hooks & (1 << hook));
374         table_base = (void *)private->entries[smp_processor_id()];
375         e = get_entry(table_base, private->hook_entry[hook]);
376
377         /* For return from builtin chain */
378         back = get_entry(table_base, private->underflow[hook]);
379
380         do {
381                 IP_NF_ASSERT(e);
382                 IP_NF_ASSERT(back);
383                 if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
384                         &protoff, &offset, &hotdrop)) {
385                         struct ip6t_entry_target *t;
386
387                         if (IP6T_MATCH_ITERATE(e, do_match,
388                                                skb, in, out,
389                                                offset, protoff, &hotdrop) != 0)
390                                 goto no_match;
391
392                         ADD_COUNTER(e->counters,
393                                     ntohs(ipv6_hdr(skb)->payload_len) +
394                                     sizeof(struct ipv6hdr), 1);
395
396                         t = ip6t_get_target(e);
397                         IP_NF_ASSERT(t->u.kernel.target);
398
399 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
400     defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
401                         /* The packet is traced: log it */
402                         if (unlikely(skb->nf_trace))
403                                 trace_packet(skb, hook, in, out,
404                                              table->name, private, e);
405 #endif
406                         /* Standard target? */
407                         if (!t->u.kernel.target->target) {
408                                 int v;
409
410                                 v = ((struct ip6t_standard_target *)t)->verdict;
411                                 if (v < 0) {
412                                         /* Pop from stack? */
413                                         if (v != IP6T_RETURN) {
414                                                 verdict = (unsigned)(-v) - 1;
415                                                 break;
416                                         }
417                                         e = back;
418                                         back = get_entry(table_base,
419                                                          back->comefrom);
420                                         continue;
421                                 }
422                                 if (table_base + v != (void *)e + e->next_offset
423                                     && !(e->ipv6.flags & IP6T_F_GOTO)) {
424                                         /* Save old back ptr in next entry */
425                                         struct ip6t_entry *next
426                                                 = (void *)e + e->next_offset;
427                                         next->comefrom
428                                                 = (void *)back - table_base;
429                                         /* set back pointer to next entry */
430                                         back = next;
431                                 }
432
433                                 e = get_entry(table_base, v);
434                         } else {
435                                 /* Targets which reenter must return
436                                    abs. verdicts */
437 #ifdef CONFIG_NETFILTER_DEBUG
438                                 ((struct ip6t_entry *)table_base)->comefrom
439                                         = 0xeeeeeeec;
440 #endif
441                                 verdict = t->u.kernel.target->target(skb,
442                                                                      in, out,
443                                                                      hook,
444                                                                      t->u.kernel.target,
445                                                                      t->data);
446
447 #ifdef CONFIG_NETFILTER_DEBUG
448                                 if (((struct ip6t_entry *)table_base)->comefrom
449                                     != 0xeeeeeeec
450                                     && verdict == IP6T_CONTINUE) {
451                                         printk("Target %s reentered!\n",
452                                                t->u.kernel.target->name);
453                                         verdict = NF_DROP;
454                                 }
455                                 ((struct ip6t_entry *)table_base)->comefrom
456                                         = 0x57acc001;
457 #endif
458                                 if (verdict == IP6T_CONTINUE)
459                                         e = (void *)e + e->next_offset;
460                                 else
461                                         /* Verdict */
462                                         break;
463                         }
464                 } else {
465
466                 no_match:
467                         e = (void *)e + e->next_offset;
468                 }
469         } while (!hotdrop);
470
471 #ifdef CONFIG_NETFILTER_DEBUG
472         ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
473 #endif
474         read_unlock_bh(&table->lock);
475
476 #ifdef DEBUG_ALLOW_ALL
477         return NF_ACCEPT;
478 #else
479         if (hotdrop)
480                 return NF_DROP;
481         else return verdict;
482 #endif
483 }
484
485 /* Figures out from what hook each rule can be called: returns 0 if
486    there are loops.  Puts hook bitmask in comefrom. */
487 static int
488 mark_source_chains(struct xt_table_info *newinfo,
489                    unsigned int valid_hooks, void *entry0)
490 {
491         unsigned int hook;
492
493         /* No recursion; use packet counter to save back ptrs (reset
494            to 0 as we leave), and comefrom to save source hook bitmask */
495         for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
496                 unsigned int pos = newinfo->hook_entry[hook];
497                 struct ip6t_entry *e
498                         = (struct ip6t_entry *)(entry0 + pos);
499                 int visited = e->comefrom & (1 << hook);
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
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
517                                 |= ((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 0;
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 ourc node/cpu */
986         loc_cpu_entry = private->entries[raw_smp_processor_id()];
987         if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
988                 ret = -EFAULT;
989                 goto free_counters;
990         }
991
992         /* FIXME: use iterator macros --RR */
993         /* ... then go back and fix counters and names */
994         for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
995                 unsigned int i;
996                 struct ip6t_entry_match *m;
997                 struct ip6t_entry_target *t;
998
999                 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1000                 if (copy_to_user(userptr + off
1001                                  + offsetof(struct ip6t_entry, counters),
1002                                  &counters[num],
1003                                  sizeof(counters[num])) != 0) {
1004                         ret = -EFAULT;
1005                         goto free_counters;
1006                 }
1007
1008                 for (i = sizeof(struct ip6t_entry);
1009                      i < e->target_offset;
1010                      i += m->u.match_size) {
1011                         m = (void *)e + i;
1012
1013                         if (copy_to_user(userptr + off + i
1014                                          + offsetof(struct ip6t_entry_match,
1015                                                     u.user.name),
1016                                          m->u.kernel.match->name,
1017                                          strlen(m->u.kernel.match->name)+1)
1018                             != 0) {
1019                                 ret = -EFAULT;
1020                                 goto free_counters;
1021                         }
1022                 }
1023
1024                 t = ip6t_get_target(e);
1025                 if (copy_to_user(userptr + off + e->target_offset
1026                                  + offsetof(struct ip6t_entry_target,
1027                                             u.user.name),
1028                                  t->u.kernel.target->name,
1029                                  strlen(t->u.kernel.target->name)+1) != 0) {
1030                         ret = -EFAULT;
1031                         goto free_counters;
1032                 }
1033         }
1034
1035  free_counters:
1036         vfree(counters);
1037         return ret;
1038 }
1039
1040 static int get_info(void __user *user, int *len)
1041 {
1042         char name[IP6T_TABLE_MAXNAMELEN];
1043         struct xt_table *t;
1044         int ret;
1045
1046         if (*len != sizeof(struct ip6t_getinfo)) {
1047                 duprintf("length %u != %u\n", *len,
1048                          sizeof(struct ip6t_getinfo));
1049                 return -EINVAL;
1050         }
1051
1052         if (copy_from_user(name, user, sizeof(name)) != 0)
1053                 return -EFAULT;
1054
1055         name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1056
1057         t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1058                                     "ip6table_%s", name);
1059         if (t && !IS_ERR(t)) {
1060                 struct ip6t_getinfo info;
1061                 struct xt_table_info *private = t->private;
1062
1063                 info.valid_hooks = t->valid_hooks;
1064                 memcpy(info.hook_entry, private->hook_entry,
1065                        sizeof(info.hook_entry));
1066                 memcpy(info.underflow, private->underflow,
1067                        sizeof(info.underflow));
1068                 info.num_entries = private->number;
1069                 info.size = private->size;
1070                 memcpy(info.name, name, sizeof(info.name));
1071
1072                 if (copy_to_user(user, &info, *len) != 0)
1073                         ret = -EFAULT;
1074                 else
1075                         ret = 0;
1076
1077                 xt_table_unlock(t);
1078                 module_put(t->me);
1079         } else
1080                 ret = t ? PTR_ERR(t) : -ENOENT;
1081         return ret;
1082 }
1083
1084 static int
1085 get_entries(struct ip6t_get_entries __user *uptr, int *len)
1086 {
1087         int ret;
1088         struct ip6t_get_entries get;
1089         struct xt_table *t;
1090
1091         if (*len < sizeof(get)) {
1092                 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1093                 return -EINVAL;
1094         }
1095         if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1096                 return -EFAULT;
1097         if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1098                 duprintf("get_entries: %u != %u\n", *len,
1099                          sizeof(struct ip6t_get_entries) + get.size);
1100                 return -EINVAL;
1101         }
1102
1103         t = xt_find_table_lock(AF_INET6, get.name);
1104         if (t && !IS_ERR(t)) {
1105                 struct xt_table_info *private = t->private;
1106                 duprintf("t->private->number = %u\n", private->number);
1107                 if (get.size == private->size)
1108                         ret = copy_entries_to_user(private->size,
1109                                                    t, uptr->entrytable);
1110                 else {
1111                         duprintf("get_entries: I've got %u not %u!\n",
1112                                  private->size, entries->size);
1113                         ret = -EINVAL;
1114                 }
1115                 module_put(t->me);
1116                 xt_table_unlock(t);
1117         } else
1118                 ret = t ? PTR_ERR(t) : -ENOENT;
1119
1120         return ret;
1121 }
1122
1123 static int
1124 do_replace(void __user *user, unsigned int len)
1125 {
1126         int ret;
1127         struct ip6t_replace tmp;
1128         struct xt_table *t;
1129         struct xt_table_info *newinfo, *oldinfo;
1130         struct xt_counters *counters;
1131         void *loc_cpu_entry, *loc_cpu_old_entry;
1132
1133         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1134                 return -EFAULT;
1135
1136         /* overflow check */
1137         if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1138                 return -ENOMEM;
1139
1140         newinfo = xt_alloc_table_info(tmp.size);
1141         if (!newinfo)
1142                 return -ENOMEM;
1143
1144         /* choose the copy that is on our node/cpu */
1145         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1146         if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1147                            tmp.size) != 0) {
1148                 ret = -EFAULT;
1149                 goto free_newinfo;
1150         }
1151
1152         counters = vmalloc_node(tmp.num_counters * sizeof(struct xt_counters),
1153                                 numa_node_id());
1154         if (!counters) {
1155                 ret = -ENOMEM;
1156                 goto free_newinfo;
1157         }
1158
1159         ret = translate_table(tmp.name, tmp.valid_hooks,
1160                               newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1161                               tmp.hook_entry, tmp.underflow);
1162         if (ret != 0)
1163                 goto free_newinfo_counters;
1164
1165         duprintf("ip_tables: Translated table\n");
1166
1167         t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
1168                                     "ip6table_%s", tmp.name);
1169         if (!t || IS_ERR(t)) {
1170                 ret = t ? PTR_ERR(t) : -ENOENT;
1171                 goto free_newinfo_counters_untrans;
1172         }
1173
1174         /* You lied! */
1175         if (tmp.valid_hooks != t->valid_hooks) {
1176                 duprintf("Valid hook crap: %08X vs %08X\n",
1177                          tmp.valid_hooks, t->valid_hooks);
1178                 ret = -EINVAL;
1179                 goto put_module;
1180         }
1181
1182         oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1183         if (!oldinfo)
1184                 goto put_module;
1185
1186         /* Update module usage count based on number of rules */
1187         duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1188                 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1189         if ((oldinfo->number > oldinfo->initial_entries) ||
1190             (newinfo->number <= oldinfo->initial_entries))
1191                 module_put(t->me);
1192         if ((oldinfo->number > oldinfo->initial_entries) &&
1193             (newinfo->number <= oldinfo->initial_entries))
1194                 module_put(t->me);
1195
1196         /* Get the old counters. */
1197         get_counters(oldinfo, counters);
1198         /* Decrease module usage counts and free resource */
1199         loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1200         IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1201         xt_free_table_info(oldinfo);
1202         if (copy_to_user(tmp.counters, counters,
1203                          sizeof(struct xt_counters) * tmp.num_counters) != 0)
1204                 ret = -EFAULT;
1205         vfree(counters);
1206         xt_table_unlock(t);
1207         return ret;
1208
1209  put_module:
1210         module_put(t->me);
1211         xt_table_unlock(t);
1212  free_newinfo_counters_untrans:
1213         IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1214  free_newinfo_counters:
1215         vfree(counters);
1216  free_newinfo:
1217         xt_free_table_info(newinfo);
1218         return ret;
1219 }
1220
1221 /* We're lazy, and add to the first CPU; overflow works its fey magic
1222  * and everything is OK. */
1223 static inline int
1224 add_counter_to_entry(struct ip6t_entry *e,
1225                      const struct xt_counters addme[],
1226                      unsigned int *i)
1227 {
1228 #if 0
1229         duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1230                  *i,
1231                  (long unsigned int)e->counters.pcnt,
1232                  (long unsigned int)e->counters.bcnt,
1233                  (long unsigned int)addme[*i].pcnt,
1234                  (long unsigned int)addme[*i].bcnt);
1235 #endif
1236
1237         ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1238
1239         (*i)++;
1240         return 0;
1241 }
1242
1243 static int
1244 do_add_counters(void __user *user, unsigned int len)
1245 {
1246         unsigned int i;
1247         struct xt_counters_info tmp, *paddc;
1248         struct xt_table_info *private;
1249         struct xt_table *t;
1250         int ret = 0;
1251         void *loc_cpu_entry;
1252
1253         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1254                 return -EFAULT;
1255
1256         if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1257                 return -EINVAL;
1258
1259         paddc = vmalloc_node(len, numa_node_id());
1260         if (!paddc)
1261                 return -ENOMEM;
1262
1263         if (copy_from_user(paddc, user, len) != 0) {
1264                 ret = -EFAULT;
1265                 goto free;
1266         }
1267
1268         t = xt_find_table_lock(AF_INET6, tmp.name);
1269         if (!t || IS_ERR(t)) {
1270                 ret = t ? PTR_ERR(t) : -ENOENT;
1271                 goto free;
1272         }
1273
1274         write_lock_bh(&t->lock);
1275         private = t->private;
1276         if (private->number != tmp.num_counters) {
1277                 ret = -EINVAL;
1278                 goto unlock_up_free;
1279         }
1280
1281         i = 0;
1282         /* Choose the copy that is on our node */
1283         loc_cpu_entry = private->entries[smp_processor_id()];
1284         IP6T_ENTRY_ITERATE(loc_cpu_entry,
1285                           private->size,
1286                           add_counter_to_entry,
1287                           paddc->counters,
1288                           &i);
1289  unlock_up_free:
1290         write_unlock_bh(&t->lock);
1291         xt_table_unlock(t);
1292         module_put(t->me);
1293  free:
1294         vfree(paddc);
1295
1296         return ret;
1297 }
1298
1299 static int
1300 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1301 {
1302         int ret;
1303
1304         if (!capable(CAP_NET_ADMIN))
1305                 return -EPERM;
1306
1307         switch (cmd) {
1308         case IP6T_SO_SET_REPLACE:
1309                 ret = do_replace(user, len);
1310                 break;
1311
1312         case IP6T_SO_SET_ADD_COUNTERS:
1313                 ret = do_add_counters(user, len);
1314                 break;
1315
1316         default:
1317                 duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1318                 ret = -EINVAL;
1319         }
1320
1321         return ret;
1322 }
1323
1324 static int
1325 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1326 {
1327         int ret;
1328
1329         if (!capable(CAP_NET_ADMIN))
1330                 return -EPERM;
1331
1332         switch (cmd) {
1333         case IP6T_SO_GET_INFO:
1334                 ret = get_info(user, len);
1335                 break;
1336
1337         case IP6T_SO_GET_ENTRIES:
1338                 ret = get_entries(user, len);
1339                 break;
1340
1341         case IP6T_SO_GET_REVISION_MATCH:
1342         case IP6T_SO_GET_REVISION_TARGET: {
1343                 struct ip6t_get_revision rev;
1344                 int target;
1345
1346                 if (*len != sizeof(rev)) {
1347                         ret = -EINVAL;
1348                         break;
1349                 }
1350                 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1351                         ret = -EFAULT;
1352                         break;
1353                 }
1354
1355                 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1356                         target = 1;
1357                 else
1358                         target = 0;
1359
1360                 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1361                                                          rev.revision,
1362                                                          target, &ret),
1363                                         "ip6t_%s", rev.name);
1364                 break;
1365         }
1366
1367         default:
1368                 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1369                 ret = -EINVAL;
1370         }
1371
1372         return ret;
1373 }
1374
1375 int ip6t_register_table(struct xt_table *table,
1376                         const struct ip6t_replace *repl)
1377 {
1378         int ret;
1379         struct xt_table_info *newinfo;
1380         struct xt_table_info bootstrap
1381                 = { 0, 0, 0, { 0 }, { 0 }, { } };
1382         void *loc_cpu_entry;
1383
1384         newinfo = xt_alloc_table_info(repl->size);
1385         if (!newinfo)
1386                 return -ENOMEM;
1387
1388         /* choose the copy on our node/cpu */
1389         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1390         memcpy(loc_cpu_entry, repl->entries, repl->size);
1391
1392         ret = translate_table(table->name, table->valid_hooks,
1393                               newinfo, loc_cpu_entry, repl->size,
1394                               repl->num_entries,
1395                               repl->hook_entry,
1396                               repl->underflow);
1397         if (ret != 0) {
1398                 xt_free_table_info(newinfo);
1399                 return ret;
1400         }
1401
1402         ret = xt_register_table(table, &bootstrap, newinfo);
1403         if (ret != 0) {
1404                 xt_free_table_info(newinfo);
1405                 return ret;
1406         }
1407
1408         return 0;
1409 }
1410
1411 void ip6t_unregister_table(struct xt_table *table)
1412 {
1413         struct xt_table_info *private;
1414         void *loc_cpu_entry;
1415
1416         private = xt_unregister_table(table);
1417
1418         /* Decrease module usage counts and free resources */
1419         loc_cpu_entry = private->entries[raw_smp_processor_id()];
1420         IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1421         xt_free_table_info(private);
1422 }
1423
1424 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1425 static inline bool
1426 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1427                      u_int8_t type, u_int8_t code,
1428                      bool invert)
1429 {
1430         return (type == test_type && code >= min_code && code <= max_code)
1431                 ^ invert;
1432 }
1433
1434 static bool
1435 icmp6_match(const struct sk_buff *skb,
1436            const struct net_device *in,
1437            const struct net_device *out,
1438            const struct xt_match *match,
1439            const void *matchinfo,
1440            int offset,
1441            unsigned int protoff,
1442            bool *hotdrop)
1443 {
1444         struct icmp6hdr _icmp, *ic;
1445         const struct ip6t_icmp *icmpinfo = matchinfo;
1446
1447         /* Must not be a fragment. */
1448         if (offset)
1449                 return false;
1450
1451         ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1452         if (ic == NULL) {
1453                 /* We've been asked to examine this packet, and we
1454                    can't.  Hence, no choice but to drop. */
1455                 duprintf("Dropping evil ICMP tinygram.\n");
1456                 *hotdrop = true;
1457                 return false;
1458         }
1459
1460         return icmp6_type_code_match(icmpinfo->type,
1461                                      icmpinfo->code[0],
1462                                      icmpinfo->code[1],
1463                                      ic->icmp6_type, ic->icmp6_code,
1464                                      !!(icmpinfo->invflags&IP6T_ICMP_INV));
1465 }
1466
1467 /* Called when user tries to insert an entry of this type. */
1468 static bool
1469 icmp6_checkentry(const char *tablename,
1470            const void *entry,
1471            const struct xt_match *match,
1472            void *matchinfo,
1473            unsigned int hook_mask)
1474 {
1475         const struct ip6t_icmp *icmpinfo = matchinfo;
1476
1477         /* Must specify no unknown invflags */
1478         return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1479 }
1480
1481 /* The built-in targets: standard (NULL) and error. */
1482 static struct xt_target ip6t_standard_target __read_mostly = {
1483         .name           = IP6T_STANDARD_TARGET,
1484         .targetsize     = sizeof(int),
1485         .family         = AF_INET6,
1486 };
1487
1488 static struct xt_target ip6t_error_target __read_mostly = {
1489         .name           = IP6T_ERROR_TARGET,
1490         .target         = ip6t_error,
1491         .targetsize     = IP6T_FUNCTION_MAXNAMELEN,
1492         .family         = AF_INET6,
1493 };
1494
1495 static struct nf_sockopt_ops ip6t_sockopts = {
1496         .pf             = PF_INET6,
1497         .set_optmin     = IP6T_BASE_CTL,
1498         .set_optmax     = IP6T_SO_SET_MAX+1,
1499         .set            = do_ip6t_set_ctl,
1500         .get_optmin     = IP6T_BASE_CTL,
1501         .get_optmax     = IP6T_SO_GET_MAX+1,
1502         .get            = do_ip6t_get_ctl,
1503         .owner          = THIS_MODULE,
1504 };
1505
1506 static struct xt_match icmp6_matchstruct __read_mostly = {
1507         .name           = "icmp6",
1508         .match          = &icmp6_match,
1509         .matchsize      = sizeof(struct ip6t_icmp),
1510         .checkentry     = icmp6_checkentry,
1511         .proto          = IPPROTO_ICMPV6,
1512         .family         = AF_INET6,
1513 };
1514
1515 static int __init ip6_tables_init(void)
1516 {
1517         int ret;
1518
1519         ret = xt_proto_init(AF_INET6);
1520         if (ret < 0)
1521                 goto err1;
1522
1523         /* Noone else will be downing sem now, so we won't sleep */
1524         ret = xt_register_target(&ip6t_standard_target);
1525         if (ret < 0)
1526                 goto err2;
1527         ret = xt_register_target(&ip6t_error_target);
1528         if (ret < 0)
1529                 goto err3;
1530         ret = xt_register_match(&icmp6_matchstruct);
1531         if (ret < 0)
1532                 goto err4;
1533
1534         /* Register setsockopt */
1535         ret = nf_register_sockopt(&ip6t_sockopts);
1536         if (ret < 0)
1537                 goto err5;
1538
1539         printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1540         return 0;
1541
1542 err5:
1543         xt_unregister_match(&icmp6_matchstruct);
1544 err4:
1545         xt_unregister_target(&ip6t_error_target);
1546 err3:
1547         xt_unregister_target(&ip6t_standard_target);
1548 err2:
1549         xt_proto_fini(AF_INET6);
1550 err1:
1551         return ret;
1552 }
1553
1554 static void __exit ip6_tables_fini(void)
1555 {
1556         nf_unregister_sockopt(&ip6t_sockopts);
1557         xt_unregister_match(&icmp6_matchstruct);
1558         xt_unregister_target(&ip6t_error_target);
1559         xt_unregister_target(&ip6t_standard_target);
1560         xt_proto_fini(AF_INET6);
1561 }
1562
1563 /*
1564  * find the offset to specified header or the protocol number of last header
1565  * if target < 0. "last header" is transport protocol header, ESP, or
1566  * "No next header".
1567  *
1568  * If target header is found, its offset is set in *offset and return protocol
1569  * number. Otherwise, return -1.
1570  *
1571  * If the first fragment doesn't contain the final protocol header or
1572  * NEXTHDR_NONE it is considered invalid.
1573  *
1574  * Note that non-1st fragment is special case that "the protocol number
1575  * of last header" is "next header" field in Fragment header. In this case,
1576  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1577  * isn't NULL.
1578  *
1579  */
1580 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1581                   int target, unsigned short *fragoff)
1582 {
1583         unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
1584         u8 nexthdr = ipv6_hdr(skb)->nexthdr;
1585         unsigned int len = skb->len - start;
1586
1587         if (fragoff)
1588                 *fragoff = 0;
1589
1590         while (nexthdr != target) {
1591                 struct ipv6_opt_hdr _hdr, *hp;
1592                 unsigned int hdrlen;
1593
1594                 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1595                         if (target < 0)
1596                                 break;
1597                         return -ENOENT;
1598                 }
1599
1600                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1601                 if (hp == NULL)
1602                         return -EBADMSG;
1603                 if (nexthdr == NEXTHDR_FRAGMENT) {
1604                         unsigned short _frag_off;
1605                         __be16 *fp;
1606                         fp = skb_header_pointer(skb,
1607                                                 start+offsetof(struct frag_hdr,
1608                                                                frag_off),
1609                                                 sizeof(_frag_off),
1610                                                 &_frag_off);
1611                         if (fp == NULL)
1612                                 return -EBADMSG;
1613
1614                         _frag_off = ntohs(*fp) & ~0x7;
1615                         if (_frag_off) {
1616                                 if (target < 0 &&
1617                                     ((!ipv6_ext_hdr(hp->nexthdr)) ||
1618                                      hp->nexthdr == NEXTHDR_NONE)) {
1619                                         if (fragoff)
1620                                                 *fragoff = _frag_off;
1621                                         return hp->nexthdr;
1622                                 }
1623                                 return -ENOENT;
1624                         }
1625                         hdrlen = 8;
1626                 } else if (nexthdr == NEXTHDR_AUTH)
1627                         hdrlen = (hp->hdrlen + 2) << 2;
1628                 else
1629                         hdrlen = ipv6_optlen(hp);
1630
1631                 nexthdr = hp->nexthdr;
1632                 len -= hdrlen;
1633                 start += hdrlen;
1634         }
1635
1636         *offset = start;
1637         return nexthdr;
1638 }
1639
1640 EXPORT_SYMBOL(ip6t_register_table);
1641 EXPORT_SYMBOL(ip6t_unregister_table);
1642 EXPORT_SYMBOL(ip6t_do_table);
1643 EXPORT_SYMBOL(ip6t_ext_hdr);
1644 EXPORT_SYMBOL(ipv6_find_hdr);
1645
1646 module_init(ip6_tables_init);
1647 module_exit(ip6_tables_fini);