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