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