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