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