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