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