[NETFILTER]: ip6_tables: move IP6T_SO_GET_INFO handling to seperate function
[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(const struct ip6t_get_entries *entries,
1086             struct ip6t_get_entries __user *uptr)
1087 {
1088         int ret;
1089         struct xt_table *t;
1090
1091         t = xt_find_table_lock(AF_INET6, entries->name);
1092         if (t && !IS_ERR(t)) {
1093                 struct xt_table_info *private = t->private;
1094                 duprintf("t->private->number = %u\n", private->number);
1095                 if (entries->size == private->size)
1096                         ret = copy_entries_to_user(private->size,
1097                                                    t, uptr->entrytable);
1098                 else {
1099                         duprintf("get_entries: I've got %u not %u!\n",
1100                                  private->size, entries->size);
1101                         ret = -EINVAL;
1102                 }
1103                 module_put(t->me);
1104                 xt_table_unlock(t);
1105         } else
1106                 ret = t ? PTR_ERR(t) : -ENOENT;
1107
1108         return ret;
1109 }
1110
1111 static int
1112 do_replace(void __user *user, unsigned int len)
1113 {
1114         int ret;
1115         struct ip6t_replace tmp;
1116         struct xt_table *t;
1117         struct xt_table_info *newinfo, *oldinfo;
1118         struct xt_counters *counters;
1119         void *loc_cpu_entry, *loc_cpu_old_entry;
1120
1121         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1122                 return -EFAULT;
1123
1124         /* overflow check */
1125         if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1126                 return -ENOMEM;
1127
1128         newinfo = xt_alloc_table_info(tmp.size);
1129         if (!newinfo)
1130                 return -ENOMEM;
1131
1132         /* choose the copy that is on our node/cpu */
1133         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1134         if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1135                            tmp.size) != 0) {
1136                 ret = -EFAULT;
1137                 goto free_newinfo;
1138         }
1139
1140         counters = vmalloc_node(tmp.num_counters * sizeof(struct xt_counters),
1141                                 numa_node_id());
1142         if (!counters) {
1143                 ret = -ENOMEM;
1144                 goto free_newinfo;
1145         }
1146
1147         ret = translate_table(tmp.name, tmp.valid_hooks,
1148                               newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1149                               tmp.hook_entry, tmp.underflow);
1150         if (ret != 0)
1151                 goto free_newinfo_counters;
1152
1153         duprintf("ip_tables: Translated table\n");
1154
1155         t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
1156                                     "ip6table_%s", tmp.name);
1157         if (!t || IS_ERR(t)) {
1158                 ret = t ? PTR_ERR(t) : -ENOENT;
1159                 goto free_newinfo_counters_untrans;
1160         }
1161
1162         /* You lied! */
1163         if (tmp.valid_hooks != t->valid_hooks) {
1164                 duprintf("Valid hook crap: %08X vs %08X\n",
1165                          tmp.valid_hooks, t->valid_hooks);
1166                 ret = -EINVAL;
1167                 goto put_module;
1168         }
1169
1170         oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1171         if (!oldinfo)
1172                 goto put_module;
1173
1174         /* Update module usage count based on number of rules */
1175         duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1176                 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1177         if ((oldinfo->number > oldinfo->initial_entries) ||
1178             (newinfo->number <= oldinfo->initial_entries))
1179                 module_put(t->me);
1180         if ((oldinfo->number > oldinfo->initial_entries) &&
1181             (newinfo->number <= oldinfo->initial_entries))
1182                 module_put(t->me);
1183
1184         /* Get the old counters. */
1185         get_counters(oldinfo, counters);
1186         /* Decrease module usage counts and free resource */
1187         loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1188         IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1189         xt_free_table_info(oldinfo);
1190         if (copy_to_user(tmp.counters, counters,
1191                          sizeof(struct xt_counters) * tmp.num_counters) != 0)
1192                 ret = -EFAULT;
1193         vfree(counters);
1194         xt_table_unlock(t);
1195         return ret;
1196
1197  put_module:
1198         module_put(t->me);
1199         xt_table_unlock(t);
1200  free_newinfo_counters_untrans:
1201         IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1202  free_newinfo_counters:
1203         vfree(counters);
1204  free_newinfo:
1205         xt_free_table_info(newinfo);
1206         return ret;
1207 }
1208
1209 /* We're lazy, and add to the first CPU; overflow works its fey magic
1210  * and everything is OK. */
1211 static inline int
1212 add_counter_to_entry(struct ip6t_entry *e,
1213                      const struct xt_counters addme[],
1214                      unsigned int *i)
1215 {
1216 #if 0
1217         duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1218                  *i,
1219                  (long unsigned int)e->counters.pcnt,
1220                  (long unsigned int)e->counters.bcnt,
1221                  (long unsigned int)addme[*i].pcnt,
1222                  (long unsigned int)addme[*i].bcnt);
1223 #endif
1224
1225         ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1226
1227         (*i)++;
1228         return 0;
1229 }
1230
1231 static int
1232 do_add_counters(void __user *user, unsigned int len)
1233 {
1234         unsigned int i;
1235         struct xt_counters_info tmp, *paddc;
1236         struct xt_table_info *private;
1237         struct xt_table *t;
1238         int ret = 0;
1239         void *loc_cpu_entry;
1240
1241         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1242                 return -EFAULT;
1243
1244         if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1245                 return -EINVAL;
1246
1247         paddc = vmalloc_node(len, numa_node_id());
1248         if (!paddc)
1249                 return -ENOMEM;
1250
1251         if (copy_from_user(paddc, user, len) != 0) {
1252                 ret = -EFAULT;
1253                 goto free;
1254         }
1255
1256         t = xt_find_table_lock(AF_INET6, tmp.name);
1257         if (!t || IS_ERR(t)) {
1258                 ret = t ? PTR_ERR(t) : -ENOENT;
1259                 goto free;
1260         }
1261
1262         write_lock_bh(&t->lock);
1263         private = t->private;
1264         if (private->number != tmp.num_counters) {
1265                 ret = -EINVAL;
1266                 goto unlock_up_free;
1267         }
1268
1269         i = 0;
1270         /* Choose the copy that is on our node */
1271         loc_cpu_entry = private->entries[smp_processor_id()];
1272         IP6T_ENTRY_ITERATE(loc_cpu_entry,
1273                           private->size,
1274                           add_counter_to_entry,
1275                           paddc->counters,
1276                           &i);
1277  unlock_up_free:
1278         write_unlock_bh(&t->lock);
1279         xt_table_unlock(t);
1280         module_put(t->me);
1281  free:
1282         vfree(paddc);
1283
1284         return ret;
1285 }
1286
1287 static int
1288 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1289 {
1290         int ret;
1291
1292         if (!capable(CAP_NET_ADMIN))
1293                 return -EPERM;
1294
1295         switch (cmd) {
1296         case IP6T_SO_SET_REPLACE:
1297                 ret = do_replace(user, len);
1298                 break;
1299
1300         case IP6T_SO_SET_ADD_COUNTERS:
1301                 ret = do_add_counters(user, len);
1302                 break;
1303
1304         default:
1305                 duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1306                 ret = -EINVAL;
1307         }
1308
1309         return ret;
1310 }
1311
1312 static int
1313 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1314 {
1315         int ret;
1316
1317         if (!capable(CAP_NET_ADMIN))
1318                 return -EPERM;
1319
1320         switch (cmd) {
1321         case IP6T_SO_GET_INFO:
1322                 ret = get_info(user, len);
1323                 break;
1324
1325         case IP6T_SO_GET_ENTRIES: {
1326                 struct ip6t_get_entries get;
1327
1328                 if (*len < sizeof(get)) {
1329                         duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1330                         ret = -EINVAL;
1331                 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1332                         ret = -EFAULT;
1333                 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1334                         duprintf("get_entries: %u != %u\n", *len,
1335                                  sizeof(struct ip6t_get_entries) + get.size);
1336                         ret = -EINVAL;
1337                 } else
1338                         ret = get_entries(&get, user);
1339                 break;
1340         }
1341
1342         case IP6T_SO_GET_REVISION_MATCH:
1343         case IP6T_SO_GET_REVISION_TARGET: {
1344                 struct ip6t_get_revision rev;
1345                 int target;
1346
1347                 if (*len != sizeof(rev)) {
1348                         ret = -EINVAL;
1349                         break;
1350                 }
1351                 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1352                         ret = -EFAULT;
1353                         break;
1354                 }
1355
1356                 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1357                         target = 1;
1358                 else
1359                         target = 0;
1360
1361                 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1362                                                          rev.revision,
1363                                                          target, &ret),
1364                                         "ip6t_%s", rev.name);
1365                 break;
1366         }
1367
1368         default:
1369                 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1370                 ret = -EINVAL;
1371         }
1372
1373         return ret;
1374 }
1375
1376 int ip6t_register_table(struct xt_table *table,
1377                         const struct ip6t_replace *repl)
1378 {
1379         int ret;
1380         struct xt_table_info *newinfo;
1381         struct xt_table_info bootstrap
1382                 = { 0, 0, 0, { 0 }, { 0 }, { } };
1383         void *loc_cpu_entry;
1384
1385         newinfo = xt_alloc_table_info(repl->size);
1386         if (!newinfo)
1387                 return -ENOMEM;
1388
1389         /* choose the copy on our node/cpu */
1390         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1391         memcpy(loc_cpu_entry, repl->entries, repl->size);
1392
1393         ret = translate_table(table->name, table->valid_hooks,
1394                               newinfo, loc_cpu_entry, repl->size,
1395                               repl->num_entries,
1396                               repl->hook_entry,
1397                               repl->underflow);
1398         if (ret != 0) {
1399                 xt_free_table_info(newinfo);
1400                 return ret;
1401         }
1402
1403         ret = xt_register_table(table, &bootstrap, newinfo);
1404         if (ret != 0) {
1405                 xt_free_table_info(newinfo);
1406                 return ret;
1407         }
1408
1409         return 0;
1410 }
1411
1412 void ip6t_unregister_table(struct xt_table *table)
1413 {
1414         struct xt_table_info *private;
1415         void *loc_cpu_entry;
1416
1417         private = xt_unregister_table(table);
1418
1419         /* Decrease module usage counts and free resources */
1420         loc_cpu_entry = private->entries[raw_smp_processor_id()];
1421         IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1422         xt_free_table_info(private);
1423 }
1424
1425 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1426 static inline bool
1427 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1428                      u_int8_t type, u_int8_t code,
1429                      bool invert)
1430 {
1431         return (type == test_type && code >= min_code && code <= max_code)
1432                 ^ invert;
1433 }
1434
1435 static bool
1436 icmp6_match(const struct sk_buff *skb,
1437            const struct net_device *in,
1438            const struct net_device *out,
1439            const struct xt_match *match,
1440            const void *matchinfo,
1441            int offset,
1442            unsigned int protoff,
1443            bool *hotdrop)
1444 {
1445         struct icmp6hdr _icmp, *ic;
1446         const struct ip6t_icmp *icmpinfo = matchinfo;
1447
1448         /* Must not be a fragment. */
1449         if (offset)
1450                 return false;
1451
1452         ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1453         if (ic == NULL) {
1454                 /* We've been asked to examine this packet, and we
1455                    can't.  Hence, no choice but to drop. */
1456                 duprintf("Dropping evil ICMP tinygram.\n");
1457                 *hotdrop = true;
1458                 return false;
1459         }
1460
1461         return icmp6_type_code_match(icmpinfo->type,
1462                                      icmpinfo->code[0],
1463                                      icmpinfo->code[1],
1464                                      ic->icmp6_type, ic->icmp6_code,
1465                                      !!(icmpinfo->invflags&IP6T_ICMP_INV));
1466 }
1467
1468 /* Called when user tries to insert an entry of this type. */
1469 static bool
1470 icmp6_checkentry(const char *tablename,
1471            const void *entry,
1472            const struct xt_match *match,
1473            void *matchinfo,
1474            unsigned int hook_mask)
1475 {
1476         const struct ip6t_icmp *icmpinfo = matchinfo;
1477
1478         /* Must specify no unknown invflags */
1479         return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1480 }
1481
1482 /* The built-in targets: standard (NULL) and error. */
1483 static struct xt_target ip6t_standard_target __read_mostly = {
1484         .name           = IP6T_STANDARD_TARGET,
1485         .targetsize     = sizeof(int),
1486         .family         = AF_INET6,
1487 };
1488
1489 static struct xt_target ip6t_error_target __read_mostly = {
1490         .name           = IP6T_ERROR_TARGET,
1491         .target         = ip6t_error,
1492         .targetsize     = IP6T_FUNCTION_MAXNAMELEN,
1493         .family         = AF_INET6,
1494 };
1495
1496 static struct nf_sockopt_ops ip6t_sockopts = {
1497         .pf             = PF_INET6,
1498         .set_optmin     = IP6T_BASE_CTL,
1499         .set_optmax     = IP6T_SO_SET_MAX+1,
1500         .set            = do_ip6t_set_ctl,
1501         .get_optmin     = IP6T_BASE_CTL,
1502         .get_optmax     = IP6T_SO_GET_MAX+1,
1503         .get            = do_ip6t_get_ctl,
1504         .owner          = THIS_MODULE,
1505 };
1506
1507 static struct xt_match icmp6_matchstruct __read_mostly = {
1508         .name           = "icmp6",
1509         .match          = &icmp6_match,
1510         .matchsize      = sizeof(struct ip6t_icmp),
1511         .checkentry     = icmp6_checkentry,
1512         .proto          = IPPROTO_ICMPV6,
1513         .family         = AF_INET6,
1514 };
1515
1516 static int __init ip6_tables_init(void)
1517 {
1518         int ret;
1519
1520         ret = xt_proto_init(AF_INET6);
1521         if (ret < 0)
1522                 goto err1;
1523
1524         /* Noone else will be downing sem now, so we won't sleep */
1525         ret = xt_register_target(&ip6t_standard_target);
1526         if (ret < 0)
1527                 goto err2;
1528         ret = xt_register_target(&ip6t_error_target);
1529         if (ret < 0)
1530                 goto err3;
1531         ret = xt_register_match(&icmp6_matchstruct);
1532         if (ret < 0)
1533                 goto err4;
1534
1535         /* Register setsockopt */
1536         ret = nf_register_sockopt(&ip6t_sockopts);
1537         if (ret < 0)
1538                 goto err5;
1539
1540         printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1541         return 0;
1542
1543 err5:
1544         xt_unregister_match(&icmp6_matchstruct);
1545 err4:
1546         xt_unregister_target(&ip6t_error_target);
1547 err3:
1548         xt_unregister_target(&ip6t_standard_target);
1549 err2:
1550         xt_proto_fini(AF_INET6);
1551 err1:
1552         return ret;
1553 }
1554
1555 static void __exit ip6_tables_fini(void)
1556 {
1557         nf_unregister_sockopt(&ip6t_sockopts);
1558         xt_unregister_match(&icmp6_matchstruct);
1559         xt_unregister_target(&ip6t_error_target);
1560         xt_unregister_target(&ip6t_standard_target);
1561         xt_proto_fini(AF_INET6);
1562 }
1563
1564 /*
1565  * find the offset to specified header or the protocol number of last header
1566  * if target < 0. "last header" is transport protocol header, ESP, or
1567  * "No next header".
1568  *
1569  * If target header is found, its offset is set in *offset and return protocol
1570  * number. Otherwise, return -1.
1571  *
1572  * If the first fragment doesn't contain the final protocol header or
1573  * NEXTHDR_NONE it is considered invalid.
1574  *
1575  * Note that non-1st fragment is special case that "the protocol number
1576  * of last header" is "next header" field in Fragment header. In this case,
1577  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1578  * isn't NULL.
1579  *
1580  */
1581 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1582                   int target, unsigned short *fragoff)
1583 {
1584         unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
1585         u8 nexthdr = ipv6_hdr(skb)->nexthdr;
1586         unsigned int len = skb->len - start;
1587
1588         if (fragoff)
1589                 *fragoff = 0;
1590
1591         while (nexthdr != target) {
1592                 struct ipv6_opt_hdr _hdr, *hp;
1593                 unsigned int hdrlen;
1594
1595                 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1596                         if (target < 0)
1597                                 break;
1598                         return -ENOENT;
1599                 }
1600
1601                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1602                 if (hp == NULL)
1603                         return -EBADMSG;
1604                 if (nexthdr == NEXTHDR_FRAGMENT) {
1605                         unsigned short _frag_off;
1606                         __be16 *fp;
1607                         fp = skb_header_pointer(skb,
1608                                                 start+offsetof(struct frag_hdr,
1609                                                                frag_off),
1610                                                 sizeof(_frag_off),
1611                                                 &_frag_off);
1612                         if (fp == NULL)
1613                                 return -EBADMSG;
1614
1615                         _frag_off = ntohs(*fp) & ~0x7;
1616                         if (_frag_off) {
1617                                 if (target < 0 &&
1618                                     ((!ipv6_ext_hdr(hp->nexthdr)) ||
1619                                      hp->nexthdr == NEXTHDR_NONE)) {
1620                                         if (fragoff)
1621                                                 *fragoff = _frag_off;
1622                                         return hp->nexthdr;
1623                                 }
1624                                 return -ENOENT;
1625                         }
1626                         hdrlen = 8;
1627                 } else if (nexthdr == NEXTHDR_AUTH)
1628                         hdrlen = (hp->hdrlen + 2) << 2;
1629                 else
1630                         hdrlen = ipv6_optlen(hp);
1631
1632                 nexthdr = hp->nexthdr;
1633                 len -= hdrlen;
1634                 start += hdrlen;
1635         }
1636
1637         *offset = start;
1638         return nexthdr;
1639 }
1640
1641 EXPORT_SYMBOL(ip6t_register_table);
1642 EXPORT_SYMBOL(ip6t_unregister_table);
1643 EXPORT_SYMBOL(ip6t_do_table);
1644 EXPORT_SYMBOL(ip6t_ext_hdr);
1645 EXPORT_SYMBOL(ipv6_find_hdr);
1646
1647 module_init(ip6_tables_init);
1648 module_exit(ip6_tables_fini);