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