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