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