[NETFILTER]: x_tables: remove obsolete overflow check
[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 IPV6_HDR_LEN    (sizeof(struct ipv6hdr))
35 #define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
36
37 /*#define DEBUG_IP_FIREWALL*/
38 /*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
39 /*#define DEBUG_IP_FIREWALL_USER*/
40
41 #ifdef DEBUG_IP_FIREWALL
42 #define dprintf(format, args...)  printk(format , ## args)
43 #else
44 #define dprintf(format, args...)
45 #endif
46
47 #ifdef DEBUG_IP_FIREWALL_USER
48 #define duprintf(format, args...) printk(format , ## args)
49 #else
50 #define duprintf(format, args...)
51 #endif
52
53 #ifdef CONFIG_NETFILTER_DEBUG
54 #define IP_NF_ASSERT(x)                                         \
55 do {                                                            \
56         if (!(x))                                               \
57                 printk("IP_NF_ASSERT: %s:%s:%u\n",              \
58                        __FUNCTION__, __FILE__, __LINE__);       \
59 } while(0)
60 #else
61 #define IP_NF_ASSERT(x)
62 #endif
63
64 #if 0
65 /* All the better to debug you with... */
66 #define static
67 #define inline
68 #endif
69
70 /*
71    We keep a set of rules for each CPU, so we can avoid write-locking
72    them in the softirq when updating the counters and therefore
73    only need to read-lock in the softirq; doing a write_lock_bh() in user
74    context stops packets coming through and allows user context to read
75    the counters or update the rules.
76
77    Hence the start of any table is given by get_table() below.  */
78
79 #if 0
80 #define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
81 #define down_interruptible(x) ({ int __r; printk("DOWNi:%u:" #x "\n", __LINE__); __r = down_interruptible(x); if (__r != 0) printk("ABORT-DOWNi:%u\n", __LINE__); __r; })
82 #define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
83 #endif
84
85 /* Check for an extension */
86 int
87 ip6t_ext_hdr(u8 nexthdr)
88 {
89         return ( (nexthdr == IPPROTO_HOPOPTS)   ||
90                  (nexthdr == IPPROTO_ROUTING)   ||
91                  (nexthdr == IPPROTO_FRAGMENT)  ||
92                  (nexthdr == IPPROTO_ESP)       ||
93                  (nexthdr == IPPROTO_AH)        ||
94                  (nexthdr == IPPROTO_NONE)      ||
95                  (nexthdr == IPPROTO_DSTOPTS) );
96 }
97
98 /* Returns whether matches rule or not. */
99 static inline bool
100 ip6_packet_match(const struct sk_buff *skb,
101                  const char *indev,
102                  const char *outdev,
103                  const struct ip6t_ip6 *ip6info,
104                  unsigned int *protoff,
105                  int *fragoff, bool *hotdrop)
106 {
107         size_t i;
108         unsigned long ret;
109         const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
110
111 #define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
112
113         if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
114                                        &ip6info->src), IP6T_INV_SRCIP)
115             || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
116                                           &ip6info->dst), IP6T_INV_DSTIP)) {
117                 dprintf("Source or dest mismatch.\n");
118 /*
119                 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
120                         ipinfo->smsk.s_addr, ipinfo->src.s_addr,
121                         ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
122                 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
123                         ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
124                         ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
125                 return false;
126         }
127
128         /* Look for ifname matches; this should unroll nicely. */
129         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
130                 ret |= (((const unsigned long *)indev)[i]
131                         ^ ((const unsigned long *)ip6info->iniface)[i])
132                         & ((const unsigned long *)ip6info->iniface_mask)[i];
133         }
134
135         if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
136                 dprintf("VIA in mismatch (%s vs %s).%s\n",
137                         indev, ip6info->iniface,
138                         ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
139                 return false;
140         }
141
142         for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
143                 ret |= (((const unsigned long *)outdev)[i]
144                         ^ ((const unsigned long *)ip6info->outiface)[i])
145                         & ((const unsigned long *)ip6info->outiface_mask)[i];
146         }
147
148         if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
149                 dprintf("VIA out mismatch (%s vs %s).%s\n",
150                         outdev, ip6info->outiface,
151                         ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
152                 return false;
153         }
154
155 /* ... might want to do something with class and flowlabel here ... */
156
157         /* look for the desired protocol header */
158         if((ip6info->flags & IP6T_F_PROTO)) {
159                 int protohdr;
160                 unsigned short _frag_off;
161
162                 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
163                 if (protohdr < 0) {
164                         if (_frag_off == 0)
165                                 *hotdrop = true;
166                         return false;
167                 }
168                 *fragoff = _frag_off;
169
170                 dprintf("Packet protocol %hi ?= %s%hi.\n",
171                                 protohdr,
172                                 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
173                                 ip6info->proto);
174
175                 if (ip6info->proto == protohdr) {
176                         if(ip6info->invflags & IP6T_INV_PROTO) {
177                                 return false;
178                         }
179                         return true;
180                 }
181
182                 /* We need match for the '-p all', too! */
183                 if ((ip6info->proto != 0) &&
184                         !(ip6info->invflags & IP6T_INV_PROTO))
185                         return false;
186         }
187         return true;
188 }
189
190 /* should be ip6 safe */
191 static inline bool
192 ip6_checkentry(const struct ip6t_ip6 *ipv6)
193 {
194         if (ipv6->flags & ~IP6T_F_MASK) {
195                 duprintf("Unknown flag bits set: %08X\n",
196                          ipv6->flags & ~IP6T_F_MASK);
197                 return false;
198         }
199         if (ipv6->invflags & ~IP6T_INV_MASK) {
200                 duprintf("Unknown invflag bits set: %08X\n",
201                          ipv6->invflags & ~IP6T_INV_MASK);
202                 return false;
203         }
204         return true;
205 }
206
207 static unsigned int
208 ip6t_error(struct sk_buff *skb,
209           const struct net_device *in,
210           const struct net_device *out,
211           unsigned int hooknum,
212           const struct xt_target *target,
213           const void *targinfo)
214 {
215         if (net_ratelimit())
216                 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
217
218         return NF_DROP;
219 }
220
221 static inline
222 bool do_match(struct ip6t_entry_match *m,
223               const struct sk_buff *skb,
224               const struct net_device *in,
225               const struct net_device *out,
226               int offset,
227               unsigned int protoff,
228               bool *hotdrop)
229 {
230         /* Stop iteration if it doesn't match */
231         if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
232                                       offset, protoff, hotdrop))
233                 return true;
234         else
235                 return false;
236 }
237
238 static inline struct ip6t_entry *
239 get_entry(void *base, unsigned int offset)
240 {
241         return (struct ip6t_entry *)(base + offset);
242 }
243
244 /* All zeroes == unconditional rule. */
245 static inline int
246 unconditional(const struct ip6t_ip6 *ipv6)
247 {
248         unsigned int i;
249
250         for (i = 0; i < sizeof(*ipv6); i++)
251                 if (((char *)ipv6)[i])
252                         break;
253
254         return (i == sizeof(*ipv6));
255 }
256
257 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
258     defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
259 /* This cries for unification! */
260 static const char *hooknames[] = {
261         [NF_INET_PRE_ROUTING]           = "PREROUTING",
262         [NF_INET_LOCAL_IN]              = "INPUT",
263         [NF_INET_FORWARD]               = "FORWARD",
264         [NF_INET_LOCAL_OUT]             = "OUTPUT",
265         [NF_INET_POST_ROUTING]          = "POSTROUTING",
266 };
267
268 enum nf_ip_trace_comments {
269         NF_IP6_TRACE_COMMENT_RULE,
270         NF_IP6_TRACE_COMMENT_RETURN,
271         NF_IP6_TRACE_COMMENT_POLICY,
272 };
273
274 static const char *comments[] = {
275         [NF_IP6_TRACE_COMMENT_RULE]     = "rule",
276         [NF_IP6_TRACE_COMMENT_RETURN]   = "return",
277         [NF_IP6_TRACE_COMMENT_POLICY]   = "policy",
278 };
279
280 static struct nf_loginfo trace_loginfo = {
281         .type = NF_LOG_TYPE_LOG,
282         .u = {
283                 .log = {
284                         .level = 4,
285                         .logflags = NF_LOG_MASK,
286                 },
287         },
288 };
289
290 static inline int
291 get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
292                       char *hookname, char **chainname,
293                       char **comment, unsigned int *rulenum)
294 {
295         struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
296
297         if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
298                 /* Head of user chain: ERROR target with chainname */
299                 *chainname = t->target.data;
300                 (*rulenum) = 0;
301         } else if (s == e) {
302                 (*rulenum)++;
303
304                 if (s->target_offset == sizeof(struct ip6t_entry)
305                    && strcmp(t->target.u.kernel.target->name,
306                              IP6T_STANDARD_TARGET) == 0
307                    && t->verdict < 0
308                    && unconditional(&s->ipv6)) {
309                         /* Tail of chains: STANDARD target (return/policy) */
310                         *comment = *chainname == hookname
311                                 ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
312                                 : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
313                 }
314                 return 1;
315         } else
316                 (*rulenum)++;
317
318         return 0;
319 }
320
321 static void trace_packet(struct sk_buff *skb,
322                          unsigned int hook,
323                          const struct net_device *in,
324                          const struct net_device *out,
325                          char *tablename,
326                          struct xt_table_info *private,
327                          struct ip6t_entry *e)
328 {
329         void *table_base;
330         struct ip6t_entry *root;
331         char *hookname, *chainname, *comment;
332         unsigned int rulenum = 0;
333
334         table_base = (void *)private->entries[smp_processor_id()];
335         root = get_entry(table_base, private->hook_entry[hook]);
336
337         hookname = chainname = (char *)hooknames[hook];
338         comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
339
340         IP6T_ENTRY_ITERATE(root,
341                            private->size - private->hook_entry[hook],
342                            get_chainname_rulenum,
343                            e, hookname, &chainname, &comment, &rulenum);
344
345         nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
346                       "TRACE: %s:%s:%s:%u ",
347                       tablename, chainname, comment, rulenum);
348 }
349 #endif
350
351 /* Returns one of the generic firewall policies, like NF_ACCEPT. */
352 unsigned int
353 ip6t_do_table(struct sk_buff *skb,
354               unsigned int hook,
355               const struct net_device *in,
356               const struct net_device *out,
357               struct xt_table *table)
358 {
359         static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
360         int offset = 0;
361         unsigned int protoff = 0;
362         bool hotdrop = false;
363         /* Initializing verdict to NF_DROP keeps gcc happy. */
364         unsigned int verdict = NF_DROP;
365         const char *indev, *outdev;
366         void *table_base;
367         struct ip6t_entry *e, *back;
368         struct xt_table_info *private;
369
370         /* Initialization */
371         indev = in ? in->name : nulldevname;
372         outdev = out ? out->name : nulldevname;
373         /* We handle fragments by dealing with the first fragment as
374          * if it was a normal packet.  All other fragments are treated
375          * normally, except that they will NEVER match rules that ask
376          * things we don't know, ie. tcp syn flag or ports).  If the
377          * rule is also a fragment-specific rule, non-fragments won't
378          * match it. */
379
380         read_lock_bh(&table->lock);
381         private = table->private;
382         IP_NF_ASSERT(table->valid_hooks & (1 << hook));
383         table_base = (void *)private->entries[smp_processor_id()];
384         e = get_entry(table_base, private->hook_entry[hook]);
385
386         /* For return from builtin chain */
387         back = get_entry(table_base, private->underflow[hook]);
388
389         do {
390                 IP_NF_ASSERT(e);
391                 IP_NF_ASSERT(back);
392                 if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
393                         &protoff, &offset, &hotdrop)) {
394                         struct ip6t_entry_target *t;
395
396                         if (IP6T_MATCH_ITERATE(e, do_match,
397                                                skb, in, out,
398                                                offset, protoff, &hotdrop) != 0)
399                                 goto no_match;
400
401                         ADD_COUNTER(e->counters,
402                                     ntohs(ipv6_hdr(skb)->payload_len)
403                                     + IPV6_HDR_LEN,
404                                     1);
405
406                         t = ip6t_get_target(e);
407                         IP_NF_ASSERT(t->u.kernel.target);
408
409 #if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
410     defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
411                         /* The packet is traced: log it */
412                         if (unlikely(skb->nf_trace))
413                                 trace_packet(skb, hook, in, out,
414                                              table->name, private, e);
415 #endif
416                         /* Standard target? */
417                         if (!t->u.kernel.target->target) {
418                                 int v;
419
420                                 v = ((struct ip6t_standard_target *)t)->verdict;
421                                 if (v < 0) {
422                                         /* Pop from stack? */
423                                         if (v != IP6T_RETURN) {
424                                                 verdict = (unsigned)(-v) - 1;
425                                                 break;
426                                         }
427                                         e = back;
428                                         back = get_entry(table_base,
429                                                          back->comefrom);
430                                         continue;
431                                 }
432                                 if (table_base + v != (void *)e + e->next_offset
433                                     && !(e->ipv6.flags & IP6T_F_GOTO)) {
434                                         /* Save old back ptr in next entry */
435                                         struct ip6t_entry *next
436                                                 = (void *)e + e->next_offset;
437                                         next->comefrom
438                                                 = (void *)back - table_base;
439                                         /* set back pointer to next entry */
440                                         back = next;
441                                 }
442
443                                 e = get_entry(table_base, v);
444                         } else {
445                                 /* Targets which reenter must return
446                                    abs. verdicts */
447 #ifdef CONFIG_NETFILTER_DEBUG
448                                 ((struct ip6t_entry *)table_base)->comefrom
449                                         = 0xeeeeeeec;
450 #endif
451                                 verdict = t->u.kernel.target->target(skb,
452                                                                      in, out,
453                                                                      hook,
454                                                                      t->u.kernel.target,
455                                                                      t->data);
456
457 #ifdef CONFIG_NETFILTER_DEBUG
458                                 if (((struct ip6t_entry *)table_base)->comefrom
459                                     != 0xeeeeeeec
460                                     && verdict == IP6T_CONTINUE) {
461                                         printk("Target %s reentered!\n",
462                                                t->u.kernel.target->name);
463                                         verdict = NF_DROP;
464                                 }
465                                 ((struct ip6t_entry *)table_base)->comefrom
466                                         = 0x57acc001;
467 #endif
468                                 if (verdict == IP6T_CONTINUE)
469                                         e = (void *)e + e->next_offset;
470                                 else
471                                         /* Verdict */
472                                         break;
473                         }
474                 } else {
475
476                 no_match:
477                         e = (void *)e + e->next_offset;
478                 }
479         } while (!hotdrop);
480
481 #ifdef CONFIG_NETFILTER_DEBUG
482         ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
483 #endif
484         read_unlock_bh(&table->lock);
485
486 #ifdef DEBUG_ALLOW_ALL
487         return NF_ACCEPT;
488 #else
489         if (hotdrop)
490                 return NF_DROP;
491         else return verdict;
492 #endif
493 }
494
495 /* Figures out from what hook each rule can be called: returns 0 if
496    there are loops.  Puts hook bitmask in comefrom. */
497 static int
498 mark_source_chains(struct xt_table_info *newinfo,
499                    unsigned int valid_hooks, void *entry0)
500 {
501         unsigned int hook;
502
503         /* No recursion; use packet counter to save back ptrs (reset
504            to 0 as we leave), and comefrom to save source hook bitmask */
505         for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
506                 unsigned int pos = newinfo->hook_entry[hook];
507                 struct ip6t_entry *e
508                         = (struct ip6t_entry *)(entry0 + pos);
509                 int visited = e->comefrom & (1 << hook);
510
511                 if (!(valid_hooks & (1 << hook)))
512                         continue;
513
514                 /* Set initial back pointer. */
515                 e->counters.pcnt = pos;
516
517                 for (;;) {
518                         struct ip6t_standard_target *t
519                                 = (void *)ip6t_get_target(e);
520
521                         if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
522                                 printk("iptables: loop hook %u pos %u %08X.\n",
523                                        hook, pos, e->comefrom);
524                                 return 0;
525                         }
526                         e->comefrom
527                                 |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
528
529                         /* Unconditional return/END. */
530                         if ((e->target_offset == sizeof(struct ip6t_entry)
531                             && (strcmp(t->target.u.user.name,
532                                        IP6T_STANDARD_TARGET) == 0)
533                             && t->verdict < 0
534                             && unconditional(&e->ipv6)) || visited) {
535                                 unsigned int oldpos, size;
536
537                                 if (t->verdict < -NF_MAX_VERDICT - 1) {
538                                         duprintf("mark_source_chains: bad "
539                                                 "negative verdict (%i)\n",
540                                                                 t->verdict);
541                                         return 0;
542                                 }
543
544                                 /* Return: backtrack through the last
545                                    big jump. */
546                                 do {
547                                         e->comefrom ^= (1<<NF_INET_NUMHOOKS);
548 #ifdef DEBUG_IP_FIREWALL_USER
549                                         if (e->comefrom
550                                             & (1 << NF_INET_NUMHOOKS)) {
551                                                 duprintf("Back unset "
552                                                          "on hook %u "
553                                                          "rule %u\n",
554                                                          hook, pos);
555                                         }
556 #endif
557                                         oldpos = pos;
558                                         pos = e->counters.pcnt;
559                                         e->counters.pcnt = 0;
560
561                                         /* We're at the start. */
562                                         if (pos == oldpos)
563                                                 goto next;
564
565                                         e = (struct ip6t_entry *)
566                                                 (entry0 + pos);
567                                 } while (oldpos == pos + e->next_offset);
568
569                                 /* Move along one */
570                                 size = e->next_offset;
571                                 e = (struct ip6t_entry *)
572                                         (entry0 + pos + size);
573                                 e->counters.pcnt = pos;
574                                 pos += size;
575                         } else {
576                                 int newpos = t->verdict;
577
578                                 if (strcmp(t->target.u.user.name,
579                                            IP6T_STANDARD_TARGET) == 0
580                                     && newpos >= 0) {
581                                         if (newpos > newinfo->size -
582                                                 sizeof(struct ip6t_entry)) {
583                                                 duprintf("mark_source_chains: "
584                                                         "bad verdict (%i)\n",
585                                                                 newpos);
586                                                 return 0;
587                                         }
588                                         /* This a jump; chase it. */
589                                         duprintf("Jump rule %u -> %u\n",
590                                                  pos, newpos);
591                                 } else {
592                                         /* ... this is a fallthru */
593                                         newpos = pos + e->next_offset;
594                                 }
595                                 e = (struct ip6t_entry *)
596                                         (entry0 + newpos);
597                                 e->counters.pcnt = pos;
598                                 pos = newpos;
599                         }
600                 }
601                 next:
602                 duprintf("Finished chain %u\n", hook);
603         }
604         return 1;
605 }
606
607 static inline int
608 cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
609 {
610         if (i && (*i)-- == 0)
611                 return 1;
612
613         if (m->u.kernel.match->destroy)
614                 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
615         module_put(m->u.kernel.match->me);
616         return 0;
617 }
618
619 static inline int
620 check_match(struct ip6t_entry_match *m,
621             const char *name,
622             const struct ip6t_ip6 *ipv6,
623             unsigned int hookmask,
624             unsigned int *i)
625 {
626         struct xt_match *match;
627         int ret;
628
629         match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
630                                         m->u.user.revision),
631                                         "ip6t_%s", m->u.user.name);
632         if (IS_ERR(match) || !match) {
633                 duprintf("check_match: `%s' not found\n", m->u.user.name);
634                 return match ? PTR_ERR(match) : -ENOENT;
635         }
636         m->u.kernel.match = match;
637
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)
642                 goto err;
643
644         if (m->u.kernel.match->checkentry
645             && !m->u.kernel.match->checkentry(name, ipv6, match,  m->data,
646                                               hookmask)) {
647                 duprintf("ip_tables: check failed for `%s'.\n",
648                          m->u.kernel.match->name);
649                 ret = -EINVAL;
650                 goto err;
651         }
652
653         (*i)++;
654         return 0;
655 err:
656         module_put(m->u.kernel.match->me);
657         return ret;
658 }
659
660 static struct xt_target ip6t_standard_target;
661
662 static inline int
663 check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
664             unsigned int *i)
665 {
666         struct ip6t_entry_target *t;
667         struct xt_target *target;
668         int ret;
669         unsigned int j;
670
671         if (!ip6_checkentry(&e->ipv6)) {
672                 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
673                 return -EINVAL;
674         }
675
676         if (e->target_offset + sizeof(struct ip6t_entry_target) >
677                                                                 e->next_offset)
678                 return -EINVAL;
679
680         j = 0;
681         ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
682         if (ret != 0)
683                 goto cleanup_matches;
684
685         t = ip6t_get_target(e);
686         ret = -EINVAL;
687         if (e->target_offset + t->u.target_size > e->next_offset)
688                         goto cleanup_matches;
689         target = try_then_request_module(xt_find_target(AF_INET6,
690                                                         t->u.user.name,
691                                                         t->u.user.revision),
692                                          "ip6t_%s", t->u.user.name);
693         if (IS_ERR(target) || !target) {
694                 duprintf("check_entry: `%s' not found\n", t->u.user.name);
695                 ret = target ? PTR_ERR(target) : -ENOENT;
696                 goto cleanup_matches;
697         }
698         t->u.kernel.target = target;
699
700         ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
701                               name, e->comefrom, e->ipv6.proto,
702                               e->ipv6.invflags & IP6T_INV_PROTO);
703         if (ret)
704                 goto err;
705
706         if (t->u.kernel.target->checkentry
707                    && !t->u.kernel.target->checkentry(name, e, target, t->data,
708                                                       e->comefrom)) {
709                 duprintf("ip_tables: check failed for `%s'.\n",
710                          t->u.kernel.target->name);
711                 ret = -EINVAL;
712                 goto err;
713         }
714
715         (*i)++;
716         return 0;
717  err:
718         module_put(t->u.kernel.target->me);
719  cleanup_matches:
720         IP6T_MATCH_ITERATE(e, cleanup_match, &j);
721         return ret;
722 }
723
724 static inline int
725 check_entry_size_and_hooks(struct ip6t_entry *e,
726                            struct xt_table_info *newinfo,
727                            unsigned char *base,
728                            unsigned char *limit,
729                            const unsigned int *hook_entries,
730                            const unsigned int *underflows,
731                            unsigned int *i)
732 {
733         unsigned int h;
734
735         if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
736             || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
737                 duprintf("Bad offset %p\n", e);
738                 return -EINVAL;
739         }
740
741         if (e->next_offset
742             < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
743                 duprintf("checking: element %p size %u\n",
744                          e, e->next_offset);
745                 return -EINVAL;
746         }
747
748         /* Check hooks & underflows */
749         for (h = 0; h < NF_INET_NUMHOOKS; h++) {
750                 if ((unsigned char *)e - base == hook_entries[h])
751                         newinfo->hook_entry[h] = hook_entries[h];
752                 if ((unsigned char *)e - base == underflows[h])
753                         newinfo->underflow[h] = underflows[h];
754         }
755
756         /* FIXME: underflows must be unconditional, standard verdicts
757            < 0 (not IP6T_RETURN). --RR */
758
759         /* Clear counters and comefrom */
760         e->counters = ((struct xt_counters) { 0, 0 });
761         e->comefrom = 0;
762
763         (*i)++;
764         return 0;
765 }
766
767 static inline int
768 cleanup_entry(struct ip6t_entry *e, unsigned int *i)
769 {
770         struct ip6t_entry_target *t;
771
772         if (i && (*i)-- == 0)
773                 return 1;
774
775         /* Cleanup all matches */
776         IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
777         t = ip6t_get_target(e);
778         if (t->u.kernel.target->destroy)
779                 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
780         module_put(t->u.kernel.target->me);
781         return 0;
782 }
783
784 /* Checks and translates the user-supplied table segment (held in
785    newinfo) */
786 static int
787 translate_table(const char *name,
788                 unsigned int valid_hooks,
789                 struct xt_table_info *newinfo,
790                 void *entry0,
791                 unsigned int size,
792                 unsigned int number,
793                 const unsigned int *hook_entries,
794                 const unsigned int *underflows)
795 {
796         unsigned int i;
797         int ret;
798
799         newinfo->size = size;
800         newinfo->number = number;
801
802         /* Init all hooks to impossible value. */
803         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
804                 newinfo->hook_entry[i] = 0xFFFFFFFF;
805                 newinfo->underflow[i] = 0xFFFFFFFF;
806         }
807
808         duprintf("translate_table: size %u\n", newinfo->size);
809         i = 0;
810         /* Walk through entries, checking offsets. */
811         ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
812                                 check_entry_size_and_hooks,
813                                 newinfo,
814                                 entry0,
815                                 entry0 + size,
816                                 hook_entries, underflows, &i);
817         if (ret != 0)
818                 return ret;
819
820         if (i != number) {
821                 duprintf("translate_table: %u not %u entries\n",
822                          i, number);
823                 return -EINVAL;
824         }
825
826         /* Check hooks all assigned */
827         for (i = 0; i < NF_INET_NUMHOOKS; i++) {
828                 /* Only hooks which are valid */
829                 if (!(valid_hooks & (1 << i)))
830                         continue;
831                 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
832                         duprintf("Invalid hook entry %u %u\n",
833                                  i, hook_entries[i]);
834                         return -EINVAL;
835                 }
836                 if (newinfo->underflow[i] == 0xFFFFFFFF) {
837                         duprintf("Invalid underflow %u %u\n",
838                                  i, underflows[i]);
839                         return -EINVAL;
840                 }
841         }
842
843         if (!mark_source_chains(newinfo, valid_hooks, entry0))
844                 return -ELOOP;
845
846         /* Finally, each sanity check must pass */
847         i = 0;
848         ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
849                                 check_entry, name, size, &i);
850
851         if (ret != 0) {
852                 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
853                                    cleanup_entry, &i);
854                 return ret;
855         }
856
857         /* And one copy for every other CPU */
858         for_each_possible_cpu(i) {
859                 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
860                         memcpy(newinfo->entries[i], entry0, newinfo->size);
861         }
862
863         return 0;
864 }
865
866 /* Gets counters. */
867 static inline int
868 add_entry_to_counter(const struct ip6t_entry *e,
869                      struct xt_counters total[],
870                      unsigned int *i)
871 {
872         ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
873
874         (*i)++;
875         return 0;
876 }
877
878 static inline int
879 set_entry_to_counter(const struct ip6t_entry *e,
880                      struct ip6t_counters total[],
881                      unsigned int *i)
882 {
883         SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
884
885         (*i)++;
886         return 0;
887 }
888
889 static void
890 get_counters(const struct xt_table_info *t,
891              struct xt_counters counters[])
892 {
893         unsigned int cpu;
894         unsigned int i;
895         unsigned int curcpu;
896
897         /* Instead of clearing (by a previous call to memset())
898          * the counters and using adds, we set the counters
899          * with data used by 'current' CPU
900          * We dont care about preemption here.
901          */
902         curcpu = raw_smp_processor_id();
903
904         i = 0;
905         IP6T_ENTRY_ITERATE(t->entries[curcpu],
906                            t->size,
907                            set_entry_to_counter,
908                            counters,
909                            &i);
910
911         for_each_possible_cpu(cpu) {
912                 if (cpu == curcpu)
913                         continue;
914                 i = 0;
915                 IP6T_ENTRY_ITERATE(t->entries[cpu],
916                                   t->size,
917                                   add_entry_to_counter,
918                                   counters,
919                                   &i);
920         }
921 }
922
923 static int
924 copy_entries_to_user(unsigned int total_size,
925                      struct xt_table *table,
926                      void __user *userptr)
927 {
928         unsigned int off, num, countersize;
929         struct ip6t_entry *e;
930         struct xt_counters *counters;
931         struct xt_table_info *private = table->private;
932         int ret = 0;
933         void *loc_cpu_entry;
934
935         /* We need atomic snapshot of counters: rest doesn't change
936            (other than comefrom, which userspace doesn't care
937            about). */
938         countersize = sizeof(struct xt_counters) * private->number;
939         counters = vmalloc(countersize);
940
941         if (counters == NULL)
942                 return -ENOMEM;
943
944         /* First, sum counters... */
945         write_lock_bh(&table->lock);
946         get_counters(private, counters);
947         write_unlock_bh(&table->lock);
948
949         /* choose the copy that is on ourc node/cpu */
950         loc_cpu_entry = private->entries[raw_smp_processor_id()];
951         if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
952                 ret = -EFAULT;
953                 goto free_counters;
954         }
955
956         /* FIXME: use iterator macros --RR */
957         /* ... then go back and fix counters and names */
958         for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
959                 unsigned int i;
960                 struct ip6t_entry_match *m;
961                 struct ip6t_entry_target *t;
962
963                 e = (struct ip6t_entry *)(loc_cpu_entry + off);
964                 if (copy_to_user(userptr + off
965                                  + offsetof(struct ip6t_entry, counters),
966                                  &counters[num],
967                                  sizeof(counters[num])) != 0) {
968                         ret = -EFAULT;
969                         goto free_counters;
970                 }
971
972                 for (i = sizeof(struct ip6t_entry);
973                      i < e->target_offset;
974                      i += m->u.match_size) {
975                         m = (void *)e + i;
976
977                         if (copy_to_user(userptr + off + i
978                                          + offsetof(struct ip6t_entry_match,
979                                                     u.user.name),
980                                          m->u.kernel.match->name,
981                                          strlen(m->u.kernel.match->name)+1)
982                             != 0) {
983                                 ret = -EFAULT;
984                                 goto free_counters;
985                         }
986                 }
987
988                 t = ip6t_get_target(e);
989                 if (copy_to_user(userptr + off + e->target_offset
990                                  + offsetof(struct ip6t_entry_target,
991                                             u.user.name),
992                                  t->u.kernel.target->name,
993                                  strlen(t->u.kernel.target->name)+1) != 0) {
994                         ret = -EFAULT;
995                         goto free_counters;
996                 }
997         }
998
999  free_counters:
1000         vfree(counters);
1001         return ret;
1002 }
1003
1004 static int
1005 get_entries(const struct ip6t_get_entries *entries,
1006             struct ip6t_get_entries __user *uptr)
1007 {
1008         int ret;
1009         struct xt_table *t;
1010
1011         t = xt_find_table_lock(AF_INET6, entries->name);
1012         if (t && !IS_ERR(t)) {
1013                 struct xt_table_info *private = t->private;
1014                 duprintf("t->private->number = %u\n", private->number);
1015                 if (entries->size == private->size)
1016                         ret = copy_entries_to_user(private->size,
1017                                                    t, uptr->entrytable);
1018                 else {
1019                         duprintf("get_entries: I've got %u not %u!\n",
1020                                  private->size, entries->size);
1021                         ret = -EINVAL;
1022                 }
1023                 module_put(t->me);
1024                 xt_table_unlock(t);
1025         } else
1026                 ret = t ? PTR_ERR(t) : -ENOENT;
1027
1028         return ret;
1029 }
1030
1031 static int
1032 do_replace(void __user *user, unsigned int len)
1033 {
1034         int ret;
1035         struct ip6t_replace tmp;
1036         struct xt_table *t;
1037         struct xt_table_info *newinfo, *oldinfo;
1038         struct xt_counters *counters;
1039         void *loc_cpu_entry, *loc_cpu_old_entry;
1040
1041         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1042                 return -EFAULT;
1043
1044         /* overflow check */
1045         if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1046                 return -ENOMEM;
1047
1048         newinfo = xt_alloc_table_info(tmp.size);
1049         if (!newinfo)
1050                 return -ENOMEM;
1051
1052         /* choose the copy that is on our node/cpu */
1053         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1054         if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1055                            tmp.size) != 0) {
1056                 ret = -EFAULT;
1057                 goto free_newinfo;
1058         }
1059
1060         counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
1061         if (!counters) {
1062                 ret = -ENOMEM;
1063                 goto free_newinfo;
1064         }
1065
1066         ret = translate_table(tmp.name, tmp.valid_hooks,
1067                               newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1068                               tmp.hook_entry, tmp.underflow);
1069         if (ret != 0)
1070                 goto free_newinfo_counters;
1071
1072         duprintf("ip_tables: Translated table\n");
1073
1074         t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
1075                                     "ip6table_%s", tmp.name);
1076         if (!t || IS_ERR(t)) {
1077                 ret = t ? PTR_ERR(t) : -ENOENT;
1078                 goto free_newinfo_counters_untrans;
1079         }
1080
1081         /* You lied! */
1082         if (tmp.valid_hooks != t->valid_hooks) {
1083                 duprintf("Valid hook crap: %08X vs %08X\n",
1084                          tmp.valid_hooks, t->valid_hooks);
1085                 ret = -EINVAL;
1086                 goto put_module;
1087         }
1088
1089         oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1090         if (!oldinfo)
1091                 goto put_module;
1092
1093         /* Update module usage count based on number of rules */
1094         duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1095                 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1096         if ((oldinfo->number > oldinfo->initial_entries) ||
1097             (newinfo->number <= oldinfo->initial_entries))
1098                 module_put(t->me);
1099         if ((oldinfo->number > oldinfo->initial_entries) &&
1100             (newinfo->number <= oldinfo->initial_entries))
1101                 module_put(t->me);
1102
1103         /* Get the old counters. */
1104         get_counters(oldinfo, counters);
1105         /* Decrease module usage counts and free resource */
1106         loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1107         IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1108         xt_free_table_info(oldinfo);
1109         if (copy_to_user(tmp.counters, counters,
1110                          sizeof(struct xt_counters) * tmp.num_counters) != 0)
1111                 ret = -EFAULT;
1112         vfree(counters);
1113         xt_table_unlock(t);
1114         return ret;
1115
1116  put_module:
1117         module_put(t->me);
1118         xt_table_unlock(t);
1119  free_newinfo_counters_untrans:
1120         IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1121  free_newinfo_counters:
1122         vfree(counters);
1123  free_newinfo:
1124         xt_free_table_info(newinfo);
1125         return ret;
1126 }
1127
1128 /* We're lazy, and add to the first CPU; overflow works its fey magic
1129  * and everything is OK. */
1130 static inline int
1131 add_counter_to_entry(struct ip6t_entry *e,
1132                      const struct xt_counters addme[],
1133                      unsigned int *i)
1134 {
1135 #if 0
1136         duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1137                  *i,
1138                  (long unsigned int)e->counters.pcnt,
1139                  (long unsigned int)e->counters.bcnt,
1140                  (long unsigned int)addme[*i].pcnt,
1141                  (long unsigned int)addme[*i].bcnt);
1142 #endif
1143
1144         ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1145
1146         (*i)++;
1147         return 0;
1148 }
1149
1150 static int
1151 do_add_counters(void __user *user, unsigned int len)
1152 {
1153         unsigned int i;
1154         struct xt_counters_info tmp, *paddc;
1155         struct xt_table_info *private;
1156         struct xt_table *t;
1157         int ret = 0;
1158         void *loc_cpu_entry;
1159
1160         if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1161                 return -EFAULT;
1162
1163         if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1164                 return -EINVAL;
1165
1166         paddc = vmalloc(len);
1167         if (!paddc)
1168                 return -ENOMEM;
1169
1170         if (copy_from_user(paddc, user, len) != 0) {
1171                 ret = -EFAULT;
1172                 goto free;
1173         }
1174
1175         t = xt_find_table_lock(AF_INET6, tmp.name);
1176         if (!t || IS_ERR(t)) {
1177                 ret = t ? PTR_ERR(t) : -ENOENT;
1178                 goto free;
1179         }
1180
1181         write_lock_bh(&t->lock);
1182         private = t->private;
1183         if (private->number != tmp.num_counters) {
1184                 ret = -EINVAL;
1185                 goto unlock_up_free;
1186         }
1187
1188         i = 0;
1189         /* Choose the copy that is on our node */
1190         loc_cpu_entry = private->entries[smp_processor_id()];
1191         IP6T_ENTRY_ITERATE(loc_cpu_entry,
1192                           private->size,
1193                           add_counter_to_entry,
1194                           paddc->counters,
1195                           &i);
1196  unlock_up_free:
1197         write_unlock_bh(&t->lock);
1198         xt_table_unlock(t);
1199         module_put(t->me);
1200  free:
1201         vfree(paddc);
1202
1203         return ret;
1204 }
1205
1206 static int
1207 do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1208 {
1209         int ret;
1210
1211         if (!capable(CAP_NET_ADMIN))
1212                 return -EPERM;
1213
1214         switch (cmd) {
1215         case IP6T_SO_SET_REPLACE:
1216                 ret = do_replace(user, len);
1217                 break;
1218
1219         case IP6T_SO_SET_ADD_COUNTERS:
1220                 ret = do_add_counters(user, len);
1221                 break;
1222
1223         default:
1224                 duprintf("do_ip6t_set_ctl:  unknown request %i\n", cmd);
1225                 ret = -EINVAL;
1226         }
1227
1228         return ret;
1229 }
1230
1231 static int
1232 do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1233 {
1234         int ret;
1235
1236         if (!capable(CAP_NET_ADMIN))
1237                 return -EPERM;
1238
1239         switch (cmd) {
1240         case IP6T_SO_GET_INFO: {
1241                 char name[IP6T_TABLE_MAXNAMELEN];
1242                 struct xt_table *t;
1243
1244                 if (*len != sizeof(struct ip6t_getinfo)) {
1245                         duprintf("length %u != %u\n", *len,
1246                                  sizeof(struct ip6t_getinfo));
1247                         ret = -EINVAL;
1248                         break;
1249                 }
1250
1251                 if (copy_from_user(name, user, sizeof(name)) != 0) {
1252                         ret = -EFAULT;
1253                         break;
1254                 }
1255                 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1256
1257                 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1258                                             "ip6table_%s", name);
1259                 if (t && !IS_ERR(t)) {
1260                         struct ip6t_getinfo info;
1261                         struct xt_table_info *private = t->private;
1262
1263                         info.valid_hooks = t->valid_hooks;
1264                         memcpy(info.hook_entry, private->hook_entry,
1265                                sizeof(info.hook_entry));
1266                         memcpy(info.underflow, private->underflow,
1267                                sizeof(info.underflow));
1268                         info.num_entries = private->number;
1269                         info.size = private->size;
1270                         memcpy(info.name, name, sizeof(info.name));
1271
1272                         if (copy_to_user(user, &info, *len) != 0)
1273                                 ret = -EFAULT;
1274                         else
1275                                 ret = 0;
1276                         xt_table_unlock(t);
1277                         module_put(t->me);
1278                 } else
1279                         ret = t ? PTR_ERR(t) : -ENOENT;
1280         }
1281         break;
1282
1283         case IP6T_SO_GET_ENTRIES: {
1284                 struct ip6t_get_entries get;
1285
1286                 if (*len < sizeof(get)) {
1287                         duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1288                         ret = -EINVAL;
1289                 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1290                         ret = -EFAULT;
1291                 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1292                         duprintf("get_entries: %u != %u\n", *len,
1293                                  sizeof(struct ip6t_get_entries) + get.size);
1294                         ret = -EINVAL;
1295                 } else
1296                         ret = get_entries(&get, user);
1297                 break;
1298         }
1299
1300         case IP6T_SO_GET_REVISION_MATCH:
1301         case IP6T_SO_GET_REVISION_TARGET: {
1302                 struct ip6t_get_revision rev;
1303                 int target;
1304
1305                 if (*len != sizeof(rev)) {
1306                         ret = -EINVAL;
1307                         break;
1308                 }
1309                 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1310                         ret = -EFAULT;
1311                         break;
1312                 }
1313
1314                 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1315                         target = 1;
1316                 else
1317                         target = 0;
1318
1319                 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1320                                                          rev.revision,
1321                                                          target, &ret),
1322                                         "ip6t_%s", rev.name);
1323                 break;
1324         }
1325
1326         default:
1327                 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1328                 ret = -EINVAL;
1329         }
1330
1331         return ret;
1332 }
1333
1334 int ip6t_register_table(struct xt_table *table,
1335                         const struct ip6t_replace *repl)
1336 {
1337         int ret;
1338         struct xt_table_info *newinfo;
1339         struct xt_table_info bootstrap
1340                 = { 0, 0, 0, { 0 }, { 0 }, { } };
1341         void *loc_cpu_entry;
1342
1343         newinfo = xt_alloc_table_info(repl->size);
1344         if (!newinfo)
1345                 return -ENOMEM;
1346
1347         /* choose the copy on our node/cpu */
1348         loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1349         memcpy(loc_cpu_entry, repl->entries, repl->size);
1350
1351         ret = translate_table(table->name, table->valid_hooks,
1352                               newinfo, loc_cpu_entry, repl->size,
1353                               repl->num_entries,
1354                               repl->hook_entry,
1355                               repl->underflow);
1356         if (ret != 0) {
1357                 xt_free_table_info(newinfo);
1358                 return ret;
1359         }
1360
1361         ret = xt_register_table(table, &bootstrap, newinfo);
1362         if (ret != 0) {
1363                 xt_free_table_info(newinfo);
1364                 return ret;
1365         }
1366
1367         return 0;
1368 }
1369
1370 void ip6t_unregister_table(struct xt_table *table)
1371 {
1372         struct xt_table_info *private;
1373         void *loc_cpu_entry;
1374
1375         private = xt_unregister_table(table);
1376
1377         /* Decrease module usage counts and free resources */
1378         loc_cpu_entry = private->entries[raw_smp_processor_id()];
1379         IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1380         xt_free_table_info(private);
1381 }
1382
1383 /* Returns 1 if the type and code is matched by the range, 0 otherwise */
1384 static inline bool
1385 icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1386                      u_int8_t type, u_int8_t code,
1387                      bool invert)
1388 {
1389         return (type == test_type && code >= min_code && code <= max_code)
1390                 ^ invert;
1391 }
1392
1393 static bool
1394 icmp6_match(const struct sk_buff *skb,
1395            const struct net_device *in,
1396            const struct net_device *out,
1397            const struct xt_match *match,
1398            const void *matchinfo,
1399            int offset,
1400            unsigned int protoff,
1401            bool *hotdrop)
1402 {
1403         struct icmp6hdr _icmp, *ic;
1404         const struct ip6t_icmp *icmpinfo = matchinfo;
1405
1406         /* Must not be a fragment. */
1407         if (offset)
1408                 return false;
1409
1410         ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1411         if (ic == NULL) {
1412                 /* We've been asked to examine this packet, and we
1413                    can't.  Hence, no choice but to drop. */
1414                 duprintf("Dropping evil ICMP tinygram.\n");
1415                 *hotdrop = true;
1416                 return false;
1417         }
1418
1419         return icmp6_type_code_match(icmpinfo->type,
1420                                      icmpinfo->code[0],
1421                                      icmpinfo->code[1],
1422                                      ic->icmp6_type, ic->icmp6_code,
1423                                      !!(icmpinfo->invflags&IP6T_ICMP_INV));
1424 }
1425
1426 /* Called when user tries to insert an entry of this type. */
1427 static bool
1428 icmp6_checkentry(const char *tablename,
1429            const void *entry,
1430            const struct xt_match *match,
1431            void *matchinfo,
1432            unsigned int hook_mask)
1433 {
1434         const struct ip6t_icmp *icmpinfo = matchinfo;
1435
1436         /* Must specify no unknown invflags */
1437         return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1438 }
1439
1440 /* The built-in targets: standard (NULL) and error. */
1441 static struct xt_target ip6t_standard_target __read_mostly = {
1442         .name           = IP6T_STANDARD_TARGET,
1443         .targetsize     = sizeof(int),
1444         .family         = AF_INET6,
1445 };
1446
1447 static struct xt_target ip6t_error_target __read_mostly = {
1448         .name           = IP6T_ERROR_TARGET,
1449         .target         = ip6t_error,
1450         .targetsize     = IP6T_FUNCTION_MAXNAMELEN,
1451         .family         = AF_INET6,
1452 };
1453
1454 static struct nf_sockopt_ops ip6t_sockopts = {
1455         .pf             = PF_INET6,
1456         .set_optmin     = IP6T_BASE_CTL,
1457         .set_optmax     = IP6T_SO_SET_MAX+1,
1458         .set            = do_ip6t_set_ctl,
1459         .get_optmin     = IP6T_BASE_CTL,
1460         .get_optmax     = IP6T_SO_GET_MAX+1,
1461         .get            = do_ip6t_get_ctl,
1462         .owner          = THIS_MODULE,
1463 };
1464
1465 static struct xt_match icmp6_matchstruct __read_mostly = {
1466         .name           = "icmp6",
1467         .match          = &icmp6_match,
1468         .matchsize      = sizeof(struct ip6t_icmp),
1469         .checkentry     = icmp6_checkentry,
1470         .proto          = IPPROTO_ICMPV6,
1471         .family         = AF_INET6,
1472 };
1473
1474 static int __init ip6_tables_init(void)
1475 {
1476         int ret;
1477
1478         ret = xt_proto_init(AF_INET6);
1479         if (ret < 0)
1480                 goto err1;
1481
1482         /* Noone else will be downing sem now, so we won't sleep */
1483         ret = xt_register_target(&ip6t_standard_target);
1484         if (ret < 0)
1485                 goto err2;
1486         ret = xt_register_target(&ip6t_error_target);
1487         if (ret < 0)
1488                 goto err3;
1489         ret = xt_register_match(&icmp6_matchstruct);
1490         if (ret < 0)
1491                 goto err4;
1492
1493         /* Register setsockopt */
1494         ret = nf_register_sockopt(&ip6t_sockopts);
1495         if (ret < 0)
1496                 goto err5;
1497
1498         printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1499         return 0;
1500
1501 err5:
1502         xt_unregister_match(&icmp6_matchstruct);
1503 err4:
1504         xt_unregister_target(&ip6t_error_target);
1505 err3:
1506         xt_unregister_target(&ip6t_standard_target);
1507 err2:
1508         xt_proto_fini(AF_INET6);
1509 err1:
1510         return ret;
1511 }
1512
1513 static void __exit ip6_tables_fini(void)
1514 {
1515         nf_unregister_sockopt(&ip6t_sockopts);
1516         xt_unregister_match(&icmp6_matchstruct);
1517         xt_unregister_target(&ip6t_error_target);
1518         xt_unregister_target(&ip6t_standard_target);
1519         xt_proto_fini(AF_INET6);
1520 }
1521
1522 /*
1523  * find the offset to specified header or the protocol number of last header
1524  * if target < 0. "last header" is transport protocol header, ESP, or
1525  * "No next header".
1526  *
1527  * If target header is found, its offset is set in *offset and return protocol
1528  * number. Otherwise, return -1.
1529  *
1530  * If the first fragment doesn't contain the final protocol header or
1531  * NEXTHDR_NONE it is considered invalid.
1532  *
1533  * Note that non-1st fragment is special case that "the protocol number
1534  * of last header" is "next header" field in Fragment header. In this case,
1535  * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1536  * isn't NULL.
1537  *
1538  */
1539 int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1540                   int target, unsigned short *fragoff)
1541 {
1542         unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
1543         u8 nexthdr = ipv6_hdr(skb)->nexthdr;
1544         unsigned int len = skb->len - start;
1545
1546         if (fragoff)
1547                 *fragoff = 0;
1548
1549         while (nexthdr != target) {
1550                 struct ipv6_opt_hdr _hdr, *hp;
1551                 unsigned int hdrlen;
1552
1553                 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1554                         if (target < 0)
1555                                 break;
1556                         return -ENOENT;
1557                 }
1558
1559                 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1560                 if (hp == NULL)
1561                         return -EBADMSG;
1562                 if (nexthdr == NEXTHDR_FRAGMENT) {
1563                         unsigned short _frag_off;
1564                         __be16 *fp;
1565                         fp = skb_header_pointer(skb,
1566                                                 start+offsetof(struct frag_hdr,
1567                                                                frag_off),
1568                                                 sizeof(_frag_off),
1569                                                 &_frag_off);
1570                         if (fp == NULL)
1571                                 return -EBADMSG;
1572
1573                         _frag_off = ntohs(*fp) & ~0x7;
1574                         if (_frag_off) {
1575                                 if (target < 0 &&
1576                                     ((!ipv6_ext_hdr(hp->nexthdr)) ||
1577                                      hp->nexthdr == NEXTHDR_NONE)) {
1578                                         if (fragoff)
1579                                                 *fragoff = _frag_off;
1580                                         return hp->nexthdr;
1581                                 }
1582                                 return -ENOENT;
1583                         }
1584                         hdrlen = 8;
1585                 } else if (nexthdr == NEXTHDR_AUTH)
1586                         hdrlen = (hp->hdrlen + 2) << 2;
1587                 else
1588                         hdrlen = ipv6_optlen(hp);
1589
1590                 nexthdr = hp->nexthdr;
1591                 len -= hdrlen;
1592                 start += hdrlen;
1593         }
1594
1595         *offset = start;
1596         return nexthdr;
1597 }
1598
1599 EXPORT_SYMBOL(ip6t_register_table);
1600 EXPORT_SYMBOL(ip6t_unregister_table);
1601 EXPORT_SYMBOL(ip6t_do_table);
1602 EXPORT_SYMBOL(ip6t_ext_hdr);
1603 EXPORT_SYMBOL(ipv6_find_hdr);
1604
1605 module_init(ip6_tables_init);
1606 module_exit(ip6_tables_fini);