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