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