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