[NETFILTER]: ip_tables: move compat offset calculation to x_tables
[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.
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
3db05fea 208ip6t_error(struct sk_buff *skb,
1da177e4
LT
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
ba9dda3a
JK
244/* All zeroes == unconditional rule. */
245static inline int
246unconditional(const struct ip6t_ip6 *ipv6)
247{
248 unsigned int i;
249
250 for (i = 0; i < sizeof(*ipv6); i++)
251 if (((char *)ipv6)[i])
252 break;
253
254 return (i == sizeof(*ipv6));
255}
256
257#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
258 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
259/* This cries for unification! */
260static const char *hooknames[] = {
6e23ae2a
PM
261 [NF_INET_PRE_ROUTING] = "PREROUTING",
262 [NF_INET_LOCAL_IN] = "INPUT",
263 [NF_INET_FORWARD] = "FORWARD",
264 [NF_INET_LOCAL_OUT] = "OUTPUT",
265 [NF_INET_POST_ROUTING] = "POSTROUTING",
ba9dda3a
JK
266};
267
268enum nf_ip_trace_comments {
269 NF_IP6_TRACE_COMMENT_RULE,
270 NF_IP6_TRACE_COMMENT_RETURN,
271 NF_IP6_TRACE_COMMENT_POLICY,
272};
273
274static const char *comments[] = {
275 [NF_IP6_TRACE_COMMENT_RULE] = "rule",
276 [NF_IP6_TRACE_COMMENT_RETURN] = "return",
277 [NF_IP6_TRACE_COMMENT_POLICY] = "policy",
278};
279
280static struct nf_loginfo trace_loginfo = {
281 .type = NF_LOG_TYPE_LOG,
282 .u = {
283 .log = {
284 .level = 4,
285 .logflags = NF_LOG_MASK,
286 },
287 },
288};
289
290static inline int
291get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
292 char *hookname, char **chainname,
293 char **comment, unsigned int *rulenum)
294{
295 struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
296
297 if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
298 /* Head of user chain: ERROR target with chainname */
299 *chainname = t->target.data;
300 (*rulenum) = 0;
301 } else if (s == e) {
302 (*rulenum)++;
303
304 if (s->target_offset == sizeof(struct ip6t_entry)
305 && strcmp(t->target.u.kernel.target->name,
306 IP6T_STANDARD_TARGET) == 0
307 && t->verdict < 0
308 && unconditional(&s->ipv6)) {
309 /* Tail of chains: STANDARD target (return/policy) */
310 *comment = *chainname == hookname
311 ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
312 : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
313 }
314 return 1;
315 } else
316 (*rulenum)++;
317
318 return 0;
319}
320
321static void trace_packet(struct sk_buff *skb,
322 unsigned int hook,
323 const struct net_device *in,
324 const struct net_device *out,
325 char *tablename,
326 struct xt_table_info *private,
327 struct ip6t_entry *e)
328{
329 void *table_base;
330 struct ip6t_entry *root;
331 char *hookname, *chainname, *comment;
332 unsigned int rulenum = 0;
333
334 table_base = (void *)private->entries[smp_processor_id()];
335 root = get_entry(table_base, private->hook_entry[hook]);
336
337 hookname = chainname = (char *)hooknames[hook];
338 comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
339
340 IP6T_ENTRY_ITERATE(root,
341 private->size - private->hook_entry[hook],
342 get_chainname_rulenum,
343 e, hookname, &chainname, &comment, &rulenum);
344
345 nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
346 "TRACE: %s:%s:%s:%u ",
347 tablename, chainname, comment, rulenum);
348}
349#endif
350
1da177e4
LT
351/* Returns one of the generic firewall policies, like NF_ACCEPT. */
352unsigned int
3db05fea 353ip6t_do_table(struct sk_buff *skb,
1da177e4
LT
354 unsigned int hook,
355 const struct net_device *in,
356 const struct net_device *out,
fe1cb108 357 struct xt_table *table)
1da177e4 358{
6b7d31fc 359 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
1da177e4
LT
360 int offset = 0;
361 unsigned int protoff = 0;
cff533ac 362 bool hotdrop = false;
1da177e4
LT
363 /* Initializing verdict to NF_DROP keeps gcc happy. */
364 unsigned int verdict = NF_DROP;
365 const char *indev, *outdev;
366 void *table_base;
367 struct ip6t_entry *e, *back;
2e4e6a17 368 struct xt_table_info *private;
1da177e4
LT
369
370 /* Initialization */
371 indev = in ? in->name : nulldevname;
372 outdev = out ? out->name : nulldevname;
1da177e4
LT
373 /* We handle fragments by dealing with the first fragment as
374 * if it was a normal packet. All other fragments are treated
375 * normally, except that they will NEVER match rules that ask
376 * things we don't know, ie. tcp syn flag or ports). If the
377 * rule is also a fragment-specific rule, non-fragments won't
378 * match it. */
379
380 read_lock_bh(&table->lock);
2e4e6a17 381 private = table->private;
1da177e4 382 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
2e4e6a17
HW
383 table_base = (void *)private->entries[smp_processor_id()];
384 e = get_entry(table_base, private->hook_entry[hook]);
1da177e4 385
1da177e4 386 /* For return from builtin chain */
2e4e6a17 387 back = get_entry(table_base, private->underflow[hook]);
1da177e4
LT
388
389 do {
390 IP_NF_ASSERT(e);
391 IP_NF_ASSERT(back);
3db05fea 392 if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
51d8b1a6 393 &protoff, &offset, &hotdrop)) {
1da177e4
LT
394 struct ip6t_entry_target *t;
395
396 if (IP6T_MATCH_ITERATE(e, do_match,
3db05fea 397 skb, in, out,
1da177e4
LT
398 offset, protoff, &hotdrop) != 0)
399 goto no_match;
400
401 ADD_COUNTER(e->counters,
3db05fea 402 ntohs(ipv6_hdr(skb)->payload_len)
1da177e4
LT
403 + IPV6_HDR_LEN,
404 1);
405
406 t = ip6t_get_target(e);
407 IP_NF_ASSERT(t->u.kernel.target);
ba9dda3a
JK
408
409#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
410 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
411 /* The packet is traced: log it */
3db05fea
HX
412 if (unlikely(skb->nf_trace))
413 trace_packet(skb, hook, in, out,
ba9dda3a
JK
414 table->name, private, e);
415#endif
1da177e4
LT
416 /* Standard target? */
417 if (!t->u.kernel.target->target) {
418 int v;
419
420 v = ((struct ip6t_standard_target *)t)->verdict;
421 if (v < 0) {
422 /* Pop from stack? */
423 if (v != IP6T_RETURN) {
424 verdict = (unsigned)(-v) - 1;
425 break;
426 }
427 e = back;
428 back = get_entry(table_base,
429 back->comefrom);
430 continue;
431 }
05465343
PM
432 if (table_base + v != (void *)e + e->next_offset
433 && !(e->ipv6.flags & IP6T_F_GOTO)) {
1da177e4
LT
434 /* Save old back ptr in next entry */
435 struct ip6t_entry *next
436 = (void *)e + e->next_offset;
437 next->comefrom
438 = (void *)back - table_base;
439 /* set back pointer to next entry */
440 back = next;
441 }
442
443 e = get_entry(table_base, v);
444 } else {
445 /* Targets which reenter must return
1ab1457c 446 abs. verdicts */
1da177e4
LT
447#ifdef CONFIG_NETFILTER_DEBUG
448 ((struct ip6t_entry *)table_base)->comefrom
449 = 0xeeeeeeec;
450#endif
3db05fea 451 verdict = t->u.kernel.target->target(skb,
1da177e4
LT
452 in, out,
453 hook,
1c524830 454 t->u.kernel.target,
fe1cb108 455 t->data);
1da177e4
LT
456
457#ifdef CONFIG_NETFILTER_DEBUG
458 if (((struct ip6t_entry *)table_base)->comefrom
459 != 0xeeeeeeec
460 && verdict == IP6T_CONTINUE) {
461 printk("Target %s reentered!\n",
462 t->u.kernel.target->name);
463 verdict = NF_DROP;
464 }
465 ((struct ip6t_entry *)table_base)->comefrom
466 = 0x57acc001;
467#endif
468 if (verdict == IP6T_CONTINUE)
469 e = (void *)e + e->next_offset;
470 else
471 /* Verdict */
472 break;
473 }
474 } else {
475
476 no_match:
477 e = (void *)e + e->next_offset;
478 }
479 } while (!hotdrop);
480
481#ifdef CONFIG_NETFILTER_DEBUG
4bdbf6c0 482 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
1da177e4
LT
483#endif
484 read_unlock_bh(&table->lock);
485
486#ifdef DEBUG_ALLOW_ALL
487 return NF_ACCEPT;
488#else
489 if (hotdrop)
490 return NF_DROP;
491 else return verdict;
492#endif
493}
494
1da177e4
LT
495/* Figures out from what hook each rule can be called: returns 0 if
496 there are loops. Puts hook bitmask in comefrom. */
497static int
2e4e6a17 498mark_source_chains(struct xt_table_info *newinfo,
31836064 499 unsigned int valid_hooks, void *entry0)
1da177e4
LT
500{
501 unsigned int hook;
502
503 /* No recursion; use packet counter to save back ptrs (reset
504 to 0 as we leave), and comefrom to save source hook bitmask */
6e23ae2a 505 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
1da177e4
LT
506 unsigned int pos = newinfo->hook_entry[hook];
507 struct ip6t_entry *e
31836064 508 = (struct ip6t_entry *)(entry0 + pos);
e1b4b9f3 509 int visited = e->comefrom & (1 << hook);
1da177e4
LT
510
511 if (!(valid_hooks & (1 << hook)))
512 continue;
513
514 /* Set initial back pointer. */
515 e->counters.pcnt = pos;
516
517 for (;;) {
518 struct ip6t_standard_target *t
519 = (void *)ip6t_get_target(e);
520
6e23ae2a 521 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
522 printk("iptables: loop hook %u pos %u %08X.\n",
523 hook, pos, e->comefrom);
524 return 0;
525 }
526 e->comefrom
6e23ae2a 527 |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
1da177e4
LT
528
529 /* Unconditional return/END. */
e1b4b9f3 530 if ((e->target_offset == sizeof(struct ip6t_entry)
1da177e4
LT
531 && (strcmp(t->target.u.user.name,
532 IP6T_STANDARD_TARGET) == 0)
533 && t->verdict < 0
e1b4b9f3 534 && unconditional(&e->ipv6)) || visited) {
1da177e4
LT
535 unsigned int oldpos, size;
536
74c9c0c1
DM
537 if (t->verdict < -NF_MAX_VERDICT - 1) {
538 duprintf("mark_source_chains: bad "
539 "negative verdict (%i)\n",
540 t->verdict);
541 return 0;
542 }
543
1da177e4
LT
544 /* Return: backtrack through the last
545 big jump. */
546 do {
6e23ae2a 547 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
1da177e4
LT
548#ifdef DEBUG_IP_FIREWALL_USER
549 if (e->comefrom
6e23ae2a 550 & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
551 duprintf("Back unset "
552 "on hook %u "
553 "rule %u\n",
554 hook, pos);
555 }
556#endif
557 oldpos = pos;
558 pos = e->counters.pcnt;
559 e->counters.pcnt = 0;
560
561 /* We're at the start. */
562 if (pos == oldpos)
563 goto next;
564
565 e = (struct ip6t_entry *)
31836064 566 (entry0 + pos);
1da177e4
LT
567 } while (oldpos == pos + e->next_offset);
568
569 /* Move along one */
570 size = e->next_offset;
571 e = (struct ip6t_entry *)
31836064 572 (entry0 + pos + size);
1da177e4
LT
573 e->counters.pcnt = pos;
574 pos += size;
575 } else {
576 int newpos = t->verdict;
577
578 if (strcmp(t->target.u.user.name,
579 IP6T_STANDARD_TARGET) == 0
580 && newpos >= 0) {
74c9c0c1
DM
581 if (newpos > newinfo->size -
582 sizeof(struct ip6t_entry)) {
583 duprintf("mark_source_chains: "
584 "bad verdict (%i)\n",
585 newpos);
586 return 0;
587 }
1da177e4
LT
588 /* This a jump; chase it. */
589 duprintf("Jump rule %u -> %u\n",
590 pos, newpos);
591 } else {
592 /* ... this is a fallthru */
593 newpos = pos + e->next_offset;
594 }
595 e = (struct ip6t_entry *)
31836064 596 (entry0 + newpos);
1da177e4
LT
597 e->counters.pcnt = pos;
598 pos = newpos;
599 }
600 }
601 next:
602 duprintf("Finished chain %u\n", hook);
603 }
604 return 1;
605}
606
607static inline int
608cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
609{
610 if (i && (*i)-- == 0)
611 return 1;
612
613 if (m->u.kernel.match->destroy)
efa74165 614 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
1da177e4
LT
615 module_put(m->u.kernel.match->me);
616 return 0;
617}
618
1da177e4
LT
619static inline int
620check_match(struct ip6t_entry_match *m,
621 const char *name,
622 const struct ip6t_ip6 *ipv6,
623 unsigned int hookmask,
624 unsigned int *i)
625{
6709dbbb 626 struct xt_match *match;
3cdc7c95 627 int ret;
1da177e4 628
2e4e6a17 629 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
1ab1457c 630 m->u.user.revision),
6b7d31fc
HW
631 "ip6t_%s", m->u.user.name);
632 if (IS_ERR(match) || !match) {
1ab1457c 633 duprintf("check_match: `%s' not found\n", m->u.user.name);
6b7d31fc 634 return match ? PTR_ERR(match) : -ENOENT;
1da177e4
LT
635 }
636 m->u.kernel.match = match;
1da177e4 637
3cdc7c95
PM
638 ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
639 name, hookmask, ipv6->proto,
640 ipv6->invflags & IP6T_INV_PROTO);
641 if (ret)
642 goto err;
643
1da177e4 644 if (m->u.kernel.match->checkentry
1c524830 645 && !m->u.kernel.match->checkentry(name, ipv6, match, m->data,
1da177e4 646 hookmask)) {
1da177e4
LT
647 duprintf("ip_tables: check failed for `%s'.\n",
648 m->u.kernel.match->name);
3cdc7c95
PM
649 ret = -EINVAL;
650 goto err;
1da177e4
LT
651 }
652
653 (*i)++;
654 return 0;
3cdc7c95
PM
655err:
656 module_put(m->u.kernel.match->me);
657 return ret;
1da177e4
LT
658}
659
6709dbbb 660static struct xt_target ip6t_standard_target;
1da177e4
LT
661
662static inline int
663check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
664 unsigned int *i)
665{
666 struct ip6t_entry_target *t;
6709dbbb 667 struct xt_target *target;
1da177e4
LT
668 int ret;
669 unsigned int j;
670
671 if (!ip6_checkentry(&e->ipv6)) {
672 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
673 return -EINVAL;
674 }
675
590bdf7f
DM
676 if (e->target_offset + sizeof(struct ip6t_entry_target) >
677 e->next_offset)
678 return -EINVAL;
679
1da177e4
LT
680 j = 0;
681 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
682 if (ret != 0)
683 goto cleanup_matches;
684
685 t = ip6t_get_target(e);
590bdf7f
DM
686 ret = -EINVAL;
687 if (e->target_offset + t->u.target_size > e->next_offset)
688 goto cleanup_matches;
2e4e6a17
HW
689 target = try_then_request_module(xt_find_target(AF_INET6,
690 t->u.user.name,
691 t->u.user.revision),
6b7d31fc
HW
692 "ip6t_%s", t->u.user.name);
693 if (IS_ERR(target) || !target) {
1da177e4 694 duprintf("check_entry: `%s' not found\n", t->u.user.name);
6b7d31fc 695 ret = target ? PTR_ERR(target) : -ENOENT;
1da177e4
LT
696 goto cleanup_matches;
697 }
698 t->u.kernel.target = target;
6b7d31fc 699
3cdc7c95
PM
700 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
701 name, e->comefrom, e->ipv6.proto,
702 e->ipv6.invflags & IP6T_INV_PROTO);
703 if (ret)
704 goto err;
705
74c9c0c1 706 if (t->u.kernel.target->checkentry
1c524830 707 && !t->u.kernel.target->checkentry(name, e, target, t->data,
1da177e4 708 e->comefrom)) {
1da177e4
LT
709 duprintf("ip_tables: check failed for `%s'.\n",
710 t->u.kernel.target->name);
711 ret = -EINVAL;
3cdc7c95 712 goto err;
1da177e4
LT
713 }
714
715 (*i)++;
716 return 0;
3cdc7c95
PM
717 err:
718 module_put(t->u.kernel.target->me);
1da177e4
LT
719 cleanup_matches:
720 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
721 return ret;
722}
723
724static inline int
725check_entry_size_and_hooks(struct ip6t_entry *e,
2e4e6a17 726 struct xt_table_info *newinfo,
1da177e4
LT
727 unsigned char *base,
728 unsigned char *limit,
729 const unsigned int *hook_entries,
730 const unsigned int *underflows,
731 unsigned int *i)
732{
733 unsigned int h;
734
735 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
736 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
737 duprintf("Bad offset %p\n", e);
738 return -EINVAL;
739 }
740
741 if (e->next_offset
742 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
743 duprintf("checking: element %p size %u\n",
744 e, e->next_offset);
745 return -EINVAL;
746 }
747
748 /* Check hooks & underflows */
6e23ae2a 749 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1da177e4
LT
750 if ((unsigned char *)e - base == hook_entries[h])
751 newinfo->hook_entry[h] = hook_entries[h];
752 if ((unsigned char *)e - base == underflows[h])
753 newinfo->underflow[h] = underflows[h];
754 }
755
756 /* FIXME: underflows must be unconditional, standard verdicts
1ab1457c 757 < 0 (not IP6T_RETURN). --RR */
1da177e4
LT
758
759 /* Clear counters and comefrom */
2e4e6a17 760 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4
LT
761 e->comefrom = 0;
762
763 (*i)++;
764 return 0;
765}
766
767static inline int
768cleanup_entry(struct ip6t_entry *e, unsigned int *i)
769{
770 struct ip6t_entry_target *t;
771
772 if (i && (*i)-- == 0)
773 return 1;
774
775 /* Cleanup all matches */
776 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
777 t = ip6t_get_target(e);
778 if (t->u.kernel.target->destroy)
efa74165 779 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
1da177e4
LT
780 module_put(t->u.kernel.target->me);
781 return 0;
782}
783
784/* Checks and translates the user-supplied table segment (held in
785 newinfo) */
786static int
787translate_table(const char *name,
788 unsigned int valid_hooks,
2e4e6a17 789 struct xt_table_info *newinfo,
31836064 790 void *entry0,
1da177e4
LT
791 unsigned int size,
792 unsigned int number,
793 const unsigned int *hook_entries,
794 const unsigned int *underflows)
795{
796 unsigned int i;
797 int ret;
798
799 newinfo->size = size;
800 newinfo->number = number;
801
802 /* Init all hooks to impossible value. */
6e23ae2a 803 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
804 newinfo->hook_entry[i] = 0xFFFFFFFF;
805 newinfo->underflow[i] = 0xFFFFFFFF;
806 }
807
808 duprintf("translate_table: size %u\n", newinfo->size);
809 i = 0;
810 /* Walk through entries, checking offsets. */
31836064 811 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
812 check_entry_size_and_hooks,
813 newinfo,
31836064
ED
814 entry0,
815 entry0 + size,
1da177e4
LT
816 hook_entries, underflows, &i);
817 if (ret != 0)
818 return ret;
819
820 if (i != number) {
821 duprintf("translate_table: %u not %u entries\n",
822 i, number);
823 return -EINVAL;
824 }
825
826 /* Check hooks all assigned */
6e23ae2a 827 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
828 /* Only hooks which are valid */
829 if (!(valid_hooks & (1 << i)))
830 continue;
831 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
832 duprintf("Invalid hook entry %u %u\n",
833 i, hook_entries[i]);
834 return -EINVAL;
835 }
836 if (newinfo->underflow[i] == 0xFFFFFFFF) {
837 duprintf("Invalid underflow %u %u\n",
838 i, underflows[i]);
839 return -EINVAL;
840 }
841 }
842
74c9c0c1
DM
843 if (!mark_source_chains(newinfo, valid_hooks, entry0))
844 return -ELOOP;
845
1da177e4
LT
846 /* Finally, each sanity check must pass */
847 i = 0;
31836064 848 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
849 check_entry, name, size, &i);
850
74c9c0c1
DM
851 if (ret != 0) {
852 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
853 cleanup_entry, &i);
854 return ret;
855 }
1da177e4
LT
856
857 /* And one copy for every other CPU */
6f912042 858 for_each_possible_cpu(i) {
31836064
ED
859 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
860 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
861 }
862
590bdf7f 863 return 0;
1da177e4
LT
864}
865
1da177e4
LT
866/* Gets counters. */
867static inline int
868add_entry_to_counter(const struct ip6t_entry *e,
2e4e6a17 869 struct xt_counters total[],
1da177e4
LT
870 unsigned int *i)
871{
872 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
873
874 (*i)++;
875 return 0;
876}
877
31836064
ED
878static inline int
879set_entry_to_counter(const struct ip6t_entry *e,
880 struct ip6t_counters total[],
881 unsigned int *i)
882{
883 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
884
885 (*i)++;
886 return 0;
887}
888
1da177e4 889static void
2e4e6a17
HW
890get_counters(const struct xt_table_info *t,
891 struct xt_counters counters[])
1da177e4
LT
892{
893 unsigned int cpu;
894 unsigned int i;
31836064
ED
895 unsigned int curcpu;
896
897 /* Instead of clearing (by a previous call to memset())
898 * the counters and using adds, we set the counters
899 * with data used by 'current' CPU
900 * We dont care about preemption here.
901 */
902 curcpu = raw_smp_processor_id();
903
904 i = 0;
905 IP6T_ENTRY_ITERATE(t->entries[curcpu],
906 t->size,
907 set_entry_to_counter,
908 counters,
909 &i);
1da177e4 910
6f912042 911 for_each_possible_cpu(cpu) {
31836064
ED
912 if (cpu == curcpu)
913 continue;
1da177e4 914 i = 0;
31836064 915 IP6T_ENTRY_ITERATE(t->entries[cpu],
1da177e4
LT
916 t->size,
917 add_entry_to_counter,
918 counters,
919 &i);
920 }
921}
922
923static int
924copy_entries_to_user(unsigned int total_size,
2e4e6a17 925 struct xt_table *table,
1da177e4
LT
926 void __user *userptr)
927{
928 unsigned int off, num, countersize;
929 struct ip6t_entry *e;
2e4e6a17
HW
930 struct xt_counters *counters;
931 struct xt_table_info *private = table->private;
1da177e4 932 int ret = 0;
31836064 933 void *loc_cpu_entry;
1da177e4
LT
934
935 /* We need atomic snapshot of counters: rest doesn't change
936 (other than comefrom, which userspace doesn't care
937 about). */
2e4e6a17 938 countersize = sizeof(struct xt_counters) * private->number;
1da177e4
LT
939 counters = vmalloc(countersize);
940
941 if (counters == NULL)
942 return -ENOMEM;
943
944 /* First, sum counters... */
1da177e4 945 write_lock_bh(&table->lock);
2e4e6a17 946 get_counters(private, counters);
1da177e4
LT
947 write_unlock_bh(&table->lock);
948
31836064 949 /* choose the copy that is on ourc node/cpu */
2e4e6a17 950 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 951 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
952 ret = -EFAULT;
953 goto free_counters;
954 }
955
956 /* FIXME: use iterator macros --RR */
957 /* ... then go back and fix counters and names */
958 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
959 unsigned int i;
960 struct ip6t_entry_match *m;
961 struct ip6t_entry_target *t;
962
31836064 963 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
964 if (copy_to_user(userptr + off
965 + offsetof(struct ip6t_entry, counters),
966 &counters[num],
967 sizeof(counters[num])) != 0) {
968 ret = -EFAULT;
969 goto free_counters;
970 }
971
972 for (i = sizeof(struct ip6t_entry);
973 i < e->target_offset;
974 i += m->u.match_size) {
975 m = (void *)e + i;
976
977 if (copy_to_user(userptr + off + i
978 + offsetof(struct ip6t_entry_match,
979 u.user.name),
980 m->u.kernel.match->name,
981 strlen(m->u.kernel.match->name)+1)
982 != 0) {
983 ret = -EFAULT;
984 goto free_counters;
985 }
986 }
987
988 t = ip6t_get_target(e);
989 if (copy_to_user(userptr + off + e->target_offset
990 + offsetof(struct ip6t_entry_target,
991 u.user.name),
992 t->u.kernel.target->name,
993 strlen(t->u.kernel.target->name)+1) != 0) {
994 ret = -EFAULT;
995 goto free_counters;
996 }
997 }
998
999 free_counters:
1000 vfree(counters);
1001 return ret;
1002}
1003
1004static int
1005get_entries(const struct ip6t_get_entries *entries,
1006 struct ip6t_get_entries __user *uptr)
1007{
1008 int ret;
2e4e6a17 1009 struct xt_table *t;
1da177e4 1010
2e4e6a17 1011 t = xt_find_table_lock(AF_INET6, entries->name);
6b7d31fc 1012 if (t && !IS_ERR(t)) {
2e4e6a17
HW
1013 struct xt_table_info *private = t->private;
1014 duprintf("t->private->number = %u\n", private->number);
1015 if (entries->size == private->size)
1016 ret = copy_entries_to_user(private->size,
1da177e4
LT
1017 t, uptr->entrytable);
1018 else {
1019 duprintf("get_entries: I've got %u not %u!\n",
2e4e6a17 1020 private->size, entries->size);
1da177e4
LT
1021 ret = -EINVAL;
1022 }
6b7d31fc 1023 module_put(t->me);
2e4e6a17 1024 xt_table_unlock(t);
1da177e4 1025 } else
6b7d31fc 1026 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1027
1028 return ret;
1029}
1030
1031static int
1032do_replace(void __user *user, unsigned int len)
1033{
1034 int ret;
1035 struct ip6t_replace tmp;
2e4e6a17
HW
1036 struct xt_table *t;
1037 struct xt_table_info *newinfo, *oldinfo;
1038 struct xt_counters *counters;
31836064 1039 void *loc_cpu_entry, *loc_cpu_old_entry;
1da177e4
LT
1040
1041 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1042 return -EFAULT;
1043
ee4bb818 1044 /* overflow check */
ee4bb818
KK
1045 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1046 return -ENOMEM;
1047
2e4e6a17 1048 newinfo = xt_alloc_table_info(tmp.size);
1da177e4
LT
1049 if (!newinfo)
1050 return -ENOMEM;
1051
31836064
ED
1052 /* choose the copy that is on our node/cpu */
1053 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1054 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1da177e4
LT
1055 tmp.size) != 0) {
1056 ret = -EFAULT;
1057 goto free_newinfo;
1058 }
1059
2e4e6a17 1060 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
1da177e4
LT
1061 if (!counters) {
1062 ret = -ENOMEM;
1063 goto free_newinfo;
1064 }
1da177e4
LT
1065
1066 ret = translate_table(tmp.name, tmp.valid_hooks,
31836064 1067 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1da177e4
LT
1068 tmp.hook_entry, tmp.underflow);
1069 if (ret != 0)
1070 goto free_newinfo_counters;
1071
1072 duprintf("ip_tables: Translated table\n");
1073
2e4e6a17 1074 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
6b7d31fc
HW
1075 "ip6table_%s", tmp.name);
1076 if (!t || IS_ERR(t)) {
1077 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1078 goto free_newinfo_counters_untrans;
6b7d31fc 1079 }
1da177e4
LT
1080
1081 /* You lied! */
1082 if (tmp.valid_hooks != t->valid_hooks) {
1083 duprintf("Valid hook crap: %08X vs %08X\n",
1084 tmp.valid_hooks, t->valid_hooks);
1085 ret = -EINVAL;
6b7d31fc 1086 goto put_module;
1da177e4
LT
1087 }
1088
2e4e6a17 1089 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1da177e4
LT
1090 if (!oldinfo)
1091 goto put_module;
1092
1093 /* Update module usage count based on number of rules */
1094 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1095 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1096 if ((oldinfo->number > oldinfo->initial_entries) ||
1097 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1098 module_put(t->me);
1099 if ((oldinfo->number > oldinfo->initial_entries) &&
1100 (newinfo->number <= oldinfo->initial_entries))
1101 module_put(t->me);
1102
1103 /* Get the old counters. */
1104 get_counters(oldinfo, counters);
1105 /* Decrease module usage counts and free resource */
31836064
ED
1106 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1107 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
2e4e6a17 1108 xt_free_table_info(oldinfo);
1da177e4 1109 if (copy_to_user(tmp.counters, counters,
2e4e6a17 1110 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1da177e4
LT
1111 ret = -EFAULT;
1112 vfree(counters);
2e4e6a17 1113 xt_table_unlock(t);
1da177e4
LT
1114 return ret;
1115
1116 put_module:
1117 module_put(t->me);
2e4e6a17 1118 xt_table_unlock(t);
1da177e4 1119 free_newinfo_counters_untrans:
31836064 1120 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1da177e4
LT
1121 free_newinfo_counters:
1122 vfree(counters);
1123 free_newinfo:
2e4e6a17 1124 xt_free_table_info(newinfo);
1da177e4
LT
1125 return ret;
1126}
1127
1128/* We're lazy, and add to the first CPU; overflow works its fey magic
1129 * and everything is OK. */
1130static inline int
1131add_counter_to_entry(struct ip6t_entry *e,
2e4e6a17 1132 const struct xt_counters addme[],
1da177e4
LT
1133 unsigned int *i)
1134{
1135#if 0
1136 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1137 *i,
1138 (long unsigned int)e->counters.pcnt,
1139 (long unsigned int)e->counters.bcnt,
1140 (long unsigned int)addme[*i].pcnt,
1141 (long unsigned int)addme[*i].bcnt);
1142#endif
1143
1144 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1145
1146 (*i)++;
1147 return 0;
1148}
1149
1150static int
1151do_add_counters(void __user *user, unsigned int len)
1152{
1153 unsigned int i;
2e4e6a17
HW
1154 struct xt_counters_info tmp, *paddc;
1155 struct xt_table_info *private;
1156 struct xt_table *t;
6b7d31fc 1157 int ret = 0;
31836064 1158 void *loc_cpu_entry;
1da177e4
LT
1159
1160 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1161 return -EFAULT;
1162
2e4e6a17 1163 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1da177e4
LT
1164 return -EINVAL;
1165
1166 paddc = vmalloc(len);
1167 if (!paddc)
1168 return -ENOMEM;
1169
1170 if (copy_from_user(paddc, user, len) != 0) {
1171 ret = -EFAULT;
1172 goto free;
1173 }
1174
2e4e6a17 1175 t = xt_find_table_lock(AF_INET6, tmp.name);
6b7d31fc
HW
1176 if (!t || IS_ERR(t)) {
1177 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1178 goto free;
6b7d31fc 1179 }
1da177e4
LT
1180
1181 write_lock_bh(&t->lock);
2e4e6a17 1182 private = t->private;
2c8ac66b 1183 if (private->number != tmp.num_counters) {
1da177e4
LT
1184 ret = -EINVAL;
1185 goto unlock_up_free;
1186 }
1187
1188 i = 0;
31836064 1189 /* Choose the copy that is on our node */
2e4e6a17 1190 loc_cpu_entry = private->entries[smp_processor_id()];
31836064 1191 IP6T_ENTRY_ITERATE(loc_cpu_entry,
2e4e6a17 1192 private->size,
1da177e4
LT
1193 add_counter_to_entry,
1194 paddc->counters,
1195 &i);
1196 unlock_up_free:
1197 write_unlock_bh(&t->lock);
2e4e6a17 1198 xt_table_unlock(t);
6b7d31fc 1199 module_put(t->me);
1da177e4
LT
1200 free:
1201 vfree(paddc);
1202
1203 return ret;
1204}
1205
1206static int
1207do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1208{
1209 int ret;
1210
1211 if (!capable(CAP_NET_ADMIN))
1212 return -EPERM;
1213
1214 switch (cmd) {
1215 case IP6T_SO_SET_REPLACE:
1216 ret = do_replace(user, len);
1217 break;
1218
1219 case IP6T_SO_SET_ADD_COUNTERS:
1220 ret = do_add_counters(user, len);
1221 break;
1222
1223 default:
1224 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1225 ret = -EINVAL;
1226 }
1227
1228 return ret;
1229}
1230
1231static int
1232do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1233{
1234 int ret;
1235
1236 if (!capable(CAP_NET_ADMIN))
1237 return -EPERM;
1238
1239 switch (cmd) {
1240 case IP6T_SO_GET_INFO: {
1241 char name[IP6T_TABLE_MAXNAMELEN];
2e4e6a17 1242 struct xt_table *t;
1da177e4
LT
1243
1244 if (*len != sizeof(struct ip6t_getinfo)) {
1245 duprintf("length %u != %u\n", *len,
1246 sizeof(struct ip6t_getinfo));
1247 ret = -EINVAL;
1248 break;
1249 }
1250
1251 if (copy_from_user(name, user, sizeof(name)) != 0) {
1252 ret = -EFAULT;
1253 break;
1254 }
1255 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
6b7d31fc 1256
2e4e6a17 1257 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
6b7d31fc
HW
1258 "ip6table_%s", name);
1259 if (t && !IS_ERR(t)) {
1da177e4 1260 struct ip6t_getinfo info;
2e4e6a17 1261 struct xt_table_info *private = t->private;
1da177e4
LT
1262
1263 info.valid_hooks = t->valid_hooks;
2e4e6a17 1264 memcpy(info.hook_entry, private->hook_entry,
1da177e4 1265 sizeof(info.hook_entry));
2e4e6a17 1266 memcpy(info.underflow, private->underflow,
1da177e4 1267 sizeof(info.underflow));
2e4e6a17
HW
1268 info.num_entries = private->number;
1269 info.size = private->size;
1da177e4
LT
1270 memcpy(info.name, name, sizeof(info.name));
1271
1272 if (copy_to_user(user, &info, *len) != 0)
1273 ret = -EFAULT;
1274 else
1275 ret = 0;
2e4e6a17 1276 xt_table_unlock(t);
6b7d31fc
HW
1277 module_put(t->me);
1278 } else
1279 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1280 }
1281 break;
1282
1283 case IP6T_SO_GET_ENTRIES: {
1284 struct ip6t_get_entries get;
1285
1286 if (*len < sizeof(get)) {
1287 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1288 ret = -EINVAL;
1289 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1290 ret = -EFAULT;
1291 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1292 duprintf("get_entries: %u != %u\n", *len,
1293 sizeof(struct ip6t_get_entries) + get.size);
1294 ret = -EINVAL;
1295 } else
1296 ret = get_entries(&get, user);
1297 break;
1298 }
1299
6b7d31fc
HW
1300 case IP6T_SO_GET_REVISION_MATCH:
1301 case IP6T_SO_GET_REVISION_TARGET: {
1302 struct ip6t_get_revision rev;
2e4e6a17 1303 int target;
6b7d31fc
HW
1304
1305 if (*len != sizeof(rev)) {
1306 ret = -EINVAL;
1307 break;
1308 }
1309 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1310 ret = -EFAULT;
1311 break;
1312 }
1313
1314 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 1315 target = 1;
6b7d31fc 1316 else
2e4e6a17 1317 target = 0;
6b7d31fc 1318
2e4e6a17
HW
1319 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1320 rev.revision,
1321 target, &ret),
6b7d31fc
HW
1322 "ip6t_%s", rev.name);
1323 break;
1324 }
1325
1da177e4
LT
1326 default:
1327 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1328 ret = -EINVAL;
1329 }
1330
1331 return ret;
1332}
1333
2e4e6a17 1334int ip6t_register_table(struct xt_table *table,
1da177e4
LT
1335 const struct ip6t_replace *repl)
1336{
1337 int ret;
2e4e6a17 1338 struct xt_table_info *newinfo;
259d4e41 1339 struct xt_table_info bootstrap
1da177e4 1340 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 1341 void *loc_cpu_entry;
1da177e4 1342
2e4e6a17 1343 newinfo = xt_alloc_table_info(repl->size);
1da177e4
LT
1344 if (!newinfo)
1345 return -ENOMEM;
1346
31836064
ED
1347 /* choose the copy on our node/cpu */
1348 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1349 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4
LT
1350
1351 ret = translate_table(table->name, table->valid_hooks,
31836064 1352 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
1353 repl->num_entries,
1354 repl->hook_entry,
1355 repl->underflow);
1356 if (ret != 0) {
2e4e6a17 1357 xt_free_table_info(newinfo);
1da177e4
LT
1358 return ret;
1359 }
1360
da298d3a
PM
1361 ret = xt_register_table(table, &bootstrap, newinfo);
1362 if (ret != 0) {
2e4e6a17 1363 xt_free_table_info(newinfo);
1da177e4
LT
1364 return ret;
1365 }
1366
2e4e6a17 1367 return 0;
1da177e4
LT
1368}
1369
2e4e6a17 1370void ip6t_unregister_table(struct xt_table *table)
1da177e4 1371{
2e4e6a17 1372 struct xt_table_info *private;
31836064
ED
1373 void *loc_cpu_entry;
1374
2e4e6a17 1375 private = xt_unregister_table(table);
1da177e4
LT
1376
1377 /* Decrease module usage counts and free resources */
2e4e6a17
HW
1378 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1379 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1380 xt_free_table_info(private);
1da177e4
LT
1381}
1382
1383/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 1384static inline bool
1da177e4
LT
1385icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1386 u_int8_t type, u_int8_t code,
ccb79bdc 1387 bool invert)
1da177e4
LT
1388{
1389 return (type == test_type && code >= min_code && code <= max_code)
1390 ^ invert;
1391}
1392
1d93a9cb 1393static bool
1da177e4
LT
1394icmp6_match(const struct sk_buff *skb,
1395 const struct net_device *in,
1396 const struct net_device *out,
c4986734 1397 const struct xt_match *match,
1da177e4
LT
1398 const void *matchinfo,
1399 int offset,
1400 unsigned int protoff,
cff533ac 1401 bool *hotdrop)
1da177e4
LT
1402{
1403 struct icmp6hdr _icmp, *ic;
1404 const struct ip6t_icmp *icmpinfo = matchinfo;
1405
1406 /* Must not be a fragment. */
1407 if (offset)
1d93a9cb 1408 return false;
1da177e4
LT
1409
1410 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1411 if (ic == NULL) {
1412 /* We've been asked to examine this packet, and we
1413 can't. Hence, no choice but to drop. */
1414 duprintf("Dropping evil ICMP tinygram.\n");
cff533ac 1415 *hotdrop = true;
1d93a9cb 1416 return false;
1da177e4
LT
1417 }
1418
1419 return icmp6_type_code_match(icmpinfo->type,
1420 icmpinfo->code[0],
1421 icmpinfo->code[1],
1422 ic->icmp6_type, ic->icmp6_code,
1423 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1424}
1425
1426/* Called when user tries to insert an entry of this type. */
ccb79bdc 1427static bool
1da177e4 1428icmp6_checkentry(const char *tablename,
2e4e6a17 1429 const void *entry,
c4986734 1430 const struct xt_match *match,
1da177e4 1431 void *matchinfo,
1da177e4
LT
1432 unsigned int hook_mask)
1433{
1434 const struct ip6t_icmp *icmpinfo = matchinfo;
1435
7f939713
PM
1436 /* Must specify no unknown invflags */
1437 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1da177e4
LT
1438}
1439
1440/* The built-in targets: standard (NULL) and error. */
9f15c530 1441static struct xt_target ip6t_standard_target __read_mostly = {
1da177e4 1442 .name = IP6T_STANDARD_TARGET,
7f939713 1443 .targetsize = sizeof(int),
a45049c5 1444 .family = AF_INET6,
1da177e4
LT
1445};
1446
9f15c530 1447static struct xt_target ip6t_error_target __read_mostly = {
1da177e4
LT
1448 .name = IP6T_ERROR_TARGET,
1449 .target = ip6t_error,
7f939713 1450 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
a45049c5 1451 .family = AF_INET6,
1da177e4
LT
1452};
1453
1454static struct nf_sockopt_ops ip6t_sockopts = {
1455 .pf = PF_INET6,
1456 .set_optmin = IP6T_BASE_CTL,
1457 .set_optmax = IP6T_SO_SET_MAX+1,
1458 .set = do_ip6t_set_ctl,
1459 .get_optmin = IP6T_BASE_CTL,
1460 .get_optmax = IP6T_SO_GET_MAX+1,
1461 .get = do_ip6t_get_ctl,
16fcec35 1462 .owner = THIS_MODULE,
1da177e4
LT
1463};
1464
9f15c530 1465static struct xt_match icmp6_matchstruct __read_mostly = {
1da177e4
LT
1466 .name = "icmp6",
1467 .match = &icmp6_match,
7f939713
PM
1468 .matchsize = sizeof(struct ip6t_icmp),
1469 .checkentry = icmp6_checkentry,
1470 .proto = IPPROTO_ICMPV6,
a45049c5 1471 .family = AF_INET6,
1da177e4
LT
1472};
1473
65b4b4e8 1474static int __init ip6_tables_init(void)
1da177e4
LT
1475{
1476 int ret;
1477
0eff66e6
PM
1478 ret = xt_proto_init(AF_INET6);
1479 if (ret < 0)
1480 goto err1;
2e4e6a17 1481
1da177e4 1482 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
1483 ret = xt_register_target(&ip6t_standard_target);
1484 if (ret < 0)
1485 goto err2;
1486 ret = xt_register_target(&ip6t_error_target);
1487 if (ret < 0)
1488 goto err3;
1489 ret = xt_register_match(&icmp6_matchstruct);
1490 if (ret < 0)
1491 goto err4;
1da177e4
LT
1492
1493 /* Register setsockopt */
1494 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
1495 if (ret < 0)
1496 goto err5;
1da177e4 1497
a887c1c1 1498 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 1499 return 0;
0eff66e6
PM
1500
1501err5:
1502 xt_unregister_match(&icmp6_matchstruct);
1503err4:
1504 xt_unregister_target(&ip6t_error_target);
1505err3:
1506 xt_unregister_target(&ip6t_standard_target);
1507err2:
1508 xt_proto_fini(AF_INET6);
1509err1:
1510 return ret;
1da177e4
LT
1511}
1512
65b4b4e8 1513static void __exit ip6_tables_fini(void)
1da177e4
LT
1514{
1515 nf_unregister_sockopt(&ip6t_sockopts);
a45049c5
PNA
1516 xt_unregister_match(&icmp6_matchstruct);
1517 xt_unregister_target(&ip6t_error_target);
1518 xt_unregister_target(&ip6t_standard_target);
2e4e6a17 1519 xt_proto_fini(AF_INET6);
1da177e4
LT
1520}
1521
e674d0f3 1522/*
b777e0ce
PM
1523 * find the offset to specified header or the protocol number of last header
1524 * if target < 0. "last header" is transport protocol header, ESP, or
1525 * "No next header".
1526 *
1527 * If target header is found, its offset is set in *offset and return protocol
1528 * number. Otherwise, return -1.
1529 *
6d381634
PM
1530 * If the first fragment doesn't contain the final protocol header or
1531 * NEXTHDR_NONE it is considered invalid.
1532 *
b777e0ce
PM
1533 * Note that non-1st fragment is special case that "the protocol number
1534 * of last header" is "next header" field in Fragment header. In this case,
1535 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1536 * isn't NULL.
e674d0f3 1537 *
e674d0f3 1538 */
b777e0ce
PM
1539int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1540 int target, unsigned short *fragoff)
e674d0f3 1541{
6b88dd96 1542 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0660e03f 1543 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
e674d0f3
YK
1544 unsigned int len = skb->len - start;
1545
b777e0ce
PM
1546 if (fragoff)
1547 *fragoff = 0;
1548
e674d0f3
YK
1549 while (nexthdr != target) {
1550 struct ipv6_opt_hdr _hdr, *hp;
1551 unsigned int hdrlen;
1552
b777e0ce
PM
1553 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1554 if (target < 0)
1555 break;
6d381634 1556 return -ENOENT;
b777e0ce
PM
1557 }
1558
e674d0f3
YK
1559 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1560 if (hp == NULL)
6d381634 1561 return -EBADMSG;
e674d0f3 1562 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
1563 unsigned short _frag_off;
1564 __be16 *fp;
e674d0f3
YK
1565 fp = skb_header_pointer(skb,
1566 start+offsetof(struct frag_hdr,
1567 frag_off),
1568 sizeof(_frag_off),
1569 &_frag_off);
1570 if (fp == NULL)
6d381634 1571 return -EBADMSG;
e674d0f3 1572
b777e0ce
PM
1573 _frag_off = ntohs(*fp) & ~0x7;
1574 if (_frag_off) {
1575 if (target < 0 &&
1576 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 1577 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
1578 if (fragoff)
1579 *fragoff = _frag_off;
1580 return hp->nexthdr;
1581 }
6d381634 1582 return -ENOENT;
b777e0ce 1583 }
e674d0f3
YK
1584 hdrlen = 8;
1585 } else if (nexthdr == NEXTHDR_AUTH)
1ab1457c 1586 hdrlen = (hp->hdrlen + 2) << 2;
e674d0f3 1587 else
1ab1457c 1588 hdrlen = ipv6_optlen(hp);
e674d0f3
YK
1589
1590 nexthdr = hp->nexthdr;
1591 len -= hdrlen;
1592 start += hdrlen;
1593 }
1594
1595 *offset = start;
b777e0ce 1596 return nexthdr;
e674d0f3
YK
1597}
1598
1da177e4
LT
1599EXPORT_SYMBOL(ip6t_register_table);
1600EXPORT_SYMBOL(ip6t_unregister_table);
1601EXPORT_SYMBOL(ip6t_do_table);
1da177e4 1602EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 1603EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 1604
65b4b4e8
AM
1605module_init(ip6_tables_init);
1606module_exit(ip6_tables_fini);
This page took 0.423625 seconds and 5 git commands to generate.