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