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