[NETFILTER]: ip_tables: ipt and ipt_compat checks unification
[deliverable/linux.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
2e4e6a17
HW
16 * 15 Oct 2005 Harald Welte <laforge@netfilter.org>
17 * - Unification of {ip,ip6}_tables into x_tables
18 * - Removed tcp and udp code, since it's not ipv6 specific
1da177e4 19 */
4fc268d2
RD
20
21#include <linux/capability.h>
14c85021 22#include <linux/in.h>
1da177e4
LT
23#include <linux/skbuff.h>
24#include <linux/kmod.h>
25#include <linux/vmalloc.h>
26#include <linux/netdevice.h>
27#include <linux/module.h>
4bdbf6c0 28#include <linux/poison.h>
1da177e4 29#include <linux/icmpv6.h>
1da177e4
LT
30#include <net/ipv6.h>
31#include <asm/uaccess.h>
57b47a53 32#include <linux/mutex.h>
1da177e4 33#include <linux/proc_fs.h>
c8923c6b 34#include <linux/cpumask.h>
1da177e4
LT
35
36#include <linux/netfilter_ipv6/ip6_tables.h>
2e4e6a17 37#include <linux/netfilter/x_tables.h>
1da177e4
LT
38
39MODULE_LICENSE("GPL");
40MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
41MODULE_DESCRIPTION("IPv6 packet filter");
42
43#define IPV6_HDR_LEN (sizeof(struct ipv6hdr))
44#define IPV6_OPTHDR_LEN (sizeof(struct ipv6_opt_hdr))
45
46/*#define DEBUG_IP_FIREWALL*/
47/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
48/*#define DEBUG_IP_FIREWALL_USER*/
49
50#ifdef DEBUG_IP_FIREWALL
51#define dprintf(format, args...) printk(format , ## args)
52#else
53#define dprintf(format, args...)
54#endif
55
56#ifdef DEBUG_IP_FIREWALL_USER
57#define duprintf(format, args...) printk(format , ## args)
58#else
59#define duprintf(format, args...)
60#endif
61
62#ifdef CONFIG_NETFILTER_DEBUG
63#define IP_NF_ASSERT(x) \
64do { \
65 if (!(x)) \
66 printk("IP_NF_ASSERT: %s:%s:%u\n", \
67 __FUNCTION__, __FILE__, __LINE__); \
68} while(0)
69#else
70#define IP_NF_ASSERT(x)
71#endif
1da177e4 72
1da177e4
LT
73#if 0
74/* All the better to debug you with... */
75#define static
76#define inline
77#endif
78
6b7d31fc 79/*
1da177e4 80 We keep a set of rules for each CPU, so we can avoid write-locking
6b7d31fc
HW
81 them in the softirq when updating the counters and therefore
82 only need to read-lock in the softirq; doing a write_lock_bh() in user
83 context stops packets coming through and allows user context to read
84 the counters or update the rules.
1da177e4 85
1da177e4
LT
86 Hence the start of any table is given by get_table() below. */
87
1da177e4
LT
88#if 0
89#define down(x) do { printk("DOWN:%u:" #x "\n", __LINE__); down(x); } while(0)
90#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; })
91#define up(x) do { printk("UP:%u:" #x "\n", __LINE__); up(x); } while(0)
92#endif
93
1da177e4
LT
94/* Check for an extension */
95int
96ip6t_ext_hdr(u8 nexthdr)
97{
98 return ( (nexthdr == IPPROTO_HOPOPTS) ||
99 (nexthdr == IPPROTO_ROUTING) ||
100 (nexthdr == IPPROTO_FRAGMENT) ||
101 (nexthdr == IPPROTO_ESP) ||
102 (nexthdr == IPPROTO_AH) ||
103 (nexthdr == IPPROTO_NONE) ||
104 (nexthdr == IPPROTO_DSTOPTS) );
105}
106
107/* Returns whether matches rule or not. */
108static inline int
109ip6_packet_match(const struct sk_buff *skb,
110 const char *indev,
111 const char *outdev,
112 const struct ip6t_ip6 *ip6info,
113 unsigned int *protoff,
51d8b1a6 114 int *fragoff, int *hotdrop)
1da177e4
LT
115{
116 size_t i;
117 unsigned long ret;
118 const struct ipv6hdr *ipv6 = skb->nh.ipv6h;
119
120#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
121
f2ffd9ee
PM
122 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
123 &ip6info->src), IP6T_INV_SRCIP)
124 || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
125 &ip6info->dst), IP6T_INV_DSTIP)) {
1da177e4
LT
126 dprintf("Source or dest mismatch.\n");
127/*
128 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
129 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
130 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
131 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
132 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
133 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
134 return 0;
135 }
136
137 /* Look for ifname matches; this should unroll nicely. */
138 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
139 ret |= (((const unsigned long *)indev)[i]
140 ^ ((const unsigned long *)ip6info->iniface)[i])
141 & ((const unsigned long *)ip6info->iniface_mask)[i];
142 }
143
144 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
145 dprintf("VIA in mismatch (%s vs %s).%s\n",
146 indev, ip6info->iniface,
147 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
148 return 0;
149 }
150
151 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
152 ret |= (((const unsigned long *)outdev)[i]
153 ^ ((const unsigned long *)ip6info->outiface)[i])
154 & ((const unsigned long *)ip6info->outiface_mask)[i];
155 }
156
157 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
158 dprintf("VIA out mismatch (%s vs %s).%s\n",
159 outdev, ip6info->outiface,
160 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
161 return 0;
162 }
163
164/* ... might want to do something with class and flowlabel here ... */
165
166 /* look for the desired protocol header */
167 if((ip6info->flags & IP6T_F_PROTO)) {
b777e0ce
PM
168 int protohdr;
169 unsigned short _frag_off;
1da177e4 170
b777e0ce 171 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
51d8b1a6
PM
172 if (protohdr < 0) {
173 if (_frag_off == 0)
174 *hotdrop = 1;
b777e0ce 175 return 0;
51d8b1a6 176 }
b777e0ce 177 *fragoff = _frag_off;
1da177e4
LT
178
179 dprintf("Packet protocol %hi ?= %s%hi.\n",
b777e0ce 180 protohdr,
1da177e4
LT
181 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
182 ip6info->proto);
183
b777e0ce 184 if (ip6info->proto == protohdr) {
1da177e4
LT
185 if(ip6info->invflags & IP6T_INV_PROTO) {
186 return 0;
187 }
188 return 1;
189 }
190
191 /* We need match for the '-p all', too! */
192 if ((ip6info->proto != 0) &&
193 !(ip6info->invflags & IP6T_INV_PROTO))
194 return 0;
195 }
196 return 1;
197}
198
199/* should be ip6 safe */
200static inline int
201ip6_checkentry(const struct ip6t_ip6 *ipv6)
202{
203 if (ipv6->flags & ~IP6T_F_MASK) {
204 duprintf("Unknown flag bits set: %08X\n",
205 ipv6->flags & ~IP6T_F_MASK);
206 return 0;
207 }
208 if (ipv6->invflags & ~IP6T_INV_MASK) {
209 duprintf("Unknown invflag bits set: %08X\n",
210 ipv6->invflags & ~IP6T_INV_MASK);
211 return 0;
212 }
213 return 1;
214}
215
216static unsigned int
217ip6t_error(struct sk_buff **pskb,
218 const struct net_device *in,
219 const struct net_device *out,
220 unsigned int hooknum,
c4986734 221 const struct xt_target *target,
fe1cb108 222 const void *targinfo)
1da177e4
LT
223{
224 if (net_ratelimit())
225 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
226
227 return NF_DROP;
228}
229
230static inline
231int do_match(struct ip6t_entry_match *m,
232 const struct sk_buff *skb,
233 const struct net_device *in,
234 const struct net_device *out,
235 int offset,
236 unsigned int protoff,
237 int *hotdrop)
238{
239 /* Stop iteration if it doesn't match */
1c524830 240 if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
1da177e4
LT
241 offset, protoff, hotdrop))
242 return 1;
243 else
244 return 0;
245}
246
247static inline struct ip6t_entry *
248get_entry(void *base, unsigned int offset)
249{
250 return (struct ip6t_entry *)(base + offset);
251}
252
253/* Returns one of the generic firewall policies, like NF_ACCEPT. */
254unsigned int
255ip6t_do_table(struct sk_buff **pskb,
256 unsigned int hook,
257 const struct net_device *in,
258 const struct net_device *out,
fe1cb108 259 struct xt_table *table)
1da177e4 260{
6b7d31fc 261 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
1da177e4
LT
262 int offset = 0;
263 unsigned int protoff = 0;
264 int hotdrop = 0;
265 /* Initializing verdict to NF_DROP keeps gcc happy. */
266 unsigned int verdict = NF_DROP;
267 const char *indev, *outdev;
268 void *table_base;
269 struct ip6t_entry *e, *back;
2e4e6a17 270 struct xt_table_info *private;
1da177e4
LT
271
272 /* Initialization */
273 indev = in ? in->name : nulldevname;
274 outdev = out ? out->name : nulldevname;
1da177e4
LT
275 /* We handle fragments by dealing with the first fragment as
276 * if it was a normal packet. All other fragments are treated
277 * normally, except that they will NEVER match rules that ask
278 * things we don't know, ie. tcp syn flag or ports). If the
279 * rule is also a fragment-specific rule, non-fragments won't
280 * match it. */
281
282 read_lock_bh(&table->lock);
2e4e6a17 283 private = table->private;
1da177e4 284 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
2e4e6a17
HW
285 table_base = (void *)private->entries[smp_processor_id()];
286 e = get_entry(table_base, private->hook_entry[hook]);
1da177e4 287
1da177e4 288 /* For return from builtin chain */
2e4e6a17 289 back = get_entry(table_base, private->underflow[hook]);
1da177e4
LT
290
291 do {
292 IP_NF_ASSERT(e);
293 IP_NF_ASSERT(back);
1da177e4 294 if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6,
51d8b1a6 295 &protoff, &offset, &hotdrop)) {
1da177e4
LT
296 struct ip6t_entry_target *t;
297
298 if (IP6T_MATCH_ITERATE(e, do_match,
299 *pskb, in, out,
300 offset, protoff, &hotdrop) != 0)
301 goto no_match;
302
303 ADD_COUNTER(e->counters,
304 ntohs((*pskb)->nh.ipv6h->payload_len)
305 + IPV6_HDR_LEN,
306 1);
307
308 t = ip6t_get_target(e);
309 IP_NF_ASSERT(t->u.kernel.target);
310 /* Standard target? */
311 if (!t->u.kernel.target->target) {
312 int v;
313
314 v = ((struct ip6t_standard_target *)t)->verdict;
315 if (v < 0) {
316 /* Pop from stack? */
317 if (v != IP6T_RETURN) {
318 verdict = (unsigned)(-v) - 1;
319 break;
320 }
321 e = back;
322 back = get_entry(table_base,
323 back->comefrom);
324 continue;
325 }
05465343
PM
326 if (table_base + v != (void *)e + e->next_offset
327 && !(e->ipv6.flags & IP6T_F_GOTO)) {
1da177e4
LT
328 /* Save old back ptr in next entry */
329 struct ip6t_entry *next
330 = (void *)e + e->next_offset;
331 next->comefrom
332 = (void *)back - table_base;
333 /* set back pointer to next entry */
334 back = next;
335 }
336
337 e = get_entry(table_base, v);
338 } else {
339 /* Targets which reenter must return
340 abs. verdicts */
341#ifdef CONFIG_NETFILTER_DEBUG
342 ((struct ip6t_entry *)table_base)->comefrom
343 = 0xeeeeeeec;
344#endif
345 verdict = t->u.kernel.target->target(pskb,
346 in, out,
347 hook,
1c524830 348 t->u.kernel.target,
fe1cb108 349 t->data);
1da177e4
LT
350
351#ifdef CONFIG_NETFILTER_DEBUG
352 if (((struct ip6t_entry *)table_base)->comefrom
353 != 0xeeeeeeec
354 && verdict == IP6T_CONTINUE) {
355 printk("Target %s reentered!\n",
356 t->u.kernel.target->name);
357 verdict = NF_DROP;
358 }
359 ((struct ip6t_entry *)table_base)->comefrom
360 = 0x57acc001;
361#endif
362 if (verdict == IP6T_CONTINUE)
363 e = (void *)e + e->next_offset;
364 else
365 /* Verdict */
366 break;
367 }
368 } else {
369
370 no_match:
371 e = (void *)e + e->next_offset;
372 }
373 } while (!hotdrop);
374
375#ifdef CONFIG_NETFILTER_DEBUG
4bdbf6c0 376 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
1da177e4
LT
377#endif
378 read_unlock_bh(&table->lock);
379
380#ifdef DEBUG_ALLOW_ALL
381 return NF_ACCEPT;
382#else
383 if (hotdrop)
384 return NF_DROP;
385 else return verdict;
386#endif
387}
388
1da177e4
LT
389/* All zeroes == unconditional rule. */
390static inline int
391unconditional(const struct ip6t_ip6 *ipv6)
392{
393 unsigned int i;
394
395 for (i = 0; i < sizeof(*ipv6); i++)
396 if (((char *)ipv6)[i])
397 break;
398
399 return (i == sizeof(*ipv6));
400}
401
402/* Figures out from what hook each rule can be called: returns 0 if
403 there are loops. Puts hook bitmask in comefrom. */
404static int
2e4e6a17 405mark_source_chains(struct xt_table_info *newinfo,
31836064 406 unsigned int valid_hooks, void *entry0)
1da177e4
LT
407{
408 unsigned int hook;
409
410 /* No recursion; use packet counter to save back ptrs (reset
411 to 0 as we leave), and comefrom to save source hook bitmask */
412 for (hook = 0; hook < NF_IP6_NUMHOOKS; hook++) {
413 unsigned int pos = newinfo->hook_entry[hook];
414 struct ip6t_entry *e
31836064 415 = (struct ip6t_entry *)(entry0 + pos);
1da177e4
LT
416
417 if (!(valid_hooks & (1 << hook)))
418 continue;
419
420 /* Set initial back pointer. */
421 e->counters.pcnt = pos;
422
423 for (;;) {
424 struct ip6t_standard_target *t
425 = (void *)ip6t_get_target(e);
426
427 if (e->comefrom & (1 << NF_IP6_NUMHOOKS)) {
428 printk("iptables: loop hook %u pos %u %08X.\n",
429 hook, pos, e->comefrom);
430 return 0;
431 }
432 e->comefrom
433 |= ((1 << hook) | (1 << NF_IP6_NUMHOOKS));
434
435 /* Unconditional return/END. */
436 if (e->target_offset == sizeof(struct ip6t_entry)
437 && (strcmp(t->target.u.user.name,
438 IP6T_STANDARD_TARGET) == 0)
439 && t->verdict < 0
440 && unconditional(&e->ipv6)) {
441 unsigned int oldpos, size;
442
74c9c0c1
DM
443 if (t->verdict < -NF_MAX_VERDICT - 1) {
444 duprintf("mark_source_chains: bad "
445 "negative verdict (%i)\n",
446 t->verdict);
447 return 0;
448 }
449
1da177e4
LT
450 /* Return: backtrack through the last
451 big jump. */
452 do {
453 e->comefrom ^= (1<<NF_IP6_NUMHOOKS);
454#ifdef DEBUG_IP_FIREWALL_USER
455 if (e->comefrom
456 & (1 << NF_IP6_NUMHOOKS)) {
457 duprintf("Back unset "
458 "on hook %u "
459 "rule %u\n",
460 hook, pos);
461 }
462#endif
463 oldpos = pos;
464 pos = e->counters.pcnt;
465 e->counters.pcnt = 0;
466
467 /* We're at the start. */
468 if (pos == oldpos)
469 goto next;
470
471 e = (struct ip6t_entry *)
31836064 472 (entry0 + pos);
1da177e4
LT
473 } while (oldpos == pos + e->next_offset);
474
475 /* Move along one */
476 size = e->next_offset;
477 e = (struct ip6t_entry *)
31836064 478 (entry0 + pos + size);
1da177e4
LT
479 e->counters.pcnt = pos;
480 pos += size;
481 } else {
482 int newpos = t->verdict;
483
484 if (strcmp(t->target.u.user.name,
485 IP6T_STANDARD_TARGET) == 0
486 && newpos >= 0) {
74c9c0c1
DM
487 if (newpos > newinfo->size -
488 sizeof(struct ip6t_entry)) {
489 duprintf("mark_source_chains: "
490 "bad verdict (%i)\n",
491 newpos);
492 return 0;
493 }
1da177e4
LT
494 /* This a jump; chase it. */
495 duprintf("Jump rule %u -> %u\n",
496 pos, newpos);
497 } else {
498 /* ... this is a fallthru */
499 newpos = pos + e->next_offset;
500 }
501 e = (struct ip6t_entry *)
31836064 502 (entry0 + newpos);
1da177e4
LT
503 e->counters.pcnt = pos;
504 pos = newpos;
505 }
506 }
507 next:
508 duprintf("Finished chain %u\n", hook);
509 }
510 return 1;
511}
512
513static inline int
514cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
515{
516 if (i && (*i)-- == 0)
517 return 1;
518
519 if (m->u.kernel.match->destroy)
efa74165 520 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
1da177e4
LT
521 module_put(m->u.kernel.match->me);
522 return 0;
523}
524
1da177e4
LT
525static inline int
526check_match(struct ip6t_entry_match *m,
527 const char *name,
528 const struct ip6t_ip6 *ipv6,
529 unsigned int hookmask,
530 unsigned int *i)
531{
1da177e4 532 struct ip6t_match *match;
3cdc7c95 533 int ret;
1da177e4 534
2e4e6a17
HW
535 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
536 m->u.user.revision),
6b7d31fc
HW
537 "ip6t_%s", m->u.user.name);
538 if (IS_ERR(match) || !match) {
2e4e6a17 539 duprintf("check_match: `%s' not found\n", m->u.user.name);
6b7d31fc 540 return match ? PTR_ERR(match) : -ENOENT;
1da177e4
LT
541 }
542 m->u.kernel.match = match;
1da177e4 543
3cdc7c95
PM
544 ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
545 name, hookmask, ipv6->proto,
546 ipv6->invflags & IP6T_INV_PROTO);
547 if (ret)
548 goto err;
549
1da177e4 550 if (m->u.kernel.match->checkentry
1c524830 551 && !m->u.kernel.match->checkentry(name, ipv6, match, m->data,
1da177e4 552 hookmask)) {
1da177e4
LT
553 duprintf("ip_tables: check failed for `%s'.\n",
554 m->u.kernel.match->name);
3cdc7c95
PM
555 ret = -EINVAL;
556 goto err;
1da177e4
LT
557 }
558
559 (*i)++;
560 return 0;
3cdc7c95
PM
561err:
562 module_put(m->u.kernel.match->me);
563 return ret;
1da177e4
LT
564}
565
566static struct ip6t_target ip6t_standard_target;
567
568static inline int
569check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
570 unsigned int *i)
571{
572 struct ip6t_entry_target *t;
573 struct ip6t_target *target;
574 int ret;
575 unsigned int j;
576
577 if (!ip6_checkentry(&e->ipv6)) {
578 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
579 return -EINVAL;
580 }
581
590bdf7f
DM
582 if (e->target_offset + sizeof(struct ip6t_entry_target) >
583 e->next_offset)
584 return -EINVAL;
585
1da177e4
LT
586 j = 0;
587 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
588 if (ret != 0)
589 goto cleanup_matches;
590
591 t = ip6t_get_target(e);
590bdf7f
DM
592 ret = -EINVAL;
593 if (e->target_offset + t->u.target_size > e->next_offset)
594 goto cleanup_matches;
2e4e6a17
HW
595 target = try_then_request_module(xt_find_target(AF_INET6,
596 t->u.user.name,
597 t->u.user.revision),
6b7d31fc
HW
598 "ip6t_%s", t->u.user.name);
599 if (IS_ERR(target) || !target) {
1da177e4 600 duprintf("check_entry: `%s' not found\n", t->u.user.name);
6b7d31fc 601 ret = target ? PTR_ERR(target) : -ENOENT;
1da177e4
LT
602 goto cleanup_matches;
603 }
604 t->u.kernel.target = target;
6b7d31fc 605
3cdc7c95
PM
606 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
607 name, e->comefrom, e->ipv6.proto,
608 e->ipv6.invflags & IP6T_INV_PROTO);
609 if (ret)
610 goto err;
611
74c9c0c1 612 if (t->u.kernel.target->checkentry
1c524830 613 && !t->u.kernel.target->checkentry(name, e, target, t->data,
1da177e4 614 e->comefrom)) {
1da177e4
LT
615 duprintf("ip_tables: check failed for `%s'.\n",
616 t->u.kernel.target->name);
617 ret = -EINVAL;
3cdc7c95 618 goto err;
1da177e4
LT
619 }
620
621 (*i)++;
622 return 0;
3cdc7c95
PM
623 err:
624 module_put(t->u.kernel.target->me);
1da177e4
LT
625 cleanup_matches:
626 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
627 return ret;
628}
629
630static inline int
631check_entry_size_and_hooks(struct ip6t_entry *e,
2e4e6a17 632 struct xt_table_info *newinfo,
1da177e4
LT
633 unsigned char *base,
634 unsigned char *limit,
635 const unsigned int *hook_entries,
636 const unsigned int *underflows,
637 unsigned int *i)
638{
639 unsigned int h;
640
641 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
642 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
643 duprintf("Bad offset %p\n", e);
644 return -EINVAL;
645 }
646
647 if (e->next_offset
648 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
649 duprintf("checking: element %p size %u\n",
650 e, e->next_offset);
651 return -EINVAL;
652 }
653
654 /* Check hooks & underflows */
655 for (h = 0; h < NF_IP6_NUMHOOKS; h++) {
656 if ((unsigned char *)e - base == hook_entries[h])
657 newinfo->hook_entry[h] = hook_entries[h];
658 if ((unsigned char *)e - base == underflows[h])
659 newinfo->underflow[h] = underflows[h];
660 }
661
662 /* FIXME: underflows must be unconditional, standard verdicts
663 < 0 (not IP6T_RETURN). --RR */
664
665 /* Clear counters and comefrom */
2e4e6a17 666 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4
LT
667 e->comefrom = 0;
668
669 (*i)++;
670 return 0;
671}
672
673static inline int
674cleanup_entry(struct ip6t_entry *e, unsigned int *i)
675{
676 struct ip6t_entry_target *t;
677
678 if (i && (*i)-- == 0)
679 return 1;
680
681 /* Cleanup all matches */
682 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
683 t = ip6t_get_target(e);
684 if (t->u.kernel.target->destroy)
efa74165 685 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
1da177e4
LT
686 module_put(t->u.kernel.target->me);
687 return 0;
688}
689
690/* Checks and translates the user-supplied table segment (held in
691 newinfo) */
692static int
693translate_table(const char *name,
694 unsigned int valid_hooks,
2e4e6a17 695 struct xt_table_info *newinfo,
31836064 696 void *entry0,
1da177e4
LT
697 unsigned int size,
698 unsigned int number,
699 const unsigned int *hook_entries,
700 const unsigned int *underflows)
701{
702 unsigned int i;
703 int ret;
704
705 newinfo->size = size;
706 newinfo->number = number;
707
708 /* Init all hooks to impossible value. */
709 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
710 newinfo->hook_entry[i] = 0xFFFFFFFF;
711 newinfo->underflow[i] = 0xFFFFFFFF;
712 }
713
714 duprintf("translate_table: size %u\n", newinfo->size);
715 i = 0;
716 /* Walk through entries, checking offsets. */
31836064 717 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
718 check_entry_size_and_hooks,
719 newinfo,
31836064
ED
720 entry0,
721 entry0 + size,
1da177e4
LT
722 hook_entries, underflows, &i);
723 if (ret != 0)
724 return ret;
725
726 if (i != number) {
727 duprintf("translate_table: %u not %u entries\n",
728 i, number);
729 return -EINVAL;
730 }
731
732 /* Check hooks all assigned */
733 for (i = 0; i < NF_IP6_NUMHOOKS; i++) {
734 /* Only hooks which are valid */
735 if (!(valid_hooks & (1 << i)))
736 continue;
737 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
738 duprintf("Invalid hook entry %u %u\n",
739 i, hook_entries[i]);
740 return -EINVAL;
741 }
742 if (newinfo->underflow[i] == 0xFFFFFFFF) {
743 duprintf("Invalid underflow %u %u\n",
744 i, underflows[i]);
745 return -EINVAL;
746 }
747 }
748
74c9c0c1
DM
749 if (!mark_source_chains(newinfo, valid_hooks, entry0))
750 return -ELOOP;
751
1da177e4
LT
752 /* Finally, each sanity check must pass */
753 i = 0;
31836064 754 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
755 check_entry, name, size, &i);
756
74c9c0c1
DM
757 if (ret != 0) {
758 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
759 cleanup_entry, &i);
760 return ret;
761 }
1da177e4
LT
762
763 /* And one copy for every other CPU */
6f912042 764 for_each_possible_cpu(i) {
31836064
ED
765 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
766 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
767 }
768
590bdf7f 769 return 0;
1da177e4
LT
770}
771
1da177e4
LT
772/* Gets counters. */
773static inline int
774add_entry_to_counter(const struct ip6t_entry *e,
2e4e6a17 775 struct xt_counters total[],
1da177e4
LT
776 unsigned int *i)
777{
778 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
779
780 (*i)++;
781 return 0;
782}
783
31836064
ED
784static inline int
785set_entry_to_counter(const struct ip6t_entry *e,
786 struct ip6t_counters total[],
787 unsigned int *i)
788{
789 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
790
791 (*i)++;
792 return 0;
793}
794
1da177e4 795static void
2e4e6a17
HW
796get_counters(const struct xt_table_info *t,
797 struct xt_counters counters[])
1da177e4
LT
798{
799 unsigned int cpu;
800 unsigned int i;
31836064
ED
801 unsigned int curcpu;
802
803 /* Instead of clearing (by a previous call to memset())
804 * the counters and using adds, we set the counters
805 * with data used by 'current' CPU
806 * We dont care about preemption here.
807 */
808 curcpu = raw_smp_processor_id();
809
810 i = 0;
811 IP6T_ENTRY_ITERATE(t->entries[curcpu],
812 t->size,
813 set_entry_to_counter,
814 counters,
815 &i);
1da177e4 816
6f912042 817 for_each_possible_cpu(cpu) {
31836064
ED
818 if (cpu == curcpu)
819 continue;
1da177e4 820 i = 0;
31836064 821 IP6T_ENTRY_ITERATE(t->entries[cpu],
1da177e4
LT
822 t->size,
823 add_entry_to_counter,
824 counters,
825 &i);
826 }
827}
828
829static int
830copy_entries_to_user(unsigned int total_size,
2e4e6a17 831 struct xt_table *table,
1da177e4
LT
832 void __user *userptr)
833{
834 unsigned int off, num, countersize;
835 struct ip6t_entry *e;
2e4e6a17
HW
836 struct xt_counters *counters;
837 struct xt_table_info *private = table->private;
1da177e4 838 int ret = 0;
31836064 839 void *loc_cpu_entry;
1da177e4
LT
840
841 /* We need atomic snapshot of counters: rest doesn't change
842 (other than comefrom, which userspace doesn't care
843 about). */
2e4e6a17 844 countersize = sizeof(struct xt_counters) * private->number;
1da177e4
LT
845 counters = vmalloc(countersize);
846
847 if (counters == NULL)
848 return -ENOMEM;
849
850 /* First, sum counters... */
1da177e4 851 write_lock_bh(&table->lock);
2e4e6a17 852 get_counters(private, counters);
1da177e4
LT
853 write_unlock_bh(&table->lock);
854
31836064 855 /* choose the copy that is on ourc node/cpu */
2e4e6a17 856 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 857 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
858 ret = -EFAULT;
859 goto free_counters;
860 }
861
862 /* FIXME: use iterator macros --RR */
863 /* ... then go back and fix counters and names */
864 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
865 unsigned int i;
866 struct ip6t_entry_match *m;
867 struct ip6t_entry_target *t;
868
31836064 869 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
870 if (copy_to_user(userptr + off
871 + offsetof(struct ip6t_entry, counters),
872 &counters[num],
873 sizeof(counters[num])) != 0) {
874 ret = -EFAULT;
875 goto free_counters;
876 }
877
878 for (i = sizeof(struct ip6t_entry);
879 i < e->target_offset;
880 i += m->u.match_size) {
881 m = (void *)e + i;
882
883 if (copy_to_user(userptr + off + i
884 + offsetof(struct ip6t_entry_match,
885 u.user.name),
886 m->u.kernel.match->name,
887 strlen(m->u.kernel.match->name)+1)
888 != 0) {
889 ret = -EFAULT;
890 goto free_counters;
891 }
892 }
893
894 t = ip6t_get_target(e);
895 if (copy_to_user(userptr + off + e->target_offset
896 + offsetof(struct ip6t_entry_target,
897 u.user.name),
898 t->u.kernel.target->name,
899 strlen(t->u.kernel.target->name)+1) != 0) {
900 ret = -EFAULT;
901 goto free_counters;
902 }
903 }
904
905 free_counters:
906 vfree(counters);
907 return ret;
908}
909
910static int
911get_entries(const struct ip6t_get_entries *entries,
912 struct ip6t_get_entries __user *uptr)
913{
914 int ret;
2e4e6a17 915 struct xt_table *t;
1da177e4 916
2e4e6a17 917 t = xt_find_table_lock(AF_INET6, entries->name);
6b7d31fc 918 if (t && !IS_ERR(t)) {
2e4e6a17
HW
919 struct xt_table_info *private = t->private;
920 duprintf("t->private->number = %u\n", private->number);
921 if (entries->size == private->size)
922 ret = copy_entries_to_user(private->size,
1da177e4
LT
923 t, uptr->entrytable);
924 else {
925 duprintf("get_entries: I've got %u not %u!\n",
2e4e6a17 926 private->size, entries->size);
1da177e4
LT
927 ret = -EINVAL;
928 }
6b7d31fc 929 module_put(t->me);
2e4e6a17 930 xt_table_unlock(t);
1da177e4 931 } else
6b7d31fc 932 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
933
934 return ret;
935}
936
937static int
938do_replace(void __user *user, unsigned int len)
939{
940 int ret;
941 struct ip6t_replace tmp;
2e4e6a17
HW
942 struct xt_table *t;
943 struct xt_table_info *newinfo, *oldinfo;
944 struct xt_counters *counters;
31836064 945 void *loc_cpu_entry, *loc_cpu_old_entry;
1da177e4
LT
946
947 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
948 return -EFAULT;
949
ee4bb818
KK
950 /* overflow check */
951 if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
952 SMP_CACHE_BYTES)
953 return -ENOMEM;
954 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
955 return -ENOMEM;
956
2e4e6a17 957 newinfo = xt_alloc_table_info(tmp.size);
1da177e4
LT
958 if (!newinfo)
959 return -ENOMEM;
960
31836064
ED
961 /* choose the copy that is on our node/cpu */
962 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
963 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1da177e4
LT
964 tmp.size) != 0) {
965 ret = -EFAULT;
966 goto free_newinfo;
967 }
968
2e4e6a17 969 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
1da177e4
LT
970 if (!counters) {
971 ret = -ENOMEM;
972 goto free_newinfo;
973 }
1da177e4
LT
974
975 ret = translate_table(tmp.name, tmp.valid_hooks,
31836064 976 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1da177e4
LT
977 tmp.hook_entry, tmp.underflow);
978 if (ret != 0)
979 goto free_newinfo_counters;
980
981 duprintf("ip_tables: Translated table\n");
982
2e4e6a17 983 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
6b7d31fc
HW
984 "ip6table_%s", tmp.name);
985 if (!t || IS_ERR(t)) {
986 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 987 goto free_newinfo_counters_untrans;
6b7d31fc 988 }
1da177e4
LT
989
990 /* You lied! */
991 if (tmp.valid_hooks != t->valid_hooks) {
992 duprintf("Valid hook crap: %08X vs %08X\n",
993 tmp.valid_hooks, t->valid_hooks);
994 ret = -EINVAL;
6b7d31fc 995 goto put_module;
1da177e4
LT
996 }
997
2e4e6a17 998 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1da177e4
LT
999 if (!oldinfo)
1000 goto put_module;
1001
1002 /* Update module usage count based on number of rules */
1003 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1004 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1005 if ((oldinfo->number > oldinfo->initial_entries) ||
1006 (newinfo->number <= oldinfo->initial_entries))
1007 module_put(t->me);
1008 if ((oldinfo->number > oldinfo->initial_entries) &&
1009 (newinfo->number <= oldinfo->initial_entries))
1010 module_put(t->me);
1011
1012 /* Get the old counters. */
1013 get_counters(oldinfo, counters);
1014 /* Decrease module usage counts and free resource */
31836064
ED
1015 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1016 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
2e4e6a17 1017 xt_free_table_info(oldinfo);
1da177e4 1018 if (copy_to_user(tmp.counters, counters,
2e4e6a17 1019 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1da177e4
LT
1020 ret = -EFAULT;
1021 vfree(counters);
2e4e6a17 1022 xt_table_unlock(t);
1da177e4
LT
1023 return ret;
1024
1025 put_module:
1026 module_put(t->me);
2e4e6a17 1027 xt_table_unlock(t);
1da177e4 1028 free_newinfo_counters_untrans:
31836064 1029 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1da177e4
LT
1030 free_newinfo_counters:
1031 vfree(counters);
1032 free_newinfo:
2e4e6a17 1033 xt_free_table_info(newinfo);
1da177e4
LT
1034 return ret;
1035}
1036
1037/* We're lazy, and add to the first CPU; overflow works its fey magic
1038 * and everything is OK. */
1039static inline int
1040add_counter_to_entry(struct ip6t_entry *e,
2e4e6a17 1041 const struct xt_counters addme[],
1da177e4
LT
1042 unsigned int *i)
1043{
1044#if 0
1045 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1046 *i,
1047 (long unsigned int)e->counters.pcnt,
1048 (long unsigned int)e->counters.bcnt,
1049 (long unsigned int)addme[*i].pcnt,
1050 (long unsigned int)addme[*i].bcnt);
1051#endif
1052
1053 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1054
1055 (*i)++;
1056 return 0;
1057}
1058
1059static int
1060do_add_counters(void __user *user, unsigned int len)
1061{
1062 unsigned int i;
2e4e6a17
HW
1063 struct xt_counters_info tmp, *paddc;
1064 struct xt_table_info *private;
1065 struct xt_table *t;
6b7d31fc 1066 int ret = 0;
31836064 1067 void *loc_cpu_entry;
1da177e4
LT
1068
1069 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1070 return -EFAULT;
1071
2e4e6a17 1072 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1da177e4
LT
1073 return -EINVAL;
1074
1075 paddc = vmalloc(len);
1076 if (!paddc)
1077 return -ENOMEM;
1078
1079 if (copy_from_user(paddc, user, len) != 0) {
1080 ret = -EFAULT;
1081 goto free;
1082 }
1083
2e4e6a17 1084 t = xt_find_table_lock(AF_INET6, tmp.name);
6b7d31fc
HW
1085 if (!t || IS_ERR(t)) {
1086 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1087 goto free;
6b7d31fc 1088 }
1da177e4
LT
1089
1090 write_lock_bh(&t->lock);
2e4e6a17 1091 private = t->private;
2c8ac66b 1092 if (private->number != tmp.num_counters) {
1da177e4
LT
1093 ret = -EINVAL;
1094 goto unlock_up_free;
1095 }
1096
1097 i = 0;
31836064 1098 /* Choose the copy that is on our node */
2e4e6a17 1099 loc_cpu_entry = private->entries[smp_processor_id()];
31836064 1100 IP6T_ENTRY_ITERATE(loc_cpu_entry,
2e4e6a17 1101 private->size,
1da177e4
LT
1102 add_counter_to_entry,
1103 paddc->counters,
1104 &i);
1105 unlock_up_free:
1106 write_unlock_bh(&t->lock);
2e4e6a17 1107 xt_table_unlock(t);
6b7d31fc 1108 module_put(t->me);
1da177e4
LT
1109 free:
1110 vfree(paddc);
1111
1112 return ret;
1113}
1114
1115static int
1116do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1117{
1118 int ret;
1119
1120 if (!capable(CAP_NET_ADMIN))
1121 return -EPERM;
1122
1123 switch (cmd) {
1124 case IP6T_SO_SET_REPLACE:
1125 ret = do_replace(user, len);
1126 break;
1127
1128 case IP6T_SO_SET_ADD_COUNTERS:
1129 ret = do_add_counters(user, len);
1130 break;
1131
1132 default:
1133 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1134 ret = -EINVAL;
1135 }
1136
1137 return ret;
1138}
1139
1140static int
1141do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1142{
1143 int ret;
1144
1145 if (!capable(CAP_NET_ADMIN))
1146 return -EPERM;
1147
1148 switch (cmd) {
1149 case IP6T_SO_GET_INFO: {
1150 char name[IP6T_TABLE_MAXNAMELEN];
2e4e6a17 1151 struct xt_table *t;
1da177e4
LT
1152
1153 if (*len != sizeof(struct ip6t_getinfo)) {
1154 duprintf("length %u != %u\n", *len,
1155 sizeof(struct ip6t_getinfo));
1156 ret = -EINVAL;
1157 break;
1158 }
1159
1160 if (copy_from_user(name, user, sizeof(name)) != 0) {
1161 ret = -EFAULT;
1162 break;
1163 }
1164 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
6b7d31fc 1165
2e4e6a17 1166 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
6b7d31fc
HW
1167 "ip6table_%s", name);
1168 if (t && !IS_ERR(t)) {
1da177e4 1169 struct ip6t_getinfo info;
2e4e6a17 1170 struct xt_table_info *private = t->private;
1da177e4
LT
1171
1172 info.valid_hooks = t->valid_hooks;
2e4e6a17 1173 memcpy(info.hook_entry, private->hook_entry,
1da177e4 1174 sizeof(info.hook_entry));
2e4e6a17 1175 memcpy(info.underflow, private->underflow,
1da177e4 1176 sizeof(info.underflow));
2e4e6a17
HW
1177 info.num_entries = private->number;
1178 info.size = private->size;
1da177e4
LT
1179 memcpy(info.name, name, sizeof(info.name));
1180
1181 if (copy_to_user(user, &info, *len) != 0)
1182 ret = -EFAULT;
1183 else
1184 ret = 0;
2e4e6a17 1185 xt_table_unlock(t);
6b7d31fc
HW
1186 module_put(t->me);
1187 } else
1188 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1189 }
1190 break;
1191
1192 case IP6T_SO_GET_ENTRIES: {
1193 struct ip6t_get_entries get;
1194
1195 if (*len < sizeof(get)) {
1196 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1197 ret = -EINVAL;
1198 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1199 ret = -EFAULT;
1200 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1201 duprintf("get_entries: %u != %u\n", *len,
1202 sizeof(struct ip6t_get_entries) + get.size);
1203 ret = -EINVAL;
1204 } else
1205 ret = get_entries(&get, user);
1206 break;
1207 }
1208
6b7d31fc
HW
1209 case IP6T_SO_GET_REVISION_MATCH:
1210 case IP6T_SO_GET_REVISION_TARGET: {
1211 struct ip6t_get_revision rev;
2e4e6a17 1212 int target;
6b7d31fc
HW
1213
1214 if (*len != sizeof(rev)) {
1215 ret = -EINVAL;
1216 break;
1217 }
1218 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1219 ret = -EFAULT;
1220 break;
1221 }
1222
1223 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 1224 target = 1;
6b7d31fc 1225 else
2e4e6a17 1226 target = 0;
6b7d31fc 1227
2e4e6a17
HW
1228 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1229 rev.revision,
1230 target, &ret),
6b7d31fc
HW
1231 "ip6t_%s", rev.name);
1232 break;
1233 }
1234
1da177e4
LT
1235 default:
1236 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1237 ret = -EINVAL;
1238 }
1239
1240 return ret;
1241}
1242
2e4e6a17 1243int ip6t_register_table(struct xt_table *table,
1da177e4
LT
1244 const struct ip6t_replace *repl)
1245{
1246 int ret;
2e4e6a17
HW
1247 struct xt_table_info *newinfo;
1248 static struct xt_table_info bootstrap
1da177e4 1249 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 1250 void *loc_cpu_entry;
1da177e4 1251
2e4e6a17 1252 newinfo = xt_alloc_table_info(repl->size);
1da177e4
LT
1253 if (!newinfo)
1254 return -ENOMEM;
1255
31836064
ED
1256 /* choose the copy on our node/cpu */
1257 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1258 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4
LT
1259
1260 ret = translate_table(table->name, table->valid_hooks,
31836064 1261 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
1262 repl->num_entries,
1263 repl->hook_entry,
1264 repl->underflow);
1265 if (ret != 0) {
2e4e6a17 1266 xt_free_table_info(newinfo);
1da177e4
LT
1267 return ret;
1268 }
1269
da298d3a
PM
1270 ret = xt_register_table(table, &bootstrap, newinfo);
1271 if (ret != 0) {
2e4e6a17 1272 xt_free_table_info(newinfo);
1da177e4
LT
1273 return ret;
1274 }
1275
2e4e6a17 1276 return 0;
1da177e4
LT
1277}
1278
2e4e6a17 1279void ip6t_unregister_table(struct xt_table *table)
1da177e4 1280{
2e4e6a17 1281 struct xt_table_info *private;
31836064
ED
1282 void *loc_cpu_entry;
1283
2e4e6a17 1284 private = xt_unregister_table(table);
1da177e4
LT
1285
1286 /* Decrease module usage counts and free resources */
2e4e6a17
HW
1287 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1288 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1289 xt_free_table_info(private);
1da177e4
LT
1290}
1291
1292/* Returns 1 if the type and code is matched by the range, 0 otherwise */
1293static inline int
1294icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1295 u_int8_t type, u_int8_t code,
1296 int invert)
1297{
1298 return (type == test_type && code >= min_code && code <= max_code)
1299 ^ invert;
1300}
1301
1302static int
1303icmp6_match(const struct sk_buff *skb,
1304 const struct net_device *in,
1305 const struct net_device *out,
c4986734 1306 const struct xt_match *match,
1da177e4
LT
1307 const void *matchinfo,
1308 int offset,
1309 unsigned int protoff,
1310 int *hotdrop)
1311{
1312 struct icmp6hdr _icmp, *ic;
1313 const struct ip6t_icmp *icmpinfo = matchinfo;
1314
1315 /* Must not be a fragment. */
1316 if (offset)
1317 return 0;
1318
1319 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1320 if (ic == NULL) {
1321 /* We've been asked to examine this packet, and we
1322 can't. Hence, no choice but to drop. */
1323 duprintf("Dropping evil ICMP tinygram.\n");
1324 *hotdrop = 1;
1325 return 0;
1326 }
1327
1328 return icmp6_type_code_match(icmpinfo->type,
1329 icmpinfo->code[0],
1330 icmpinfo->code[1],
1331 ic->icmp6_type, ic->icmp6_code,
1332 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1333}
1334
1335/* Called when user tries to insert an entry of this type. */
1336static int
1337icmp6_checkentry(const char *tablename,
2e4e6a17 1338 const void *entry,
c4986734 1339 const struct xt_match *match,
1da177e4 1340 void *matchinfo,
1da177e4
LT
1341 unsigned int hook_mask)
1342{
1343 const struct ip6t_icmp *icmpinfo = matchinfo;
1344
7f939713
PM
1345 /* Must specify no unknown invflags */
1346 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1da177e4
LT
1347}
1348
1349/* The built-in targets: standard (NULL) and error. */
1350static struct ip6t_target ip6t_standard_target = {
1351 .name = IP6T_STANDARD_TARGET,
7f939713 1352 .targetsize = sizeof(int),
a45049c5 1353 .family = AF_INET6,
1da177e4
LT
1354};
1355
1356static struct ip6t_target ip6t_error_target = {
1357 .name = IP6T_ERROR_TARGET,
1358 .target = ip6t_error,
7f939713 1359 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
a45049c5 1360 .family = AF_INET6,
1da177e4
LT
1361};
1362
1363static struct nf_sockopt_ops ip6t_sockopts = {
1364 .pf = PF_INET6,
1365 .set_optmin = IP6T_BASE_CTL,
1366 .set_optmax = IP6T_SO_SET_MAX+1,
1367 .set = do_ip6t_set_ctl,
1368 .get_optmin = IP6T_BASE_CTL,
1369 .get_optmax = IP6T_SO_GET_MAX+1,
1370 .get = do_ip6t_get_ctl,
1371};
1372
1da177e4
LT
1373static struct ip6t_match icmp6_matchstruct = {
1374 .name = "icmp6",
1375 .match = &icmp6_match,
7f939713
PM
1376 .matchsize = sizeof(struct ip6t_icmp),
1377 .checkentry = icmp6_checkentry,
1378 .proto = IPPROTO_ICMPV6,
a45049c5 1379 .family = AF_INET6,
1da177e4
LT
1380};
1381
65b4b4e8 1382static int __init ip6_tables_init(void)
1da177e4
LT
1383{
1384 int ret;
1385
0eff66e6
PM
1386 ret = xt_proto_init(AF_INET6);
1387 if (ret < 0)
1388 goto err1;
2e4e6a17 1389
1da177e4 1390 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
1391 ret = xt_register_target(&ip6t_standard_target);
1392 if (ret < 0)
1393 goto err2;
1394 ret = xt_register_target(&ip6t_error_target);
1395 if (ret < 0)
1396 goto err3;
1397 ret = xt_register_match(&icmp6_matchstruct);
1398 if (ret < 0)
1399 goto err4;
1da177e4
LT
1400
1401 /* Register setsockopt */
1402 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
1403 if (ret < 0)
1404 goto err5;
1da177e4 1405
2e4e6a17 1406 printk("ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 1407 return 0;
0eff66e6
PM
1408
1409err5:
1410 xt_unregister_match(&icmp6_matchstruct);
1411err4:
1412 xt_unregister_target(&ip6t_error_target);
1413err3:
1414 xt_unregister_target(&ip6t_standard_target);
1415err2:
1416 xt_proto_fini(AF_INET6);
1417err1:
1418 return ret;
1da177e4
LT
1419}
1420
65b4b4e8 1421static void __exit ip6_tables_fini(void)
1da177e4
LT
1422{
1423 nf_unregister_sockopt(&ip6t_sockopts);
a45049c5
PNA
1424 xt_unregister_match(&icmp6_matchstruct);
1425 xt_unregister_target(&ip6t_error_target);
1426 xt_unregister_target(&ip6t_standard_target);
2e4e6a17 1427 xt_proto_fini(AF_INET6);
1da177e4
LT
1428}
1429
e674d0f3 1430/*
b777e0ce
PM
1431 * find the offset to specified header or the protocol number of last header
1432 * if target < 0. "last header" is transport protocol header, ESP, or
1433 * "No next header".
1434 *
1435 * If target header is found, its offset is set in *offset and return protocol
1436 * number. Otherwise, return -1.
1437 *
6d381634
PM
1438 * If the first fragment doesn't contain the final protocol header or
1439 * NEXTHDR_NONE it is considered invalid.
1440 *
b777e0ce
PM
1441 * Note that non-1st fragment is special case that "the protocol number
1442 * of last header" is "next header" field in Fragment header. In this case,
1443 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1444 * isn't NULL.
e674d0f3 1445 *
e674d0f3 1446 */
b777e0ce
PM
1447int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1448 int target, unsigned short *fragoff)
e674d0f3
YK
1449{
1450 unsigned int start = (u8*)(skb->nh.ipv6h + 1) - skb->data;
1451 u8 nexthdr = skb->nh.ipv6h->nexthdr;
1452 unsigned int len = skb->len - start;
1453
b777e0ce
PM
1454 if (fragoff)
1455 *fragoff = 0;
1456
e674d0f3
YK
1457 while (nexthdr != target) {
1458 struct ipv6_opt_hdr _hdr, *hp;
1459 unsigned int hdrlen;
1460
b777e0ce
PM
1461 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1462 if (target < 0)
1463 break;
6d381634 1464 return -ENOENT;
b777e0ce
PM
1465 }
1466
e674d0f3
YK
1467 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1468 if (hp == NULL)
6d381634 1469 return -EBADMSG;
e674d0f3 1470 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
1471 unsigned short _frag_off;
1472 __be16 *fp;
e674d0f3
YK
1473 fp = skb_header_pointer(skb,
1474 start+offsetof(struct frag_hdr,
1475 frag_off),
1476 sizeof(_frag_off),
1477 &_frag_off);
1478 if (fp == NULL)
6d381634 1479 return -EBADMSG;
e674d0f3 1480
b777e0ce
PM
1481 _frag_off = ntohs(*fp) & ~0x7;
1482 if (_frag_off) {
1483 if (target < 0 &&
1484 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 1485 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
1486 if (fragoff)
1487 *fragoff = _frag_off;
1488 return hp->nexthdr;
1489 }
6d381634 1490 return -ENOENT;
b777e0ce 1491 }
e674d0f3
YK
1492 hdrlen = 8;
1493 } else if (nexthdr == NEXTHDR_AUTH)
1494 hdrlen = (hp->hdrlen + 2) << 2;
1495 else
1496 hdrlen = ipv6_optlen(hp);
1497
1498 nexthdr = hp->nexthdr;
1499 len -= hdrlen;
1500 start += hdrlen;
1501 }
1502
1503 *offset = start;
b777e0ce 1504 return nexthdr;
e674d0f3
YK
1505}
1506
1da177e4
LT
1507EXPORT_SYMBOL(ip6t_register_table);
1508EXPORT_SYMBOL(ip6t_unregister_table);
1509EXPORT_SYMBOL(ip6t_do_table);
1da177e4 1510EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 1511EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 1512
65b4b4e8
AM
1513module_init(ip6_tables_init);
1514module_exit(ip6_tables_fini);
This page took 0.499895 seconds and 5 git commands to generate.