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