blob: b03e90649eb5f7517e437f40c95c55475e878680 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Packet matching code.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
5 * Copyright (C) 2000-2002 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 */
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>
David S. Millerc8923c62005-10-13 14:41:23 -070031#include <linux/cpumask.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33#include <linux/netfilter_ipv6/ip6_tables.h>
34
35MODULE_LICENSE("GPL");
36MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
37MODULE_DESCRIPTION("IPv6 packet filter");
38
39#define IPV6_HDR_LEN (sizeof(struct ipv6hdr))
40#define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
41
42/*#define DEBUG_IP_FIREWALL*/
43/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
44/*#define DEBUG_IP_FIREWALL_USER*/
45
46#ifdef DEBUG_IP_FIREWALL
47#define dprintf(format, args...) printk(format , ## args)
48#else
49#define dprintf(format, args...)
50#endif
51
52#ifdef DEBUG_IP_FIREWALL_USER
53#define duprintf(format, args...) printk(format , ## args)
54#else
55#define duprintf(format, args...)
56#endif
57
58#ifdef CONFIG_NETFILTER_DEBUG
59#define IP_NF_ASSERT(x) \
60do { \
61 if (!(x)) \
62 printk("IP_NF_ASSERT: %s:%s:%u\n", \
63 __FUNCTION__, __FILE__, __LINE__); \
64} while(0)
65#else
66#define IP_NF_ASSERT(x)
67#endif
68#define SMP_ALIGN(x) (((x) + SMP_CACHE_BYTES-1) & ~(SMP_CACHE_BYTES-1))
69
70static DECLARE_MUTEX(ip6t_mutex);
71
72/* Must have mutex */
73#define ASSERT_READ_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
74#define ASSERT_WRITE_LOCK(x) IP_NF_ASSERT(down_trylock(&ip6t_mutex) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -070075#include <linux/netfilter_ipv4/listhelp.h>
76
77#if 0
78/* All the better to debug you with... */
79#define static
80#define inline
81#endif
82
83/* Locking is simple: we assume at worst case there will be one packet
84 in user context and one from bottom halves (or soft irq if Alexey's
85 softnet patch was applied).
86
87 We keep a set of rules for each CPU, so we can avoid write-locking
88 them; doing a readlock_bh() stops packets coming through if we're
89 in user context.
90
91 To be cache friendly on SMP, we arrange them like so:
92 [ n-entries ]
93 ... cache-align padding ...
94 [ n-entries ]
95
96 Hence the start of any table is given by get_table() below. */
97
98/* The table itself */
99struct ip6t_table_info
100{
101 /* Size per table */
102 unsigned int size;
103 /* Number of entries: FIXME. --RR */
104 unsigned int number;
105 /* Initial number of entries. Needed for module usage count */
106 unsigned int initial_entries;
107
108 /* Entry points and underflows */
109 unsigned int hook_entry[NF_IP6_NUMHOOKS];
110 unsigned int underflow[NF_IP6_NUMHOOKS];
111
112 /* ip6t_entry tables: one per CPU */
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/* Check for an extension */
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/* Returns whether matches rule or not. */
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 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
180 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
181 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
182 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
183 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
184 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
185 return 0;
186 }
187
188 /* Look for ifname matches; this should unroll nicely. */
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/* ... might want to do something with class and flowlabel here ... */
216
217 /* look for the desired protocol header */
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; /* Header offset in skb */
222 u_int16_t hdrlen; /* Header */
223 u_int16_t _fragoff = 0, *fp = NULL;
224
225 ptr = IPV6_HDR_LEN;
226
227 while (ip6t_ext_hdr(currenthdr)) {
228 /* Is there enough space for the next ext header? */
229 if (skb->len - ptr < IPV6_OPTHDR_LEN)
230 return 0;
231
232 /* NONE or ESP: there isn't protocol part */
233 /* If we want to count these packets in '-p all',
234 * we will change the return 0 to 1*/
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 /* Size calculation */
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 /* ptr is too large */
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 /* currenthdr contains the protocol header */
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 /* We need match for the '-p all', too! */
289 if ((ip6info->proto != 0) &&
290 !(ip6info->invflags & IP6T_INV_PROTO))
291 return 0;
292 }
293 return 1;
294}
295
296/* should be ip6 safe */
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 /* Stop iteration if it doesn't match */
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/* Returns one of the generic firewall policies, like NF_ACCEPT. */
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 /* Initializing verdict to NF_DROP keeps gcc happy. */
364 unsigned int verdict = NF_DROP;
365 const char *indev, *outdev;
366 void *table_base;
367 struct ip6t_entry *e, *back;
368
369 /* Initialization */
370 indev = in ? in->name : nulldevname;
371 outdev = out ? out->name : nulldevname;
372
373 /* We handle fragments by dealing with the first fragment as
374 * if it was a normal packet. All other fragments are treated
375 * normally, except that they will NEVER match rules that ask
376 * things we don't know, ie. tcp syn flag or ports). If the
377 * rule is also a fragment-specific rule, non-fragments won't
378 * match it. */
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 /* Check noone else using our table */
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 /* For return from builtin chain */
400 back = get_entry(table_base, table->private->underflow[hook]);
401
402 do {
403 IP_NF_ASSERT(e);
404 IP_NF_ASSERT(back);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
406 &protoff, &offset)) {
407 struct ip6t_entry_target *t;
408
409 if (IP6T_MATCH_ITERATE(e, do_match,
410 *pskb, in, out,
411 offset, protoff, &hotdrop) != 0)
412 goto no_match;
413
414 ADD_COUNTER(e->counters,
415 ntohs((*pskb)->nh.ipv6h->payload_len)
416 + IPV6_HDR_LEN,
417 1);
418
419 t = ip6t_get_target(e);
420 IP_NF_ASSERT(t->u.kernel.target);
421 /* Standard target? */
422 if (!t->u.kernel.target->target) {
423 int v;
424
425 v = ((struct ip6t_standard_target *)t)->verdict;
426 if (v < 0) {
427 /* Pop from stack? */
428 if (v != IP6T_RETURN) {
429 verdict = (unsigned)(-v) - 1;
430 break;
431 }
432 e = back;
433 back = get_entry(table_base,
434 back->comefrom);
435 continue;
436 }
Patrick McHardy05465342005-08-21 23:31:43 -0700437 if (table_base + v != (void *)e + e->next_offset
438 && !(e->ipv6.flags & IP6T_F_GOTO)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 /* Save old back ptr in next entry */
440 struct ip6t_entry *next
441 = (void *)e + e->next_offset;
442 next->comefrom
443 = (void *)back - table_base;
444 /* set back pointer to next entry */
445 back = next;
446 }
447
448 e = get_entry(table_base, v);
449 } else {
450 /* Targets which reenter must return
451 abs. verdicts */
452#ifdef CONFIG_NETFILTER_DEBUG
453 ((struct ip6t_entry *)table_base)->comefrom
454 = 0xeeeeeeec;
455#endif
456 verdict = t->u.kernel.target->target(pskb,
457 in, out,
458 hook,
459 t->data,
460 userdata);
461
462#ifdef CONFIG_NETFILTER_DEBUG
463 if (((struct ip6t_entry *)table_base)->comefrom
464 != 0xeeeeeeec
465 && verdict == IP6T_CONTINUE) {
466 printk("Target %s reentered!\n",
467 t->u.kernel.target->name);
468 verdict = NF_DROP;
469 }
470 ((struct ip6t_entry *)table_base)->comefrom
471 = 0x57acc001;
472#endif
473 if (verdict == IP6T_CONTINUE)
474 e = (void *)e + e->next_offset;
475 else
476 /* Verdict */
477 break;
478 }
479 } else {
480
481 no_match:
482 e = (void *)e + e->next_offset;
483 }
484 } while (!hotdrop);
485
486#ifdef CONFIG_NETFILTER_DEBUG
487 ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
488#endif
489 read_unlock_bh(&table->lock);
490
491#ifdef DEBUG_ALLOW_ALL
492 return NF_ACCEPT;
493#else
494 if (hotdrop)
495 return NF_DROP;
496 else return verdict;
497#endif
498}
499
500/* If it succeeds, returns element and locks mutex */
501static inline void *
502find_inlist_lock_noload(struct list_head *head,
503 const char *name,
504 int *error,
505 struct semaphore *mutex)
506{
507 void *ret;
508
509#if 1
510 duprintf("find_inlist: searching for `%s' in %s.\n",
511 name, head == &ip6t_target ? "ip6t_target"
512 : head == &ip6t_match ? "ip6t_match"
513 : head == &ip6t_tables ? "ip6t_tables" : "UNKNOWN");
514#endif
515
516 *error = down_interruptible(mutex);
517 if (*error != 0)
518 return NULL;
519
520 ret = list_named_find(head, name);
521 if (!ret) {
522 *error = -ENOENT;
523 up(mutex);
524 }
525 return ret;
526}
527
528#ifndef CONFIG_KMOD
529#define find_inlist_lock(h,n,p,e,m) find_inlist_lock_noload((h),(n),(e),(m))
530#else
531static void *
532find_inlist_lock(struct list_head *head,
533 const char *name,
534 const char *prefix,
535 int *error,
536 struct semaphore *mutex)
537{
538 void *ret;
539
540 ret = find_inlist_lock_noload(head, name, error, mutex);
541 if (!ret) {
542 duprintf("find_inlist: loading `%s%s'.\n", prefix, name);
543 request_module("%s%s", prefix, name);
544 ret = find_inlist_lock_noload(head, name, error, mutex);
545 }
546
547 return ret;
548}
549#endif
550
551static inline struct ip6t_table *
552ip6t_find_table_lock(const char *name, int *error, struct semaphore *mutex)
553{
554 return find_inlist_lock(&ip6t_tables, name, "ip6table_", error, mutex);
555}
556
557static inline struct ip6t_match *
558find_match_lock(const char *name, int *error, struct semaphore *mutex)
559{
560 return find_inlist_lock(&ip6t_match, name, "ip6t_", error, mutex);
561}
562
563static struct ip6t_target *
564ip6t_find_target_lock(const char *name, int *error, struct semaphore *mutex)
565{
566 return find_inlist_lock(&ip6t_target, name, "ip6t_", error, mutex);
567}
568
569/* All zeroes == unconditional rule. */
570static inline int
571unconditional(const struct ip6t_ip6 *ipv6)
572{
573 unsigned int i;
574
575 for (i = 0; i < sizeof(*ipv6); i++)
576 if (((char *)ipv6)[i])
577 break;
578
579 return (i == sizeof(*ipv6));
580}
581
582/* Figures out from what hook each rule can be called: returns 0 if
583 there are loops. Puts hook bitmask in comefrom. */
584static int
585mark_source_chains(struct ip6t_table_info *newinfo, unsigned int valid_hooks)
586{
587 unsigned int hook;
588
589 /* No recursion; use packet counter to save back ptrs (reset
590 to 0 as we leave), and comefrom to save source hook bitmask */
591 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
592 unsigned int pos = newinfo->hook_entry[hook];
593 struct ip6t_entry *e
594 = (struct ip6t_entry *)(newinfo->entries + pos);
595
596 if (!(valid_hooks & (1 << hook)))
597 continue;
598
599 /* Set initial back pointer. */
600 e->counters.pcnt = pos;
601
602 for (;;) {
603 struct ip6t_standard_target *t
604 = (void *)ip6t_get_target(e);
605
606 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
607 printk("iptables: loop hook %u pos %u %08X.\n",
608 hook, pos, e->comefrom);
609 return 0;
610 }
611 e->comefrom
612 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
613
614 /* Unconditional return/END. */
615 if (e->target_offset == sizeof(struct ip6t_entry)
616 && (strcmp(t->target.u.user.name,
617 IP6T_STANDARD_TARGET) == 0)
618 && t->verdict < 0
619 && unconditional(&e->ipv6)) {
620 unsigned int oldpos, size;
621
622 /* Return: backtrack through the last
623 big jump. */
624 do {
625 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
626#ifdef DEBUG_IP_FIREWALL_USER
627 if (e->comefrom
628 & (1 << NF_IP6_NUMHOOKS)) {
629 duprintf("Back unset "
630 "on hook %u "
631 "rule %u\n",
632 hook, pos);
633 }
634#endif
635 oldpos = pos;
636 pos = e->counters.pcnt;
637 e->counters.pcnt = 0;
638
639 /* We're at the start. */
640 if (pos == oldpos)
641 goto next;
642
643 e = (struct ip6t_entry *)
644 (newinfo->entries + pos);
645 } while (oldpos == pos + e->next_offset);
646
647 /* Move along one */
648 size = e->next_offset;
649 e = (struct ip6t_entry *)
650 (newinfo->entries + pos + size);
651 e->counters.pcnt = pos;
652 pos += size;
653 } else {
654 int newpos = t->verdict;
655
656 if (strcmp(t->target.u.user.name,
657 IP6T_STANDARD_TARGET) == 0
658 && newpos >= 0) {
659 /* This a jump; chase it. */
660 duprintf("Jump rule %u -> %u\n",
661 pos, newpos);
662 } else {
663 /* ... this is a fallthru */
664 newpos = pos + e->next_offset;
665 }
666 e = (struct ip6t_entry *)
667 (newinfo->entries + newpos);
668 e->counters.pcnt = pos;
669 pos = newpos;
670 }
671 }
672 next:
673 duprintf("Finished chain %u\n", hook);
674 }
675 return 1;
676}
677
678static inline int
679cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
680{
681 if (i && (*i)-- == 0)
682 return 1;
683
684 if (m->u.kernel.match->destroy)
685 m->u.kernel.match->destroy(m->data,
686 m->u.match_size - sizeof(*m));
687 module_put(m->u.kernel.match->me);
688 return 0;
689}
690
691static inline int
692standard_check(const struct ip6t_entry_target *t,
693 unsigned int max_offset)
694{
695 struct ip6t_standard_target *targ = (void *)t;
696
697 /* Check standard info. */
698 if (t->u.target_size
699 != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
700 duprintf("standard_check: target size %u != %u\n",
701 t->u.target_size,
702 IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
703 return 0;
704 }
705
706 if (targ->verdict >= 0
707 && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
708 duprintf("ip6t_standard_check: bad verdict (%i)\n",
709 targ->verdict);
710 return 0;
711 }
712
713 if (targ->verdict < -NF_MAX_VERDICT - 1) {
714 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
715 targ->verdict);
716 return 0;
717 }
718 return 1;
719}
720
721static inline int
722check_match(struct ip6t_entry_match *m,
723 const char *name,
724 const struct ip6t_ip6 *ipv6,
725 unsigned int hookmask,
726 unsigned int *i)
727{
728 int ret;
729 struct ip6t_match *match;
730
731 match = find_match_lock(m->u.user.name, &ret, &ip6t_mutex);
732 if (!match) {
733 // duprintf("check_match: `%s' not found\n", m->u.name);
734 return ret;
735 }
736 if (!try_module_get(match->me)) {
737 up(&ip6t_mutex);
738 return -ENOENT;
739 }
740 m->u.kernel.match = match;
741 up(&ip6t_mutex);
742
743 if (m->u.kernel.match->checkentry
744 && !m->u.kernel.match->checkentry(name, ipv6, m->data,
745 m->u.match_size - sizeof(*m),
746 hookmask)) {
747 module_put(m->u.kernel.match->me);
748 duprintf("ip_tables: check failed for `%s'.\n",
749 m->u.kernel.match->name);
750 return -EINVAL;
751 }
752
753 (*i)++;
754 return 0;
755}
756
757static struct ip6t_target ip6t_standard_target;
758
759static inline int
760check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
761 unsigned int *i)
762{
763 struct ip6t_entry_target *t;
764 struct ip6t_target *target;
765 int ret;
766 unsigned int j;
767
768 if (!ip6_checkentry(&e->ipv6)) {
769 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
770 return -EINVAL;
771 }
772
773 j = 0;
774 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
775 if (ret != 0)
776 goto cleanup_matches;
777
778 t = ip6t_get_target(e);
779 target = ip6t_find_target_lock(t->u.user.name, &ret, &ip6t_mutex);
780 if (!target) {
781 duprintf("check_entry: `%s' not found\n", t->u.user.name);
782 goto cleanup_matches;
783 }
784 if (!try_module_get(target->me)) {
785 up(&ip6t_mutex);
786 ret = -ENOENT;
787 goto cleanup_matches;
788 }
789 t->u.kernel.target = target;
790 up(&ip6t_mutex);
791 if (!t->u.kernel.target) {
792 ret = -EBUSY;
793 goto cleanup_matches;
794 }
795 if (t->u.kernel.target == &ip6t_standard_target) {
796 if (!standard_check(t, size)) {
797 ret = -EINVAL;
798 goto cleanup_matches;
799 }
800 } else if (t->u.kernel.target->checkentry
801 && !t->u.kernel.target->checkentry(name, e, t->data,
802 t->u.target_size
803 - sizeof(*t),
804 e->comefrom)) {
805 module_put(t->u.kernel.target->me);
806 duprintf("ip_tables: check failed for `%s'.\n",
807 t->u.kernel.target->name);
808 ret = -EINVAL;
809 goto cleanup_matches;
810 }
811
812 (*i)++;
813 return 0;
814
815 cleanup_matches:
816 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
817 return ret;
818}
819
820static inline int
821check_entry_size_and_hooks(struct ip6t_entry *e,
822 struct ip6t_table_info *newinfo,
823 unsigned char *base,
824 unsigned char *limit,
825 const unsigned int *hook_entries,
826 const unsigned int *underflows,
827 unsigned int *i)
828{
829 unsigned int h;
830
831 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
832 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
833 duprintf("Bad offset %p\n", e);
834 return -EINVAL;
835 }
836
837 if (e->next_offset
838 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
839 duprintf("checking: element %p size %u\n",
840 e, e->next_offset);
841 return -EINVAL;
842 }
843
844 /* Check hooks & underflows */
845 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
846 if ((unsigned char *)e - base == hook_entries[h])
847 newinfo->hook_entry[h] = hook_entries[h];
848 if ((unsigned char *)e - base == underflows[h])
849 newinfo->underflow[h] = underflows[h];
850 }
851
852 /* FIXME: underflows must be unconditional, standard verdicts
853 < 0 (not IP6T_RETURN). --RR */
854
855 /* Clear counters and comefrom */
856 e->counters = ((struct ip6t_counters) { 0, 0 });
857 e->comefrom = 0;
858
859 (*i)++;
860 return 0;
861}
862
863static inline int
864cleanup_entry(struct ip6t_entry *e, unsigned int *i)
865{
866 struct ip6t_entry_target *t;
867
868 if (i && (*i)-- == 0)
869 return 1;
870
871 /* Cleanup all matches */
872 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
873 t = ip6t_get_target(e);
874 if (t->u.kernel.target->destroy)
875 t->u.kernel.target->destroy(t->data,
876 t->u.target_size - sizeof(*t));
877 module_put(t->u.kernel.target->me);
878 return 0;
879}
880
881/* Checks and translates the user-supplied table segment (held in
882 newinfo) */
883static int
884translate_table(const char *name,
885 unsigned int valid_hooks,
886 struct ip6t_table_info *newinfo,
887 unsigned int size,
888 unsigned int number,
889 const unsigned int *hook_entries,
890 const unsigned int *underflows)
891{
892 unsigned int i;
893 int ret;
894
895 newinfo->size = size;
896 newinfo->number = number;
897
898 /* Init all hooks to impossible value. */
899 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
900 newinfo->hook_entry[i] = 0xFFFFFFFF;
901 newinfo->underflow[i] = 0xFFFFFFFF;
902 }
903
904 duprintf("translate_table: size %u\n", newinfo->size);
905 i = 0;
906 /* Walk through entries, checking offsets. */
907 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
908 check_entry_size_and_hooks,
909 newinfo,
910 newinfo->entries,
911 newinfo->entries + size,
912 hook_entries, underflows, &i);
913 if (ret != 0)
914 return ret;
915
916 if (i != number) {
917 duprintf("translate_table: %u not %u entries\n",
918 i, number);
919 return -EINVAL;
920 }
921
922 /* Check hooks all assigned */
923 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
924 /* Only hooks which are valid */
925 if (!(valid_hooks & (1 << i)))
926 continue;
927 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
928 duprintf("Invalid hook entry %u %u\n",
929 i, hook_entries[i]);
930 return -EINVAL;
931 }
932 if (newinfo->underflow[i] == 0xFFFFFFFF) {
933 duprintf("Invalid underflow %u %u\n",
934 i, underflows[i]);
935 return -EINVAL;
936 }
937 }
938
939 if (!mark_source_chains(newinfo, valid_hooks))
940 return -ELOOP;
941
942 /* Finally, each sanity check must pass */
943 i = 0;
944 ret = IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
945 check_entry, name, size, &i);
946
947 if (ret != 0) {
948 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size,
949 cleanup_entry, &i);
950 return ret;
951 }
952
953 /* And one copy for every other CPU */
David S. Millerc8923c62005-10-13 14:41:23 -0700954 for_each_cpu(i) {
955 if (i == 0)
956 continue;
957 memcpy(newinfo->entries + SMP_ALIGN(newinfo->size) * i,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 newinfo->entries,
959 SMP_ALIGN(newinfo->size));
960 }
961
962 return ret;
963}
964
965static struct ip6t_table_info *
966replace_table(struct ip6t_table *table,
967 unsigned int num_counters,
968 struct ip6t_table_info *newinfo,
969 int *error)
970{
971 struct ip6t_table_info *oldinfo;
972
973#ifdef CONFIG_NETFILTER_DEBUG
974 {
975 struct ip6t_entry *table_base;
976 unsigned int i;
977
978 for (i = 0; i < num_possible_cpus(); i++) {
David S. Millerc8923c62005-10-13 14:41:23 -0700979 for_each_cpu(i) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 table_base =
981 (void *)newinfo->entries
982 + TABLE_OFFSET(newinfo, i);
983
984 table_base->comefrom = 0xdead57ac;
985 }
986 }
987#endif
988
989 /* Do the substitution. */
990 write_lock_bh(&table->lock);
991 /* Check inside lock: is the old number correct? */
992 if (num_counters != table->private->number) {
993 duprintf("num_counters != table->private->number (%u/%u)\n",
994 num_counters, table->private->number);
995 write_unlock_bh(&table->lock);
996 *error = -EAGAIN;
997 return NULL;
998 }
999 oldinfo = table->private;
1000 table->private = newinfo;
1001 newinfo->initial_entries = oldinfo->initial_entries;
1002 write_unlock_bh(&table->lock);
1003
1004 return oldinfo;
1005}
1006
1007/* Gets counters. */
1008static inline int
1009add_entry_to_counter(const struct ip6t_entry *e,
1010 struct ip6t_counters total[],
1011 unsigned int *i)
1012{
1013 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
1014
1015 (*i)++;
1016 return 0;
1017}
1018
1019static void
1020get_counters(const struct ip6t_table_info *t,
1021 struct ip6t_counters counters[])
1022{
1023 unsigned int cpu;
1024 unsigned int i;
1025
David S. Millerc8923c62005-10-13 14:41:23 -07001026 for_each_cpu(cpu) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027 i = 0;
1028 IP6T_ENTRY_ITERATE(t->entries + TABLE_OFFSET(t, cpu),
1029 t->size,
1030 add_entry_to_counter,
1031 counters,
1032 &i);
1033 }
1034}
1035
1036static int
1037copy_entries_to_user(unsigned int total_size,
1038 struct ip6t_table *table,
1039 void __user *userptr)
1040{
1041 unsigned int off, num, countersize;
1042 struct ip6t_entry *e;
1043 struct ip6t_counters *counters;
1044 int ret = 0;
1045
1046 /* We need atomic snapshot of counters: rest doesn't change
1047 (other than comefrom, which userspace doesn't care
1048 about). */
1049 countersize = sizeof(struct ip6t_counters) * table->private->number;
1050 counters = vmalloc(countersize);
1051
1052 if (counters == NULL)
1053 return -ENOMEM;
1054
1055 /* First, sum counters... */
1056 memset(counters, 0, countersize);
1057 write_lock_bh(&table->lock);
1058 get_counters(table->private, counters);
1059 write_unlock_bh(&table->lock);
1060
1061 /* ... then copy entire thing from CPU 0... */
1062 if (copy_to_user(userptr, table->private->entries, total_size) != 0) {
1063 ret = -EFAULT;
1064 goto free_counters;
1065 }
1066
1067 /* FIXME: use iterator macros --RR */
1068 /* ... then go back and fix counters and names */
1069 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1070 unsigned int i;
1071 struct ip6t_entry_match *m;
1072 struct ip6t_entry_target *t;
1073
1074 e = (struct ip6t_entry *)(table->private->entries + off);
1075 if (copy_to_user(userptr + off
1076 + offsetof(struct ip6t_entry, counters),
1077 &counters[num],
1078 sizeof(counters[num])) != 0) {
1079 ret = -EFAULT;
1080 goto free_counters;
1081 }
1082
1083 for (i = sizeof(struct ip6t_entry);
1084 i < e->target_offset;
1085 i += m->u.match_size) {
1086 m = (void *)e + i;
1087
1088 if (copy_to_user(userptr + off + i
1089 + offsetof(struct ip6t_entry_match,
1090 u.user.name),
1091 m->u.kernel.match->name,
1092 strlen(m->u.kernel.match->name)+1)
1093 != 0) {
1094 ret = -EFAULT;
1095 goto free_counters;
1096 }
1097 }
1098
1099 t = ip6t_get_target(e);
1100 if (copy_to_user(userptr + off + e->target_offset
1101 + offsetof(struct ip6t_entry_target,
1102 u.user.name),
1103 t->u.kernel.target->name,
1104 strlen(t->u.kernel.target->name)+1) != 0) {
1105 ret = -EFAULT;
1106 goto free_counters;
1107 }
1108 }
1109
1110 free_counters:
1111 vfree(counters);
1112 return ret;
1113}
1114
1115static int
1116get_entries(const struct ip6t_get_entries *entries,
1117 struct ip6t_get_entries __user *uptr)
1118{
1119 int ret;
1120 struct ip6t_table *t;
1121
1122 t = ip6t_find_table_lock(entries->name, &ret, &ip6t_mutex);
1123 if (t) {
1124 duprintf("t->private->number = %u\n",
1125 t->private->number);
1126 if (entries->size == t->private->size)
1127 ret = copy_entries_to_user(t->private->size,
1128 t, uptr->entrytable);
1129 else {
1130 duprintf("get_entries: I've got %u not %u!\n",
1131 t->private->size,
1132 entries->size);
1133 ret = -EINVAL;
1134 }
1135 up(&ip6t_mutex);
1136 } else
1137 duprintf("get_entries: Can't find %s!\n",
1138 entries->name);
1139
1140 return ret;
1141}
1142
1143static int
1144do_replace(void __user *user, unsigned int len)
1145{
1146 int ret;
1147 struct ip6t_replace tmp;
1148 struct ip6t_table *t;
1149 struct ip6t_table_info *newinfo, *oldinfo;
1150 struct ip6t_counters *counters;
1151
1152 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1153 return -EFAULT;
1154
1155 /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
1156 if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
1157 return -ENOMEM;
1158
1159 newinfo = vmalloc(sizeof(struct ip6t_table_info)
David S. Millerc8923c62005-10-13 14:41:23 -07001160 + SMP_ALIGN(tmp.size) *
1161 (highest_possible_processor_id()+1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 if (!newinfo)
1163 return -ENOMEM;
1164
1165 if (copy_from_user(newinfo->entries, user + sizeof(tmp),
1166 tmp.size) != 0) {
1167 ret = -EFAULT;
1168 goto free_newinfo;
1169 }
1170
1171 counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
1172 if (!counters) {
1173 ret = -ENOMEM;
1174 goto free_newinfo;
1175 }
1176 memset(counters, 0, tmp.num_counters * sizeof(struct ip6t_counters));
1177
1178 ret = translate_table(tmp.name, tmp.valid_hooks,
1179 newinfo, tmp.size, tmp.num_entries,
1180 tmp.hook_entry, tmp.underflow);
1181 if (ret != 0)
1182 goto free_newinfo_counters;
1183
1184 duprintf("ip_tables: Translated table\n");
1185
1186 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1187 if (!t)
1188 goto free_newinfo_counters_untrans;
1189
1190 /* You lied! */
1191 if (tmp.valid_hooks != t->valid_hooks) {
1192 duprintf("Valid hook crap: %08X vs %08X\n",
1193 tmp.valid_hooks, t->valid_hooks);
1194 ret = -EINVAL;
1195 goto free_newinfo_counters_untrans_unlock;
1196 }
1197
1198 /* Get a reference in advance, we're not allowed fail later */
1199 if (!try_module_get(t->me)) {
1200 ret = -EBUSY;
1201 goto free_newinfo_counters_untrans_unlock;
1202 }
1203
1204 oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
1205 if (!oldinfo)
1206 goto put_module;
1207
1208 /* Update module usage count based on number of rules */
1209 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1210 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1211 if ((oldinfo->number > oldinfo->initial_entries) ||
1212 (newinfo->number <= oldinfo->initial_entries))
1213 module_put(t->me);
1214 if ((oldinfo->number > oldinfo->initial_entries) &&
1215 (newinfo->number <= oldinfo->initial_entries))
1216 module_put(t->me);
1217
1218 /* Get the old counters. */
1219 get_counters(oldinfo, counters);
1220 /* Decrease module usage counts and free resource */
1221 IP6T_ENTRY_ITERATE(oldinfo->entries, oldinfo->size, cleanup_entry,NULL);
1222 vfree(oldinfo);
1223 /* Silent error: too late now. */
1224 if (copy_to_user(tmp.counters, counters,
1225 sizeof(struct ip6t_counters) * tmp.num_counters) != 0)
1226 ret = -EFAULT;
1227 vfree(counters);
1228 up(&ip6t_mutex);
1229 return ret;
1230
1231 put_module:
1232 module_put(t->me);
1233 free_newinfo_counters_untrans_unlock:
1234 up(&ip6t_mutex);
1235 free_newinfo_counters_untrans:
1236 IP6T_ENTRY_ITERATE(newinfo->entries, newinfo->size, cleanup_entry,NULL);
1237 free_newinfo_counters:
1238 vfree(counters);
1239 free_newinfo:
1240 vfree(newinfo);
1241 return ret;
1242}
1243
1244/* We're lazy, and add to the first CPU; overflow works its fey magic
1245 * and everything is OK. */
1246static inline int
1247add_counter_to_entry(struct ip6t_entry *e,
1248 const struct ip6t_counters addme[],
1249 unsigned int *i)
1250{
1251#if 0
1252 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1253 *i,
1254 (long unsigned int)e->counters.pcnt,
1255 (long unsigned int)e->counters.bcnt,
1256 (long unsigned int)addme[*i].pcnt,
1257 (long unsigned int)addme[*i].bcnt);
1258#endif
1259
1260 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1261
1262 (*i)++;
1263 return 0;
1264}
1265
1266static int
1267do_add_counters(void __user *user, unsigned int len)
1268{
1269 unsigned int i;
1270 struct ip6t_counters_info tmp, *paddc;
1271 struct ip6t_table *t;
1272 int ret;
1273
1274 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1275 return -EFAULT;
1276
1277 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
1278 return -EINVAL;
1279
1280 paddc = vmalloc(len);
1281 if (!paddc)
1282 return -ENOMEM;
1283
1284 if (copy_from_user(paddc, user, len) != 0) {
1285 ret = -EFAULT;
1286 goto free;
1287 }
1288
1289 t = ip6t_find_table_lock(tmp.name, &ret, &ip6t_mutex);
1290 if (!t)
1291 goto free;
1292
1293 write_lock_bh(&t->lock);
1294 if (t->private->number != paddc->num_counters) {
1295 ret = -EINVAL;
1296 goto unlock_up_free;
1297 }
1298
1299 i = 0;
1300 IP6T_ENTRY_ITERATE(t->private->entries,
1301 t->private->size,
1302 add_counter_to_entry,
1303 paddc->counters,
1304 &i);
1305 unlock_up_free:
1306 write_unlock_bh(&t->lock);
1307 up(&ip6t_mutex);
1308 free:
1309 vfree(paddc);
1310
1311 return ret;
1312}
1313
1314static int
1315do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1316{
1317 int ret;
1318
1319 if (!capable(CAP_NET_ADMIN))
1320 return -EPERM;
1321
1322 switch (cmd) {
1323 case IP6T_SO_SET_REPLACE:
1324 ret = do_replace(user, len);
1325 break;
1326
1327 case IP6T_SO_SET_ADD_COUNTERS:
1328 ret = do_add_counters(user, len);
1329 break;
1330
1331 default:
1332 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1333 ret = -EINVAL;
1334 }
1335
1336 return ret;
1337}
1338
1339static int
1340do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1341{
1342 int ret;
1343
1344 if (!capable(CAP_NET_ADMIN))
1345 return -EPERM;
1346
1347 switch (cmd) {
1348 case IP6T_SO_GET_INFO: {
1349 char name[IP6T_TABLE_MAXNAMELEN];
1350 struct ip6t_table *t;
1351
1352 if (*len != sizeof(struct ip6t_getinfo)) {
1353 duprintf("length %u != %u\n", *len,
1354 sizeof(struct ip6t_getinfo));
1355 ret = -EINVAL;
1356 break;
1357 }
1358
1359 if (copy_from_user(name, user, sizeof(name)) != 0) {
1360 ret = -EFAULT;
1361 break;
1362 }
1363 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1364 t = ip6t_find_table_lock(name, &ret, &ip6t_mutex);
1365 if (t) {
1366 struct ip6t_getinfo info;
1367
1368 info.valid_hooks = t->valid_hooks;
1369 memcpy(info.hook_entry, t->private->hook_entry,
1370 sizeof(info.hook_entry));
1371 memcpy(info.underflow, t->private->underflow,
1372 sizeof(info.underflow));
1373 info.num_entries = t->private->number;
1374 info.size = t->private->size;
1375 memcpy(info.name, name, sizeof(info.name));
1376
1377 if (copy_to_user(user, &info, *len) != 0)
1378 ret = -EFAULT;
1379 else
1380 ret = 0;
1381
1382 up(&ip6t_mutex);
1383 }
1384 }
1385 break;
1386
1387 case IP6T_SO_GET_ENTRIES: {
1388 struct ip6t_get_entries get;
1389
1390 if (*len < sizeof(get)) {
1391 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1392 ret = -EINVAL;
1393 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1394 ret = -EFAULT;
1395 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1396 duprintf("get_entries: %u != %u\n", *len,
1397 sizeof(struct ip6t_get_entries) + get.size);
1398 ret = -EINVAL;
1399 } else
1400 ret = get_entries(&get, user);
1401 break;
1402 }
1403
1404 default:
1405 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1406 ret = -EINVAL;
1407 }
1408
1409 return ret;
1410}
1411
1412/* Registration hooks for targets. */
1413int
1414ip6t_register_target(struct ip6t_target *target)
1415{
1416 int ret;
1417
1418 ret = down_interruptible(&ip6t_mutex);
1419 if (ret != 0)
1420 return ret;
1421
1422 if (!list_named_insert(&ip6t_target, target)) {
1423 duprintf("ip6t_register_target: `%s' already in list!\n",
1424 target->name);
1425 ret = -EINVAL;
1426 }
1427 up(&ip6t_mutex);
1428 return ret;
1429}
1430
1431void
1432ip6t_unregister_target(struct ip6t_target *target)
1433{
1434 down(&ip6t_mutex);
1435 LIST_DELETE(&ip6t_target, target);
1436 up(&ip6t_mutex);
1437}
1438
1439int
1440ip6t_register_match(struct ip6t_match *match)
1441{
1442 int ret;
1443
1444 ret = down_interruptible(&ip6t_mutex);
1445 if (ret != 0)
1446 return ret;
1447
1448 if (!list_named_insert(&ip6t_match, match)) {
1449 duprintf("ip6t_register_match: `%s' already in list!\n",
1450 match->name);
1451 ret = -EINVAL;
1452 }
1453 up(&ip6t_mutex);
1454
1455 return ret;
1456}
1457
1458void
1459ip6t_unregister_match(struct ip6t_match *match)
1460{
1461 down(&ip6t_mutex);
1462 LIST_DELETE(&ip6t_match, match);
1463 up(&ip6t_mutex);
1464}
1465
1466int ip6t_register_table(struct ip6t_table *table,
1467 const struct ip6t_replace *repl)
1468{
1469 int ret;
1470 struct ip6t_table_info *newinfo;
1471 static struct ip6t_table_info bootstrap
1472 = { 0, 0, 0, { 0 }, { 0 }, { } };
1473
1474 newinfo = vmalloc(sizeof(struct ip6t_table_info)
David S. Millerc8923c62005-10-13 14:41:23 -07001475 + SMP_ALIGN(repl->size) *
1476 (highest_possible_processor_id()+1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477 if (!newinfo)
1478 return -ENOMEM;
1479
1480 memcpy(newinfo->entries, repl->entries, repl->size);
1481
1482 ret = translate_table(table->name, table->valid_hooks,
1483 newinfo, repl->size,
1484 repl->num_entries,
1485 repl->hook_entry,
1486 repl->underflow);
1487 if (ret != 0) {
1488 vfree(newinfo);
1489 return ret;
1490 }
1491
1492 ret = down_interruptible(&ip6t_mutex);
1493 if (ret != 0) {
1494 vfree(newinfo);
1495 return ret;
1496 }
1497
1498 /* Don't autoload: we'd eat our tail... */
1499 if (list_named_find(&ip6t_tables, table->name)) {
1500 ret = -EEXIST;
1501 goto free_unlock;
1502 }
1503
1504 /* Simplifies replace_table code. */
1505 table->private = &bootstrap;
1506 if (!replace_table(table, 0, newinfo, &ret))
1507 goto free_unlock;
1508
1509 duprintf("table->private->number = %u\n",
1510 table->private->number);
1511
1512 /* save number of initial entries */
1513 table->private->initial_entries = table->private->number;
1514
1515 rwlock_init(&table->lock);
1516 list_prepend(&ip6t_tables, table);
1517
1518 unlock:
1519 up(&ip6t_mutex);
1520 return ret;
1521
1522 free_unlock:
1523 vfree(newinfo);
1524 goto unlock;
1525}
1526
1527void ip6t_unregister_table(struct ip6t_table *table)
1528{
1529 down(&ip6t_mutex);
1530 LIST_DELETE(&ip6t_tables, table);
1531 up(&ip6t_mutex);
1532
1533 /* Decrease module usage counts and free resources */
1534 IP6T_ENTRY_ITERATE(table->private->entries, table->private->size,
1535 cleanup_entry, NULL);
1536 vfree(table->private);
1537}
1538
1539/* Returns 1 if the port is matched by the range, 0 otherwise */
1540static inline int
1541port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
1542{
1543 int ret;
1544
1545 ret = (port >= min && port <= max) ^ invert;
1546 return ret;
1547}
1548
1549static int
1550tcp_find_option(u_int8_t option,
1551 const struct sk_buff *skb,
1552 unsigned int tcpoff,
1553 unsigned int optlen,
1554 int invert,
1555 int *hotdrop)
1556{
1557 /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
1558 u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
1559 unsigned int i;
1560
1561 duprintf("tcp_match: finding option\n");
1562 if (!optlen)
1563 return invert;
1564 /* If we don't have the whole header, drop packet. */
1565 op = skb_header_pointer(skb, tcpoff + sizeof(struct tcphdr), optlen,
1566 _opt);
1567 if (op == NULL) {
1568 *hotdrop = 1;
1569 return 0;
1570 }
1571
1572 for (i = 0; i < optlen; ) {
1573 if (op[i] == option) return !invert;
1574 if (op[i] < 2) i++;
1575 else i += op[i+1]?:1;
1576 }
1577
1578 return invert;
1579}
1580
1581static int
1582tcp_match(const struct sk_buff *skb,
1583 const struct net_device *in,
1584 const struct net_device *out,
1585 const void *matchinfo,
1586 int offset,
1587 unsigned int protoff,
1588 int *hotdrop)
1589{
1590 struct tcphdr _tcph, *th;
1591 const struct ip6t_tcp *tcpinfo = matchinfo;
1592
1593 if (offset) {
1594 /* To quote Alan:
1595
1596 Don't allow a fragment of TCP 8 bytes in. Nobody normal
1597 causes this. Its a cracker trying to break in by doing a
1598 flag overwrite to pass the direction checks.
1599 */
1600 if (offset == 1) {
1601 duprintf("Dropping evil TCP offset=1 frag.\n");
1602 *hotdrop = 1;
1603 }
1604 /* Must not be a fragment. */
1605 return 0;
1606 }
1607
1608#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
1609
1610 th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
1611 if (th == NULL) {
1612 /* We've been asked to examine this packet, and we
1613 can't. Hence, no choice but to drop. */
1614 duprintf("Dropping evil TCP offset=0 tinygram.\n");
1615 *hotdrop = 1;
1616 return 0;
1617 }
1618
1619 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
1620 ntohs(th->source),
1621 !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT)))
1622 return 0;
1623 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
1624 ntohs(th->dest),
1625 !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT)))
1626 return 0;
1627 if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
1628 == tcpinfo->flg_cmp,
1629 IP6T_TCP_INV_FLAGS))
1630 return 0;
1631 if (tcpinfo->option) {
1632 if (th->doff * 4 < sizeof(_tcph)) {
1633 *hotdrop = 1;
1634 return 0;
1635 }
1636 if (!tcp_find_option(tcpinfo->option, skb, protoff,
1637 th->doff*4 - sizeof(*th),
1638 tcpinfo->invflags & IP6T_TCP_INV_OPTION,
1639 hotdrop))
1640 return 0;
1641 }
1642 return 1;
1643}
1644
1645/* Called when user tries to insert an entry of this type. */
1646static int
1647tcp_checkentry(const char *tablename,
1648 const struct ip6t_ip6 *ipv6,
1649 void *matchinfo,
1650 unsigned int matchsize,
1651 unsigned int hook_mask)
1652{
1653 const struct ip6t_tcp *tcpinfo = matchinfo;
1654
1655 /* Must specify proto == TCP, and no unknown invflags */
1656 return ipv6->proto == IPPROTO_TCP
1657 && !(ipv6->invflags & IP6T_INV_PROTO)
1658 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
1659 && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
1660}
1661
1662static int
1663udp_match(const struct sk_buff *skb,
1664 const struct net_device *in,
1665 const struct net_device *out,
1666 const void *matchinfo,
1667 int offset,
1668 unsigned int protoff,
1669 int *hotdrop)
1670{
1671 struct udphdr _udph, *uh;
1672 const struct ip6t_udp *udpinfo = matchinfo;
1673
1674 /* Must not be a fragment. */
1675 if (offset)
1676 return 0;
1677
1678 uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
1679 if (uh == NULL) {
1680 /* We've been asked to examine this packet, and we
1681 can't. Hence, no choice but to drop. */
1682 duprintf("Dropping evil UDP tinygram.\n");
1683 *hotdrop = 1;
1684 return 0;
1685 }
1686
1687 return port_match(udpinfo->spts[0], udpinfo->spts[1],
1688 ntohs(uh->source),
1689 !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
1690 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
1691 ntohs(uh->dest),
1692 !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
1693}
1694
1695/* Called when user tries to insert an entry of this type. */
1696static int
1697udp_checkentry(const char *tablename,
1698 const struct ip6t_ip6 *ipv6,
1699 void *matchinfo,
1700 unsigned int matchinfosize,
1701 unsigned int hook_mask)
1702{
1703 const struct ip6t_udp *udpinfo = matchinfo;
1704
1705 /* Must specify proto == UDP, and no unknown invflags */
1706 if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
1707 duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
1708 IPPROTO_UDP);
1709 return 0;
1710 }
1711 if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
1712 duprintf("ip6t_udp: matchsize %u != %u\n",
1713 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
1714 return 0;
1715 }
1716 if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
1717 duprintf("ip6t_udp: unknown flags %X\n",
1718 udpinfo->invflags);
1719 return 0;
1720 }
1721
1722 return 1;
1723}
1724
1725/* Returns 1 if the type and code is matched by the range, 0 otherwise */
1726static inline int
1727icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1728 u_int8_t type, u_int8_t code,
1729 int invert)
1730{
1731 return (type == test_type && code >= min_code && code <= max_code)
1732 ^ invert;
1733}
1734
1735static int
1736icmp6_match(const struct sk_buff *skb,
1737 const struct net_device *in,
1738 const struct net_device *out,
1739 const void *matchinfo,
1740 int offset,
1741 unsigned int protoff,
1742 int *hotdrop)
1743{
1744 struct icmp6hdr _icmp, *ic;
1745 const struct ip6t_icmp *icmpinfo = matchinfo;
1746
1747 /* Must not be a fragment. */
1748 if (offset)
1749 return 0;
1750
1751 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1752 if (ic == NULL) {
1753 /* We've been asked to examine this packet, and we
1754 can't. Hence, no choice but to drop. */
1755 duprintf("Dropping evil ICMP tinygram.\n");
1756 *hotdrop = 1;
1757 return 0;
1758 }
1759
1760 return icmp6_type_code_match(icmpinfo->type,
1761 icmpinfo->code[0],
1762 icmpinfo->code[1],
1763 ic->icmp6_type, ic->icmp6_code,
1764 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1765}
1766
1767/* Called when user tries to insert an entry of this type. */
1768static int
1769icmp6_checkentry(const char *tablename,
1770 const struct ip6t_ip6 *ipv6,
1771 void *matchinfo,
1772 unsigned int matchsize,
1773 unsigned int hook_mask)
1774{
1775 const struct ip6t_icmp *icmpinfo = matchinfo;
1776
1777 /* Must specify proto == ICMP, and no unknown invflags */
1778 return ipv6->proto == IPPROTO_ICMPV6
1779 && !(ipv6->invflags & IP6T_INV_PROTO)
1780 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
1781 && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1782}
1783
1784/* The built-in targets: standard (NULL) and error. */
1785static struct ip6t_target ip6t_standard_target = {
1786 .name = IP6T_STANDARD_TARGET,
1787};
1788
1789static struct ip6t_target ip6t_error_target = {
1790 .name = IP6T_ERROR_TARGET,
1791 .target = ip6t_error,
1792};
1793
1794static struct nf_sockopt_ops ip6t_sockopts = {
1795 .pf = PF_INET6,
1796 .set_optmin = IP6T_BASE_CTL,
1797 .set_optmax = IP6T_SO_SET_MAX+1,
1798 .set = do_ip6t_set_ctl,
1799 .get_optmin = IP6T_BASE_CTL,
1800 .get_optmax = IP6T_SO_GET_MAX+1,
1801 .get = do_ip6t_get_ctl,
1802};
1803
1804static struct ip6t_match tcp_matchstruct = {
1805 .name = "tcp",
1806 .match = &tcp_match,
1807 .checkentry = &tcp_checkentry,
1808};
1809
1810static struct ip6t_match udp_matchstruct = {
1811 .name = "udp",
1812 .match = &udp_match,
1813 .checkentry = &udp_checkentry,
1814};
1815
1816static struct ip6t_match icmp6_matchstruct = {
1817 .name = "icmp6",
1818 .match = &icmp6_match,
1819 .checkentry = &icmp6_checkentry,
1820};
1821
1822#ifdef CONFIG_PROC_FS
1823static inline int print_name(const char *i,
1824 off_t start_offset, char *buffer, int length,
1825 off_t *pos, unsigned int *count)
1826{
1827 if ((*count)++ >= start_offset) {
1828 unsigned int namelen;
1829
1830 namelen = sprintf(buffer + *pos, "%s\n",
1831 i + sizeof(struct list_head));
1832 if (*pos + namelen > length) {
1833 /* Stop iterating */
1834 return 1;
1835 }
1836 *pos += namelen;
1837 }
1838 return 0;
1839}
1840
1841static inline int print_target(const struct ip6t_target *t,
1842 off_t start_offset, char *buffer, int length,
1843 off_t *pos, unsigned int *count)
1844{
1845 if (t == &ip6t_standard_target || t == &ip6t_error_target)
1846 return 0;
1847 return print_name((char *)t, start_offset, buffer, length, pos, count);
1848}
1849
1850static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
1851{
1852 off_t pos = 0;
1853 unsigned int count = 0;
1854
1855 if (down_interruptible(&ip6t_mutex) != 0)
1856 return 0;
1857
1858 LIST_FIND(&ip6t_tables, print_name, char *,
1859 offset, buffer, length, &pos, &count);
1860
1861 up(&ip6t_mutex);
1862
1863 /* `start' hack - see fs/proc/generic.c line ~105 */
1864 *start=(char *)((unsigned long)count-offset);
1865 return pos;
1866}
1867
1868static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
1869{
1870 off_t pos = 0;
1871 unsigned int count = 0;
1872
1873 if (down_interruptible(&ip6t_mutex) != 0)
1874 return 0;
1875
1876 LIST_FIND(&ip6t_target, print_target, struct ip6t_target *,
1877 offset, buffer, length, &pos, &count);
1878
1879 up(&ip6t_mutex);
1880
1881 *start = (char *)((unsigned long)count - offset);
1882 return pos;
1883}
1884
1885static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
1886{
1887 off_t pos = 0;
1888 unsigned int count = 0;
1889
1890 if (down_interruptible(&ip6t_mutex) != 0)
1891 return 0;
1892
1893 LIST_FIND(&ip6t_match, print_name, char *,
1894 offset, buffer, length, &pos, &count);
1895
1896 up(&ip6t_mutex);
1897
1898 *start = (char *)((unsigned long)count - offset);
1899 return pos;
1900}
1901
1902static struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
1903{ { "ip6_tables_names", ip6t_get_tables },
1904 { "ip6_tables_targets", ip6t_get_targets },
1905 { "ip6_tables_matches", ip6t_get_matches },
1906 { NULL, NULL} };
1907#endif /*CONFIG_PROC_FS*/
1908
1909static int __init init(void)
1910{
1911 int ret;
1912
1913 /* Noone else will be downing sem now, so we won't sleep */
1914 down(&ip6t_mutex);
1915 list_append(&ip6t_target, &ip6t_standard_target);
1916 list_append(&ip6t_target, &ip6t_error_target);
1917 list_append(&ip6t_match, &tcp_matchstruct);
1918 list_append(&ip6t_match, &udp_matchstruct);
1919 list_append(&ip6t_match, &icmp6_matchstruct);
1920 up(&ip6t_mutex);
1921
1922 /* Register setsockopt */
1923 ret = nf_register_sockopt(&ip6t_sockopts);
1924 if (ret < 0) {
1925 duprintf("Unable to register sockopts.\n");
1926 return ret;
1927 }
1928
1929#ifdef CONFIG_PROC_FS
1930 {
1931 struct proc_dir_entry *proc;
1932 int i;
1933
1934 for (i = 0; ip6t_proc_entry[i].name; i++) {
1935 proc = proc_net_create(ip6t_proc_entry[i].name, 0,
1936 ip6t_proc_entry[i].get_info);
1937 if (!proc) {
1938 while (--i >= 0)
1939 proc_net_remove(ip6t_proc_entry[i].name);
1940 nf_unregister_sockopt(&ip6t_sockopts);
1941 return -ENOMEM;
1942 }
1943 proc->owner = THIS_MODULE;
1944 }
1945 }
1946#endif
1947
1948 printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
1949 return 0;
1950}
1951
1952static void __exit fini(void)
1953{
1954 nf_unregister_sockopt(&ip6t_sockopts);
1955#ifdef CONFIG_PROC_FS
1956 {
1957 int i;
1958 for (i = 0; ip6t_proc_entry[i].name; i++)
1959 proc_net_remove(ip6t_proc_entry[i].name);
1960 }
1961#endif
1962}
1963
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07001964/*
1965 * find specified header up to transport protocol header.
1966 * If found target header, the offset to the header is set to *offset
1967 * and return 0. otherwise, return -1.
1968 *
1969 * Notes: - non-1st Fragment Header isn't skipped.
1970 * - ESP header isn't skipped.
1971 * - The target header may be trancated.
1972 */
1973int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, u8 target)
1974{
1975 unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data;
1976 u8 nexthdr = skb->nh.ipv6h->nexthdr;
1977 unsigned int len = skb->len - start;
1978
1979 while (nexthdr != target) {
1980 struct ipv6_opt_hdr _hdr, *hp;
1981 unsigned int hdrlen;
1982
1983 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE)
1984 return -1;
1985 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1986 if (hp == NULL)
1987 return -1;
1988 if (nexthdr == NEXTHDR_FRAGMENT) {
1989 unsigned short _frag_off, *fp;
1990 fp = skb_header_pointer(skb,
1991 start+offsetof(struct frag_hdr,
1992 frag_off),
1993 sizeof(_frag_off),
1994 &_frag_off);
1995 if (fp == NULL)
1996 return -1;
1997
1998 if (ntohs(*fp) & ~0x7)
1999 return -1;
2000 hdrlen = 8;
2001 } else if (nexthdr == NEXTHDR_AUTH)
2002 hdrlen = (hp->hdrlen + 2) << 2;
2003 else
2004 hdrlen = ipv6_optlen(hp);
2005
2006 nexthdr = hp->nexthdr;
2007 len -= hdrlen;
2008 start += hdrlen;
2009 }
2010
2011 *offset = start;
2012 return 0;
2013}
2014
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015EXPORT_SYMBOL(ip6t_register_table);
2016EXPORT_SYMBOL(ip6t_unregister_table);
2017EXPORT_SYMBOL(ip6t_do_table);
2018EXPORT_SYMBOL(ip6t_register_match);
2019EXPORT_SYMBOL(ip6t_unregister_match);
2020EXPORT_SYMBOL(ip6t_register_target);
2021EXPORT_SYMBOL(ip6t_unregister_target);
2022EXPORT_SYMBOL(ip6t_ext_hdr);
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002023EXPORT_SYMBOL(ipv6_find_hdr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024
2025module_init(init);
2026module_exit(fini);