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