[TCP] BIC: CUBIC window growth (2.0)
[GitHub/mt8127/android_kernel_alcatel_ttab.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 */
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>
1da177e4
LT
26#include <net/ipv6.h>
27#include <asm/uaccess.h>
28#include <asm/semaphore.h>
29#include <linux/proc_fs.h>
c8923c6b 30#include <linux/cpumask.h>
1da177e4
LT
31
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)
1da177e4
LT
74#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
6b7d31fc 82/*
1da177e4 83 We keep a set of rules for each CPU, so we can avoid write-locking
6b7d31fc
HW
84 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.
1da177e4
LT
88
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{
6b7d31fc 357 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
1da177e4
LT
358 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;
1da177e4
LT
370 /* 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);
1da177e4
LT
402 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 }
05465343
PM
434 if (table_base + v != (void *)e + e->next_offset
435 && !(e->ipv6.flags & IP6T_F_GOTO)) {
1da177e4
LT
436 /* 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
6b7d31fc
HW
497/*
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)
1da177e4 505{
6b7d31fc 506 struct ip6t_table *t;
1da177e4 507
6b7d31fc
HW
508 if (down_interruptible(&ip6t_mutex) != 0)
509 return ERR_PTR(-EINTR);
1da177e4 510
6b7d31fc
HW
511 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}
1da177e4 517
6b7d31fc
HW
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 }
1da177e4 537 }
6b7d31fc
HW
538 up(&ip6t_mutex);
539 return ERR_PTR(err);
1da177e4
LT
540}
541
6b7d31fc
HW
542/* Find target, grabs ref. Returns ERR_PTR() on error. */
543static inline struct ip6t_target *find_target(const char *name, u8 revision)
1da177e4 544{
6b7d31fc
HW
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 }
1da177e4 561 }
6b7d31fc
HW
562 up(&ip6t_mutex);
563 return ERR_PTR(err);
564}
1da177e4 565
6b7d31fc
HW
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)
573 return NULL;
574 return target;
1da177e4 575}
1da177e4 576
6b7d31fc 577static int match_revfn(const char *name, u8 revision, int *bestp)
1da177e4 578{
6b7d31fc
HW
579 struct ip6t_match *m;
580 int have_rev = 0;
581
582 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;
1da177e4
LT
591}
592
6b7d31fc 593static int target_revfn(const char *name, u8 revision, int *bestp)
1da177e4 594{
6b7d31fc
HW
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;
1da177e4
LT
607}
608
6b7d31fc
HW
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)
1da177e4 613{
6b7d31fc
HW
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;
627 }
628
629 *err = best;
630 if (!have_rev)
631 *err = -EPROTONOSUPPORT;
632 return 1;
1da177e4
LT
633}
634
6b7d31fc 635
1da177e4
LT
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{
1da177e4
LT
795 struct ip6t_match *match;
796
6b7d31fc
HW
797 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;
1da177e4
LT
803 }
804 m->u.kernel.match = match;
1da177e4
LT
805
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);
6b7d31fc
HW
842 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) {
1da177e4 846 duprintf("check_entry: `%s' not found\n", t->u.user.name);
6b7d31fc 847 ret = target ? PTR_ERR(target) : -ENOENT;
1da177e4
LT
848 goto cleanup_matches;
849 }
850 t->u.kernel.target = target;
6b7d31fc 851
1da177e4
LT
852 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 */
c8923c6b
DM
1011 for_each_cpu(i) {
1012 if (i == 0)
1013 continue;
1014 memcpy(newinfo->entries + SMP_ALIGN(newinfo->size) * i,
1da177e4
LT
1015 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
c8923c6b 1035 for_each_cpu(i) {
1da177e4
LT
1036 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
c8923c6b 1082 for_each_cpu(cpu) {
1da177e4
LT
1083 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
6b7d31fc
HW
1178 t = find_table_lock(entries->name);
1179 if (t && !IS_ERR(t)) {
1da177e4
LT
1180 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 }
6b7d31fc 1191 module_put(t->me);
1da177e4
LT
1192 up(&ip6t_mutex);
1193 } else
6b7d31fc 1194 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1195
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)
c8923c6b
DM
1216 + SMP_ALIGN(tmp.size) *
1217 (highest_possible_processor_id()+1));
1da177e4
LT
1218 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
6b7d31fc
HW
1242 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;
1da177e4 1246 goto free_newinfo_counters_untrans;
6b7d31fc 1247 }
1da177e4
LT
1248
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;
6b7d31fc 1254 goto put_module;
1da177e4
LT
1255 }
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);
1da177e4
LT
1276 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);
1da177e4
LT
1285 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;
6b7d31fc 1323 int ret = 0;
1da177e4
LT
1324
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
6b7d31fc
HW
1340 t = find_table_lock(tmp.name);
1341 if (!t || IS_ERR(t)) {
1342 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1343 goto free;
6b7d31fc 1344 }
1da177e4
LT
1345
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);
6b7d31fc 1361 module_put(t->me);
1da177e4
LT
1362 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';
6b7d31fc
HW
1418
1419 t = try_then_request_module(find_table_lock(name),
1420 "ip6table_%s", name);
1421 if (t && !IS_ERR(t)) {
1da177e4
LT
1422 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;
1da177e4 1437 up(&ip6t_mutex);
6b7d31fc
HW
1438 module_put(t->me);
1439 } else
1440 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1441 }
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
6b7d31fc
HW
1461 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
1da177e4
LT
1486 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;
6b7d31fc 1503 list_add(&target->list, &ip6t_target);
1da177e4
LT
1504 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
6b7d31fc 1525 list_add(&match->list, &ip6t_match);
1da177e4
LT
1526 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)
c8923c6b
DM
1548 + SMP_ALIGN(repl->size) *
1549 (highest_possible_processor_id()+1));
1da177e4
LT
1550 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
9b5b5cff 1975static const struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
1da177e4
LT
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
e674d0f3
YK
2037/*
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
1da177e4
LT
2088EXPORT_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);
e674d0f3 2096EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4
LT
2097
2098module_init(init);
2099module_exit(fini);