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