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