1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/config.h>
18#include <linux/skbuff.h>
19#include <linux/kmod.h>
20#include <linux/vmalloc.h>
21#include <linux/netdevice.h>
22#include <linux/module.h>
23#include <linux/tcp.h>
24#include <linux/udp.h>
25#include <linux/icmpv6.h>
26#include <net/ip.h>
27#include <net/ipv6.h>
28#include <asm/uaccess.h>
29#include <asm/semaphore.h>
30#include <linux/proc_fs.h>
31
32#include <linux/netfilter_ipv6/ip6_tables.h>
33
34MODULE_LICENSE("GPL");
35MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
36MODULE_DESCRIPTION("IPv6 packet filter");
37
38#define IPV6_HDR_LEN (sizeof(struct ipv6hdr))
39#define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
40
41
42
43
44
45#ifdef DEBUG_IP_FIREWALL
46#define dprintf(format, args...) printk(format , ## args)
47#else
48#define dprintf(format, args...)
49#endif
50
51#ifdef DEBUG_IP_FIREWALL_USER
52#define duprintf(format, args...) printk(format , ## args)
53#else
54#define duprintf(format, args...)
55#endif
56
57#ifdef CONFIG_NETFILTER_DEBUG
58#define IP_NF_ASSERT(x) \
59do { \
60 if (!(x)) \
61 printk("IP_NF_ASSERT: %s:%s:%u\n", \
62 __FUNCTION__, __FILE__, __LINE__); \
63} while(0)
64#else
65#define IP_NF_ASSERT(x)
66#endif
67#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
68
69static DECLARE_MUTEX(ip6t_mutex);
70
71
72#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
73#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
74#include <linux/netfilter_ipv4/lockhelp.h>
75#include <linux/netfilter_ipv4/listhelp.h>
76
77#if 0
78
79#define static
80#define inline
81#endif
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99struct ip6t_table_info
100{
101
102 unsigned int size;
103
104 unsigned int number;
105
106 unsigned int initial_entries;
107
108
109 unsigned int hook_entry[NF_IP6_NUMHOOKS];
110 unsigned int underflow[NF_IP6_NUMHOOKS];
111
112
113 char entries[0] ____cacheline_aligned;
114};
115
116static LIST_HEAD(ip6t_target);
117static LIST_HEAD(ip6t_match);
118static LIST_HEAD(ip6t_tables);
119#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
120
121#ifdef CONFIG_SMP
122#define TABLE_OFFSET(t,p) (SMP_ALIGN((t)->size)*(p))
123#else
124#define TABLE_OFFSET(t,p) 0
125#endif
126
127#if 0
128#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
129#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; })
130#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
131#endif
132
133static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask,
134 struct in6_addr addr2)
135{
136 int i;
137 for( i = 0; i < 16; i++){
138 if((addr1.s6_addr[i] & mask.s6_addr[i]) !=
139 (addr2.s6_addr[i] & mask.s6_addr[i]))
140 return 1;
141 }
142 return 0;
143}
144
145
146int
147ip6t_ext_hdr(u8 nexthdr)
148{
149 return ( (nexthdr == IPPROTO_HOPOPTS) ||
150 (nexthdr == IPPROTO_ROUTING) ||
151 (nexthdr == IPPROTO_FRAGMENT) ||
152 (nexthdr == IPPROTO_ESP) ||
153 (nexthdr == IPPROTO_AH) ||
154 (nexthdr == IPPROTO_NONE) ||
155 (nexthdr == IPPROTO_DSTOPTS) );
156}
157
158
159static inline int
160ip6_packet_match(const struct sk_buff *skb,
161 const char *indev,
162 const char *outdev,
163 const struct ip6t_ip6 *ip6info,
164 unsigned int *protoff,
165 int *fragoff)
166{
167 size_t i;
168 unsigned long ret;
169 const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
170
171#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
172
173 if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
174 IP6T_INV_SRCIP)
175 || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
176 IP6T_INV_DSTIP)) {
177 dprintf("Source or dest mismatch.\n");
178
179
180
181
182
183
184
185 return 0;
186 }
187
188
189 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
190 ret |= (((const unsigned long *)indev)[i]
191 ^ ((const unsigned long *)ip6info->iniface)[i])
192 & ((const unsigned long *)ip6info->iniface_mask)[i];
193 }
194
195 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
196 dprintf("VIA in mismatch (%s vs %s).%s\n",
197 indev, ip6info->iniface,
198 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
199 return 0;
200 }
201
202 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
203 ret |= (((const unsigned long *)outdev)[i]
204 ^ ((const unsigned long *)ip6info->outiface)[i])
205 & ((const unsigned long *)ip6info->outiface_mask)[i];
206 }
207
208 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
209 dprintf("VIA out mismatch (%s vs %s).%s\n",
210 outdev, ip6info->outiface,
211 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
212 return 0;
213 }
214
215
216
217
218 if((ip6info->flags & IP6T_F_PROTO)) {
219 u_int8_t currenthdr = ipv6->nexthdr;
220 struct ipv6_opt_hdr _hdr, *hp;
221 u_int16_t ptr;
222 u_int16_t hdrlen;
223 u_int16_t _fragoff = 0, *fp = NULL;
224
225 ptr = IPV6_HDR_LEN;
226
227 while (ip6t_ext_hdr(currenthdr)) {
228
229 if (skb->len - ptr < IPV6_OPTHDR_LEN)
230 return 0;
231
232
233
234
235 if ((currenthdr == IPPROTO_NONE) ||
236 (currenthdr == IPPROTO_ESP))
237 break;
238
239 hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
240 BUG_ON(hp == NULL);
241
242
243 if (currenthdr == IPPROTO_FRAGMENT) {
244 fp = skb_header_pointer(skb,
245 ptr+offsetof(struct frag_hdr,
246 frag_off),
247 sizeof(_fragoff),
248 &_fragoff);
249 if (fp == NULL)
250 return 0;
251
252 _fragoff = ntohs(*fp) & ~0x7;
253 hdrlen = 8;
254 } else if (currenthdr == IPPROTO_AH)
255 hdrlen = (hp->hdrlen+2)<<2;
256 else
257 hdrlen = ipv6_optlen(hp);
258
259 currenthdr = hp->nexthdr;
260 ptr += hdrlen;
261
262 if ( ptr > skb->len )
263 return 0;
264 if (_fragoff) {
265 if (ip6t_ext_hdr(currenthdr))
266 return 0;
267 break;
268 }
269 }
270
271 *protoff = ptr;
272 *fragoff = _fragoff;
273
274
275
276 dprintf("Packet protocol %hi ?= %s%hi.\n",
277 currenthdr,
278 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
279 ip6info->proto);
280
281 if (ip6info->proto == currenthdr) {
282 if(ip6info->invflags & IP6T_INV_PROTO) {
283 return 0;
284 }
285 return 1;
286 }
287
288
289 if ((ip6info->proto != 0) &&
290 !(ip6info->invflags & IP6T_INV_PROTO))
291 return 0;
292 }
293 return 1;
294}
295
296
297static inline int
298ip6_checkentry(const struct ip6t_ip6 *ipv6)
299{
300 if (ipv6->flags & ~IP6T_F_MASK) {
301 duprintf("Unknown flag bits set: %08X\n",
302 ipv6->flags & ~IP6T_F_MASK);
303 return 0;
304 }
305 if (ipv6->invflags & ~IP6T_INV_MASK) {
306 duprintf("Unknown invflag bits set: %08X\n",
307 ipv6->invflags & ~IP6T_INV_MASK);
308 return 0;
309 }
310 return 1;
311}
312
313static unsigned int
314ip6t_error(struct sk_buff **pskb,
315 const struct net_device *in,
316 const struct net_device *out,
317 unsigned int hooknum,
318 const void *targinfo,
319 void *userinfo)
320{
321 if (net_ratelimit())
322 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
323
324 return NF_DROP;
325}
326
327static inline
328int do_match(struct ip6t_entry_match *m,
329 const struct sk_buff *skb,
330 const struct net_device *in,
331 const struct net_device *out,
332 int offset,
333 unsigned int protoff,
334 int *hotdrop)
335{
336
337 if (!m->u.kernel.match->match(skb, in, out, m->data,
338 offset, protoff, hotdrop))
339 return 1;
340 else
341 return 0;
342}
343
344static inline struct ip6t_entry *
345get_entry(void *base, unsigned int offset)
346{
347 return (struct ip6t_entry *)(base + offset);
348}
349
350
351unsigned int
352ip6t_do_table(struct sk_buff **pskb,
353 unsigned int hook,
354 const struct net_device *in,
355 const struct net_device *out,
356 struct ip6t_table *table,
357 void *userdata)
358{
359 static const char nulldevname[IFNAMSIZ];
360 int offset = 0;
361 unsigned int protoff = 0;
362 int hotdrop = 0;
363
364 unsigned int verdict = NF_DROP;
365 const char *indev, *outdev;
366 void *table_base;
367 struct ip6t_entry *e, *back;
368
369
370 indev = in ? in->name : nulldevname;
371 outdev = out ? out->name : nulldevname;
372
373
374
375
376
377
378
379
380 read_lock_bh(&table->lock);
381 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
382 table_base = (void *)table->private->entries
383 + TABLE_OFFSET(table->private, smp_processor_id());
384 e = get_entry(table_base, table->private->hook_entry[hook]);
385
386#ifdef CONFIG_NETFILTER_DEBUG
387
388 if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
389 && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
390 printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
391 smp_processor_id(),
392 table->name,
393 &((struct ip6t_entry *)table_base)->comefrom,
394 ((struct ip6t_entry *)table_base)->comefrom);
395 }
396 ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
397#endif
398
399
400 back = get_entry(table_base, table->private->underflow[hook]);
401
402 do {
403 IP_NF_ASSERT(e);
404 IP_NF_ASSERT(back);
405 (*pskb)->nfcache |= e->nfcache;
406 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
407 &protoff, &offset)) {
408 struct ip6t_entry_target *t;
409
410 if (IP6T_MATCH_ITERATE(e, do_match,
411 *pskb, in, out,
412 offset, protoff, &hotdrop) != 0)
413 goto no_match;
414
415 ADD_COUNTER(e->counters,
416 ntohs((*pskb)->nh.ipv6h->payload_len)
417 + IPV6_HDR_LEN,
418 1);
419
420 t = ip6t_get_target(e);
421 IP_NF_ASSERT(t->u.kernel.target);
422
423 if (!t->u.kernel.target->target) {
424 int v;
425
426 v = ((struct ip6t_standard_target *)t)->verdict;
427 if (v < 0) {
428
429 if (v != IP6T_RETURN) {
430 verdict = (unsigned)(-v) - 1;
431 break;
432 }
433 e = back;
434 back = get_entry(table_base,
435 back->comefrom);
436 continue;
437 }
438 if (table_base + v
439 != (void *)e + e->next_offset) {
440
441 struct ip6t_entry *next
442 = (void *)e + e->next_offset;
443 next->comefrom
444 = (void *)back - table_base;
445
446 back = next;
447 }
448
449 e = get_entry(table_base, v);
450 } else {
451
452
453#ifdef CONFIG_NETFILTER_DEBUG
454 ((struct ip6t_entry *)table_base)->comefrom
455 = 0xeeeeeeec;
456#endif
457 verdict = t->u.kernel.target->target(pskb,
458 in, out,
459 hook,
460 t->data,
461 userdata);
462
463#ifdef CONFIG_NETFILTER_DEBUG
464 if (((struct ip6t_entry *)table_base)->comefrom
465 != 0xeeeeeeec
466 && verdict == IP6T_CONTINUE) {
467 printk("Target %s reentered!\n",
468 t->u.kernel.target->name);
469 verdict = NF_DROP;
470 }
471 ((struct ip6t_entry *)table_base)->comefrom
472 = 0x57acc001;
473#endif
474 if (verdict == IP6T_CONTINUE)
475 e = (void *)e + e->next_offset;
476 else
477
478 break;
479 }
480 } else {
481
482 no_match:
483 e = (void *)e + e->next_offset;
484 }
485 } while (!hotdrop);
486
487#ifdef CONFIG_NETFILTER_DEBUG
488 ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
489#endif
490 read_unlock_bh(&table->lock);
491
492#ifdef DEBUG_ALLOW_ALL
493 return NF_ACCEPT;
494#else
495 if (hotdrop)
496 return NF_DROP;
497 else return verdict;
498#endif
499}
500
501
502static inline void *
503find_inlist_lock_noload(struct list_head *head,
504 const char *name,
505 int *error,
506 struct semaphore *mutex)
507{
508 void *ret;
509
510#if 1
511 duprintf("find_inlist: searching for `%s' in %s.\n",
512 name, head == &ip6t_target ? "ip6t_target"
513 : head == &ip6t_match ? "ip6t_match"
514 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
515#endif
516
517 *error = down_interruptible(mutex);
518 if (*error != 0)
519 return NULL;
520
521 ret = list_named_find(head, name);
522 if (!ret) {
523 *error = -ENOENT;
524 up(mutex);
525 }
526 return ret;
527}
528
529#ifndef CONFIG_KMOD
530#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
531#else
532static void *
533find_inlist_lock(struct list_head *head,
534 const char *name,
535 const char *prefix,
536 int *error,
537 struct semaphore *mutex)
538{
539 void *ret;
540
541 ret = find_inlist_lock_noload(head, name, error, mutex);
542 if (!ret) {
543 duprintf("find_inlist: loading `%s%s'.\n", prefix, name);
544 request_module("%s%s", prefix, name);
545 ret = find_inlist_lock_noload(head, name, error, mutex);
546 }
547
548 return ret;
549}
550#endif
551
552static inline struct ip6t_table *
553ip6t_find_table_lock(const char *name, int *error, struct semaphore *mutex)
554{
555 return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
556}
557
558static inline struct ip6t_match *
559find_match_lock(const char *name, int *error, struct semaphore *mutex)
560{
561 return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
562}
563
564static struct ip6t_target *
565ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex)
566{
567 return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
568}
569
570
571static inline int
572unconditional(const struct ip6t_ip6 *ipv6)
573{
574 unsigned int i;
575
576 for (i = 0; i < sizeof(*ipv6); i++)
577 if (((char *)ipv6)[i])
578 break;
579
580 return (i == sizeof(*ipv6));
581}
582
583
584
585static int
586mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
587{
588 unsigned int hook;
589
590
591
592 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
593 unsigned int pos = newinfo->hook_entry[hook];
594 struct ip6t_entry *e
595 = (struct ip6t_entry *)(newinfo->entries + pos);
596
597 if (!(valid_hooks & (1 << hook)))
598 continue;
599
600
601 e->counters.pcnt = pos;
602
603 for (;;) {
604 struct ip6t_standard_target *t
605 = (void *)ip6t_get_target(e);
606
607 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
608 printk("iptables: loop hook %u pos %u %08X.\n",
609 hook, pos, e->comefrom);
610 return 0;
611 }
612 e->comefrom
613 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
614
615
616 if (e->target_offset == sizeof(struct ip6t_entry)
617 && (strcmp(t->target.u.user.name,
618 IP6T_STANDARD_TARGET) == 0)
619 && t->verdict < 0
620 && unconditional(&e->ipv6)) {
621 unsigned int oldpos, size;
622
623
624
625 do {
626 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
627#ifdef DEBUG_IP_FIREWALL_USER
628 if (e->comefrom
629 & (1 << NF_IP6_NUMHOOKS)) {
630 duprintf("Back unset "
631 "on hook %u "
632 "rule %u\n",
633 hook, pos);
634 }
635#endif
636 oldpos = pos;
637 pos = e->counters.pcnt;
638 e->counters.pcnt = 0;
639
640
641 if (pos == oldpos)
642 goto next;
643
644 e = (struct ip6t_entry *)
645 (newinfo->entries + pos);
646 } while (oldpos == pos + e->next_offset);
647
648
649 size = e->next_offset;
650 e = (struct ip6t_entry *)
651 (newinfo->entries + pos + size);
652 e->counters.pcnt = pos;
653 pos += size;
654 } else {
655 int newpos = t->verdict;
656
657 if (strcmp(t->target.u.user.name,
658 IP6T_STANDARD_TARGET) == 0
659 && newpos >= 0) {
660
661 duprintf("Jump rule %u -> %u\n",
662 pos, newpos);
663 } else {
664
665 newpos = pos + e->next_offset;
666 }
667 e = (struct ip6t_entry *)
668 (newinfo->entries + newpos);
669 e->counters.pcnt = pos;
670 pos = newpos;
671 }
672 }
673 next:
674 duprintf("Finished chain %u\n", hook);
675 }
676 return 1;
677}
678
679static inline int
680cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
681{
682 if (i && (*i)-- == 0)
683 return 1;
684
685 if (m->u.kernel.match->destroy)
686 m->u.kernel.match->destroy(m->data,
687 m->u.match_size - sizeof(*m));
688 module_put(m->u.kernel.match->me);
689 return 0;
690}
691
692static inline int
693standard_check(const struct ip6t_entry_target *t,
694 unsigned int max_offset)
695{
696 struct ip6t_standard_target *targ = (void *)t;
697
698
699 if (t->u.target_size
700 != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
701 duprintf("standard_check: target size %u != %u\n",
702 t->u.target_size,
703 IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
704 return 0;
705 }
706
707 if (targ->verdict >= 0
708 && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
709 duprintf("ip6t_standard_check: bad verdict (%i)\n",
710 targ->verdict);
711 return 0;
712 }
713
714 if (targ->verdict < -NF_MAX_VERDICT - 1) {
715 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
716 targ->verdict);
717 return 0;
718 }
719 return 1;
720}
721
722static inline int
723check_match(struct ip6t_entry_match *m,
724 const char *name,
725 const struct ip6t_ip6 *ipv6,
726 unsigned int hookmask,
727 unsigned int *i)
728{
729 int ret;
730 struct ip6t_match *match;
731
732 match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
733 if (!match) {
734
735 return ret;
736 }
737 if (!try_module_get(match->me)) {
738 up(&ip6t_mutex);
739 return -ENOENT;
740 }
741 m->u.kernel.match = match;
742 up(&ip6t_mutex);
743
744 if (m->u.kernel.match->checkentry
745 && !m->u.kernel.match->checkentry(name, ipv6, m->data,
746 m->u.match_size - sizeof(*m),
747 hookmask)) {
748 module_put(m->u.kernel.match->me);
749 duprintf("ip_tables: check failed for `%s'.\n",
750 m->u.kernel.match->name);
751 return -EINVAL;
752 }
753
754 (*i)++;
755 return 0;
756}
757
758static struct ip6t_target ip6t_standard_target;
759
760static inline int
761check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
762 unsigned int *i)
763{
764 struct ip6t_entry_target *t;
765 struct ip6t_target *target;
766 int ret;
767 unsigned int j;
768
769 if (!ip6_checkentry(&e->ipv6)) {
770 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
771 return -EINVAL;
772 }
773
774 j = 0;
775 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
776 if (ret != 0)
777 goto cleanup_matches;
778
779 t = ip6t_get_target(e);
780 target = ip6t_find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
781 if (!target) {
782 duprintf("check_entry: `%s' not found\n", t->u.user.name);
783 goto cleanup_matches;
784 }
785 if (!try_module_get(target->me)) {
786 up(&ip6t_mutex);
787 ret = -ENOENT;
788 goto cleanup_matches;
789 }
790 t->u.kernel.target = target;
791 up(&ip6t_mutex);
792 if (!t->u.kernel.target) {
793 ret = -EBUSY;
794 goto cleanup_matches;
795 }
796 if (t->u.kernel.target == &ip6t_standard_target) {
797 if (!standard_check(t, size)) {
798 ret = -EINVAL;
799 goto cleanup_matches;
800 }
801 } else if (t->u.kernel.target->checkentry
802 && !t->u.kernel.target->checkentry(name, e, t->data,
803 t->u.target_size
804 - sizeof(*t),
805 e->comefrom)) {
806 module_put(t->u.kernel.target->me);
807 duprintf("ip_tables: check failed for `%s'.\n",
808 t->u.kernel.target->name);
809 ret = -EINVAL;
810 goto cleanup_matches;
811 }
812
813 (*i)++;
814 return 0;
815
816 cleanup_matches:
817 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
818 return ret;
819}
820
821static inline int
822check_entry_size_and_hooks(struct ip6t_entry *e,
823 struct ip6t_table_info *newinfo,
824 unsigned char *base,
825 unsigned char *limit,
826 const unsigned int *hook_entries,
827 const unsigned int *underflows,
828 unsigned int *i)
829{
830 unsigned int h;
831
832 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
833 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
834 duprintf("Bad offset %p\n", e);
835 return -EINVAL;
836 }
837
838 if (e->next_offset
839 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
840 duprintf("checking: element %p size %u\n",
841 e, e->next_offset);
842 return -EINVAL;
843 }
844
845
846 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
847 if ((unsigned char *)e - base == hook_entries[h])
848 newinfo->hook_entry[h] = hook_entries[h];
849 if ((unsigned char *)e - base == underflows[h])
850 newinfo->underflow[h] = underflows[h];
851 }
852
853
854
855
856
857 e->counters = ((struct ip6t_counters) { 0, 0 });
858 e->comefrom = 0;
859
860 (*i)++;
861 return 0;
862}
863
864static inline int
865cleanup_entry(struct ip6t_entry *e, unsigned int *i)
866{
867 struct ip6t_entry_target *t;
868
869 if (i && (*i)-- == 0)
870 return 1;
871
872
873 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
874 t = ip6t_get_target(e);
875 if (t->u.kernel.target->destroy)
876 t->u.kernel.target->destroy(t->data,
877 t->u.target_size - sizeof(*t));
878 module_put(t->u.kernel.target->me);
879 return 0;
880}
881
882
883
884static int
885translate_table(const char *name,
886 unsigned int valid_hooks,
887 struct ip6t_table_info *newinfo,
888 unsigned int size,
889 unsigned int number,
890 const unsigned int *hook_entries,
891 const unsigned int *underflows)
892{
893 unsigned int i;
894 int ret;
895
896 newinfo->size = size;
897 newinfo->number = number;
898
899
900 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
901 newinfo->hook_entry[i] = 0xFFFFFFFF;
902 newinfo->underflow[i] = 0xFFFFFFFF;
903 }
904
905 duprintf("translate_table: size %u\n", newinfo->size);
906 i = 0;
907
908 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
909 check_entry_size_and_hooks,
910 newinfo,
911 newinfo->entries,
912 newinfo->entries + size,
913 hook_entries, underflows, &i);
914 if (ret != 0)
915 return ret;
916
917 if (i != number) {
918 duprintf("translate_table: %u not %u entries\n",
919 i, number);
920 return -EINVAL;
921 }
922
923
924 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
925
926 if (!(valid_hooks & (1 << i)))
927 continue;
928 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
929 duprintf("Invalid hook entry %u %u\n",
930 i, hook_entries[i]);
931 return -EINVAL;
932 }
933 if (newinfo->underflow[i] == 0xFFFFFFFF) {
934 duprintf("Invalid underflow %u %u\n",
935 i, underflows[i]);
936 return -EINVAL;
937 }
938 }
939
940 if (!mark_source_chains(newinfo, valid_hooks))
941 return -ELOOP;
942
943
944 i = 0;
945 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
946 check_entry, name, size, &i);
947
948 if (ret != 0) {
949 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
950 cleanup_entry, &i);
951 return ret;
952 }
953
954
955 for (i = 1; i < NR_CPUS; i++) {
956 memcpy(newinfo->entries + SMP_ALIGN(newinfo->size)*i,
957 newinfo->entries,
958 SMP_ALIGN(newinfo->size));
959 }
960
961 return ret;
962}
963
964static struct ip6t_table_info *
965replace_table(struct ip6t_table *table,
966 unsigned int num_counters,
967 struct ip6t_table_info *newinfo,
968 int *error)
969{
970 struct ip6t_table_info *oldinfo;
971
972#ifdef CONFIG_NETFILTER_DEBUG
973 {
974 struct ip6t_entry *table_base;
975 unsigned int i;
976
977 for (i = 0; i < NR_CPUS; i++) {
978 table_base =
979 (void *)newinfo->entries
980 + TABLE_OFFSET(newinfo, i);
981
982 table_base->comefrom = 0xdead57ac;
983 }
984 }
985#endif
986
987
988 write_lock_bh(&table->lock);
989
990 if (num_counters != table->private->number) {
991 duprintf("num_counters != table->private->number (%u/%u)\n",
992 num_counters, table->private->number);
993 write_unlock_bh(&table->lock);
994 *error = -EAGAIN;
995 return NULL;
996 }
997 oldinfo = table->private;
998 table->private = newinfo;
999 newinfo->initial_entries = oldinfo->initial_entries;
1000 write_unlock_bh(&table->lock);
1001
1002 return oldinfo;
1003}
1004
1005
1006static inline int
1007add_entry_to_counter(const struct ip6t_entry *e,
1008 struct ip6t_counters total[],
1009 unsigned int *i)
1010{
1011 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
1012
1013 (*i)++;
1014 return 0;
1015}
1016
1017static void
1018get_counters(const struct ip6t_table_info *t,
1019 struct ip6t_counters counters[])
1020{
1021 unsigned int cpu;
1022 unsigned int i;
1023
1024 for (cpu = 0; cpu < NR_CPUS; cpu++) {
1025 i = 0;
1026 IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
1027 t->size,
1028 add_entry_to_counter,
1029 counters,
1030 &i);
1031 }
1032}
1033
1034static int
1035copy_entries_to_user(unsigned int total_size,
1036 struct ip6t_table *table,
1037 void __user *userptr)
1038{
1039 unsigned int off, num, countersize;
1040 struct ip6t_entry *e;
1041 struct ip6t_counters *counters;
1042 int ret = 0;
1043
1044
1045
1046
1047 countersize = sizeof(struct ip6t_counters) * table->private->number;
1048 counters = vmalloc(countersize);
1049
1050 if (counters == NULL)
1051 return -ENOMEM;
1052
1053
1054 memset(counters, 0, countersize);
1055 write_lock_bh(&table->lock);
1056 get_counters(table->private, counters);
1057 write_unlock_bh(&table->lock);
1058
1059
1060 if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
1061 ret = -EFAULT;
1062 goto free_counters;
1063 }
1064
1065
1066
1067 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1068 unsigned int i;
1069 struct ip6t_entry_match *m;
1070 struct ip6t_entry_target *t;
1071
1072 e = (struct ip6t_entry *)(table->private->entries + off);
1073 if (copy_to_user(userptr + off
1074 + offsetof(struct ip6t_entry, counters),
1075 &counters[num],
1076 sizeof(counters[num])) != 0) {
1077 ret = -EFAULT;
1078 goto free_counters;
1079 }
1080
1081 for (i = sizeof(struct ip6t_entry);
1082 i < e->target_offset;
1083 i += m->u.match_size) {
1084 m = (void *)e + i;
1085
1086 if (copy_to_user(userptr + off + i
1087 + offsetof(struct ip6t_entry_match,
1088 u.user.name),
1089 m->u.kernel.match->name,
1090 strlen(m->u.kernel.match->name)+1)
1091 != 0) {
1092 ret = -EFAULT;
1093 goto free_counters;
1094 }
1095 }
1096
1097 t = ip6t_get_target(e);
1098 if (copy_to_user(userptr + off + e->target_offset
1099 + offsetof(struct ip6t_entry_target,
1100 u.user.name),
1101 t->u.kernel.target->name,
1102 strlen(t->u.kernel.target->name)+1) != 0) {
1103 ret = -EFAULT;
1104 goto free_counters;
1105 }
1106 }
1107
1108 free_counters:
1109 vfree(counters);
1110 return ret;
1111}
1112
1113static int
1114get_entries(const struct ip6t_get_entries *entries,
1115 struct ip6t_get_entries __user *uptr)
1116{
1117 int ret;
1118 struct ip6t_table *t;
1119
1120 t = ip6t_find_table_lock(entries->name, &ret, &ip6t_mutex);
1121 if (t) {
1122 duprintf("t->private->number = %u\n",
1123 t->private->number);
1124 if (entries->size == t->private->size)
1125 ret = copy_entries_to_user(t->private->size,
1126 t, uptr->entrytable);
1127 else {
1128 duprintf("get_entries: I've got %u not %u!\n",
1129 t->private->size,
1130 entries->size);
1131 ret = -EINVAL;
1132 }
1133 up(&ip6t_mutex);
1134 } else
1135 duprintf("get_entries: Can't find %s!\n",
1136 entries->name);
1137
1138 return ret;
1139}
1140
1141static int
1142do_replace(void __user *user, unsigned int len)
1143{
1144 int ret;
1145 struct ip6t_replace tmp;
1146 struct ip6t_table *t;
1147 struct ip6t_table_info *newinfo, *oldinfo;
1148 struct ip6t_counters *counters;
1149
1150 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1151 return -EFAULT;
1152
1153
1154 if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
1155 return -ENOMEM;
1156
1157 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1158 + SMP_ALIGN(tmp.size) * NR_CPUS);
1159 if (!newinfo)
1160 return -ENOMEM;
1161
1162 if (copy_from_user(newinfo->entries, user + sizeof(tmp),
1163 tmp.size) != 0) {
1164 ret = -EFAULT;
1165 goto free_newinfo;
1166 }
1167
1168 counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
1169 if (!counters) {
1170 ret = -ENOMEM;
1171 goto free_newinfo;
1172 }
1173 memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
1174
1175 ret = translate_table(tmp.name, tmp.valid_hooks,
1176 newinfo, tmp.size, tmp.num_entries,
1177 tmp.hook_entry, tmp.underflow);
1178 if (ret != 0)
1179 goto free_newinfo_counters;
1180
1181 duprintf("ip_tables: Translated table\n");
1182
1183 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1184 if (!t)
1185 goto free_newinfo_counters_untrans;
1186
1187
1188 if (tmp.valid_hooks != t->valid_hooks) {
1189 duprintf("Valid hook crap: %08X vs %08X\n",
1190 tmp.valid_hooks, t->valid_hooks);
1191 ret = -EINVAL;
1192 goto free_newinfo_counters_untrans_unlock;
1193 }
1194
1195
1196 if (!try_module_get(t->me)) {
1197 ret = -EBUSY;
1198 goto free_newinfo_counters_untrans_unlock;
1199 }
1200
1201 oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
1202 if (!oldinfo)
1203 goto put_module;
1204
1205
1206 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1207 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1208 if ((oldinfo->number > oldinfo->initial_entries) ||
1209 (newinfo->number <= oldinfo->initial_entries))
1210 module_put(t->me);
1211 if ((oldinfo->number > oldinfo->initial_entries) &&
1212 (newinfo->number <= oldinfo->initial_entries))
1213 module_put(t->me);
1214
1215
1216 get_counters(oldinfo, counters);
1217
1218 IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
1219 vfree(oldinfo);
1220
1221 if (copy_to_user(tmp.counters, counters,
1222 sizeof(struct ip6t_counters) * tmp.num_counters) != 0)
1223 ret = -EFAULT;
1224 vfree(counters);
1225 up(&ip6t_mutex);
1226 return ret;
1227
1228 put_module:
1229 module_put(t->me);
1230 free_newinfo_counters_untrans_unlock:
1231 up(&ip6t_mutex);
1232 free_newinfo_counters_untrans:
1233 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
1234 free_newinfo_counters:
1235 vfree(counters);
1236 free_newinfo:
1237 vfree(newinfo);
1238 return ret;
1239}
1240
1241
1242
1243static inline int
1244add_counter_to_entry(struct ip6t_entry *e,
1245 const struct ip6t_counters addme[],
1246 unsigned int *i)
1247{
1248#if 0
1249 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1250 *i,
1251 (long unsigned int)e->counters.pcnt,
1252 (long unsigned int)e->counters.bcnt,
1253 (long unsigned int)addme[*i].pcnt,
1254 (long unsigned int)addme[*i].bcnt);
1255#endif
1256
1257 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1258
1259 (*i)++;
1260 return 0;
1261}
1262
1263static int
1264do_add_counters(void __user *user, unsigned int len)
1265{
1266 unsigned int i;
1267 struct ip6t_counters_info tmp, *paddc;
1268 struct ip6t_table *t;
1269 int ret;
1270
1271 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1272 return -EFAULT;
1273
1274 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
1275 return -EINVAL;
1276
1277 paddc = vmalloc(len);
1278 if (!paddc)
1279 return -ENOMEM;
1280
1281 if (copy_from_user(paddc, user, len) != 0) {
1282 ret = -EFAULT;
1283 goto free;
1284 }
1285
1286 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1287 if (!t)
1288 goto free;
1289
1290 write_lock_bh(&t->lock);
1291 if (t->private->number != paddc->num_counters) {
1292 ret = -EINVAL;
1293 goto unlock_up_free;
1294 }
1295
1296 i = 0;
1297 IP6T_ENTRY_ITERATE(t->private->entries,
1298 t->private->size,
1299 add_counter_to_entry,
1300 paddc->counters,
1301 &i);
1302 unlock_up_free:
1303 write_unlock_bh(&t->lock);
1304 up(&ip6t_mutex);
1305 free:
1306 vfree(paddc);
1307
1308 return ret;
1309}
1310
1311static int
1312do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1313{
1314 int ret;
1315
1316 if (!capable(CAP_NET_ADMIN))
1317 return -EPERM;
1318
1319 switch (cmd) {
1320 case IP6T_SO_SET_REPLACE:
1321 ret = do_replace(user, len);
1322 break;
1323
1324 case IP6T_SO_SET_ADD_COUNTERS:
1325 ret = do_add_counters(user, len);
1326 break;
1327
1328 default:
1329 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1330 ret = -EINVAL;
1331 }
1332
1333 return ret;
1334}
1335
1336static int
1337do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1338{
1339 int ret;
1340
1341 if (!capable(CAP_NET_ADMIN))
1342 return -EPERM;
1343
1344 switch (cmd) {
1345 case IP6T_SO_GET_INFO: {
1346 char name[IP6T_TABLE_MAXNAMELEN];
1347 struct ip6t_table *t;
1348
1349 if (*len != sizeof(struct ip6t_getinfo)) {
1350 duprintf("length %u != %u\n", *len,
1351 sizeof(struct ip6t_getinfo));
1352 ret = -EINVAL;
1353 break;
1354 }
1355
1356 if (copy_from_user(name, user, sizeof(name)) != 0) {
1357 ret = -EFAULT;
1358 break;
1359 }
1360 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1361 t = ip6t_find_table_lock(name, &ret, &ip6t_mutex);
1362 if (t) {
1363 struct ip6t_getinfo info;
1364
1365 info.valid_hooks = t->valid_hooks;
1366 memcpy(info.hook_entry, t->private->hook_entry,
1367 sizeof(info.hook_entry));
1368 memcpy(info.underflow, t->private->underflow,
1369 sizeof(info.underflow));
1370 info.num_entries = t->private->number;
1371 info.size = t->private->size;
1372 memcpy(info.name, name, sizeof(info.name));
1373
1374 if (copy_to_user(user, &info, *len) != 0)
1375 ret = -EFAULT;
1376 else
1377 ret = 0;
1378
1379 up(&ip6t_mutex);
1380 }
1381 }
1382 break;
1383
1384 case IP6T_SO_GET_ENTRIES: {
1385 struct ip6t_get_entries get;
1386
1387 if (*len < sizeof(get)) {
1388 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1389 ret = -EINVAL;
1390 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1391 ret = -EFAULT;
1392 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1393 duprintf("get_entries: %u != %u\n", *len,
1394 sizeof(struct ip6t_get_entries) + get.size);
1395 ret = -EINVAL;
1396 } else
1397 ret = get_entries(&get, user);
1398 break;
1399 }
1400
1401 default:
1402 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1403 ret = -EINVAL;
1404 }
1405
1406 return ret;
1407}
1408
1409
1410int
1411ip6t_register_target(struct ip6t_target *target)
1412{
1413 int ret;
1414
1415 ret = down_interruptible(&ip6t_mutex);
1416 if (ret != 0)
1417 return ret;
1418
1419 if (!list_named_insert(&ip6t_target, target)) {
1420 duprintf("ip6t_register_target: `%s' already in list!\n",
1421 target->name);
1422 ret = -EINVAL;
1423 }
1424 up(&ip6t_mutex);
1425 return ret;
1426}
1427
1428void
1429ip6t_unregister_target(struct ip6t_target *target)
1430{
1431 down(&ip6t_mutex);
1432 LIST_DELETE(&ip6t_target, target);
1433 up(&ip6t_mutex);
1434}
1435
1436int
1437ip6t_register_match(struct ip6t_match *match)
1438{
1439 int ret;
1440
1441 ret = down_interruptible(&ip6t_mutex);
1442 if (ret != 0)
1443 return ret;
1444
1445 if (!list_named_insert(&ip6t_match, match)) {
1446 duprintf("ip6t_register_match: `%s' already in list!\n",
1447 match->name);
1448 ret = -EINVAL;
1449 }
1450 up(&ip6t_mutex);
1451
1452 return ret;
1453}
1454
1455void
1456ip6t_unregister_match(struct ip6t_match *match)
1457{
1458 down(&ip6t_mutex);
1459 LIST_DELETE(&ip6t_match, match);
1460 up(&ip6t_mutex);
1461}
1462
1463int ip6t_register_table(struct ip6t_table *table,
1464 const struct ip6t_replace *repl)
1465{
1466 int ret;
1467 struct ip6t_table_info *newinfo;
1468 static struct ip6t_table_info bootstrap
1469 = { 0, 0, 0, { 0 }, { 0 }, { } };
1470
1471 newinfo = vmalloc(sizeof(struct ip6t_table_info)
1472 + SMP_ALIGN(repl->size) * NR_CPUS);
1473 if (!newinfo)
1474 return -ENOMEM;
1475
1476 memcpy(newinfo->entries, repl->entries, repl->size);
1477
1478 ret = translate_table(table->name, table->valid_hooks,
1479 newinfo, repl->size,
1480 repl->num_entries,
1481 repl->hook_entry,
1482 repl->underflow);
1483 if (ret != 0) {
1484 vfree(newinfo);
1485 return ret;
1486 }
1487
1488 ret = down_interruptible(&ip6t_mutex);
1489 if (ret != 0) {
1490 vfree(newinfo);
1491 return ret;
1492 }
1493
1494
1495 if (list_named_find(&ip6t_tables, table->name)) {
1496 ret = -EEXIST;
1497 goto free_unlock;
1498 }
1499
1500
1501 table->private = &bootstrap;
1502 if (!replace_table(table, 0, newinfo, &ret))
1503 goto free_unlock;
1504
1505 duprintf("table->private->number = %u\n",
1506 table->private->number);
1507
1508
1509 table->private->initial_entries = table->private->number;
1510
1511 rwlock_init(&table->lock);
1512 list_prepend(&ip6t_tables, table);
1513
1514 unlock:
1515 up(&ip6t_mutex);
1516 return ret;
1517
1518 free_unlock:
1519 vfree(newinfo);
1520 goto unlock;
1521}
1522
1523void ip6t_unregister_table(struct ip6t_table *table)
1524{
1525 down(&ip6t_mutex);
1526 LIST_DELETE(&ip6t_tables, table);
1527 up(&ip6t_mutex);
1528
1529
1530 IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
1531 cleanup_entry, NULL);
1532 vfree(table->private);
1533}
1534
1535
1536static inline int
1537port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
1538{
1539 int ret;
1540
1541 ret = (port >= min && port <= max) ^ invert;
1542 return ret;
1543}
1544
1545static int
1546tcp_find_option(u_int8_t option,
1547 const struct sk_buff *skb,
1548 unsigned int tcpoff,
1549 unsigned int optlen,
1550 int invert,
1551 int *hotdrop)
1552{
1553
1554 u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
1555 unsigned int i;
1556
1557 duprintf("tcp_match: finding option\n");
1558 if (!optlen)
1559 return invert;
1560
1561 op = skb_header_pointer(skb, tcpoff + sizeof(struct tcphdr), optlen,
1562 _opt);
1563 if (op == NULL) {
1564 *hotdrop = 1;
1565 return 0;
1566 }
1567
1568 for (i = 0; i < optlen; ) {
1569 if (op[i] == option) return !invert;
1570 if (op[i] < 2) i++;
1571 else i += op[i+1]?:1;
1572 }
1573
1574 return invert;
1575}
1576
1577static int
1578tcp_match(const struct sk_buff *skb,
1579 const struct net_device *in,
1580 const struct net_device *out,
1581 const void *matchinfo,
1582 int offset,
1583 unsigned int protoff,
1584 int *hotdrop)
1585{
1586 struct tcphdr _tcph, *th;
1587 const struct ip6t_tcp *tcpinfo = matchinfo;
1588
1589 if (offset) {
1590
1591
1592
1593
1594
1595
1596 if (offset == 1) {
1597 duprintf("Dropping evil TCP offset=1 frag.\n");
1598 *hotdrop = 1;
1599 }
1600
1601 return 0;
1602 }
1603
1604#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
1605
1606 th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
1607 if (th == NULL) {
1608
1609
1610 duprintf("Dropping evil TCP offset=0 tinygram.\n");
1611 *hotdrop = 1;
1612 return 0;
1613 }
1614
1615 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
1616 ntohs(th->source),
1617 !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT)))
1618 return 0;
1619 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
1620 ntohs(th->dest),
1621 !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT)))
1622 return 0;
1623 if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
1624 == tcpinfo->flg_cmp,
1625 IP6T_TCP_INV_FLAGS))
1626 return 0;
1627 if (tcpinfo->option) {
1628 if (th->doff * 4 < sizeof(_tcph)) {
1629 *hotdrop = 1;
1630 return 0;
1631 }
1632 if (!tcp_find_option(tcpinfo->option, skb, protoff,
1633 th->doff*4 - sizeof(*th),
1634 tcpinfo->invflags & IP6T_TCP_INV_OPTION,
1635 hotdrop))
1636 return 0;
1637 }
1638 return 1;
1639}
1640
1641
1642static int
1643tcp_checkentry(const char *tablename,
1644 const struct ip6t_ip6 *ipv6,
1645 void *matchinfo,
1646 unsigned int matchsize,
1647 unsigned int hook_mask)
1648{
1649 const struct ip6t_tcp *tcpinfo = matchinfo;
1650
1651
1652 return ipv6->proto == IPPROTO_TCP
1653 && !(ipv6->invflags & IP6T_INV_PROTO)
1654 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
1655 && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
1656}
1657
1658static int
1659udp_match(const struct sk_buff *skb,
1660 const struct net_device *in,
1661 const struct net_device *out,
1662 const void *matchinfo,
1663 int offset,
1664 unsigned int protoff,
1665 int *hotdrop)
1666{
1667 struct udphdr _udph, *uh;
1668 const struct ip6t_udp *udpinfo = matchinfo;
1669
1670
1671 if (offset)
1672 return 0;
1673
1674 uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
1675 if (uh == NULL) {
1676
1677
1678 duprintf("Dropping evil UDP tinygram.\n");
1679 *hotdrop = 1;
1680 return 0;
1681 }
1682
1683 return port_match(udpinfo->spts[0], udpinfo->spts[1],
1684 ntohs(uh->source),
1685 !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
1686 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
1687 ntohs(uh->dest),
1688 !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
1689}
1690
1691
1692static int
1693udp_checkentry(const char *tablename,
1694 const struct ip6t_ip6 *ipv6,
1695 void *matchinfo,
1696 unsigned int matchinfosize,
1697 unsigned int hook_mask)
1698{
1699 const struct ip6t_udp *udpinfo = matchinfo;
1700
1701
1702 if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
1703 duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
1704 IPPROTO_UDP);
1705 return 0;
1706 }
1707 if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
1708 duprintf("ip6t_udp: matchsize %u != %u\n",
1709 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
1710 return 0;
1711 }
1712 if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
1713 duprintf("ip6t_udp: unknown flags %X\n",
1714 udpinfo->invflags);
1715 return 0;
1716 }
1717
1718 return 1;
1719}
1720
1721
1722static inline int
1723icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1724 u_int8_t type, u_int8_t code,
1725 int invert)
1726{
1727 return (type == test_type && code >= min_code && code <= max_code)
1728 ^ invert;
1729}
1730
1731static int
1732icmp6_match(const struct sk_buff *skb,
1733 const struct net_device *in,
1734 const struct net_device *out,
1735 const void *matchinfo,
1736 int offset,
1737 unsigned int protoff,
1738 int *hotdrop)
1739{
1740 struct icmp6hdr _icmp, *ic;
1741 const struct ip6t_icmp *icmpinfo = matchinfo;
1742
1743
1744 if (offset)
1745 return 0;
1746
1747 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1748 if (ic == NULL) {
1749
1750
1751 duprintf("Dropping evil ICMP tinygram.\n");
1752 *hotdrop = 1;
1753 return 0;
1754 }
1755
1756 return icmp6_type_code_match(icmpinfo->type,
1757 icmpinfo->code[0],
1758 icmpinfo->code[1],
1759 ic->icmp6_type, ic->icmp6_code,
1760 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1761}
1762
1763
1764static int
1765icmp6_checkentry(const char *tablename,
1766 const struct ip6t_ip6 *ipv6,
1767 void *matchinfo,
1768 unsigned int matchsize,
1769 unsigned int hook_mask)
1770{
1771 const struct ip6t_icmp *icmpinfo = matchinfo;
1772
1773
1774 return ipv6->proto == IPPROTO_ICMPV6
1775 && !(ipv6->invflags & IP6T_INV_PROTO)
1776 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
1777 && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1778}
1779
1780
1781static struct ip6t_target ip6t_standard_target = {
1782 .name = IP6T_STANDARD_TARGET,
1783};
1784
1785static struct ip6t_target ip6t_error_target = {
1786 .name = IP6T_ERROR_TARGET,
1787 .target = ip6t_error,
1788};
1789
1790static struct nf_sockopt_ops ip6t_sockopts = {
1791 .pf = PF_INET6,
1792 .set_optmin = IP6T_BASE_CTL,
1793 .set_optmax = IP6T_SO_SET_MAX+1,
1794 .set = do_ip6t_set_ctl,
1795 .get_optmin = IP6T_BASE_CTL,
1796 .get_optmax = IP6T_SO_GET_MAX+1,
1797 .get = do_ip6t_get_ctl,
1798};
1799
1800static struct ip6t_match tcp_matchstruct = {
1801 .name = "tcp",
1802 .match = &tcp_match,
1803 .checkentry = &tcp_checkentry,
1804};
1805
1806static struct ip6t_match udp_matchstruct = {
1807 .name = "udp",
1808 .match = &udp_match,
1809 .checkentry = &udp_checkentry,
1810};
1811
1812static struct ip6t_match icmp6_matchstruct = {
1813 .name = "icmp6",
1814 .match = &icmp6_match,
1815 .checkentry = &icmp6_checkentry,
1816};
1817
1818#ifdef CONFIG_PROC_FS
1819static inline int print_name(const char *i,
1820 off_t start_offset, char *buffer, int length,
1821 off_t *pos, unsigned int *count)
1822{
1823 if ((*count)++ >= start_offset) {
1824 unsigned int namelen;
1825
1826 namelen = sprintf(buffer + *pos, "%s\n",
1827 i + sizeof(struct list_head));
1828 if (*pos + namelen > length) {
1829
1830 return 1;
1831 }
1832 *pos += namelen;
1833 }
1834 return 0;
1835}
1836
1837static inline int print_target(const struct ip6t_target *t,
1838 off_t start_offset, char *buffer, int length,
1839 off_t *pos, unsigned int *count)
1840{
1841 if (t == &ip6t_standard_target || t == &ip6t_error_target)
1842 return 0;
1843 return print_name((char *)t, start_offset, buffer, length, pos, count);
1844}
1845
1846static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
1847{
1848 off_t pos = 0;
1849 unsigned int count = 0;
1850
1851 if (down_interruptible(&ip6t_mutex) != 0)
1852 return 0;
1853
1854 LIST_FIND(&ip6t_tables, print_name, char *,
1855 offset, buffer, length, &pos, &count);
1856
1857 up(&ip6t_mutex);
1858
1859
1860 *start=(char *)((unsigned long)count-offset);
1861 return pos;
1862}
1863
1864static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
1865{
1866 off_t pos = 0;
1867 unsigned int count = 0;
1868
1869 if (down_interruptible(&ip6t_mutex) != 0)
1870 return 0;
1871
1872 LIST_FIND(&ip6t_target, print_target, struct ip6t_target *,
1873 offset, buffer, length, &pos, &count);
1874
1875 up(&ip6t_mutex);
1876
1877 *start = (char *)((unsigned long)count - offset);
1878 return pos;
1879}
1880
1881static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
1882{
1883 off_t pos = 0;
1884 unsigned int count = 0;
1885
1886 if (down_interruptible(&ip6t_mutex) != 0)
1887 return 0;
1888
1889 LIST_FIND(&ip6t_match, print_name, char *,
1890 offset, buffer, length, &pos, &count);
1891
1892 up(&ip6t_mutex);
1893
1894 *start = (char *)((unsigned long)count - offset);
1895 return pos;
1896}
1897
1898static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
1899{ { "ip6_tables_names", ip6t_get_tables },
1900 { "ip6_tables_targets", ip6t_get_targets },
1901 { "ip6_tables_matches", ip6t_get_matches },
1902 { NULL, NULL} };
1903#endif
1904
1905static int __init init(void)
1906{
1907 int ret;
1908
1909
1910 down(&ip6t_mutex);
1911 list_append(&ip6t_target, &ip6t_standard_target);
1912 list_append(&ip6t_target, &ip6t_error_target);
1913 list_append(&ip6t_match, &tcp_matchstruct);
1914 list_append(&ip6t_match, &udp_matchstruct);
1915 list_append(&ip6t_match, &icmp6_matchstruct);
1916 up(&ip6t_mutex);
1917
1918
1919 ret = nf_register_sockopt(&ip6t_sockopts);
1920 if (ret < 0) {
1921 duprintf("Unable to register sockopts.\n");
1922 return ret;
1923 }
1924
1925#ifdef CONFIG_PROC_FS
1926 {
1927 struct proc_dir_entry *proc;
1928 int i;
1929
1930 for (i = 0; ip6t_proc_entry[i].name; i++) {
1931 proc = proc_net_create(ip6t_proc_entry[i].name, 0,
1932 ip6t_proc_entry[i].get_info);
1933 if (!proc) {
1934 while (--i >= 0)
1935 proc_net_remove(ip6t_proc_entry[i].name);
1936 nf_unregister_sockopt(&ip6t_sockopts);
1937 return -ENOMEM;
1938 }
1939 proc->owner = THIS_MODULE;
1940 }
1941 }
1942#endif
1943
1944 printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
1945 return 0;
1946}
1947
1948static void __exit fini(void)
1949{
1950 nf_unregister_sockopt(&ip6t_sockopts);
1951#ifdef CONFIG_PROC_FS
1952 {
1953 int i;
1954 for (i = 0; ip6t_proc_entry[i].name; i++)
1955 proc_net_remove(ip6t_proc_entry[i].name);
1956 }
1957#endif
1958}
1959
1960EXPORT_SYMBOL(ip6t_register_table);
1961EXPORT_SYMBOL(ip6t_unregister_table);
1962EXPORT_SYMBOL(ip6t_do_table);
1963EXPORT_SYMBOL(ip6t_register_match);
1964EXPORT_SYMBOL(ip6t_unregister_match);
1965EXPORT_SYMBOL(ip6t_register_target);
1966EXPORT_SYMBOL(ip6t_unregister_target);
1967EXPORT_SYMBOL(ip6t_ext_hdr);
1968
1969module_init(init);
1970module_exit(fini);
1971