blob: dd80020d874035cfbcd058e035d5641b018d2fe2 [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
Harald Welte6b7d31f2005-10-26 09:34:24 +02005 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <net/ipv6.h>
27#include <asm/uaccess.h>
28#include <asm/semaphore.h>
29#include <linux/proc_fs.h>
David S. Millerc8923c62005-10-13 14:41:23 -070030#include <linux/cpumask.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
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/*#define DEBUG_IP_FIREWALL*/
42/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
43/*#define DEBUG_IP_FIREWALL_USER*/
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/* Must have mutex */
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)
Linus Torvalds1da177e2005-04-16 15:20:36 -070074#include <linux/netfilter_ipv4/listhelp.h>
75
76#if 0
77/* All the better to debug you with... */
78#define static
79#define inline
80#endif
81
Harald Welte6b7d31f2005-10-26 09:34:24 +020082/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 We keep a set of rules for each CPU, so we can avoid write-locking
Harald Welte6b7d31f2005-10-26 09:34:24 +020084 them in the softirq when updating the counters and therefore
85 only need to read-lock in the softirq; doing a write_lock_bh() in user
86 context stops packets coming through and allows user context to read
87 the counters or update the rules.
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 Hence the start of any table is given by get_table() below. */
90
91/* The table itself */
92struct ip6t_table_info
93{
94 /* Size per table */
95 unsigned int size;
96 /* Number of entries: FIXME. --RR */
97 unsigned int number;
98 /* Initial number of entries. Needed for module usage count */
99 unsigned int initial_entries;
100
101 /* Entry points and underflows */
102 unsigned int hook_entry[NF_IP6_NUMHOOKS];
103 unsigned int underflow[NF_IP6_NUMHOOKS];
104
105 /* ip6t_entry tables: one per CPU */
Eric Dumazet31836062005-12-13 23:13:48 -0800106 void *entries[NR_CPUS];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107};
108
109static LIST_HEAD(ip6t_target);
110static LIST_HEAD(ip6t_match);
111static LIST_HEAD(ip6t_tables);
Eric Dumazet31836062005-12-13 23:13:48 -0800112#define SET_COUNTER(c,b,p) do { (c).bcnt = (b); (c).pcnt = (p); } while(0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
114
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115#if 0
116#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
117#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; })
118#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
119#endif
120
121static int ip6_masked_addrcmp(struct in6_addr addr1, struct in6_addr mask,
122 struct in6_addr addr2)
123{
124 int i;
125 for( i = 0; i < 16; i++){
126 if((addr1.s6_addr[i] & mask.s6_addr[i]) !=
127 (addr2.s6_addr[i] & mask.s6_addr[i]))
128 return 1;
129 }
130 return 0;
131}
132
133/* Check for an extension */
134int
135ip6t_ext_hdr(u8 nexthdr)
136{
137 return ( (nexthdr == IPPROTO_HOPOPTS) ||
138 (nexthdr == IPPROTO_ROUTING) ||
139 (nexthdr == IPPROTO_FRAGMENT) ||
140 (nexthdr == IPPROTO_ESP) ||
141 (nexthdr == IPPROTO_AH) ||
142 (nexthdr == IPPROTO_NONE) ||
143 (nexthdr == IPPROTO_DSTOPTS) );
144}
145
146/* Returns whether matches rule or not. */
147static inline int
148ip6_packet_match(const struct sk_buff *skb,
149 const char *indev,
150 const char *outdev,
151 const struct ip6t_ip6 *ip6info,
152 unsigned int *protoff,
153 int *fragoff)
154{
155 size_t i;
156 unsigned long ret;
157 const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
158
159#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
160
161 if (FWINV(ip6_masked_addrcmp(ipv6->saddr,ip6info->smsk,ip6info->src),
162 IP6T_INV_SRCIP)
163 || FWINV(ip6_masked_addrcmp(ipv6->daddr,ip6info->dmsk,ip6info->dst),
164 IP6T_INV_DSTIP)) {
165 dprintf("Source or dest mismatch.\n");
166/*
167 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
168 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
169 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
170 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
171 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
172 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
173 return 0;
174 }
175
176 /* Look for ifname matches; this should unroll nicely. */
177 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
178 ret |= (((const unsigned long *)indev)[i]
179 ^ ((const unsigned long *)ip6info->iniface)[i])
180 & ((const unsigned long *)ip6info->iniface_mask)[i];
181 }
182
183 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
184 dprintf("VIA in mismatch (%s vs %s).%s\n",
185 indev, ip6info->iniface,
186 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
187 return 0;
188 }
189
190 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
191 ret |= (((const unsigned long *)outdev)[i]
192 ^ ((const unsigned long *)ip6info->outiface)[i])
193 & ((const unsigned long *)ip6info->outiface_mask)[i];
194 }
195
196 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
197 dprintf("VIA out mismatch (%s vs %s).%s\n",
198 outdev, ip6info->outiface,
199 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
200 return 0;
201 }
202
203/* ... might want to do something with class and flowlabel here ... */
204
205 /* look for the desired protocol header */
206 if((ip6info->flags & IP6T_F_PROTO)) {
207 u_int8_t currenthdr = ipv6->nexthdr;
208 struct ipv6_opt_hdr _hdr, *hp;
209 u_int16_t ptr; /* Header offset in skb */
210 u_int16_t hdrlen; /* Header */
211 u_int16_t _fragoff = 0, *fp = NULL;
212
213 ptr = IPV6_HDR_LEN;
214
215 while (ip6t_ext_hdr(currenthdr)) {
216 /* Is there enough space for the next ext header? */
217 if (skb->len - ptr < IPV6_OPTHDR_LEN)
218 return 0;
219
220 /* NONE or ESP: there isn't protocol part */
221 /* If we want to count these packets in '-p all',
222 * we will change the return 0 to 1*/
223 if ((currenthdr == IPPROTO_NONE) ||
224 (currenthdr == IPPROTO_ESP))
225 break;
226
227 hp = skb_header_pointer(skb, ptr, sizeof(_hdr), &_hdr);
228 BUG_ON(hp == NULL);
229
230 /* Size calculation */
231 if (currenthdr == IPPROTO_FRAGMENT) {
232 fp = skb_header_pointer(skb,
233 ptr+offsetof(struct frag_hdr,
234 frag_off),
235 sizeof(_fragoff),
236 &_fragoff);
237 if (fp == NULL)
238 return 0;
239
240 _fragoff = ntohs(*fp) & ~0x7;
241 hdrlen = 8;
242 } else if (currenthdr == IPPROTO_AH)
243 hdrlen = (hp->hdrlen+2)<<2;
244 else
245 hdrlen = ipv6_optlen(hp);
246
247 currenthdr = hp->nexthdr;
248 ptr += hdrlen;
249 /* ptr is too large */
250 if ( ptr > skb->len )
251 return 0;
252 if (_fragoff) {
253 if (ip6t_ext_hdr(currenthdr))
254 return 0;
255 break;
256 }
257 }
258
259 *protoff = ptr;
260 *fragoff = _fragoff;
261
262 /* currenthdr contains the protocol header */
263
264 dprintf("Packet protocol %hi ?= %s%hi.\n",
265 currenthdr,
266 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
267 ip6info->proto);
268
269 if (ip6info->proto == currenthdr) {
270 if(ip6info->invflags & IP6T_INV_PROTO) {
271 return 0;
272 }
273 return 1;
274 }
275
276 /* We need match for the '-p all', too! */
277 if ((ip6info->proto != 0) &&
278 !(ip6info->invflags & IP6T_INV_PROTO))
279 return 0;
280 }
281 return 1;
282}
283
284/* should be ip6 safe */
285static inline int
286ip6_checkentry(const struct ip6t_ip6 *ipv6)
287{
288 if (ipv6->flags & ~IP6T_F_MASK) {
289 duprintf("Unknown flag bits set: %08X\n",
290 ipv6->flags & ~IP6T_F_MASK);
291 return 0;
292 }
293 if (ipv6->invflags & ~IP6T_INV_MASK) {
294 duprintf("Unknown invflag bits set: %08X\n",
295 ipv6->invflags & ~IP6T_INV_MASK);
296 return 0;
297 }
298 return 1;
299}
300
301static unsigned int
302ip6t_error(struct sk_buff **pskb,
303 const struct net_device *in,
304 const struct net_device *out,
305 unsigned int hooknum,
306 const void *targinfo,
307 void *userinfo)
308{
309 if (net_ratelimit())
310 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
311
312 return NF_DROP;
313}
314
315static inline
316int do_match(struct ip6t_entry_match *m,
317 const struct sk_buff *skb,
318 const struct net_device *in,
319 const struct net_device *out,
320 int offset,
321 unsigned int protoff,
322 int *hotdrop)
323{
324 /* Stop iteration if it doesn't match */
325 if (!m->u.kernel.match->match(skb, in, out, m->data,
326 offset, protoff, hotdrop))
327 return 1;
328 else
329 return 0;
330}
331
332static inline struct ip6t_entry *
333get_entry(void *base, unsigned int offset)
334{
335 return (struct ip6t_entry *)(base + offset);
336}
337
338/* Returns one of the generic firewall policies, like NF_ACCEPT. */
339unsigned int
340ip6t_do_table(struct sk_buff **pskb,
341 unsigned int hook,
342 const struct net_device *in,
343 const struct net_device *out,
344 struct ip6t_table *table,
345 void *userdata)
346{
Harald Welte6b7d31f2005-10-26 09:34:24 +0200347 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 int offset = 0;
349 unsigned int protoff = 0;
350 int hotdrop = 0;
351 /* Initializing verdict to NF_DROP keeps gcc happy. */
352 unsigned int verdict = NF_DROP;
353 const char *indev, *outdev;
354 void *table_base;
355 struct ip6t_entry *e, *back;
356
357 /* Initialization */
358 indev = in ? in->name : nulldevname;
359 outdev = out ? out->name : nulldevname;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 /* We handle fragments by dealing with the first fragment as
361 * if it was a normal packet. All other fragments are treated
362 * normally, except that they will NEVER match rules that ask
363 * things we don't know, ie. tcp syn flag or ports). If the
364 * rule is also a fragment-specific rule, non-fragments won't
365 * match it. */
366
367 read_lock_bh(&table->lock);
368 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
Eric Dumazet31836062005-12-13 23:13:48 -0800369 table_base = (void *)table->private->entries[smp_processor_id()];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 e = get_entry(table_base, table->private->hook_entry[hook]);
371
372#ifdef CONFIG_NETFILTER_DEBUG
373 /* Check noone else using our table */
374 if (((struct ip6t_entry *)table_base)->comefrom != 0xdead57ac
375 && ((struct ip6t_entry *)table_base)->comefrom != 0xeeeeeeec) {
376 printk("ASSERT: CPU #%u, %s comefrom(%p) = %X\n",
377 smp_processor_id(),
378 table->name,
379 &((struct ip6t_entry *)table_base)->comefrom,
380 ((struct ip6t_entry *)table_base)->comefrom);
381 }
382 ((struct ip6t_entry *)table_base)->comefrom = 0x57acc001;
383#endif
384
385 /* For return from builtin chain */
386 back = get_entry(table_base, table->private->underflow[hook]);
387
388 do {
389 IP_NF_ASSERT(e);
390 IP_NF_ASSERT(back);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
392 &protoff, &offset)) {
393 struct ip6t_entry_target *t;
394
395 if (IP6T_MATCH_ITERATE(e, do_match,
396 *pskb, in, out,
397 offset, protoff, &hotdrop) != 0)
398 goto no_match;
399
400 ADD_COUNTER(e->counters,
401 ntohs((*pskb)->nh.ipv6h->payload_len)
402 + IPV6_HDR_LEN,
403 1);
404
405 t = ip6t_get_target(e);
406 IP_NF_ASSERT(t->u.kernel.target);
407 /* Standard target? */
408 if (!t->u.kernel.target->target) {
409 int v;
410
411 v = ((struct ip6t_standard_target *)t)->verdict;
412 if (v < 0) {
413 /* Pop from stack? */
414 if (v != IP6T_RETURN) {
415 verdict = (unsigned)(-v) - 1;
416 break;
417 }
418 e = back;
419 back = get_entry(table_base,
420 back->comefrom);
421 continue;
422 }
Patrick McHardy05465342005-08-21 23:31:43 -0700423 if (table_base + v != (void *)e + e->next_offset
424 && !(e->ipv6.flags & IP6T_F_GOTO)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 /* Save old back ptr in next entry */
426 struct ip6t_entry *next
427 = (void *)e + e->next_offset;
428 next->comefrom
429 = (void *)back - table_base;
430 /* set back pointer to next entry */
431 back = next;
432 }
433
434 e = get_entry(table_base, v);
435 } else {
436 /* Targets which reenter must return
437 abs. verdicts */
438#ifdef CONFIG_NETFILTER_DEBUG
439 ((struct ip6t_entry *)table_base)->comefrom
440 = 0xeeeeeeec;
441#endif
442 verdict = t->u.kernel.target->target(pskb,
443 in, out,
444 hook,
445 t->data,
446 userdata);
447
448#ifdef CONFIG_NETFILTER_DEBUG
449 if (((struct ip6t_entry *)table_base)->comefrom
450 != 0xeeeeeeec
451 && verdict == IP6T_CONTINUE) {
452 printk("Target %s reentered!\n",
453 t->u.kernel.target->name);
454 verdict = NF_DROP;
455 }
456 ((struct ip6t_entry *)table_base)->comefrom
457 = 0x57acc001;
458#endif
459 if (verdict == IP6T_CONTINUE)
460 e = (void *)e + e->next_offset;
461 else
462 /* Verdict */
463 break;
464 }
465 } else {
466
467 no_match:
468 e = (void *)e + e->next_offset;
469 }
470 } while (!hotdrop);
471
472#ifdef CONFIG_NETFILTER_DEBUG
473 ((struct ip6t_entry *)table_base)->comefrom = 0xdead57ac;
474#endif
475 read_unlock_bh(&table->lock);
476
477#ifdef DEBUG_ALLOW_ALL
478 return NF_ACCEPT;
479#else
480 if (hotdrop)
481 return NF_DROP;
482 else return verdict;
483#endif
484}
485
Harald Welte6b7d31f2005-10-26 09:34:24 +0200486/*
487 * These are weird, but module loading must not be done with mutex
488 * held (since they will register), and we have to have a single
489 * function to use try_then_request_module().
490 */
491
492/* Find table by name, grabs mutex & ref. Returns ERR_PTR() on error. */
493static inline struct ip6t_table *find_table_lock(const char *name)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494{
Harald Welte6b7d31f2005-10-26 09:34:24 +0200495 struct ip6t_table *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
Harald Welte6b7d31f2005-10-26 09:34:24 +0200497 if (down_interruptible(&ip6t_mutex) != 0)
498 return ERR_PTR(-EINTR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499
Harald Welte6b7d31f2005-10-26 09:34:24 +0200500 list_for_each_entry(t, &ip6t_tables, list)
501 if (strcmp(t->name, name) == 0 && try_module_get(t->me))
502 return t;
503 up(&ip6t_mutex);
504 return NULL;
505}
506
507/* Find match, grabs ref. Returns ERR_PTR() on error. */
508static inline struct ip6t_match *find_match(const char *name, u8 revision)
509{
510 struct ip6t_match *m;
511 int err = 0;
512
513 if (down_interruptible(&ip6t_mutex) != 0)
514 return ERR_PTR(-EINTR);
515
516 list_for_each_entry(m, &ip6t_match, list) {
517 if (strcmp(m->name, name) == 0) {
518 if (m->revision == revision) {
519 if (try_module_get(m->me)) {
520 up(&ip6t_mutex);
521 return m;
522 }
523 } else
524 err = -EPROTOTYPE; /* Found something. */
525 }
526 }
527 up(&ip6t_mutex);
528 return ERR_PTR(err);
529}
530
531/* Find target, grabs ref. Returns ERR_PTR() on error. */
532static inline struct ip6t_target *find_target(const char *name, u8 revision)
533{
534 struct ip6t_target *t;
535 int err = 0;
536
537 if (down_interruptible(&ip6t_mutex) != 0)
538 return ERR_PTR(-EINTR);
539
540 list_for_each_entry(t, &ip6t_target, list) {
541 if (strcmp(t->name, name) == 0) {
542 if (t->revision == revision) {
543 if (try_module_get(t->me)) {
544 up(&ip6t_mutex);
545 return t;
546 }
547 } else
548 err = -EPROTOTYPE; /* Found something. */
549 }
550 }
551 up(&ip6t_mutex);
552 return ERR_PTR(err);
553}
554
555struct ip6t_target *ip6t_find_target(const char *name, u8 revision)
556{
557 struct ip6t_target *target;
558
559 target = try_then_request_module(find_target(name, revision),
560 "ip6t_%s", name);
561 if (IS_ERR(target) || !target)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 return NULL;
Harald Welte6b7d31f2005-10-26 09:34:24 +0200563 return target;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564}
565
Harald Welte6b7d31f2005-10-26 09:34:24 +0200566static int match_revfn(const char *name, u8 revision, int *bestp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567{
Harald Welte6b7d31f2005-10-26 09:34:24 +0200568 struct ip6t_match *m;
569 int have_rev = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
Harald Welte6b7d31f2005-10-26 09:34:24 +0200571 list_for_each_entry(m, &ip6t_match, list) {
572 if (strcmp(m->name, name) == 0) {
573 if (m->revision > *bestp)
574 *bestp = m->revision;
575 if (m->revision == revision)
576 have_rev = 1;
577 }
578 }
579 return have_rev;
580}
581
582static int target_revfn(const char *name, u8 revision, int *bestp)
583{
584 struct ip6t_target *t;
585 int have_rev = 0;
586
587 list_for_each_entry(t, &ip6t_target, list) {
588 if (strcmp(t->name, name) == 0) {
589 if (t->revision > *bestp)
590 *bestp = t->revision;
591 if (t->revision == revision)
592 have_rev = 1;
593 }
594 }
595 return have_rev;
596}
597
598/* Returns true or fals (if no such extension at all) */
599static inline int find_revision(const char *name, u8 revision,
600 int (*revfn)(const char *, u8, int *),
601 int *err)
602{
603 int have_rev, best = -1;
604
605 if (down_interruptible(&ip6t_mutex) != 0) {
606 *err = -EINTR;
607 return 1;
608 }
609 have_rev = revfn(name, revision, &best);
610 up(&ip6t_mutex);
611
612 /* Nothing at all? Return 0 to try loading module. */
613 if (best == -1) {
614 *err = -ENOENT;
615 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 }
617
Harald Welte6b7d31f2005-10-26 09:34:24 +0200618 *err = best;
619 if (!have_rev)
620 *err = -EPROTONOSUPPORT;
621 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622}
623
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624
625/* All zeroes == unconditional rule. */
626static inline int
627unconditional(const struct ip6t_ip6 *ipv6)
628{
629 unsigned int i;
630
631 for (i = 0; i < sizeof(*ipv6); i++)
632 if (((char *)ipv6)[i])
633 break;
634
635 return (i == sizeof(*ipv6));
636}
637
638/* Figures out from what hook each rule can be called: returns 0 if
639 there are loops. Puts hook bitmask in comefrom. */
640static int
Eric Dumazet31836062005-12-13 23:13:48 -0800641mark_source_chains(struct ip6t_table_info *newinfo,
642 unsigned int valid_hooks, void *entry0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643{
644 unsigned int hook;
645
646 /* No recursion; use packet counter to save back ptrs (reset
647 to 0 as we leave), and comefrom to save source hook bitmask */
648 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
649 unsigned int pos = newinfo->hook_entry[hook];
650 struct ip6t_entry *e
Eric Dumazet31836062005-12-13 23:13:48 -0800651 = (struct ip6t_entry *)(entry0 + pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
653 if (!(valid_hooks & (1 << hook)))
654 continue;
655
656 /* Set initial back pointer. */
657 e->counters.pcnt = pos;
658
659 for (;;) {
660 struct ip6t_standard_target *t
661 = (void *)ip6t_get_target(e);
662
663 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
664 printk("iptables: loop hook %u pos %u %08X.\n",
665 hook, pos, e->comefrom);
666 return 0;
667 }
668 e->comefrom
669 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
670
671 /* Unconditional return/END. */
672 if (e->target_offset == sizeof(struct ip6t_entry)
673 && (strcmp(t->target.u.user.name,
674 IP6T_STANDARD_TARGET) == 0)
675 && t->verdict < 0
676 && unconditional(&e->ipv6)) {
677 unsigned int oldpos, size;
678
679 /* Return: backtrack through the last
680 big jump. */
681 do {
682 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
683#ifdef DEBUG_IP_FIREWALL_USER
684 if (e->comefrom
685 & (1 << NF_IP6_NUMHOOKS)) {
686 duprintf("Back unset "
687 "on hook %u "
688 "rule %u\n",
689 hook, pos);
690 }
691#endif
692 oldpos = pos;
693 pos = e->counters.pcnt;
694 e->counters.pcnt = 0;
695
696 /* We're at the start. */
697 if (pos == oldpos)
698 goto next;
699
700 e = (struct ip6t_entry *)
Eric Dumazet31836062005-12-13 23:13:48 -0800701 (entry0 + pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 } while (oldpos == pos + e->next_offset);
703
704 /* Move along one */
705 size = e->next_offset;
706 e = (struct ip6t_entry *)
Eric Dumazet31836062005-12-13 23:13:48 -0800707 (entry0 + pos + size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 e->counters.pcnt = pos;
709 pos += size;
710 } else {
711 int newpos = t->verdict;
712
713 if (strcmp(t->target.u.user.name,
714 IP6T_STANDARD_TARGET) == 0
715 && newpos >= 0) {
716 /* This a jump; chase it. */
717 duprintf("Jump rule %u -> %u\n",
718 pos, newpos);
719 } else {
720 /* ... this is a fallthru */
721 newpos = pos + e->next_offset;
722 }
723 e = (struct ip6t_entry *)
Eric Dumazet31836062005-12-13 23:13:48 -0800724 (entry0 + newpos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 e->counters.pcnt = pos;
726 pos = newpos;
727 }
728 }
729 next:
730 duprintf("Finished chain %u\n", hook);
731 }
732 return 1;
733}
734
735static inline int
736cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
737{
738 if (i && (*i)-- == 0)
739 return 1;
740
741 if (m->u.kernel.match->destroy)
742 m->u.kernel.match->destroy(m->data,
743 m->u.match_size - sizeof(*m));
744 module_put(m->u.kernel.match->me);
745 return 0;
746}
747
748static inline int
749standard_check(const struct ip6t_entry_target *t,
750 unsigned int max_offset)
751{
752 struct ip6t_standard_target *targ = (void *)t;
753
754 /* Check standard info. */
755 if (t->u.target_size
756 != IP6T_ALIGN(sizeof(struct ip6t_standard_target))) {
757 duprintf("standard_check: target size %u != %u\n",
758 t->u.target_size,
759 IP6T_ALIGN(sizeof(struct ip6t_standard_target)));
760 return 0;
761 }
762
763 if (targ->verdict >= 0
764 && targ->verdict > max_offset - sizeof(struct ip6t_entry)) {
765 duprintf("ip6t_standard_check: bad verdict (%i)\n",
766 targ->verdict);
767 return 0;
768 }
769
770 if (targ->verdict < -NF_MAX_VERDICT - 1) {
771 duprintf("ip6t_standard_check: bad negative verdict (%i)\n",
772 targ->verdict);
773 return 0;
774 }
775 return 1;
776}
777
778static inline int
779check_match(struct ip6t_entry_match *m,
780 const char *name,
781 const struct ip6t_ip6 *ipv6,
782 unsigned int hookmask,
783 unsigned int *i)
784{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 struct ip6t_match *match;
786
Harald Welte6b7d31f2005-10-26 09:34:24 +0200787 match = try_then_request_module(find_match(m->u.user.name,
788 m->u.user.revision),
789 "ip6t_%s", m->u.user.name);
790 if (IS_ERR(match) || !match) {
791 duprintf("check_match: `%s' not found\n", m->u.user.name);
792 return match ? PTR_ERR(match) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 }
794 m->u.kernel.match = match;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
796 if (m->u.kernel.match->checkentry
797 && !m->u.kernel.match->checkentry(name, ipv6, m->data,
798 m->u.match_size - sizeof(*m),
799 hookmask)) {
800 module_put(m->u.kernel.match->me);
801 duprintf("ip_tables: check failed for `%s'.\n",
802 m->u.kernel.match->name);
803 return -EINVAL;
804 }
805
806 (*i)++;
807 return 0;
808}
809
810static struct ip6t_target ip6t_standard_target;
811
812static inline int
813check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
814 unsigned int *i)
815{
816 struct ip6t_entry_target *t;
817 struct ip6t_target *target;
818 int ret;
819 unsigned int j;
820
821 if (!ip6_checkentry(&e->ipv6)) {
822 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
823 return -EINVAL;
824 }
825
826 j = 0;
827 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
828 if (ret != 0)
829 goto cleanup_matches;
830
831 t = ip6t_get_target(e);
Harald Welte6b7d31f2005-10-26 09:34:24 +0200832 target = try_then_request_module(find_target(t->u.user.name,
833 t->u.user.revision),
834 "ip6t_%s", t->u.user.name);
835 if (IS_ERR(target) || !target) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 duprintf("check_entry: `%s' not found\n", t->u.user.name);
Harald Welte6b7d31f2005-10-26 09:34:24 +0200837 ret = target ? PTR_ERR(target) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 goto cleanup_matches;
839 }
840 t->u.kernel.target = target;
Harald Welte6b7d31f2005-10-26 09:34:24 +0200841
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 if (t->u.kernel.target == &ip6t_standard_target) {
843 if (!standard_check(t, size)) {
844 ret = -EINVAL;
845 goto cleanup_matches;
846 }
847 } else if (t->u.kernel.target->checkentry
848 && !t->u.kernel.target->checkentry(name, e, t->data,
849 t->u.target_size
850 - sizeof(*t),
851 e->comefrom)) {
852 module_put(t->u.kernel.target->me);
853 duprintf("ip_tables: check failed for `%s'.\n",
854 t->u.kernel.target->name);
855 ret = -EINVAL;
856 goto cleanup_matches;
857 }
858
859 (*i)++;
860 return 0;
861
862 cleanup_matches:
863 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
864 return ret;
865}
866
867static inline int
868check_entry_size_and_hooks(struct ip6t_entry *e,
869 struct ip6t_table_info *newinfo,
870 unsigned char *base,
871 unsigned char *limit,
872 const unsigned int *hook_entries,
873 const unsigned int *underflows,
874 unsigned int *i)
875{
876 unsigned int h;
877
878 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
879 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
880 duprintf("Bad offset %p\n", e);
881 return -EINVAL;
882 }
883
884 if (e->next_offset
885 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
886 duprintf("checking: element %p size %u\n",
887 e, e->next_offset);
888 return -EINVAL;
889 }
890
891 /* Check hooks & underflows */
892 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
893 if ((unsigned char *)e - base == hook_entries[h])
894 newinfo->hook_entry[h] = hook_entries[h];
895 if ((unsigned char *)e - base == underflows[h])
896 newinfo->underflow[h] = underflows[h];
897 }
898
899 /* FIXME: underflows must be unconditional, standard verdicts
900 < 0 (not IP6T_RETURN). --RR */
901
902 /* Clear counters and comefrom */
903 e->counters = ((struct ip6t_counters) { 0, 0 });
904 e->comefrom = 0;
905
906 (*i)++;
907 return 0;
908}
909
910static inline int
911cleanup_entry(struct ip6t_entry *e, unsigned int *i)
912{
913 struct ip6t_entry_target *t;
914
915 if (i && (*i)-- == 0)
916 return 1;
917
918 /* Cleanup all matches */
919 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
920 t = ip6t_get_target(e);
921 if (t->u.kernel.target->destroy)
922 t->u.kernel.target->destroy(t->data,
923 t->u.target_size - sizeof(*t));
924 module_put(t->u.kernel.target->me);
925 return 0;
926}
927
928/* Checks and translates the user-supplied table segment (held in
929 newinfo) */
930static int
931translate_table(const char *name,
932 unsigned int valid_hooks,
933 struct ip6t_table_info *newinfo,
Eric Dumazet31836062005-12-13 23:13:48 -0800934 void *entry0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 unsigned int size,
936 unsigned int number,
937 const unsigned int *hook_entries,
938 const unsigned int *underflows)
939{
940 unsigned int i;
941 int ret;
942
943 newinfo->size = size;
944 newinfo->number = number;
945
946 /* Init all hooks to impossible value. */
947 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
948 newinfo->hook_entry[i] = 0xFFFFFFFF;
949 newinfo->underflow[i] = 0xFFFFFFFF;
950 }
951
952 duprintf("translate_table: size %u\n", newinfo->size);
953 i = 0;
954 /* Walk through entries, checking offsets. */
Eric Dumazet31836062005-12-13 23:13:48 -0800955 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 check_entry_size_and_hooks,
957 newinfo,
Eric Dumazet31836062005-12-13 23:13:48 -0800958 entry0,
959 entry0 + size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 hook_entries, underflows, &i);
961 if (ret != 0)
962 return ret;
963
964 if (i != number) {
965 duprintf("translate_table: %u not %u entries\n",
966 i, number);
967 return -EINVAL;
968 }
969
970 /* Check hooks all assigned */
971 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
972 /* Only hooks which are valid */
973 if (!(valid_hooks & (1 << i)))
974 continue;
975 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
976 duprintf("Invalid hook entry %u %u\n",
977 i, hook_entries[i]);
978 return -EINVAL;
979 }
980 if (newinfo->underflow[i] == 0xFFFFFFFF) {
981 duprintf("Invalid underflow %u %u\n",
982 i, underflows[i]);
983 return -EINVAL;
984 }
985 }
986
Eric Dumazet31836062005-12-13 23:13:48 -0800987 if (!mark_source_chains(newinfo, valid_hooks, entry0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 return -ELOOP;
989
990 /* Finally, each sanity check must pass */
991 i = 0;
Eric Dumazet31836062005-12-13 23:13:48 -0800992 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 check_entry, name, size, &i);
994
995 if (ret != 0) {
Eric Dumazet31836062005-12-13 23:13:48 -0800996 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 cleanup_entry, &i);
998 return ret;
999 }
1000
1001 /* And one copy for every other CPU */
David S. Millerc8923c62005-10-13 14:41:23 -07001002 for_each_cpu(i) {
Eric Dumazet31836062005-12-13 23:13:48 -08001003 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
1004 memcpy(newinfo->entries[i], entry0, newinfo->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 }
1006
1007 return ret;
1008}
1009
1010static struct ip6t_table_info *
1011replace_table(struct ip6t_table *table,
1012 unsigned int num_counters,
1013 struct ip6t_table_info *newinfo,
1014 int *error)
1015{
1016 struct ip6t_table_info *oldinfo;
1017
1018#ifdef CONFIG_NETFILTER_DEBUG
1019 {
Eric Dumazet31836062005-12-13 23:13:48 -08001020 int cpu;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021
Eric Dumazet31836062005-12-13 23:13:48 -08001022 for_each_cpu(cpu) {
1023 struct ip6t_entry *table_base = newinfo->entries[cpu];
1024 if (table_base)
1025 table_base->comefrom = 0xdead57ac;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 }
1027 }
1028#endif
1029
1030 /* Do the substitution. */
1031 write_lock_bh(&table->lock);
1032 /* Check inside lock: is the old number correct? */
1033 if (num_counters != table->private->number) {
1034 duprintf("num_counters != table->private->number (%u/%u)\n",
1035 num_counters, table->private->number);
1036 write_unlock_bh(&table->lock);
1037 *error = -EAGAIN;
1038 return NULL;
1039 }
1040 oldinfo = table->private;
1041 table->private = newinfo;
1042 newinfo->initial_entries = oldinfo->initial_entries;
1043 write_unlock_bh(&table->lock);
1044
1045 return oldinfo;
1046}
1047
1048/* Gets counters. */
1049static inline int
1050add_entry_to_counter(const struct ip6t_entry *e,
1051 struct ip6t_counters total[],
1052 unsigned int *i)
1053{
1054 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
1055
1056 (*i)++;
1057 return 0;
1058}
1059
Eric Dumazet31836062005-12-13 23:13:48 -08001060static inline int
1061set_entry_to_counter(const struct ip6t_entry *e,
1062 struct ip6t_counters total[],
1063 unsigned int *i)
1064{
1065 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
1066
1067 (*i)++;
1068 return 0;
1069}
1070
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071static void
1072get_counters(const struct ip6t_table_info *t,
1073 struct ip6t_counters counters[])
1074{
1075 unsigned int cpu;
1076 unsigned int i;
Eric Dumazet31836062005-12-13 23:13:48 -08001077 unsigned int curcpu;
1078
1079 /* Instead of clearing (by a previous call to memset())
1080 * the counters and using adds, we set the counters
1081 * with data used by 'current' CPU
1082 * We dont care about preemption here.
1083 */
1084 curcpu = raw_smp_processor_id();
1085
1086 i = 0;
1087 IP6T_ENTRY_ITERATE(t->entries[curcpu],
1088 t->size,
1089 set_entry_to_counter,
1090 counters,
1091 &i);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092
David S. Millerc8923c62005-10-13 14:41:23 -07001093 for_each_cpu(cpu) {
Eric Dumazet31836062005-12-13 23:13:48 -08001094 if (cpu == curcpu)
1095 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 i = 0;
Eric Dumazet31836062005-12-13 23:13:48 -08001097 IP6T_ENTRY_ITERATE(t->entries[cpu],
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 t->size,
1099 add_entry_to_counter,
1100 counters,
1101 &i);
1102 }
1103}
1104
1105static int
1106copy_entries_to_user(unsigned int total_size,
1107 struct ip6t_table *table,
1108 void __user *userptr)
1109{
1110 unsigned int off, num, countersize;
1111 struct ip6t_entry *e;
1112 struct ip6t_counters *counters;
1113 int ret = 0;
Eric Dumazet31836062005-12-13 23:13:48 -08001114 void *loc_cpu_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115
1116 /* We need atomic snapshot of counters: rest doesn't change
1117 (other than comefrom, which userspace doesn't care
1118 about). */
1119 countersize = sizeof(struct ip6t_counters) * table->private->number;
1120 counters = vmalloc(countersize);
1121
1122 if (counters == NULL)
1123 return -ENOMEM;
1124
1125 /* First, sum counters... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001126 write_lock_bh(&table->lock);
1127 get_counters(table->private, counters);
1128 write_unlock_bh(&table->lock);
1129
Eric Dumazet31836062005-12-13 23:13:48 -08001130 /* choose the copy that is on ourc node/cpu */
1131 loc_cpu_entry = table->private->entries[raw_smp_processor_id()];
1132 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 ret = -EFAULT;
1134 goto free_counters;
1135 }
1136
1137 /* FIXME: use iterator macros --RR */
1138 /* ... then go back and fix counters and names */
1139 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1140 unsigned int i;
1141 struct ip6t_entry_match *m;
1142 struct ip6t_entry_target *t;
1143
Eric Dumazet31836062005-12-13 23:13:48 -08001144 e = (struct ip6t_entry *)(loc_cpu_entry + off);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 if (copy_to_user(userptr + off
1146 + offsetof(struct ip6t_entry, counters),
1147 &counters[num],
1148 sizeof(counters[num])) != 0) {
1149 ret = -EFAULT;
1150 goto free_counters;
1151 }
1152
1153 for (i = sizeof(struct ip6t_entry);
1154 i < e->target_offset;
1155 i += m->u.match_size) {
1156 m = (void *)e + i;
1157
1158 if (copy_to_user(userptr + off + i
1159 + offsetof(struct ip6t_entry_match,
1160 u.user.name),
1161 m->u.kernel.match->name,
1162 strlen(m->u.kernel.match->name)+1)
1163 != 0) {
1164 ret = -EFAULT;
1165 goto free_counters;
1166 }
1167 }
1168
1169 t = ip6t_get_target(e);
1170 if (copy_to_user(userptr + off + e->target_offset
1171 + offsetof(struct ip6t_entry_target,
1172 u.user.name),
1173 t->u.kernel.target->name,
1174 strlen(t->u.kernel.target->name)+1) != 0) {
1175 ret = -EFAULT;
1176 goto free_counters;
1177 }
1178 }
1179
1180 free_counters:
1181 vfree(counters);
1182 return ret;
1183}
1184
1185static int
1186get_entries(const struct ip6t_get_entries *entries,
1187 struct ip6t_get_entries __user *uptr)
1188{
1189 int ret;
1190 struct ip6t_table *t;
1191
Harald Welte6b7d31f2005-10-26 09:34:24 +02001192 t = find_table_lock(entries->name);
1193 if (t && !IS_ERR(t)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 duprintf("t->private->number = %u\n",
1195 t->private->number);
1196 if (entries->size == t->private->size)
1197 ret = copy_entries_to_user(t->private->size,
1198 t, uptr->entrytable);
1199 else {
1200 duprintf("get_entries: I've got %u not %u!\n",
1201 t->private->size,
1202 entries->size);
1203 ret = -EINVAL;
1204 }
Harald Welte6b7d31f2005-10-26 09:34:24 +02001205 module_put(t->me);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 up(&ip6t_mutex);
1207 } else
Harald Welte6b7d31f2005-10-26 09:34:24 +02001208 ret = t ? PTR_ERR(t) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209
1210 return ret;
1211}
1212
Eric Dumazet31836062005-12-13 23:13:48 -08001213static void free_table_info(struct ip6t_table_info *info)
1214{
1215 int cpu;
1216 for_each_cpu(cpu) {
1217 if (info->size <= PAGE_SIZE)
1218 kfree(info->entries[cpu]);
1219 else
1220 vfree(info->entries[cpu]);
1221 }
1222 kfree(info);
1223}
1224
1225static struct ip6t_table_info *alloc_table_info(unsigned int size)
1226{
1227 struct ip6t_table_info *newinfo;
1228 int cpu;
1229
1230 newinfo = kzalloc(sizeof(struct ip6t_table_info), GFP_KERNEL);
1231 if (!newinfo)
1232 return NULL;
1233
1234 newinfo->size = size;
1235
1236 for_each_cpu(cpu) {
1237 if (size <= PAGE_SIZE)
1238 newinfo->entries[cpu] = kmalloc_node(size,
1239 GFP_KERNEL,
1240 cpu_to_node(cpu));
1241 else
1242 newinfo->entries[cpu] = vmalloc_node(size,
1243 cpu_to_node(cpu));
1244 if (newinfo->entries[cpu] == NULL) {
1245 free_table_info(newinfo);
1246 return NULL;
1247 }
1248 }
1249
1250 return newinfo;
1251}
1252
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253static int
1254do_replace(void __user *user, unsigned int len)
1255{
1256 int ret;
1257 struct ip6t_replace tmp;
1258 struct ip6t_table *t;
1259 struct ip6t_table_info *newinfo, *oldinfo;
1260 struct ip6t_counters *counters;
Eric Dumazet31836062005-12-13 23:13:48 -08001261 void *loc_cpu_entry, *loc_cpu_old_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262
1263 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1264 return -EFAULT;
1265
1266 /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */
1267 if ((SMP_ALIGN(tmp.size) >> PAGE_SHIFT) + 2 > num_physpages)
1268 return -ENOMEM;
1269
Eric Dumazet31836062005-12-13 23:13:48 -08001270 newinfo = alloc_table_info(tmp.size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001271 if (!newinfo)
1272 return -ENOMEM;
1273
Eric Dumazet31836062005-12-13 23:13:48 -08001274 /* choose the copy that is on our node/cpu */
1275 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1276 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 tmp.size) != 0) {
1278 ret = -EFAULT;
1279 goto free_newinfo;
1280 }
1281
1282 counters = vmalloc(tmp.num_counters * sizeof(struct ip6t_counters));
1283 if (!counters) {
1284 ret = -ENOMEM;
1285 goto free_newinfo;
1286 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
1288 ret = translate_table(tmp.name, tmp.valid_hooks,
Eric Dumazet31836062005-12-13 23:13:48 -08001289 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 tmp.hook_entry, tmp.underflow);
1291 if (ret != 0)
1292 goto free_newinfo_counters;
1293
1294 duprintf("ip_tables: Translated table\n");
1295
Harald Welte6b7d31f2005-10-26 09:34:24 +02001296 t = try_then_request_module(find_table_lock(tmp.name),
1297 "ip6table_%s", tmp.name);
1298 if (!t || IS_ERR(t)) {
1299 ret = t ? PTR_ERR(t) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 goto free_newinfo_counters_untrans;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001301 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302
1303 /* You lied! */
1304 if (tmp.valid_hooks != t->valid_hooks) {
1305 duprintf("Valid hook crap: %08X vs %08X\n",
1306 tmp.valid_hooks, t->valid_hooks);
1307 ret = -EINVAL;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001308 goto put_module;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 }
1310
1311 oldinfo = replace_table(t, tmp.num_counters, newinfo, &ret);
1312 if (!oldinfo)
1313 goto put_module;
1314
1315 /* Update module usage count based on number of rules */
1316 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1317 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1318 if ((oldinfo->number > oldinfo->initial_entries) ||
1319 (newinfo->number <= oldinfo->initial_entries))
1320 module_put(t->me);
1321 if ((oldinfo->number > oldinfo->initial_entries) &&
1322 (newinfo->number <= oldinfo->initial_entries))
1323 module_put(t->me);
1324
1325 /* Get the old counters. */
1326 get_counters(oldinfo, counters);
1327 /* Decrease module usage counts and free resource */
Eric Dumazet31836062005-12-13 23:13:48 -08001328 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1329 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1330 free_table_info(oldinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 if (copy_to_user(tmp.counters, counters,
1332 sizeof(struct ip6t_counters) * tmp.num_counters) != 0)
1333 ret = -EFAULT;
1334 vfree(counters);
1335 up(&ip6t_mutex);
1336 return ret;
1337
1338 put_module:
1339 module_put(t->me);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 up(&ip6t_mutex);
1341 free_newinfo_counters_untrans:
Eric Dumazet31836062005-12-13 23:13:48 -08001342 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 free_newinfo_counters:
1344 vfree(counters);
1345 free_newinfo:
Eric Dumazet31836062005-12-13 23:13:48 -08001346 free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347 return ret;
1348}
1349
1350/* We're lazy, and add to the first CPU; overflow works its fey magic
1351 * and everything is OK. */
1352static inline int
1353add_counter_to_entry(struct ip6t_entry *e,
1354 const struct ip6t_counters addme[],
1355 unsigned int *i)
1356{
1357#if 0
1358 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1359 *i,
1360 (long unsigned int)e->counters.pcnt,
1361 (long unsigned int)e->counters.bcnt,
1362 (long unsigned int)addme[*i].pcnt,
1363 (long unsigned int)addme[*i].bcnt);
1364#endif
1365
1366 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1367
1368 (*i)++;
1369 return 0;
1370}
1371
1372static int
1373do_add_counters(void __user *user, unsigned int len)
1374{
1375 unsigned int i;
1376 struct ip6t_counters_info tmp, *paddc;
1377 struct ip6t_table *t;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001378 int ret = 0;
Eric Dumazet31836062005-12-13 23:13:48 -08001379 void *loc_cpu_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
1381 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1382 return -EFAULT;
1383
1384 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct ip6t_counters))
1385 return -EINVAL;
1386
1387 paddc = vmalloc(len);
1388 if (!paddc)
1389 return -ENOMEM;
1390
1391 if (copy_from_user(paddc, user, len) != 0) {
1392 ret = -EFAULT;
1393 goto free;
1394 }
1395
Harald Welte6b7d31f2005-10-26 09:34:24 +02001396 t = find_table_lock(tmp.name);
1397 if (!t || IS_ERR(t)) {
1398 ret = t ? PTR_ERR(t) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 goto free;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001400 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401
1402 write_lock_bh(&t->lock);
1403 if (t->private->number != paddc->num_counters) {
1404 ret = -EINVAL;
1405 goto unlock_up_free;
1406 }
1407
1408 i = 0;
Eric Dumazet31836062005-12-13 23:13:48 -08001409 /* Choose the copy that is on our node */
1410 loc_cpu_entry = t->private->entries[smp_processor_id()];
1411 IP6T_ENTRY_ITERATE(loc_cpu_entry,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 t->private->size,
1413 add_counter_to_entry,
1414 paddc->counters,
1415 &i);
1416 unlock_up_free:
1417 write_unlock_bh(&t->lock);
1418 up(&ip6t_mutex);
Harald Welte6b7d31f2005-10-26 09:34:24 +02001419 module_put(t->me);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420 free:
1421 vfree(paddc);
1422
1423 return ret;
1424}
1425
1426static int
1427do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1428{
1429 int ret;
1430
1431 if (!capable(CAP_NET_ADMIN))
1432 return -EPERM;
1433
1434 switch (cmd) {
1435 case IP6T_SO_SET_REPLACE:
1436 ret = do_replace(user, len);
1437 break;
1438
1439 case IP6T_SO_SET_ADD_COUNTERS:
1440 ret = do_add_counters(user, len);
1441 break;
1442
1443 default:
1444 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1445 ret = -EINVAL;
1446 }
1447
1448 return ret;
1449}
1450
1451static int
1452do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1453{
1454 int ret;
1455
1456 if (!capable(CAP_NET_ADMIN))
1457 return -EPERM;
1458
1459 switch (cmd) {
1460 case IP6T_SO_GET_INFO: {
1461 char name[IP6T_TABLE_MAXNAMELEN];
1462 struct ip6t_table *t;
1463
1464 if (*len != sizeof(struct ip6t_getinfo)) {
1465 duprintf("length %u != %u\n", *len,
1466 sizeof(struct ip6t_getinfo));
1467 ret = -EINVAL;
1468 break;
1469 }
1470
1471 if (copy_from_user(name, user, sizeof(name)) != 0) {
1472 ret = -EFAULT;
1473 break;
1474 }
1475 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
Harald Welte6b7d31f2005-10-26 09:34:24 +02001476
1477 t = try_then_request_module(find_table_lock(name),
1478 "ip6table_%s", name);
1479 if (t && !IS_ERR(t)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480 struct ip6t_getinfo info;
1481
1482 info.valid_hooks = t->valid_hooks;
1483 memcpy(info.hook_entry, t->private->hook_entry,
1484 sizeof(info.hook_entry));
1485 memcpy(info.underflow, t->private->underflow,
1486 sizeof(info.underflow));
1487 info.num_entries = t->private->number;
1488 info.size = t->private->size;
1489 memcpy(info.name, name, sizeof(info.name));
1490
1491 if (copy_to_user(user, &info, *len) != 0)
1492 ret = -EFAULT;
1493 else
1494 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 up(&ip6t_mutex);
Harald Welte6b7d31f2005-10-26 09:34:24 +02001496 module_put(t->me);
1497 } else
1498 ret = t ? PTR_ERR(t) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 }
1500 break;
1501
1502 case IP6T_SO_GET_ENTRIES: {
1503 struct ip6t_get_entries get;
1504
1505 if (*len < sizeof(get)) {
1506 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1507 ret = -EINVAL;
1508 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1509 ret = -EFAULT;
1510 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1511 duprintf("get_entries: %u != %u\n", *len,
1512 sizeof(struct ip6t_get_entries) + get.size);
1513 ret = -EINVAL;
1514 } else
1515 ret = get_entries(&get, user);
1516 break;
1517 }
1518
Harald Welte6b7d31f2005-10-26 09:34:24 +02001519 case IP6T_SO_GET_REVISION_MATCH:
1520 case IP6T_SO_GET_REVISION_TARGET: {
1521 struct ip6t_get_revision rev;
1522 int (*revfn)(const char *, u8, int *);
1523
1524 if (*len != sizeof(rev)) {
1525 ret = -EINVAL;
1526 break;
1527 }
1528 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1529 ret = -EFAULT;
1530 break;
1531 }
1532
1533 if (cmd == IP6T_SO_GET_REVISION_TARGET)
1534 revfn = target_revfn;
1535 else
1536 revfn = match_revfn;
1537
1538 try_then_request_module(find_revision(rev.name, rev.revision,
1539 revfn, &ret),
1540 "ip6t_%s", rev.name);
1541 break;
1542 }
1543
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 default:
1545 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1546 ret = -EINVAL;
1547 }
1548
1549 return ret;
1550}
1551
1552/* Registration hooks for targets. */
1553int
1554ip6t_register_target(struct ip6t_target *target)
1555{
1556 int ret;
1557
1558 ret = down_interruptible(&ip6t_mutex);
1559 if (ret != 0)
1560 return ret;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001561 list_add(&target->list, &ip6t_target);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 up(&ip6t_mutex);
1563 return ret;
1564}
1565
1566void
1567ip6t_unregister_target(struct ip6t_target *target)
1568{
1569 down(&ip6t_mutex);
1570 LIST_DELETE(&ip6t_target, target);
1571 up(&ip6t_mutex);
1572}
1573
1574int
1575ip6t_register_match(struct ip6t_match *match)
1576{
1577 int ret;
1578
1579 ret = down_interruptible(&ip6t_mutex);
1580 if (ret != 0)
1581 return ret;
1582
Harald Welte6b7d31f2005-10-26 09:34:24 +02001583 list_add(&match->list, &ip6t_match);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 up(&ip6t_mutex);
1585
1586 return ret;
1587}
1588
1589void
1590ip6t_unregister_match(struct ip6t_match *match)
1591{
1592 down(&ip6t_mutex);
1593 LIST_DELETE(&ip6t_match, match);
1594 up(&ip6t_mutex);
1595}
1596
1597int ip6t_register_table(struct ip6t_table *table,
1598 const struct ip6t_replace *repl)
1599{
1600 int ret;
1601 struct ip6t_table_info *newinfo;
1602 static struct ip6t_table_info bootstrap
1603 = { 0, 0, 0, { 0 }, { 0 }, { } };
Eric Dumazet31836062005-12-13 23:13:48 -08001604 void *loc_cpu_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
Eric Dumazet31836062005-12-13 23:13:48 -08001606 newinfo = alloc_table_info(repl->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 if (!newinfo)
1608 return -ENOMEM;
1609
Eric Dumazet31836062005-12-13 23:13:48 -08001610 /* choose the copy on our node/cpu */
1611 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1612 memcpy(loc_cpu_entry, repl->entries, repl->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613
1614 ret = translate_table(table->name, table->valid_hooks,
Eric Dumazet31836062005-12-13 23:13:48 -08001615 newinfo, loc_cpu_entry, repl->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 repl->num_entries,
1617 repl->hook_entry,
1618 repl->underflow);
1619 if (ret != 0) {
Eric Dumazet31836062005-12-13 23:13:48 -08001620 free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 return ret;
1622 }
1623
1624 ret = down_interruptible(&ip6t_mutex);
1625 if (ret != 0) {
Eric Dumazet31836062005-12-13 23:13:48 -08001626 free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627 return ret;
1628 }
1629
1630 /* Don't autoload: we'd eat our tail... */
1631 if (list_named_find(&ip6t_tables, table->name)) {
1632 ret = -EEXIST;
1633 goto free_unlock;
1634 }
1635
1636 /* Simplifies replace_table code. */
1637 table->private = &bootstrap;
1638 if (!replace_table(table, 0, newinfo, &ret))
1639 goto free_unlock;
1640
1641 duprintf("table->private->number = %u\n",
1642 table->private->number);
1643
1644 /* save number of initial entries */
1645 table->private->initial_entries = table->private->number;
1646
1647 rwlock_init(&table->lock);
1648 list_prepend(&ip6t_tables, table);
1649
1650 unlock:
1651 up(&ip6t_mutex);
1652 return ret;
1653
1654 free_unlock:
Eric Dumazet31836062005-12-13 23:13:48 -08001655 free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 goto unlock;
1657}
1658
1659void ip6t_unregister_table(struct ip6t_table *table)
1660{
Eric Dumazet31836062005-12-13 23:13:48 -08001661 void *loc_cpu_entry;
1662
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 down(&ip6t_mutex);
1664 LIST_DELETE(&ip6t_tables, table);
1665 up(&ip6t_mutex);
1666
1667 /* Decrease module usage counts and free resources */
Eric Dumazet31836062005-12-13 23:13:48 -08001668 loc_cpu_entry = table->private->entries[raw_smp_processor_id()];
1669 IP6T_ENTRY_ITERATE(loc_cpu_entry, table->private->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 cleanup_entry, NULL);
Eric Dumazet31836062005-12-13 23:13:48 -08001671 free_table_info(table->private);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672}
1673
1674/* Returns 1 if the port is matched by the range, 0 otherwise */
1675static inline int
1676port_match(u_int16_t min, u_int16_t max, u_int16_t port, int invert)
1677{
1678 int ret;
1679
1680 ret = (port >= min && port <= max) ^ invert;
1681 return ret;
1682}
1683
1684static int
1685tcp_find_option(u_int8_t option,
1686 const struct sk_buff *skb,
1687 unsigned int tcpoff,
1688 unsigned int optlen,
1689 int invert,
1690 int *hotdrop)
1691{
1692 /* tcp.doff is only 4 bits, ie. max 15 * 4 bytes */
1693 u_int8_t _opt[60 - sizeof(struct tcphdr)], *op;
1694 unsigned int i;
1695
1696 duprintf("tcp_match: finding option\n");
1697 if (!optlen)
1698 return invert;
1699 /* If we don't have the whole header, drop packet. */
1700 op = skb_header_pointer(skb, tcpoff + sizeof(struct tcphdr), optlen,
1701 _opt);
1702 if (op == NULL) {
1703 *hotdrop = 1;
1704 return 0;
1705 }
1706
1707 for (i = 0; i < optlen; ) {
1708 if (op[i] == option) return !invert;
1709 if (op[i] < 2) i++;
1710 else i += op[i+1]?:1;
1711 }
1712
1713 return invert;
1714}
1715
1716static int
1717tcp_match(const struct sk_buff *skb,
1718 const struct net_device *in,
1719 const struct net_device *out,
1720 const void *matchinfo,
1721 int offset,
1722 unsigned int protoff,
1723 int *hotdrop)
1724{
1725 struct tcphdr _tcph, *th;
1726 const struct ip6t_tcp *tcpinfo = matchinfo;
1727
1728 if (offset) {
1729 /* To quote Alan:
1730
1731 Don't allow a fragment of TCP 8 bytes in. Nobody normal
1732 causes this. Its a cracker trying to break in by doing a
1733 flag overwrite to pass the direction checks.
1734 */
1735 if (offset == 1) {
1736 duprintf("Dropping evil TCP offset=1 frag.\n");
1737 *hotdrop = 1;
1738 }
1739 /* Must not be a fragment. */
1740 return 0;
1741 }
1742
1743#define FWINVTCP(bool,invflg) ((bool) ^ !!(tcpinfo->invflags & invflg))
1744
1745 th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
1746 if (th == NULL) {
1747 /* We've been asked to examine this packet, and we
1748 can't. Hence, no choice but to drop. */
1749 duprintf("Dropping evil TCP offset=0 tinygram.\n");
1750 *hotdrop = 1;
1751 return 0;
1752 }
1753
1754 if (!port_match(tcpinfo->spts[0], tcpinfo->spts[1],
1755 ntohs(th->source),
1756 !!(tcpinfo->invflags & IP6T_TCP_INV_SRCPT)))
1757 return 0;
1758 if (!port_match(tcpinfo->dpts[0], tcpinfo->dpts[1],
1759 ntohs(th->dest),
1760 !!(tcpinfo->invflags & IP6T_TCP_INV_DSTPT)))
1761 return 0;
1762 if (!FWINVTCP((((unsigned char *)th)[13] & tcpinfo->flg_mask)
1763 == tcpinfo->flg_cmp,
1764 IP6T_TCP_INV_FLAGS))
1765 return 0;
1766 if (tcpinfo->option) {
1767 if (th->doff * 4 < sizeof(_tcph)) {
1768 *hotdrop = 1;
1769 return 0;
1770 }
1771 if (!tcp_find_option(tcpinfo->option, skb, protoff,
1772 th->doff*4 - sizeof(*th),
1773 tcpinfo->invflags & IP6T_TCP_INV_OPTION,
1774 hotdrop))
1775 return 0;
1776 }
1777 return 1;
1778}
1779
1780/* Called when user tries to insert an entry of this type. */
1781static int
1782tcp_checkentry(const char *tablename,
1783 const struct ip6t_ip6 *ipv6,
1784 void *matchinfo,
1785 unsigned int matchsize,
1786 unsigned int hook_mask)
1787{
1788 const struct ip6t_tcp *tcpinfo = matchinfo;
1789
1790 /* Must specify proto == TCP, and no unknown invflags */
1791 return ipv6->proto == IPPROTO_TCP
1792 && !(ipv6->invflags & IP6T_INV_PROTO)
1793 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_tcp))
1794 && !(tcpinfo->invflags & ~IP6T_TCP_INV_MASK);
1795}
1796
1797static int
1798udp_match(const struct sk_buff *skb,
1799 const struct net_device *in,
1800 const struct net_device *out,
1801 const void *matchinfo,
1802 int offset,
1803 unsigned int protoff,
1804 int *hotdrop)
1805{
1806 struct udphdr _udph, *uh;
1807 const struct ip6t_udp *udpinfo = matchinfo;
1808
1809 /* Must not be a fragment. */
1810 if (offset)
1811 return 0;
1812
1813 uh = skb_header_pointer(skb, protoff, sizeof(_udph), &_udph);
1814 if (uh == NULL) {
1815 /* We've been asked to examine this packet, and we
1816 can't. Hence, no choice but to drop. */
1817 duprintf("Dropping evil UDP tinygram.\n");
1818 *hotdrop = 1;
1819 return 0;
1820 }
1821
1822 return port_match(udpinfo->spts[0], udpinfo->spts[1],
1823 ntohs(uh->source),
1824 !!(udpinfo->invflags & IP6T_UDP_INV_SRCPT))
1825 && port_match(udpinfo->dpts[0], udpinfo->dpts[1],
1826 ntohs(uh->dest),
1827 !!(udpinfo->invflags & IP6T_UDP_INV_DSTPT));
1828}
1829
1830/* Called when user tries to insert an entry of this type. */
1831static int
1832udp_checkentry(const char *tablename,
1833 const struct ip6t_ip6 *ipv6,
1834 void *matchinfo,
1835 unsigned int matchinfosize,
1836 unsigned int hook_mask)
1837{
1838 const struct ip6t_udp *udpinfo = matchinfo;
1839
1840 /* Must specify proto == UDP, and no unknown invflags */
1841 if (ipv6->proto != IPPROTO_UDP || (ipv6->invflags & IP6T_INV_PROTO)) {
1842 duprintf("ip6t_udp: Protocol %u != %u\n", ipv6->proto,
1843 IPPROTO_UDP);
1844 return 0;
1845 }
1846 if (matchinfosize != IP6T_ALIGN(sizeof(struct ip6t_udp))) {
1847 duprintf("ip6t_udp: matchsize %u != %u\n",
1848 matchinfosize, IP6T_ALIGN(sizeof(struct ip6t_udp)));
1849 return 0;
1850 }
1851 if (udpinfo->invflags & ~IP6T_UDP_INV_MASK) {
1852 duprintf("ip6t_udp: unknown flags %X\n",
1853 udpinfo->invflags);
1854 return 0;
1855 }
1856
1857 return 1;
1858}
1859
1860/* Returns 1 if the type and code is matched by the range, 0 otherwise */
1861static inline int
1862icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1863 u_int8_t type, u_int8_t code,
1864 int invert)
1865{
1866 return (type == test_type && code >= min_code && code <= max_code)
1867 ^ invert;
1868}
1869
1870static int
1871icmp6_match(const struct sk_buff *skb,
1872 const struct net_device *in,
1873 const struct net_device *out,
1874 const void *matchinfo,
1875 int offset,
1876 unsigned int protoff,
1877 int *hotdrop)
1878{
1879 struct icmp6hdr _icmp, *ic;
1880 const struct ip6t_icmp *icmpinfo = matchinfo;
1881
1882 /* Must not be a fragment. */
1883 if (offset)
1884 return 0;
1885
1886 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1887 if (ic == NULL) {
1888 /* We've been asked to examine this packet, and we
1889 can't. Hence, no choice but to drop. */
1890 duprintf("Dropping evil ICMP tinygram.\n");
1891 *hotdrop = 1;
1892 return 0;
1893 }
1894
1895 return icmp6_type_code_match(icmpinfo->type,
1896 icmpinfo->code[0],
1897 icmpinfo->code[1],
1898 ic->icmp6_type, ic->icmp6_code,
1899 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1900}
1901
1902/* Called when user tries to insert an entry of this type. */
1903static int
1904icmp6_checkentry(const char *tablename,
1905 const struct ip6t_ip6 *ipv6,
1906 void *matchinfo,
1907 unsigned int matchsize,
1908 unsigned int hook_mask)
1909{
1910 const struct ip6t_icmp *icmpinfo = matchinfo;
1911
1912 /* Must specify proto == ICMP, and no unknown invflags */
1913 return ipv6->proto == IPPROTO_ICMPV6
1914 && !(ipv6->invflags & IP6T_INV_PROTO)
1915 && matchsize == IP6T_ALIGN(sizeof(struct ip6t_icmp))
1916 && !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1917}
1918
1919/* The built-in targets: standard (NULL) and error. */
1920static struct ip6t_target ip6t_standard_target = {
1921 .name = IP6T_STANDARD_TARGET,
1922};
1923
1924static struct ip6t_target ip6t_error_target = {
1925 .name = IP6T_ERROR_TARGET,
1926 .target = ip6t_error,
1927};
1928
1929static struct nf_sockopt_ops ip6t_sockopts = {
1930 .pf = PF_INET6,
1931 .set_optmin = IP6T_BASE_CTL,
1932 .set_optmax = IP6T_SO_SET_MAX+1,
1933 .set = do_ip6t_set_ctl,
1934 .get_optmin = IP6T_BASE_CTL,
1935 .get_optmax = IP6T_SO_GET_MAX+1,
1936 .get = do_ip6t_get_ctl,
1937};
1938
1939static struct ip6t_match tcp_matchstruct = {
1940 .name = "tcp",
1941 .match = &tcp_match,
1942 .checkentry = &tcp_checkentry,
1943};
1944
1945static struct ip6t_match udp_matchstruct = {
1946 .name = "udp",
1947 .match = &udp_match,
1948 .checkentry = &udp_checkentry,
1949};
1950
1951static struct ip6t_match icmp6_matchstruct = {
1952 .name = "icmp6",
1953 .match = &icmp6_match,
1954 .checkentry = &icmp6_checkentry,
1955};
1956
1957#ifdef CONFIG_PROC_FS
1958static inline int print_name(const char *i,
1959 off_t start_offset, char *buffer, int length,
1960 off_t *pos, unsigned int *count)
1961{
1962 if ((*count)++ >= start_offset) {
1963 unsigned int namelen;
1964
1965 namelen = sprintf(buffer + *pos, "%s\n",
1966 i + sizeof(struct list_head));
1967 if (*pos + namelen > length) {
1968 /* Stop iterating */
1969 return 1;
1970 }
1971 *pos += namelen;
1972 }
1973 return 0;
1974}
1975
1976static inline int print_target(const struct ip6t_target *t,
1977 off_t start_offset, char *buffer, int length,
1978 off_t *pos, unsigned int *count)
1979{
1980 if (t == &ip6t_standard_target || t == &ip6t_error_target)
1981 return 0;
1982 return print_name((char *)t, start_offset, buffer, length, pos, count);
1983}
1984
1985static int ip6t_get_tables(char *buffer, char **start, off_t offset, int length)
1986{
1987 off_t pos = 0;
1988 unsigned int count = 0;
1989
1990 if (down_interruptible(&ip6t_mutex) != 0)
1991 return 0;
1992
1993 LIST_FIND(&ip6t_tables, print_name, char *,
1994 offset, buffer, length, &pos, &count);
1995
1996 up(&ip6t_mutex);
1997
1998 /* `start' hack - see fs/proc/generic.c line ~105 */
1999 *start=(char *)((unsigned long)count-offset);
2000 return pos;
2001}
2002
2003static int ip6t_get_targets(char *buffer, char **start, off_t offset, int length)
2004{
2005 off_t pos = 0;
2006 unsigned int count = 0;
2007
2008 if (down_interruptible(&ip6t_mutex) != 0)
2009 return 0;
2010
2011 LIST_FIND(&ip6t_target, print_target, struct ip6t_target *,
2012 offset, buffer, length, &pos, &count);
2013
2014 up(&ip6t_mutex);
2015
2016 *start = (char *)((unsigned long)count - offset);
2017 return pos;
2018}
2019
2020static int ip6t_get_matches(char *buffer, char **start, off_t offset, int length)
2021{
2022 off_t pos = 0;
2023 unsigned int count = 0;
2024
2025 if (down_interruptible(&ip6t_mutex) != 0)
2026 return 0;
2027
2028 LIST_FIND(&ip6t_match, print_name, char *,
2029 offset, buffer, length, &pos, &count);
2030
2031 up(&ip6t_mutex);
2032
2033 *start = (char *)((unsigned long)count - offset);
2034 return pos;
2035}
2036
Arjan van de Ven9b5b5cf2005-11-29 16:21:38 -08002037static const struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038{ { "ip6_tables_names", ip6t_get_tables },
2039 { "ip6_tables_targets", ip6t_get_targets },
2040 { "ip6_tables_matches", ip6t_get_matches },
2041 { NULL, NULL} };
2042#endif /*CONFIG_PROC_FS*/
2043
2044static int __init init(void)
2045{
2046 int ret;
2047
2048 /* Noone else will be downing sem now, so we won't sleep */
2049 down(&ip6t_mutex);
2050 list_append(&ip6t_target, &ip6t_standard_target);
2051 list_append(&ip6t_target, &ip6t_error_target);
2052 list_append(&ip6t_match, &tcp_matchstruct);
2053 list_append(&ip6t_match, &udp_matchstruct);
2054 list_append(&ip6t_match, &icmp6_matchstruct);
2055 up(&ip6t_mutex);
2056
2057 /* Register setsockopt */
2058 ret = nf_register_sockopt(&ip6t_sockopts);
2059 if (ret < 0) {
2060 duprintf("Unable to register sockopts.\n");
2061 return ret;
2062 }
2063
2064#ifdef CONFIG_PROC_FS
2065 {
2066 struct proc_dir_entry *proc;
2067 int i;
2068
2069 for (i = 0; ip6t_proc_entry[i].name; i++) {
2070 proc = proc_net_create(ip6t_proc_entry[i].name, 0,
2071 ip6t_proc_entry[i].get_info);
2072 if (!proc) {
2073 while (--i >= 0)
2074 proc_net_remove(ip6t_proc_entry[i].name);
2075 nf_unregister_sockopt(&ip6t_sockopts);
2076 return -ENOMEM;
2077 }
2078 proc->owner = THIS_MODULE;
2079 }
2080 }
2081#endif
2082
2083 printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
2084 return 0;
2085}
2086
2087static void __exit fini(void)
2088{
2089 nf_unregister_sockopt(&ip6t_sockopts);
2090#ifdef CONFIG_PROC_FS
2091 {
2092 int i;
2093 for (i = 0; ip6t_proc_entry[i].name; i++)
2094 proc_net_remove(ip6t_proc_entry[i].name);
2095 }
2096#endif
2097}
2098
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002099/*
2100 * find specified header up to transport protocol header.
2101 * If found target header, the offset to the header is set to *offset
2102 * and return 0. otherwise, return -1.
2103 *
2104 * Notes: - non-1st Fragment Header isn't skipped.
2105 * - ESP header isn't skipped.
2106 * - The target header may be trancated.
2107 */
2108int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, u8 target)
2109{
2110 unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data;
2111 u8 nexthdr = skb->nh.ipv6h->nexthdr;
2112 unsigned int len = skb->len - start;
2113
2114 while (nexthdr != target) {
2115 struct ipv6_opt_hdr _hdr, *hp;
2116 unsigned int hdrlen;
2117
2118 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE)
2119 return -1;
2120 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2121 if (hp == NULL)
2122 return -1;
2123 if (nexthdr == NEXTHDR_FRAGMENT) {
2124 unsigned short _frag_off, *fp;
2125 fp = skb_header_pointer(skb,
2126 start+offsetof(struct frag_hdr,
2127 frag_off),
2128 sizeof(_frag_off),
2129 &_frag_off);
2130 if (fp == NULL)
2131 return -1;
2132
2133 if (ntohs(*fp) & ~0x7)
2134 return -1;
2135 hdrlen = 8;
2136 } else if (nexthdr == NEXTHDR_AUTH)
2137 hdrlen = (hp->hdrlen + 2) << 2;
2138 else
2139 hdrlen = ipv6_optlen(hp);
2140
2141 nexthdr = hp->nexthdr;
2142 len -= hdrlen;
2143 start += hdrlen;
2144 }
2145
2146 *offset = start;
2147 return 0;
2148}
2149
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150EXPORT_SYMBOL(ip6t_register_table);
2151EXPORT_SYMBOL(ip6t_unregister_table);
2152EXPORT_SYMBOL(ip6t_do_table);
2153EXPORT_SYMBOL(ip6t_register_match);
2154EXPORT_SYMBOL(ip6t_unregister_match);
2155EXPORT_SYMBOL(ip6t_register_target);
2156EXPORT_SYMBOL(ip6t_unregister_target);
2157EXPORT_SYMBOL(ip6t_ext_hdr);
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002158EXPORT_SYMBOL(ipv6_find_hdr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159
2160module_init(init);
2161module_exit(fini);