[NETFILTER]: ip6_tables: kill a few useless defines/forward declarations
[powerpc.git] / 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_match(struct ip6t_entry_match *m,
611             const char *name,
612             const struct ip6t_ip6 *ipv6,
613             unsigned int hookmask,
614             unsigned int *i)
615 {
616         struct xt_match *match;
617         int ret;
618
619         match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
620                                         m->u.user.revision),
621                                         "ip6t_%s", m->u.user.name);
622         if (IS_ERR(match) || !match) {
623                 duprintf("check_match: `%s' not found\n", m->u.user.name);
624                 return match ? PTR_ERR(match) : -ENOENT;
625         }
626         m->u.kernel.match = match;
627
628         ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
629                              name, hookmask, ipv6->proto,
630                              ipv6->invflags & IP6T_INV_PROTO);
631         if (ret)
632                 goto err;
633
634         if (m->u.kernel.match->checkentry
635             && !m->u.kernel.match->checkentry(name, ipv6, match,  m->data,
636                                               hookmask)) {
637                 duprintf("ip_tables: check failed for `%s'.\n",
638                          m->u.kernel.match->name);
639                 ret = -EINVAL;
640                 goto err;
641         }
642
643         (*i)++;
644         return 0;
645 err:
646         module_put(m->u.kernel.match->me);
647         return ret;
648 }
649
650 static inline int
651 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
652             unsigned int *i)
653 {
654         struct ip6t_entry_target *t;
655         struct xt_target *target;
656         int ret;
657         unsigned int j;
658
659         if (!ip6_checkentry(&e->ipv6)) {
660                 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
661                 return -EINVAL;
662         }
663
664         if (e->target_offset + sizeof(struct ip6t_entry_target) >
665                                                                 e->next_offset)
666                 return -EINVAL;
667
668         j = 0;
669         ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
670         if (ret != 0)
671                 goto cleanup_matches;
672
673         t = ip6t_get_target(e);
674         ret = -EINVAL;
675         if (e->target_offset + t->u.target_size > e->next_offset)
676                         goto cleanup_matches;
677         target = try_then_request_module(xt_find_target(AF_INET6,
678                                                         t->u.user.name,
679                                                         t->u.user.revision),
680                                          "ip6t_%s", t->u.user.name);
681         if (IS_ERR(target) || !target) {
682                 duprintf("check_entry: `%s' not found\n", t->u.user.name);
683                 ret = target ? PTR_ERR(target) : -ENOENT;
684                 goto cleanup_matches;
685         }
686         t->u.kernel.target = target;
687
688         ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
689                               name, e->comefrom, e->ipv6.proto,
690                               e->ipv6.invflags & IP6T_INV_PROTO);
691         if (ret)
692                 goto err;
693
694         if (t->u.kernel.target->checkentry
695                    && !t->u.kernel.target->checkentry(name, e, target, t->data,
696                                                       e->comefrom)) {
697                 duprintf("ip_tables: check failed for `%s'.\n",
698                          t->u.kernel.target->name);
699                 ret = -EINVAL;
700                 goto err;
701         }
702
703         (*i)++;
704         return 0;
705  err:
706         module_put(t->u.kernel.target->me);
707  cleanup_matches:
708         IP6T_MATCH_ITERATE(e, cleanup_match, &j);
709         return ret;
710 }
711
712 static inline int
713 check_entry_size_and_hooks(struct ip6t_entry *e,
714                            struct xt_table_info *newinfo,
715                            unsigned char *base,
716                            unsigned char *limit,
717                            const unsigned int *hook_entries,
718                            const unsigned int *underflows,
719                            unsigned int *i)
720 {
721         unsigned int h;
722
723         if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
724             || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
725                 duprintf("Bad offset %p\n", e);
726                 return -EINVAL;
727         }
728
729         if (e->next_offset
730             < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
731                 duprintf("checking: element %p size %u\n",
732                          e, e->next_offset);
733                 return -EINVAL;
734         }
735
736         /* Check hooks & underflows */
737         for (h = 0; h < NF_INET_NUMHOOKS; h++) {
738                 if ((unsigned char *)e - base == hook_entries[h])
739                         newinfo->hook_entry[h] = hook_entries[h];
740                 if ((unsigned char *)e - base == underflows[h])
741                         newinfo->underflow[h] = underflows[h];
742         }
743
744         /* FIXME: underflows must be unconditional, standard verdicts
745            < 0 (not IP6T_RETURN). --RR */
746
747         /* Clear counters and comefrom */
748         e->counters = ((struct xt_counters) { 0, 0 });
749         e->comefrom = 0;
750
751         (*i)++;
752         return 0;
753 }
754
755 static inline int
756 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
757 {
758         struct ip6t_entry_target *t;
759
760         if (i && (*i)-- == 0)
761                 return 1;
762
763         /* Cleanup all matches */
764         IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
765         t = ip6t_get_target(e);
766         if (t->u.kernel.target->destroy)
767                 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
768         module_put(t->u.kernel.target->me);
769         return 0;
770 }
771
772 /* Checks and translates the user-supplied table segment (held in
773    newinfo) */
774 static int
775 translate_table(const char *name,
776                 unsigned int valid_hooks,
777                 struct xt_table_info *newinfo,
778                 void *entry0,
779                 unsigned int size,
780                 unsigned int number,
781                 const unsigned int *hook_entries,
782                 const unsigned int *underflows)
783 {
784         unsigned int i;
785         int ret;
786
787         newinfo->size = size;
788         newinfo->number = number;
789
790         /* Init all hooks to impossible value. */
791         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
792                 newinfo->hook_entry[i] = 0xFFFFFFFF;
793                 newinfo->underflow[i] = 0xFFFFFFFF;
794         }
795
796         duprintf("translate_table: size %u\n", newinfo->size);
797         i = 0;
798         /* Walk through entries, checking offsets. */
799         ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
800                                 check_entry_size_and_hooks,
801                                 newinfo,
802                                 entry0,
803                                 entry0 + size,
804                                 hook_entries, underflows, &i);
805         if (ret != 0)
806                 return ret;
807
808         if (i != number) {
809                 duprintf("translate_table: %u not %u entries\n",
810                          i, number);
811                 return -EINVAL;
812         }
813
814         /* Check hooks all assigned */
815         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
816                 /* Only hooks which are valid */
817                 if (!(valid_hooks & (1 << i)))
818                         continue;
819                 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
820                         duprintf("Invalid hook entry %u %u\n",
821                                  i, hook_entries[i]);
822                         return -EINVAL;
823                 }
824                 if (newinfo->underflow[i] == 0xFFFFFFFF) {
825                         duprintf("Invalid underflow %u %u\n",
826                                  i, underflows[i]);
827                         return -EINVAL;
828                 }
829         }
830
831         if (!mark_source_chains(newinfo, valid_hooks, entry0))
832                 return -ELOOP;
833
834         /* Finally, each sanity check must pass */
835         i = 0;
836         ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
837                                 check_entry, name, size, &i);
838
839         if (ret != 0) {
840                 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
841                                    cleanup_entry, &i);
842                 return ret;
843         }
844
845         /* And one copy for every other CPU */
846         for_each_possible_cpu(i) {
847                 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
848                         memcpy(newinfo->entries[i], entry0, newinfo->size);
849         }
850
851         return 0;
852 }
853
854 /* Gets counters. */
855 static inline int
856 add_entry_to_counter(const struct ip6t_entry *e,
857                      struct xt_counters total[],
858                      unsigned int *i)
859 {
860         ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
861
862         (*i)++;
863         return 0;
864 }
865
866 static inline int
867 set_entry_to_counter(const struct ip6t_entry *e,
868                      struct ip6t_counters total[],
869                      unsigned int *i)
870 {
871         SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
872
873         (*i)++;
874         return 0;
875 }
876
877 static void
878 get_counters(const struct xt_table_info *t,
879              struct xt_counters counters[])
880 {
881         unsigned int cpu;
882         unsigned int i;
883         unsigned int curcpu;
884
885         /* Instead of clearing (by a previous call to memset())
886          * the counters and using adds, we set the counters
887          * with data used by 'current' CPU
888          * We dont care about preemption here.
889          */
890         curcpu = raw_smp_processor_id();
891
892         i = 0;
893         IP6T_ENTRY_ITERATE(t->entries[curcpu],
894                            t->size,
895                            set_entry_to_counter,
896                            counters,
897                            &i);
898
899         for_each_possible_cpu(cpu) {
900                 if (cpu == curcpu)
901                         continue;
902                 i = 0;
903                 IP6T_ENTRY_ITERATE(t->entries[cpu],
904                                   t->size,
905                                   add_entry_to_counter,
906                                   counters,
907                                   &i);
908         }
909 }
910
911 static int
912 copy_entries_to_user(unsigned int total_size,
913                      struct xt_table *table,
914                      void __user *userptr)
915 {
916         unsigned int off, num, countersize;
917         struct ip6t_entry *e;
918         struct xt_counters *counters;
919         struct xt_table_info *private = table->private;
920         int ret = 0;
921         void *loc_cpu_entry;
922
923         /* We need atomic snapshot of counters: rest doesn't change
924            (other than comefrom, which userspace doesn't care
925            about). */
926         countersize = sizeof(struct xt_counters) * private->number;
927         counters = vmalloc(countersize);
928
929         if (counters == NULL)
930                 return -ENOMEM;
931
932         /* First, sum counters... */
933         write_lock_bh(&table->lock);
934         get_counters(private, counters);
935         write_unlock_bh(&table->lock);
936
937         /* choose the copy that is on ourc node/cpu */
938         loc_cpu_entry = private->entries[raw_smp_processor_id()];
939         if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
940                 ret = -EFAULT;
941                 goto free_counters;
942         }
943
944         /* FIXME: use iterator macros --RR */
945         /* ... then go back and fix counters and names */
946         for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
947                 unsigned int i;
948                 struct ip6t_entry_match *m;
949                 struct ip6t_entry_target *t;
950
951                 e = (struct ip6t_entry *)(loc_cpu_entry + off);
952                 if (copy_to_user(userptr + off
953                                  + offsetof(struct ip6t_entry, counters),
954                                  &counters[num],
955                                  sizeof(counters[num])) != 0) {
956                         ret = -EFAULT;
957                         goto free_counters;
958                 }
959
960                 for (i = sizeof(struct ip6t_entry);
961                      i < e->target_offset;
962                      i += m->u.match_size) {
963                         m = (void *)e + i;
964
965                         if (copy_to_user(userptr + off + i
966                                          + offsetof(struct ip6t_entry_match,
967                                                     u.user.name),
968                                          m->u.kernel.match->name,
969                                          strlen(m->u.kernel.match->name)+1)
970                             != 0) {
971                                 ret = -EFAULT;
972                                 goto free_counters;
973                         }
974                 }
975
976                 t = ip6t_get_target(e);
977                 if (copy_to_user(userptr + off + e->target_offset
978                                  + offsetof(struct ip6t_entry_target,
979                                             u.user.name),
980                                  t->u.kernel.target->name,
981                                  strlen(t->u.kernel.target->name)+1) != 0) {
982                         ret = -EFAULT;
983                         goto free_counters;
984                 }
985         }
986
987  free_counters:
988         vfree(counters);
989         return ret;
990 }
991
992 static int
993 get_entries(const struct ip6t_get_entries *entries,
994             struct ip6t_get_entries __user *uptr)
995 {
996         int ret;
997         struct xt_table *t;
998
999         t = xt_find_table_lock(AF_INET6, entries->name);
1000         if (t && !IS_ERR(t)) {
1001                 struct xt_table_info *private = t->private;
1002                 duprintf("t->private->number = %u\n", private->number);
1003                 if (entries->size == private->size)
1004                         ret = copy_entries_to_user(private->size,
1005                                                    t, uptr->entrytable);
1006                 else {
1007                         duprintf("get_entries: I've got %u not %u!\n",
1008                                  private->size, entries->size);
1009                         ret = -EINVAL;
1010                 }
1011                 module_put(t->me);
1012                 xt_table_unlock(t);
1013         } else
1014                 ret = t ? PTR_ERR(t) : -ENOENT;
1015
1016         return ret;
1017 }
1018
1019 static int
1020 do_replace(void __user *user, unsigned int len)
1021 {
1022         int ret;
1023         struct ip6t_replace tmp;
1024         struct xt_table *t;
1025         struct xt_table_info *newinfo, *oldinfo;
1026         struct xt_counters *counters;
1027         void *loc_cpu_entry, *loc_cpu_old_entry;
1028
1029         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1030                 return -EFAULT;
1031
1032         /* overflow check */
1033         if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1034                 return -ENOMEM;
1035
1036         newinfo = xt_alloc_table_info(tmp.size);
1037         if (!newinfo)
1038                 return -ENOMEM;
1039
1040         /* choose the copy that is on our node/cpu */
1041         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1042         if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1043                            tmp.size) != 0) {
1044                 ret = -EFAULT;
1045                 goto free_newinfo;
1046         }
1047
1048         counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
1049         if (!counters) {
1050                 ret = -ENOMEM;
1051                 goto free_newinfo;
1052         }
1053
1054         ret = translate_table(tmp.name, tmp.valid_hooks,
1055                               newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1056                               tmp.hook_entry, tmp.underflow);
1057         if (ret != 0)
1058                 goto free_newinfo_counters;
1059
1060         duprintf("ip_tables: Translated table\n");
1061
1062         t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
1063                                     "ip6table_%s", tmp.name);
1064         if (!t || IS_ERR(t)) {
1065                 ret = t ? PTR_ERR(t) : -ENOENT;
1066                 goto free_newinfo_counters_untrans;
1067         }
1068
1069         /* You lied! */
1070         if (tmp.valid_hooks != t->valid_hooks) {
1071                 duprintf("Valid hook crap: %08X vs %08X\n",
1072                          tmp.valid_hooks, t->valid_hooks);
1073                 ret = -EINVAL;
1074                 goto put_module;
1075         }
1076
1077         oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1078         if (!oldinfo)
1079                 goto put_module;
1080
1081         /* Update module usage count based on number of rules */
1082         duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1083                 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1084         if ((oldinfo->number > oldinfo->initial_entries) ||
1085             (newinfo->number <= oldinfo->initial_entries))
1086                 module_put(t->me);
1087         if ((oldinfo->number > oldinfo->initial_entries) &&
1088             (newinfo->number <= oldinfo->initial_entries))
1089                 module_put(t->me);
1090
1091         /* Get the old counters. */
1092         get_counters(oldinfo, counters);
1093         /* Decrease module usage counts and free resource */
1094         loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1095         IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1096         xt_free_table_info(oldinfo);
1097         if (copy_to_user(tmp.counters, counters,
1098                          sizeof(struct xt_counters) * tmp.num_counters) != 0)
1099                 ret = -EFAULT;
1100         vfree(counters);
1101         xt_table_unlock(t);
1102         return ret;
1103
1104  put_module:
1105         module_put(t->me);
1106         xt_table_unlock(t);
1107  free_newinfo_counters_untrans:
1108         IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1109  free_newinfo_counters:
1110         vfree(counters);
1111  free_newinfo:
1112         xt_free_table_info(newinfo);
1113         return ret;
1114 }
1115
1116 /* We're lazy, and add to the first CPU; overflow works its fey magic
1117  * and everything is OK. */
1118 static inline int
1119 add_counter_to_entry(struct ip6t_entry *e,
1120                      const struct xt_counters addme[],
1121                      unsigned int *i)
1122 {
1123 #if 0
1124         duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1125                  *i,
1126                  (long unsigned int)e->counters.pcnt,
1127                  (long unsigned int)e->counters.bcnt,
1128                  (long unsigned int)addme[*i].pcnt,
1129                  (long unsigned int)addme[*i].bcnt);
1130 #endif
1131
1132         ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1133
1134         (*i)++;
1135         return 0;
1136 }
1137
1138 static int
1139 do_add_counters(void __user *user, unsigned int len)
1140 {
1141         unsigned int i;
1142         struct xt_counters_info tmp, *paddc;
1143         struct xt_table_info *private;
1144         struct xt_table *t;
1145         int ret = 0;
1146         void *loc_cpu_entry;
1147
1148         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1149                 return -EFAULT;
1150
1151         if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1152                 return -EINVAL;
1153
1154         paddc = vmalloc(len);
1155         if (!paddc)
1156                 return -ENOMEM;
1157
1158         if (copy_from_user(paddc, user, len) != 0) {
1159                 ret = -EFAULT;
1160                 goto free;
1161         }
1162
1163         t = xt_find_table_lock(AF_INET6, tmp.name);
1164         if (!t || IS_ERR(t)) {
1165                 ret = t ? PTR_ERR(t) : -ENOENT;
1166                 goto free;
1167         }
1168
1169         write_lock_bh(&t->lock);
1170         private = t->private;
1171         if (private->number != tmp.num_counters) {
1172                 ret = -EINVAL;
1173                 goto unlock_up_free;
1174         }
1175
1176         i = 0;
1177         /* Choose the copy that is on our node */
1178         loc_cpu_entry = private->entries[smp_processor_id()];
1179         IP6T_ENTRY_ITERATE(loc_cpu_entry,
1180                           private->size,
1181                           add_counter_to_entry,
1182                           paddc->counters,
1183                           &i);
1184  unlock_up_free:
1185         write_unlock_bh(&t->lock);
1186         xt_table_unlock(t);
1187         module_put(t->me);
1188  free:
1189         vfree(paddc);
1190
1191         return ret;
1192 }
1193
1194 static int
1195 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1196 {
1197         int ret;
1198
1199         if (!capable(CAP_NET_ADMIN))
1200                 return -EPERM;
1201
1202         switch (cmd) {
1203         case IP6T_SO_SET_REPLACE:
1204                 ret = do_replace(user, len);
1205                 break;
1206
1207         case IP6T_SO_SET_ADD_COUNTERS:
1208                 ret = do_add_counters(user, len);
1209                 break;
1210
1211         default:
1212                 duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1213                 ret = -EINVAL;
1214         }
1215
1216         return ret;
1217 }
1218
1219 static int
1220 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1221 {
1222         int ret;
1223
1224         if (!capable(CAP_NET_ADMIN))
1225                 return -EPERM;
1226
1227         switch (cmd) {
1228         case IP6T_SO_GET_INFO: {
1229                 char name[IP6T_TABLE_MAXNAMELEN];
1230                 struct xt_table *t;
1231
1232                 if (*len != sizeof(struct ip6t_getinfo)) {
1233                         duprintf("length %u != %u\n", *len,
1234                                  sizeof(struct ip6t_getinfo));
1235                         ret = -EINVAL;
1236                         break;
1237                 }
1238
1239                 if (copy_from_user(name, user, sizeof(name)) != 0) {
1240                         ret = -EFAULT;
1241                         break;
1242                 }
1243                 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1244
1245                 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1246                                             "ip6table_%s", name);
1247                 if (t && !IS_ERR(t)) {
1248                         struct ip6t_getinfo info;
1249                         struct xt_table_info *private = t->private;
1250
1251                         info.valid_hooks = t->valid_hooks;
1252                         memcpy(info.hook_entry, private->hook_entry,
1253                                sizeof(info.hook_entry));
1254                         memcpy(info.underflow, private->underflow,
1255                                sizeof(info.underflow));
1256                         info.num_entries = private->number;
1257                         info.size = private->size;
1258                         memcpy(info.name, name, sizeof(info.name));
1259
1260                         if (copy_to_user(user, &info, *len) != 0)
1261                                 ret = -EFAULT;
1262                         else
1263                                 ret = 0;
1264                         xt_table_unlock(t);
1265                         module_put(t->me);
1266                 } else
1267                         ret = t ? PTR_ERR(t) : -ENOENT;
1268         }
1269         break;
1270
1271         case IP6T_SO_GET_ENTRIES: {
1272                 struct ip6t_get_entries get;
1273
1274                 if (*len < sizeof(get)) {
1275                         duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1276                         ret = -EINVAL;
1277                 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1278                         ret = -EFAULT;
1279                 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1280                         duprintf("get_entries: %u != %u\n", *len,
1281                                  sizeof(struct ip6t_get_entries) + get.size);
1282                         ret = -EINVAL;
1283                 } else
1284                         ret = get_entries(&get, user);
1285                 break;
1286         }
1287
1288         case IP6T_SO_GET_REVISION_MATCH:
1289         case IP6T_SO_GET_REVISION_TARGET: {
1290                 struct ip6t_get_revision rev;
1291                 int target;
1292
1293                 if (*len != sizeof(rev)) {
1294                         ret = -EINVAL;
1295                         break;
1296                 }
1297                 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1298                         ret = -EFAULT;
1299                         break;
1300                 }
1301
1302                 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1303                         target = 1;
1304                 else
1305                         target = 0;
1306
1307                 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1308                                                          rev.revision,
1309                                                          target, &ret),
1310                                         "ip6t_%s", rev.name);
1311                 break;
1312         }
1313
1314         default:
1315                 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1316                 ret = -EINVAL;
1317         }
1318
1319         return ret;
1320 }
1321
1322 int ip6t_register_table(struct xt_table *table,
1323                         const struct ip6t_replace *repl)
1324 {
1325         int ret;
1326         struct xt_table_info *newinfo;
1327         struct xt_table_info bootstrap
1328                 = { 0, 0, 0, { 0 }, { 0 }, { } };
1329         void *loc_cpu_entry;
1330
1331         newinfo = xt_alloc_table_info(repl->size);
1332         if (!newinfo)
1333                 return -ENOMEM;
1334
1335         /* choose the copy on our node/cpu */
1336         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1337         memcpy(loc_cpu_entry, repl->entries, repl->size);
1338
1339         ret = translate_table(table->name, table->valid_hooks,
1340                               newinfo, loc_cpu_entry, repl->size,
1341                               repl->num_entries,
1342                               repl->hook_entry,
1343                               repl->underflow);
1344         if (ret != 0) {
1345                 xt_free_table_info(newinfo);
1346                 return ret;
1347         }
1348
1349         ret = xt_register_table(table, &bootstrap, newinfo);
1350         if (ret != 0) {
1351                 xt_free_table_info(newinfo);
1352                 return ret;
1353         }
1354
1355         return 0;
1356 }
1357
1358 void ip6t_unregister_table(struct xt_table *table)
1359 {
1360         struct xt_table_info *private;
1361         void *loc_cpu_entry;
1362
1363         private = xt_unregister_table(table);
1364
1365         /* Decrease module usage counts and free resources */
1366         loc_cpu_entry = private->entries[raw_smp_processor_id()];
1367         IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1368         xt_free_table_info(private);
1369 }
1370
1371 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1372 static inline bool
1373 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1374                      u_int8_t type, u_int8_t code,
1375                      bool invert)
1376 {
1377         return (type == test_type && code >= min_code && code <= max_code)
1378                 ^ invert;
1379 }
1380
1381 static bool
1382 icmp6_match(const struct sk_buff *skb,
1383            const struct net_device *in,
1384            const struct net_device *out,
1385            const struct xt_match *match,
1386            const void *matchinfo,
1387            int offset,
1388            unsigned int protoff,
1389            bool *hotdrop)
1390 {
1391         struct icmp6hdr _icmp, *ic;
1392         const struct ip6t_icmp *icmpinfo = matchinfo;
1393
1394         /* Must not be a fragment. */
1395         if (offset)
1396                 return false;
1397
1398         ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1399         if (ic == NULL) {
1400                 /* We've been asked to examine this packet, and we
1401                    can't.  Hence, no choice but to drop. */
1402                 duprintf("Dropping evil ICMP tinygram.\n");
1403                 *hotdrop = true;
1404                 return false;
1405         }
1406
1407         return icmp6_type_code_match(icmpinfo->type,
1408                                      icmpinfo->code[0],
1409                                      icmpinfo->code[1],
1410                                      ic->icmp6_type, ic->icmp6_code,
1411                                      !!(icmpinfo->invflags&IP6T_ICMP_INV));
1412 }
1413
1414 /* Called when user tries to insert an entry of this type. */
1415 static bool
1416 icmp6_checkentry(const char *tablename,
1417            const void *entry,
1418            const struct xt_match *match,
1419            void *matchinfo,
1420            unsigned int hook_mask)
1421 {
1422         const struct ip6t_icmp *icmpinfo = matchinfo;
1423
1424         /* Must specify no unknown invflags */
1425         return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1426 }
1427
1428 /* The built-in targets: standard (NULL) and error. */
1429 static struct xt_target ip6t_standard_target __read_mostly = {
1430         .name           = IP6T_STANDARD_TARGET,
1431         .targetsize     = sizeof(int),
1432         .family         = AF_INET6,
1433 };
1434
1435 static struct xt_target ip6t_error_target __read_mostly = {
1436         .name           = IP6T_ERROR_TARGET,
1437         .target         = ip6t_error,
1438         .targetsize     = IP6T_FUNCTION_MAXNAMELEN,
1439         .family         = AF_INET6,
1440 };
1441
1442 static struct nf_sockopt_ops ip6t_sockopts = {
1443         .pf             = PF_INET6,
1444         .set_optmin     = IP6T_BASE_CTL,
1445         .set_optmax     = IP6T_SO_SET_MAX+1,
1446         .set            = do_ip6t_set_ctl,
1447         .get_optmin     = IP6T_BASE_CTL,
1448         .get_optmax     = IP6T_SO_GET_MAX+1,
1449         .get            = do_ip6t_get_ctl,
1450         .owner          = THIS_MODULE,
1451 };
1452
1453 static struct xt_match icmp6_matchstruct __read_mostly = {
1454         .name           = "icmp6",
1455         .match          = &icmp6_match,
1456         .matchsize      = sizeof(struct ip6t_icmp),
1457         .checkentry     = icmp6_checkentry,
1458         .proto          = IPPROTO_ICMPV6,
1459         .family         = AF_INET6,
1460 };
1461
1462 static int __init ip6_tables_init(void)
1463 {
1464         int ret;
1465
1466         ret = xt_proto_init(AF_INET6);
1467         if (ret < 0)
1468                 goto err1;
1469
1470         /* Noone else will be downing sem now, so we won't sleep */
1471         ret = xt_register_target(&ip6t_standard_target);
1472         if (ret < 0)
1473                 goto err2;
1474         ret = xt_register_target(&ip6t_error_target);
1475         if (ret < 0)
1476                 goto err3;
1477         ret = xt_register_match(&icmp6_matchstruct);
1478         if (ret < 0)
1479                 goto err4;
1480
1481         /* Register setsockopt */
1482         ret = nf_register_sockopt(&ip6t_sockopts);
1483         if (ret < 0)
1484                 goto err5;
1485
1486         printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1487         return 0;
1488
1489 err5:
1490         xt_unregister_match(&icmp6_matchstruct);
1491 err4:
1492         xt_unregister_target(&ip6t_error_target);
1493 err3:
1494         xt_unregister_target(&ip6t_standard_target);
1495 err2:
1496         xt_proto_fini(AF_INET6);
1497 err1:
1498         return ret;
1499 }
1500
1501 static void __exit ip6_tables_fini(void)
1502 {
1503         nf_unregister_sockopt(&ip6t_sockopts);
1504         xt_unregister_match(&icmp6_matchstruct);
1505         xt_unregister_target(&ip6t_error_target);
1506         xt_unregister_target(&ip6t_standard_target);
1507         xt_proto_fini(AF_INET6);
1508 }
1509
1510 /*
1511  * find the offset to specified header or the protocol number of last header
1512  * if target < 0. "last header" is transport protocol header, ESP, or
1513  * "No next header".
1514  *
1515  * If target header is found, its offset is set in *offset and return protocol
1516  * number. Otherwise, return -1.
1517  *
1518  * If the first fragment doesn't contain the final protocol header or
1519  * NEXTHDR_NONE it is considered invalid.
1520  *
1521  * Note that non-1st fragment is special case that "the protocol number
1522  * of last header" is "next header" field in Fragment header. In this case,
1523  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1524  * isn't NULL.
1525  *
1526  */
1527 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1528                   int target, unsigned short *fragoff)
1529 {
1530         unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
1531         u8 nexthdr = ipv6_hdr(skb)->nexthdr;
1532         unsigned int len = skb->len - start;
1533
1534         if (fragoff)
1535                 *fragoff = 0;
1536
1537         while (nexthdr != target) {
1538                 struct ipv6_opt_hdr _hdr, *hp;
1539                 unsigned int hdrlen;
1540
1541                 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1542                         if (target < 0)
1543                                 break;
1544                         return -ENOENT;
1545                 }
1546
1547                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1548                 if (hp == NULL)
1549                         return -EBADMSG;
1550                 if (nexthdr == NEXTHDR_FRAGMENT) {
1551                         unsigned short _frag_off;
1552                         __be16 *fp;
1553                         fp = skb_header_pointer(skb,
1554                                                 start+offsetof(struct frag_hdr,
1555                                                                frag_off),
1556                                                 sizeof(_frag_off),
1557                                                 &_frag_off);
1558                         if (fp == NULL)
1559                                 return -EBADMSG;
1560
1561                         _frag_off = ntohs(*fp) & ~0x7;
1562                         if (_frag_off) {
1563                                 if (target < 0 &&
1564                                     ((!ipv6_ext_hdr(hp->nexthdr)) ||
1565                                      hp->nexthdr == NEXTHDR_NONE)) {
1566                                         if (fragoff)
1567                                                 *fragoff = _frag_off;
1568                                         return hp->nexthdr;
1569                                 }
1570                                 return -ENOENT;
1571                         }
1572                         hdrlen = 8;
1573                 } else if (nexthdr == NEXTHDR_AUTH)
1574                         hdrlen = (hp->hdrlen + 2) << 2;
1575                 else
1576                         hdrlen = ipv6_optlen(hp);
1577
1578                 nexthdr = hp->nexthdr;
1579                 len -= hdrlen;
1580                 start += hdrlen;
1581         }
1582
1583         *offset = start;
1584         return nexthdr;
1585 }
1586
1587 EXPORT_SYMBOL(ip6t_register_table);
1588 EXPORT_SYMBOL(ip6t_unregister_table);
1589 EXPORT_SYMBOL(ip6t_do_table);
1590 EXPORT_SYMBOL(ip6t_ext_hdr);
1591 EXPORT_SYMBOL(ipv6_find_hdr);
1592
1593 module_init(ip6_tables_init);
1594 module_exit(ip6_tables_fini);