blob: 1390370186d975ff1c98bab91d65ee0ca85985b9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Packet matching code.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
Harald Welte6b7d31f2005-10-26 09:34:24 +02005 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
12 * - increase module usage count as soon as we have rules inside
13 * a table
14 * 06 Jun 2002 Andras Kis-Szabo <kisza@sch.bme.hu>
15 * - new extension header parser code
16 */
Randy Dunlap4fc268d2006-01-11 12:17:47 -080017
18#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/config.h>
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020020#include <linux/in.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029#include <net/ipv6.h>
30#include <asm/uaccess.h>
31#include <asm/semaphore.h>
32#include <linux/proc_fs.h>
David S. Millerc8923c62005-10-13 14:41:23 -070033#include <linux/cpumask.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
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)
Linus Torvalds1da177e2005-04-16 15:20:36 -070077#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
Harald Welte6b7d31f2005-10-26 09:34:24 +020085/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 We keep a set of rules for each CPU, so we can avoid write-locking
Harald Welte6b7d31f2005-10-26 09:34:24 +020087 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.
Linus Torvalds1da177e2005-04-16 15:20:36 -070091
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 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 */
Eric Dumazet31836062005-12-13 23:13:48 -0800109 void *entries[NR_CPUS];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110};
111
112static LIST_HEAD(ip6t_target);
113static LIST_HEAD(ip6t_match);
114static LIST_HEAD(ip6t_tables);
Eric Dumazet31836062005-12-13 23:13:48 -0800115#define SET_COUNTER(c,b,p) do { (c).bcnt = (b); (c).pcnt = (p); } while(0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116#define ADD_COUNTER(c,b,p) do { (c).bcnt += (b); (c).pcnt += (p); } while(0)
117
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118#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
Patrick McHardy22dea562006-01-05 12:21:34 -0800124int
125ip6_masked_addrcmp(const struct in6_addr *addr1, const struct in6_addr *mask,
126 const struct in6_addr *addr2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127{
128 int i;
129 for( i = 0; i < 16; i++){
Patrick McHardy22dea562006-01-05 12:21:34 -0800130 if((addr1->s6_addr[i] & mask->s6_addr[i]) !=
131 (addr2->s6_addr[i] & mask->s6_addr[i]))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 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
Patrick McHardy22dea562006-01-05 12:21:34 -0800165 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)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 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)) {
Patrick McHardyb777e0c2006-01-05 12:21:16 -0800211 int protohdr;
212 unsigned short _frag_off;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213
Patrick McHardyb777e0c2006-01-05 12:21:16 -0800214 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
215 if (protohdr < 0)
216 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
Patrick McHardyb777e0c2006-01-05 12:21:16 -0800218 *fragoff = _frag_off;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
220 dprintf("Packet protocol %hi ?= %s%hi.\n",
Patrick McHardyb777e0c2006-01-05 12:21:16 -0800221 protohdr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
223 ip6info->proto);
224
Patrick McHardyb777e0c2006-01-05 12:21:16 -0800225 if (ip6info->proto == protohdr) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 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{
Harald Welte6b7d31f2005-10-26 09:34:24 +0200303 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 /* 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));
Eric Dumazet31836062005-12-13 23:13:48 -0800325 table_base = (void *)table->private->entries[smp_processor_id()];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 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 }
Patrick McHardy05465342005-08-21 23:31:43 -0700379 if (table_base + v != (void *)e + e->next_offset
380 && !(e->ipv6.flags & IP6T_F_GOTO)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 /* 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
Harald Welte6b7d31f2005-10-26 09:34:24 +0200442/*
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)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450{
Harald Welte6b7d31f2005-10-26 09:34:24 +0200451 struct ip6t_table *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
Harald Welte6b7d31f2005-10-26 09:34:24 +0200453 if (down_interruptible(&ip6t_mutex) != 0)
454 return ERR_PTR(-EINTR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
Harald Welte6b7d31f2005-10-26 09:34:24 +0200456 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}
462
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 }
482 }
483 up(&ip6t_mutex);
484 return ERR_PTR(err);
485}
486
487/* Find target, grabs ref. Returns ERR_PTR() on error. */
488static inline struct ip6t_target *find_target(const char *name, u8 revision)
489{
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 }
506 }
507 up(&ip6t_mutex);
508 return ERR_PTR(err);
509}
510
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)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 return NULL;
Harald Welte6b7d31f2005-10-26 09:34:24 +0200519 return target;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520}
521
Harald Welte6b7d31f2005-10-26 09:34:24 +0200522static int match_revfn(const char *name, u8 revision, int *bestp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523{
Harald Welte6b7d31f2005-10-26 09:34:24 +0200524 struct ip6t_match *m;
525 int have_rev = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526
Harald Welte6b7d31f2005-10-26 09:34:24 +0200527 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;
536}
537
538static int target_revfn(const char *name, u8 revision, int *bestp)
539{
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;
552}
553
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)
558{
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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 }
573
Harald Welte6b7d31f2005-10-26 09:34:24 +0200574 *err = best;
575 if (!have_rev)
576 *err = -EPROTONOSUPPORT;
577 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578}
579
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
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
Eric Dumazet31836062005-12-13 23:13:48 -0800597mark_source_chains(struct ip6t_table_info *newinfo,
598 unsigned int valid_hooks, void *entry0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599{
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
Eric Dumazet31836062005-12-13 23:13:48 -0800607 = (struct ip6t_entry *)(entry0 + pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
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 *)
Eric Dumazet31836062005-12-13 23:13:48 -0800657 (entry0 + pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 } while (oldpos == pos + e->next_offset);
659
660 /* Move along one */
661 size = e->next_offset;
662 e = (struct ip6t_entry *)
Eric Dumazet31836062005-12-13 23:13:48 -0800663 (entry0 + pos + size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 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 *)
Eric Dumazet31836062005-12-13 23:13:48 -0800680 (entry0 + newpos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 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{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 struct ip6t_match *match;
742
Harald Welte6b7d31f2005-10-26 09:34:24 +0200743 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 }
750 m->u.kernel.match = match;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
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);
Harald Welte6b7d31f2005-10-26 09:34:24 +0200788 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) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 duprintf("check_entry: `%s' not found\n", t->u.user.name);
Harald Welte6b7d31f2005-10-26 09:34:24 +0200793 ret = target ? PTR_ERR(target) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 goto cleanup_matches;
795 }
796 t->u.kernel.target = target;
Harald Welte6b7d31f2005-10-26 09:34:24 +0200797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 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,
Eric Dumazet31836062005-12-13 23:13:48 -0800890 void *entry0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 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. */
Eric Dumazet31836062005-12-13 23:13:48 -0800911 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 check_entry_size_and_hooks,
913 newinfo,
Eric Dumazet31836062005-12-13 23:13:48 -0800914 entry0,
915 entry0 + size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 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
Eric Dumazet31836062005-12-13 23:13:48 -0800943 if (!mark_source_chains(newinfo, valid_hooks, entry0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 return -ELOOP;
945
946 /* Finally, each sanity check must pass */
947 i = 0;
Eric Dumazet31836062005-12-13 23:13:48 -0800948 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 check_entry, name, size, &i);
950
951 if (ret != 0) {
Eric Dumazet31836062005-12-13 23:13:48 -0800952 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 cleanup_entry, &i);
954 return ret;
955 }
956
957 /* And one copy for every other CPU */
David S. Millerc8923c62005-10-13 14:41:23 -0700958 for_each_cpu(i) {
Eric Dumazet31836062005-12-13 23:13:48 -0800959 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
960 memcpy(newinfo->entries[i], entry0, newinfo->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 }
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 {
Eric Dumazet31836062005-12-13 23:13:48 -0800976 int cpu;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
Eric Dumazet31836062005-12-13 23:13:48 -0800978 for_each_cpu(cpu) {
979 struct ip6t_entry *table_base = newinfo->entries[cpu];
980 if (table_base)
981 table_base->comefrom = 0xdead57ac;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 }
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
Eric Dumazet31836062005-12-13 23:13:48 -08001016static 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027static void
1028get_counters(const struct ip6t_table_info *t,
1029 struct ip6t_counters counters[])
1030{
1031 unsigned int cpu;
1032 unsigned int i;
Eric Dumazet31836062005-12-13 23:13:48 -08001033 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
David S. Millerc8923c62005-10-13 14:41:23 -07001049 for_each_cpu(cpu) {
Eric Dumazet31836062005-12-13 23:13:48 -08001050 if (cpu == curcpu)
1051 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 i = 0;
Eric Dumazet31836062005-12-13 23:13:48 -08001053 IP6T_ENTRY_ITERATE(t->entries[cpu],
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 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;
Eric Dumazet31836062005-12-13 23:13:48 -08001070 void *loc_cpu_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
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... */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 write_lock_bh(&table->lock);
1083 get_counters(table->private, counters);
1084 write_unlock_bh(&table->lock);
1085
Eric Dumazet31836062005-12-13 23:13:48 -08001086 /* 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) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 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
Eric Dumazet31836062005-12-13 23:13:48 -08001100 e = (struct ip6t_entry *)(loc_cpu_entry + off);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 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
Harald Welte6b7d31f2005-10-26 09:34:24 +02001148 t = find_table_lock(entries->name);
1149 if (t && !IS_ERR(t)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 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 }
Harald Welte6b7d31f2005-10-26 09:34:24 +02001161 module_put(t->me);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 up(&ip6t_mutex);
1163 } else
Harald Welte6b7d31f2005-10-26 09:34:24 +02001164 ret = t ? PTR_ERR(t) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165
1166 return ret;
1167}
1168
Eric Dumazet31836062005-12-13 23:13:48 -08001169static 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209static 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;
Eric Dumazet31836062005-12-13 23:13:48 -08001217 void *loc_cpu_entry, *loc_cpu_old_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
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
Eric Dumazet31836062005-12-13 23:13:48 -08001226 newinfo = alloc_table_info(tmp.size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 if (!newinfo)
1228 return -ENOMEM;
1229
Eric Dumazet31836062005-12-13 23:13:48 -08001230 /* 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),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243
1244 ret = translate_table(tmp.name, tmp.valid_hooks,
Eric Dumazet31836062005-12-13 23:13:48 -08001245 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 tmp.hook_entry, tmp.underflow);
1247 if (ret != 0)
1248 goto free_newinfo_counters;
1249
1250 duprintf("ip_tables: Translated table\n");
1251
Harald Welte6b7d31f2005-10-26 09:34:24 +02001252 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 goto free_newinfo_counters_untrans;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001257 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258
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;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001264 goto put_module;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 }
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 */
Eric Dumazet31836062005-12-13 23:13:48 -08001284 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 up(&ip6t_mutex);
1297 free_newinfo_counters_untrans:
Eric Dumazet31836062005-12-13 23:13:48 -08001298 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299 free_newinfo_counters:
1300 vfree(counters);
1301 free_newinfo:
Eric Dumazet31836062005-12-13 23:13:48 -08001302 free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 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;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001334 int ret = 0;
Eric Dumazet31836062005-12-13 23:13:48 -08001335 void *loc_cpu_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336
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
Harald Welte6b7d31f2005-10-26 09:34:24 +02001352 t = find_table_lock(tmp.name);
1353 if (!t || IS_ERR(t)) {
1354 ret = t ? PTR_ERR(t) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 goto free;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001356 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
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;
Eric Dumazet31836062005-12-13 23:13:48 -08001365 /* 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,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 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);
Harald Welte6b7d31f2005-10-26 09:34:24 +02001375 module_put(t->me);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376 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';
Harald Welte6b7d31f2005-10-26 09:34:24 +02001432
1433 t = try_then_request_module(find_table_lock(name),
1434 "ip6table_%s", name);
1435 if (t && !IS_ERR(t)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436 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;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001451 up(&ip6t_mutex);
Harald Welte6b7d31f2005-10-26 09:34:24 +02001452 module_put(t->me);
1453 } else
1454 ret = t ? PTR_ERR(t) : -ENOENT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 }
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
Harald Welte6b7d31f2005-10-26 09:34:24 +02001475 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
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 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;
Harald Welte6b7d31f2005-10-26 09:34:24 +02001517 list_add(&target->list, &ip6t_target);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 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
Harald Welte6b7d31f2005-10-26 09:34:24 +02001539 list_add(&match->list, &ip6t_match);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 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 }, { } };
Eric Dumazet31836062005-12-13 23:13:48 -08001560 void *loc_cpu_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561
Eric Dumazet31836062005-12-13 23:13:48 -08001562 newinfo = alloc_table_info(repl->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 if (!newinfo)
1564 return -ENOMEM;
1565
Eric Dumazet31836062005-12-13 23:13:48 -08001566 /* 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);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569
1570 ret = translate_table(table->name, table->valid_hooks,
Eric Dumazet31836062005-12-13 23:13:48 -08001571 newinfo, loc_cpu_entry, repl->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001572 repl->num_entries,
1573 repl->hook_entry,
1574 repl->underflow);
1575 if (ret != 0) {
Eric Dumazet31836062005-12-13 23:13:48 -08001576 free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577 return ret;
1578 }
1579
1580 ret = down_interruptible(&ip6t_mutex);
1581 if (ret != 0) {
Eric Dumazet31836062005-12-13 23:13:48 -08001582 free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583 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:
Eric Dumazet31836062005-12-13 23:13:48 -08001611 free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612 goto unlock;
1613}
1614
1615void ip6t_unregister_table(struct ip6t_table *table)
1616{
Eric Dumazet31836062005-12-13 23:13:48 -08001617 void *loc_cpu_entry;
1618
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 down(&ip6t_mutex);
1620 LIST_DELETE(&ip6t_tables, table);
1621 up(&ip6t_mutex);
1622
1623 /* Decrease module usage counts and free resources */
Eric Dumazet31836062005-12-13 23:13:48 -08001624 loc_cpu_entry = table->private->entries[raw_smp_processor_id()];
1625 IP6T_ENTRY_ITERATE(loc_cpu_entry, table->private->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 cleanup_entry, NULL);
Eric Dumazet31836062005-12-13 23:13:48 -08001627 free_table_info(table->private);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001628}
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
Arjan van de Ven9b5b5cf2005-11-29 16:21:38 -08001993static const struct { char *name; get_info_t *get_info; } ip6t_proc_entry[] =
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994{ { "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
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002055/*
Patrick McHardyb777e0c2006-01-05 12:21:16 -08002056 * 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".
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002059 *
Patrick McHardyb777e0c2006-01-05 12:21:16 -08002060 * 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.
2067 *
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002068 */
Patrick McHardyb777e0c2006-01-05 12:21:16 -08002069int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2070 int target, unsigned short *fragoff)
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002071{
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
Patrick McHardyb777e0c2006-01-05 12:21:16 -08002076 if (fragoff)
2077 *fragoff = 0;
2078
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002079 while (nexthdr != target) {
2080 struct ipv6_opt_hdr _hdr, *hp;
2081 unsigned int hdrlen;
2082
Patrick McHardyb777e0c2006-01-05 12:21:16 -08002083 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2084 if (target < 0)
2085 break;
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002086 return -1;
Patrick McHardyb777e0c2006-01-05 12:21:16 -08002087 }
2088
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002089 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
Patrick McHardyb777e0c2006-01-05 12:21:16 -08002102 _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 }
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002111 return -1;
Patrick McHardyb777e0c2006-01-05 12:21:16 -08002112 }
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002113 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;
Patrick McHardyb777e0c2006-01-05 12:21:16 -08002125 return nexthdr;
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002126}
2127
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128EXPORT_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);
Yasuyuki Kozakaie674d0f2005-09-19 15:34:40 -07002136EXPORT_SYMBOL(ipv6_find_hdr);
Patrick McHardy22dea562006-01-05 12:21:34 -08002137EXPORT_SYMBOL(ip6_masked_addrcmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138
2139module_init(init);
2140module_exit(fini);