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