[NETFILTER]: Supress some sparse warnings
[linux-2.6-block.git] / net / ipv6 / netfilter / ip6_tables.c
CommitLineData
1da177e4
LT
1/*
2 * Packet matching code.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
6b7d31fc 5 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
1da177e4
LT
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
1da177e4 10 */
4fc268d2
RD
11
12#include <linux/capability.h>
14c85021 13#include <linux/in.h>
1da177e4
LT
14#include <linux/skbuff.h>
15#include <linux/kmod.h>
16#include <linux/vmalloc.h>
17#include <linux/netdevice.h>
18#include <linux/module.h>
4bdbf6c0 19#include <linux/poison.h>
1da177e4 20#include <linux/icmpv6.h>
1da177e4 21#include <net/ipv6.h>
3bc3fe5e 22#include <net/compat.h>
1da177e4 23#include <asm/uaccess.h>
57b47a53 24#include <linux/mutex.h>
1da177e4 25#include <linux/proc_fs.h>
3bc3fe5e 26#include <linux/err.h>
c8923c6b 27#include <linux/cpumask.h>
1da177e4
LT
28
29#include <linux/netfilter_ipv6/ip6_tables.h>
2e4e6a17 30#include <linux/netfilter/x_tables.h>
f01ffbd6 31#include <net/netfilter/nf_log.h>
1da177e4
LT
32
33MODULE_LICENSE("GPL");
34MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
35MODULE_DESCRIPTION("IPv6 packet filter");
36
1da177e4
LT
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 79/* Check for an extension */
1ab1457c 80int
1da177e4
LT
81ip6t_ext_hdr(u8 nexthdr)
82{
1ab1457c
YH
83 return ( (nexthdr == IPPROTO_HOPOPTS) ||
84 (nexthdr == IPPROTO_ROUTING) ||
85 (nexthdr == IPPROTO_FRAGMENT) ||
86 (nexthdr == IPPROTO_ESP) ||
87 (nexthdr == IPPROTO_AH) ||
88 (nexthdr == IPPROTO_NONE) ||
89 (nexthdr == IPPROTO_DSTOPTS) );
1da177e4
LT
90}
91
92/* Returns whether matches rule or not. */
022748a9 93/* Performance critical - called for every packet */
1d93a9cb 94static inline bool
1da177e4
LT
95ip6_packet_match(const struct sk_buff *skb,
96 const char *indev,
97 const char *outdev,
98 const struct ip6t_ip6 *ip6info,
99 unsigned int *protoff,
cff533ac 100 int *fragoff, bool *hotdrop)
1da177e4
LT
101{
102 size_t i;
103 unsigned long ret;
0660e03f 104 const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
1da177e4 105
e79ec50b 106#define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
1da177e4 107
f2ffd9ee 108 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
1ab1457c 109 &ip6info->src), IP6T_INV_SRCIP)
f2ffd9ee 110 || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
1ab1457c 111 &ip6info->dst), IP6T_INV_DSTIP)) {
1da177e4
LT
112 dprintf("Source or dest mismatch.\n");
113/*
114 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
115 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
116 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
117 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
118 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
119 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
1d93a9cb 120 return false;
1da177e4
LT
121 }
122
123 /* Look for ifname matches; this should unroll nicely. */
124 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
125 ret |= (((const unsigned long *)indev)[i]
126 ^ ((const unsigned long *)ip6info->iniface)[i])
127 & ((const unsigned long *)ip6info->iniface_mask)[i];
128 }
129
130 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
131 dprintf("VIA in mismatch (%s vs %s).%s\n",
132 indev, ip6info->iniface,
133 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
1d93a9cb 134 return false;
1da177e4
LT
135 }
136
137 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
138 ret |= (((const unsigned long *)outdev)[i]
139 ^ ((const unsigned long *)ip6info->outiface)[i])
140 & ((const unsigned long *)ip6info->outiface_mask)[i];
141 }
142
143 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
144 dprintf("VIA out mismatch (%s vs %s).%s\n",
145 outdev, ip6info->outiface,
146 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
1d93a9cb 147 return false;
1da177e4
LT
148 }
149
150/* ... might want to do something with class and flowlabel here ... */
151
152 /* look for the desired protocol header */
153 if((ip6info->flags & IP6T_F_PROTO)) {
b777e0ce
PM
154 int protohdr;
155 unsigned short _frag_off;
1da177e4 156
b777e0ce 157 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
51d8b1a6
PM
158 if (protohdr < 0) {
159 if (_frag_off == 0)
cff533ac 160 *hotdrop = true;
1d93a9cb 161 return false;
51d8b1a6 162 }
b777e0ce 163 *fragoff = _frag_off;
1da177e4
LT
164
165 dprintf("Packet protocol %hi ?= %s%hi.\n",
1ab1457c 166 protohdr,
1da177e4
LT
167 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
168 ip6info->proto);
169
b777e0ce 170 if (ip6info->proto == protohdr) {
1da177e4 171 if(ip6info->invflags & IP6T_INV_PROTO) {
1d93a9cb 172 return false;
1da177e4 173 }
1d93a9cb 174 return true;
1da177e4
LT
175 }
176
177 /* We need match for the '-p all', too! */
178 if ((ip6info->proto != 0) &&
179 !(ip6info->invflags & IP6T_INV_PROTO))
1d93a9cb 180 return false;
1da177e4 181 }
1d93a9cb 182 return true;
1da177e4
LT
183}
184
185/* should be ip6 safe */
022748a9 186static bool
1da177e4
LT
187ip6_checkentry(const struct ip6t_ip6 *ipv6)
188{
189 if (ipv6->flags & ~IP6T_F_MASK) {
190 duprintf("Unknown flag bits set: %08X\n",
191 ipv6->flags & ~IP6T_F_MASK);
ccb79bdc 192 return false;
1da177e4
LT
193 }
194 if (ipv6->invflags & ~IP6T_INV_MASK) {
195 duprintf("Unknown invflag bits set: %08X\n",
196 ipv6->invflags & ~IP6T_INV_MASK);
ccb79bdc 197 return false;
1da177e4 198 }
ccb79bdc 199 return true;
1da177e4
LT
200}
201
202static unsigned int
3db05fea 203ip6t_error(struct sk_buff *skb,
1da177e4
LT
204 const struct net_device *in,
205 const struct net_device *out,
206 unsigned int hooknum,
c4986734 207 const struct xt_target *target,
fe1cb108 208 const void *targinfo)
1da177e4
LT
209{
210 if (net_ratelimit())
211 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
212
213 return NF_DROP;
214}
215
022748a9
DV
216/* Performance critical - called for every packet */
217static inline bool
218do_match(struct ip6t_entry_match *m,
1d93a9cb
JE
219 const struct sk_buff *skb,
220 const struct net_device *in,
221 const struct net_device *out,
222 int offset,
223 unsigned int protoff,
224 bool *hotdrop)
1da177e4
LT
225{
226 /* Stop iteration if it doesn't match */
1c524830 227 if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
1da177e4 228 offset, protoff, hotdrop))
1d93a9cb 229 return true;
1da177e4 230 else
1d93a9cb 231 return false;
1da177e4
LT
232}
233
234static inline struct ip6t_entry *
235get_entry(void *base, unsigned int offset)
236{
237 return (struct ip6t_entry *)(base + offset);
238}
239
ba9dda3a 240/* All zeroes == unconditional rule. */
022748a9 241/* Mildly perf critical (only if packet tracing is on) */
ba9dda3a
JK
242static inline int
243unconditional(const struct ip6t_ip6 *ipv6)
244{
245 unsigned int i;
246
247 for (i = 0; i < sizeof(*ipv6); i++)
248 if (((char *)ipv6)[i])
249 break;
250
251 return (i == sizeof(*ipv6));
252}
253
254#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
255 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
256/* This cries for unification! */
022748a9 257static const char *const hooknames[] = {
6e23ae2a
PM
258 [NF_INET_PRE_ROUTING] = "PREROUTING",
259 [NF_INET_LOCAL_IN] = "INPUT",
260 [NF_INET_FORWARD] = "FORWARD",
261 [NF_INET_LOCAL_OUT] = "OUTPUT",
262 [NF_INET_POST_ROUTING] = "POSTROUTING",
ba9dda3a
JK
263};
264
265enum nf_ip_trace_comments {
266 NF_IP6_TRACE_COMMENT_RULE,
267 NF_IP6_TRACE_COMMENT_RETURN,
268 NF_IP6_TRACE_COMMENT_POLICY,
269};
270
022748a9 271static const char *const comments[] = {
ba9dda3a
JK
272 [NF_IP6_TRACE_COMMENT_RULE] = "rule",
273 [NF_IP6_TRACE_COMMENT_RETURN] = "return",
274 [NF_IP6_TRACE_COMMENT_POLICY] = "policy",
275};
276
277static struct nf_loginfo trace_loginfo = {
278 .type = NF_LOG_TYPE_LOG,
279 .u = {
280 .log = {
281 .level = 4,
282 .logflags = NF_LOG_MASK,
283 },
284 },
285};
286
022748a9 287/* Mildly perf critical (only if packet tracing is on) */
ba9dda3a
JK
288static inline int
289get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
290 char *hookname, char **chainname,
291 char **comment, unsigned int *rulenum)
292{
293 struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
294
295 if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
296 /* Head of user chain: ERROR target with chainname */
297 *chainname = t->target.data;
298 (*rulenum) = 0;
299 } else if (s == e) {
300 (*rulenum)++;
301
302 if (s->target_offset == sizeof(struct ip6t_entry)
303 && strcmp(t->target.u.kernel.target->name,
304 IP6T_STANDARD_TARGET) == 0
305 && t->verdict < 0
306 && unconditional(&s->ipv6)) {
307 /* Tail of chains: STANDARD target (return/policy) */
308 *comment = *chainname == hookname
309 ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
310 : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
311 }
312 return 1;
313 } else
314 (*rulenum)++;
315
316 return 0;
317}
318
319static void trace_packet(struct sk_buff *skb,
320 unsigned int hook,
321 const struct net_device *in,
322 const struct net_device *out,
323 char *tablename,
324 struct xt_table_info *private,
325 struct ip6t_entry *e)
326{
327 void *table_base;
328 struct ip6t_entry *root;
329 char *hookname, *chainname, *comment;
330 unsigned int rulenum = 0;
331
332 table_base = (void *)private->entries[smp_processor_id()];
333 root = get_entry(table_base, private->hook_entry[hook]);
334
335 hookname = chainname = (char *)hooknames[hook];
336 comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
337
338 IP6T_ENTRY_ITERATE(root,
339 private->size - private->hook_entry[hook],
340 get_chainname_rulenum,
341 e, hookname, &chainname, &comment, &rulenum);
342
343 nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
344 "TRACE: %s:%s:%s:%u ",
345 tablename, chainname, comment, rulenum);
346}
347#endif
348
1da177e4
LT
349/* Returns one of the generic firewall policies, like NF_ACCEPT. */
350unsigned int
3db05fea 351ip6t_do_table(struct sk_buff *skb,
1da177e4
LT
352 unsigned int hook,
353 const struct net_device *in,
354 const struct net_device *out,
fe1cb108 355 struct xt_table *table)
1da177e4 356{
6b7d31fc 357 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
1da177e4
LT
358 int offset = 0;
359 unsigned int protoff = 0;
cff533ac 360 bool hotdrop = false;
1da177e4
LT
361 /* Initializing verdict to NF_DROP keeps gcc happy. */
362 unsigned int verdict = NF_DROP;
363 const char *indev, *outdev;
364 void *table_base;
365 struct ip6t_entry *e, *back;
2e4e6a17 366 struct xt_table_info *private;
1da177e4
LT
367
368 /* Initialization */
369 indev = in ? in->name : nulldevname;
370 outdev = out ? out->name : nulldevname;
1da177e4
LT
371 /* We handle fragments by dealing with the first fragment as
372 * if it was a normal packet. All other fragments are treated
373 * normally, except that they will NEVER match rules that ask
374 * things we don't know, ie. tcp syn flag or ports). If the
375 * rule is also a fragment-specific rule, non-fragments won't
376 * match it. */
377
378 read_lock_bh(&table->lock);
379 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
9c547959 380 private = table->private;
2e4e6a17
HW
381 table_base = (void *)private->entries[smp_processor_id()];
382 e = get_entry(table_base, private->hook_entry[hook]);
1da177e4 383
1da177e4 384 /* For return from builtin chain */
2e4e6a17 385 back = get_entry(table_base, private->underflow[hook]);
1da177e4
LT
386
387 do {
388 IP_NF_ASSERT(e);
389 IP_NF_ASSERT(back);
3db05fea 390 if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
51d8b1a6 391 &protoff, &offset, &hotdrop)) {
1da177e4
LT
392 struct ip6t_entry_target *t;
393
394 if (IP6T_MATCH_ITERATE(e, do_match,
3db05fea 395 skb, in, out,
1da177e4
LT
396 offset, protoff, &hotdrop) != 0)
397 goto no_match;
398
399 ADD_COUNTER(e->counters,
72f36ec1
PM
400 ntohs(ipv6_hdr(skb)->payload_len) +
401 sizeof(struct ipv6hdr), 1);
1da177e4
LT
402
403 t = ip6t_get_target(e);
404 IP_NF_ASSERT(t->u.kernel.target);
ba9dda3a
JK
405
406#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
407 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
408 /* The packet is traced: log it */
3db05fea
HX
409 if (unlikely(skb->nf_trace))
410 trace_packet(skb, hook, in, out,
ba9dda3a
JK
411 table->name, private, e);
412#endif
1da177e4
LT
413 /* Standard target? */
414 if (!t->u.kernel.target->target) {
415 int v;
416
417 v = ((struct ip6t_standard_target *)t)->verdict;
418 if (v < 0) {
419 /* Pop from stack? */
420 if (v != IP6T_RETURN) {
421 verdict = (unsigned)(-v) - 1;
422 break;
423 }
424 e = back;
425 back = get_entry(table_base,
426 back->comefrom);
427 continue;
428 }
05465343
PM
429 if (table_base + v != (void *)e + e->next_offset
430 && !(e->ipv6.flags & IP6T_F_GOTO)) {
1da177e4
LT
431 /* Save old back ptr in next entry */
432 struct ip6t_entry *next
433 = (void *)e + e->next_offset;
434 next->comefrom
435 = (void *)back - table_base;
436 /* set back pointer to next entry */
437 back = next;
438 }
439
440 e = get_entry(table_base, v);
441 } else {
442 /* Targets which reenter must return
1ab1457c 443 abs. verdicts */
1da177e4
LT
444#ifdef CONFIG_NETFILTER_DEBUG
445 ((struct ip6t_entry *)table_base)->comefrom
446 = 0xeeeeeeec;
447#endif
3db05fea 448 verdict = t->u.kernel.target->target(skb,
1da177e4
LT
449 in, out,
450 hook,
1c524830 451 t->u.kernel.target,
fe1cb108 452 t->data);
1da177e4
LT
453
454#ifdef CONFIG_NETFILTER_DEBUG
455 if (((struct ip6t_entry *)table_base)->comefrom
456 != 0xeeeeeeec
457 && verdict == IP6T_CONTINUE) {
458 printk("Target %s reentered!\n",
459 t->u.kernel.target->name);
460 verdict = NF_DROP;
461 }
462 ((struct ip6t_entry *)table_base)->comefrom
463 = 0x57acc001;
464#endif
465 if (verdict == IP6T_CONTINUE)
466 e = (void *)e + e->next_offset;
467 else
468 /* Verdict */
469 break;
470 }
471 } else {
472
473 no_match:
474 e = (void *)e + e->next_offset;
475 }
476 } while (!hotdrop);
477
478#ifdef CONFIG_NETFILTER_DEBUG
4bdbf6c0 479 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
1da177e4
LT
480#endif
481 read_unlock_bh(&table->lock);
482
483#ifdef DEBUG_ALLOW_ALL
484 return NF_ACCEPT;
485#else
486 if (hotdrop)
487 return NF_DROP;
488 else return verdict;
489#endif
490}
491
1da177e4
LT
492/* Figures out from what hook each rule can be called: returns 0 if
493 there are loops. Puts hook bitmask in comefrom. */
494static int
2e4e6a17 495mark_source_chains(struct xt_table_info *newinfo,
31836064 496 unsigned int valid_hooks, void *entry0)
1da177e4
LT
497{
498 unsigned int hook;
499
500 /* No recursion; use packet counter to save back ptrs (reset
501 to 0 as we leave), and comefrom to save source hook bitmask */
6e23ae2a 502 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
1da177e4 503 unsigned int pos = newinfo->hook_entry[hook];
9c547959 504 struct ip6t_entry *e = (struct ip6t_entry *)(entry0 + pos);
1da177e4
LT
505
506 if (!(valid_hooks & (1 << hook)))
507 continue;
508
509 /* Set initial back pointer. */
510 e->counters.pcnt = pos;
511
512 for (;;) {
513 struct ip6t_standard_target *t
514 = (void *)ip6t_get_target(e);
9c547959 515 int visited = e->comefrom & (1 << hook);
1da177e4 516
6e23ae2a 517 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
518 printk("iptables: loop hook %u pos %u %08X.\n",
519 hook, pos, e->comefrom);
520 return 0;
521 }
9c547959 522 e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
1da177e4
LT
523
524 /* Unconditional return/END. */
e1b4b9f3 525 if ((e->target_offset == sizeof(struct ip6t_entry)
1da177e4
LT
526 && (strcmp(t->target.u.user.name,
527 IP6T_STANDARD_TARGET) == 0)
528 && t->verdict < 0
e1b4b9f3 529 && unconditional(&e->ipv6)) || visited) {
1da177e4
LT
530 unsigned int oldpos, size;
531
74c9c0c1
DM
532 if (t->verdict < -NF_MAX_VERDICT - 1) {
533 duprintf("mark_source_chains: bad "
534 "negative verdict (%i)\n",
535 t->verdict);
536 return 0;
537 }
538
1da177e4
LT
539 /* Return: backtrack through the last
540 big jump. */
541 do {
6e23ae2a 542 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
1da177e4
LT
543#ifdef DEBUG_IP_FIREWALL_USER
544 if (e->comefrom
6e23ae2a 545 & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
546 duprintf("Back unset "
547 "on hook %u "
548 "rule %u\n",
549 hook, pos);
550 }
551#endif
552 oldpos = pos;
553 pos = e->counters.pcnt;
554 e->counters.pcnt = 0;
555
556 /* We're at the start. */
557 if (pos == oldpos)
558 goto next;
559
560 e = (struct ip6t_entry *)
31836064 561 (entry0 + pos);
1da177e4
LT
562 } while (oldpos == pos + e->next_offset);
563
564 /* Move along one */
565 size = e->next_offset;
566 e = (struct ip6t_entry *)
31836064 567 (entry0 + pos + size);
1da177e4
LT
568 e->counters.pcnt = pos;
569 pos += size;
570 } else {
571 int newpos = t->verdict;
572
573 if (strcmp(t->target.u.user.name,
574 IP6T_STANDARD_TARGET) == 0
575 && newpos >= 0) {
74c9c0c1
DM
576 if (newpos > newinfo->size -
577 sizeof(struct ip6t_entry)) {
578 duprintf("mark_source_chains: "
579 "bad verdict (%i)\n",
580 newpos);
581 return 0;
582 }
1da177e4
LT
583 /* This a jump; chase it. */
584 duprintf("Jump rule %u -> %u\n",
585 pos, newpos);
586 } else {
587 /* ... this is a fallthru */
588 newpos = pos + e->next_offset;
589 }
590 e = (struct ip6t_entry *)
31836064 591 (entry0 + newpos);
1da177e4
LT
592 e->counters.pcnt = pos;
593 pos = newpos;
594 }
595 }
596 next:
597 duprintf("Finished chain %u\n", hook);
598 }
599 return 1;
600}
601
022748a9 602static int
1da177e4
LT
603cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
604{
605 if (i && (*i)-- == 0)
606 return 1;
607
608 if (m->u.kernel.match->destroy)
efa74165 609 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
1da177e4
LT
610 module_put(m->u.kernel.match->me);
611 return 0;
612}
613
022748a9 614static int
f173c8a1
PM
615check_entry(struct ip6t_entry *e, const char *name)
616{
617 struct ip6t_entry_target *t;
618
619 if (!ip6_checkentry(&e->ipv6)) {
620 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
621 return -EINVAL;
622 }
623
624 if (e->target_offset + sizeof(struct ip6t_entry_target) >
625 e->next_offset)
626 return -EINVAL;
627
628 t = ip6t_get_target(e);
629 if (e->target_offset + t->u.target_size > e->next_offset)
630 return -EINVAL;
631
632 return 0;
633}
634
022748a9 635static int check_match(struct ip6t_entry_match *m, const char *name,
f173c8a1
PM
636 const struct ip6t_ip6 *ipv6,
637 unsigned int hookmask, unsigned int *i)
638{
639 struct xt_match *match;
640 int ret;
641
642 match = m->u.kernel.match;
643 ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
644 name, hookmask, ipv6->proto,
645 ipv6->invflags & IP6T_INV_PROTO);
646 if (!ret && m->u.kernel.match->checkentry
647 && !m->u.kernel.match->checkentry(name, ipv6, match, m->data,
648 hookmask)) {
649 duprintf("ip_tables: check failed for `%s'.\n",
650 m->u.kernel.match->name);
651 ret = -EINVAL;
652 }
653 if (!ret)
654 (*i)++;
655 return ret;
656}
657
022748a9 658static int
f173c8a1
PM
659find_check_match(struct ip6t_entry_match *m,
660 const char *name,
661 const struct ip6t_ip6 *ipv6,
662 unsigned int hookmask,
663 unsigned int *i)
1da177e4 664{
6709dbbb 665 struct xt_match *match;
3cdc7c95 666 int ret;
1da177e4 667
2e4e6a17 668 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
9c547959 669 m->u.user.revision),
6b7d31fc
HW
670 "ip6t_%s", m->u.user.name);
671 if (IS_ERR(match) || !match) {
f173c8a1 672 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
6b7d31fc 673 return match ? PTR_ERR(match) : -ENOENT;
1da177e4
LT
674 }
675 m->u.kernel.match = match;
1da177e4 676
f173c8a1 677 ret = check_match(m, name, ipv6, hookmask, i);
3cdc7c95
PM
678 if (ret)
679 goto err;
680
1da177e4 681 return 0;
3cdc7c95
PM
682err:
683 module_put(m->u.kernel.match->me);
684 return ret;
1da177e4
LT
685}
686
022748a9 687static int check_target(struct ip6t_entry *e, const char *name)
1da177e4
LT
688{
689 struct ip6t_entry_target *t;
6709dbbb 690 struct xt_target *target;
1da177e4 691 int ret;
1da177e4 692
f173c8a1
PM
693 t = ip6t_get_target(e);
694 target = t->u.kernel.target;
695 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
696 name, e->comefrom, e->ipv6.proto,
697 e->ipv6.invflags & IP6T_INV_PROTO);
698 if (!ret && t->u.kernel.target->checkentry
699 && !t->u.kernel.target->checkentry(name, e, target, t->data,
700 e->comefrom)) {
701 duprintf("ip_tables: check failed for `%s'.\n",
702 t->u.kernel.target->name);
703 ret = -EINVAL;
1da177e4 704 }
f173c8a1
PM
705 return ret;
706}
1da177e4 707
022748a9 708static int
f173c8a1
PM
709find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
710 unsigned int *i)
711{
712 struct ip6t_entry_target *t;
713 struct xt_target *target;
714 int ret;
715 unsigned int j;
716
717 ret = check_entry(e, name);
718 if (ret)
719 return ret;
590bdf7f 720
1da177e4 721 j = 0;
f173c8a1
PM
722 ret = IP6T_MATCH_ITERATE(e, find_check_match, name, &e->ipv6,
723 e->comefrom, &j);
1da177e4
LT
724 if (ret != 0)
725 goto cleanup_matches;
726
727 t = ip6t_get_target(e);
2e4e6a17
HW
728 target = try_then_request_module(xt_find_target(AF_INET6,
729 t->u.user.name,
730 t->u.user.revision),
6b7d31fc
HW
731 "ip6t_%s", t->u.user.name);
732 if (IS_ERR(target) || !target) {
f173c8a1 733 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
6b7d31fc 734 ret = target ? PTR_ERR(target) : -ENOENT;
1da177e4
LT
735 goto cleanup_matches;
736 }
737 t->u.kernel.target = target;
6b7d31fc 738
f173c8a1 739 ret = check_target(e, name);
3cdc7c95
PM
740 if (ret)
741 goto err;
742
1da177e4
LT
743 (*i)++;
744 return 0;
3cdc7c95
PM
745 err:
746 module_put(t->u.kernel.target->me);
1da177e4
LT
747 cleanup_matches:
748 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
749 return ret;
750}
751
022748a9 752static int
1da177e4 753check_entry_size_and_hooks(struct ip6t_entry *e,
2e4e6a17 754 struct xt_table_info *newinfo,
1da177e4
LT
755 unsigned char *base,
756 unsigned char *limit,
757 const unsigned int *hook_entries,
758 const unsigned int *underflows,
759 unsigned int *i)
760{
761 unsigned int h;
762
763 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
764 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
765 duprintf("Bad offset %p\n", e);
766 return -EINVAL;
767 }
768
769 if (e->next_offset
770 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
771 duprintf("checking: element %p size %u\n",
772 e, e->next_offset);
773 return -EINVAL;
774 }
775
776 /* Check hooks & underflows */
6e23ae2a 777 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1da177e4
LT
778 if ((unsigned char *)e - base == hook_entries[h])
779 newinfo->hook_entry[h] = hook_entries[h];
780 if ((unsigned char *)e - base == underflows[h])
781 newinfo->underflow[h] = underflows[h];
782 }
783
784 /* FIXME: underflows must be unconditional, standard verdicts
1ab1457c 785 < 0 (not IP6T_RETURN). --RR */
1da177e4
LT
786
787 /* Clear counters and comefrom */
2e4e6a17 788 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4
LT
789 e->comefrom = 0;
790
791 (*i)++;
792 return 0;
793}
794
022748a9 795static int
1da177e4
LT
796cleanup_entry(struct ip6t_entry *e, unsigned int *i)
797{
798 struct ip6t_entry_target *t;
799
800 if (i && (*i)-- == 0)
801 return 1;
802
803 /* Cleanup all matches */
804 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
805 t = ip6t_get_target(e);
806 if (t->u.kernel.target->destroy)
efa74165 807 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
1da177e4
LT
808 module_put(t->u.kernel.target->me);
809 return 0;
810}
811
812/* Checks and translates the user-supplied table segment (held in
813 newinfo) */
814static int
815translate_table(const char *name,
816 unsigned int valid_hooks,
2e4e6a17 817 struct xt_table_info *newinfo,
31836064 818 void *entry0,
1da177e4
LT
819 unsigned int size,
820 unsigned int number,
821 const unsigned int *hook_entries,
822 const unsigned int *underflows)
823{
824 unsigned int i;
825 int ret;
826
827 newinfo->size = size;
828 newinfo->number = number;
829
830 /* Init all hooks to impossible value. */
6e23ae2a 831 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
832 newinfo->hook_entry[i] = 0xFFFFFFFF;
833 newinfo->underflow[i] = 0xFFFFFFFF;
834 }
835
836 duprintf("translate_table: size %u\n", newinfo->size);
837 i = 0;
838 /* Walk through entries, checking offsets. */
31836064 839 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
840 check_entry_size_and_hooks,
841 newinfo,
31836064
ED
842 entry0,
843 entry0 + size,
1da177e4
LT
844 hook_entries, underflows, &i);
845 if (ret != 0)
846 return ret;
847
848 if (i != number) {
849 duprintf("translate_table: %u not %u entries\n",
850 i, number);
851 return -EINVAL;
852 }
853
854 /* Check hooks all assigned */
6e23ae2a 855 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
856 /* Only hooks which are valid */
857 if (!(valid_hooks & (1 << i)))
858 continue;
859 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
860 duprintf("Invalid hook entry %u %u\n",
861 i, hook_entries[i]);
862 return -EINVAL;
863 }
864 if (newinfo->underflow[i] == 0xFFFFFFFF) {
865 duprintf("Invalid underflow %u %u\n",
866 i, underflows[i]);
867 return -EINVAL;
868 }
869 }
870
74c9c0c1
DM
871 if (!mark_source_chains(newinfo, valid_hooks, entry0))
872 return -ELOOP;
873
1da177e4
LT
874 /* Finally, each sanity check must pass */
875 i = 0;
31836064 876 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
f173c8a1 877 find_check_entry, name, size, &i);
1da177e4 878
74c9c0c1
DM
879 if (ret != 0) {
880 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
881 cleanup_entry, &i);
882 return ret;
883 }
1da177e4
LT
884
885 /* And one copy for every other CPU */
6f912042 886 for_each_possible_cpu(i) {
31836064
ED
887 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
888 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
889 }
890
9c547959 891 return ret;
1da177e4
LT
892}
893
1da177e4
LT
894/* Gets counters. */
895static inline int
896add_entry_to_counter(const struct ip6t_entry *e,
2e4e6a17 897 struct xt_counters total[],
1da177e4
LT
898 unsigned int *i)
899{
900 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
901
902 (*i)++;
903 return 0;
904}
905
31836064
ED
906static inline int
907set_entry_to_counter(const struct ip6t_entry *e,
908 struct ip6t_counters total[],
909 unsigned int *i)
910{
911 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
912
913 (*i)++;
914 return 0;
915}
916
1da177e4 917static void
2e4e6a17
HW
918get_counters(const struct xt_table_info *t,
919 struct xt_counters counters[])
1da177e4
LT
920{
921 unsigned int cpu;
922 unsigned int i;
31836064
ED
923 unsigned int curcpu;
924
925 /* Instead of clearing (by a previous call to memset())
926 * the counters and using adds, we set the counters
927 * with data used by 'current' CPU
928 * We dont care about preemption here.
929 */
930 curcpu = raw_smp_processor_id();
931
932 i = 0;
933 IP6T_ENTRY_ITERATE(t->entries[curcpu],
934 t->size,
935 set_entry_to_counter,
936 counters,
937 &i);
1da177e4 938
6f912042 939 for_each_possible_cpu(cpu) {
31836064
ED
940 if (cpu == curcpu)
941 continue;
1da177e4 942 i = 0;
31836064 943 IP6T_ENTRY_ITERATE(t->entries[cpu],
1da177e4
LT
944 t->size,
945 add_entry_to_counter,
946 counters,
947 &i);
948 }
949}
950
022748a9 951static struct xt_counters *alloc_counters(struct xt_table *table)
1da177e4 952{
ed1a6f5e 953 unsigned int countersize;
2e4e6a17
HW
954 struct xt_counters *counters;
955 struct xt_table_info *private = table->private;
1da177e4
LT
956
957 /* We need atomic snapshot of counters: rest doesn't change
958 (other than comefrom, which userspace doesn't care
959 about). */
2e4e6a17 960 countersize = sizeof(struct xt_counters) * private->number;
3b84e92b 961 counters = vmalloc_node(countersize, numa_node_id());
1da177e4
LT
962
963 if (counters == NULL)
ed1a6f5e 964 return ERR_PTR(-ENOMEM);
1da177e4
LT
965
966 /* First, sum counters... */
1da177e4 967 write_lock_bh(&table->lock);
2e4e6a17 968 get_counters(private, counters);
1da177e4
LT
969 write_unlock_bh(&table->lock);
970
ed1a6f5e
PM
971 return counters;
972}
973
974static int
975copy_entries_to_user(unsigned int total_size,
976 struct xt_table *table,
977 void __user *userptr)
978{
979 unsigned int off, num;
980 struct ip6t_entry *e;
981 struct xt_counters *counters;
982 struct xt_table_info *private = table->private;
983 int ret = 0;
984 void *loc_cpu_entry;
985
986 counters = alloc_counters(table);
987 if (IS_ERR(counters))
988 return PTR_ERR(counters);
989
9c547959
PM
990 /* choose the copy that is on our node/cpu, ...
991 * This choice is lazy (because current thread is
992 * allowed to migrate to another cpu)
993 */
2e4e6a17 994 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 995 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
996 ret = -EFAULT;
997 goto free_counters;
998 }
999
1000 /* FIXME: use iterator macros --RR */
1001 /* ... then go back and fix counters and names */
1002 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1003 unsigned int i;
1004 struct ip6t_entry_match *m;
1005 struct ip6t_entry_target *t;
1006
31836064 1007 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
1008 if (copy_to_user(userptr + off
1009 + offsetof(struct ip6t_entry, counters),
1010 &counters[num],
1011 sizeof(counters[num])) != 0) {
1012 ret = -EFAULT;
1013 goto free_counters;
1014 }
1015
1016 for (i = sizeof(struct ip6t_entry);
1017 i < e->target_offset;
1018 i += m->u.match_size) {
1019 m = (void *)e + i;
1020
1021 if (copy_to_user(userptr + off + i
1022 + offsetof(struct ip6t_entry_match,
1023 u.user.name),
1024 m->u.kernel.match->name,
1025 strlen(m->u.kernel.match->name)+1)
1026 != 0) {
1027 ret = -EFAULT;
1028 goto free_counters;
1029 }
1030 }
1031
1032 t = ip6t_get_target(e);
1033 if (copy_to_user(userptr + off + e->target_offset
1034 + offsetof(struct ip6t_entry_target,
1035 u.user.name),
1036 t->u.kernel.target->name,
1037 strlen(t->u.kernel.target->name)+1) != 0) {
1038 ret = -EFAULT;
1039 goto free_counters;
1040 }
1041 }
1042
1043 free_counters:
1044 vfree(counters);
1045 return ret;
1046}
1047
3bc3fe5e
PM
1048#ifdef CONFIG_COMPAT
1049static void compat_standard_from_user(void *dst, void *src)
1050{
1051 int v = *(compat_int_t *)src;
1052
1053 if (v > 0)
1054 v += xt_compat_calc_jump(AF_INET6, v);
1055 memcpy(dst, &v, sizeof(v));
1056}
1057
1058static int compat_standard_to_user(void __user *dst, void *src)
1059{
1060 compat_int_t cv = *(int *)src;
1061
1062 if (cv > 0)
1063 cv -= xt_compat_calc_jump(AF_INET6, cv);
1064 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1065}
1066
1067static inline int
1068compat_calc_match(struct ip6t_entry_match *m, int *size)
1069{
1070 *size += xt_compat_match_offset(m->u.kernel.match);
1071 return 0;
1072}
1073
1074static int compat_calc_entry(struct ip6t_entry *e,
1075 const struct xt_table_info *info,
1076 void *base, struct xt_table_info *newinfo)
1077{
1078 struct ip6t_entry_target *t;
1079 unsigned int entry_offset;
1080 int off, i, ret;
1081
1082 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1083 entry_offset = (void *)e - base;
1084 IP6T_MATCH_ITERATE(e, compat_calc_match, &off);
1085 t = ip6t_get_target(e);
1086 off += xt_compat_target_offset(t->u.kernel.target);
1087 newinfo->size -= off;
1088 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1089 if (ret)
1090 return ret;
1091
1092 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1093 if (info->hook_entry[i] &&
1094 (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1095 newinfo->hook_entry[i] -= off;
1096 if (info->underflow[i] &&
1097 (e < (struct ip6t_entry *)(base + info->underflow[i])))
1098 newinfo->underflow[i] -= off;
1099 }
1100 return 0;
1101}
1102
1103static int compat_table_info(const struct xt_table_info *info,
1104 struct xt_table_info *newinfo)
1105{
1106 void *loc_cpu_entry;
1107
1108 if (!newinfo || !info)
1109 return -EINVAL;
1110
1111 /* we dont care about newinfo->entries[] */
1112 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1113 newinfo->initial_entries = 0;
1114 loc_cpu_entry = info->entries[raw_smp_processor_id()];
1115 return IP6T_ENTRY_ITERATE(loc_cpu_entry, info->size,
1116 compat_calc_entry, info, loc_cpu_entry,
1117 newinfo);
1118}
1119#endif
1120
1121static int get_info(void __user *user, int *len, int compat)
433665c9
PM
1122{
1123 char name[IP6T_TABLE_MAXNAMELEN];
1124 struct xt_table *t;
1125 int ret;
1126
1127 if (*len != sizeof(struct ip6t_getinfo)) {
c9d8fe13 1128 duprintf("length %u != %zu\n", *len,
433665c9
PM
1129 sizeof(struct ip6t_getinfo));
1130 return -EINVAL;
1131 }
1132
1133 if (copy_from_user(name, user, sizeof(name)) != 0)
1134 return -EFAULT;
1135
1136 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
3bc3fe5e
PM
1137#ifdef CONFIG_COMPAT
1138 if (compat)
1139 xt_compat_lock(AF_INET6);
1140#endif
433665c9
PM
1141 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1142 "ip6table_%s", name);
1143 if (t && !IS_ERR(t)) {
1144 struct ip6t_getinfo info;
1145 struct xt_table_info *private = t->private;
1146
3bc3fe5e
PM
1147#ifdef CONFIG_COMPAT
1148 if (compat) {
1149 struct xt_table_info tmp;
1150 ret = compat_table_info(private, &tmp);
1151 xt_compat_flush_offsets(AF_INET6);
1152 private = &tmp;
1153 }
1154#endif
433665c9
PM
1155 info.valid_hooks = t->valid_hooks;
1156 memcpy(info.hook_entry, private->hook_entry,
1157 sizeof(info.hook_entry));
1158 memcpy(info.underflow, private->underflow,
1159 sizeof(info.underflow));
1160 info.num_entries = private->number;
1161 info.size = private->size;
b5dd674b 1162 strcpy(info.name, name);
433665c9
PM
1163
1164 if (copy_to_user(user, &info, *len) != 0)
1165 ret = -EFAULT;
1166 else
1167 ret = 0;
1168
1169 xt_table_unlock(t);
1170 module_put(t->me);
1171 } else
1172 ret = t ? PTR_ERR(t) : -ENOENT;
3bc3fe5e
PM
1173#ifdef CONFIG_COMPAT
1174 if (compat)
1175 xt_compat_unlock(AF_INET6);
1176#endif
433665c9
PM
1177 return ret;
1178}
1179
1da177e4 1180static int
d924357c 1181get_entries(struct ip6t_get_entries __user *uptr, int *len)
1da177e4
LT
1182{
1183 int ret;
d924357c 1184 struct ip6t_get_entries get;
2e4e6a17 1185 struct xt_table *t;
1da177e4 1186
d924357c 1187 if (*len < sizeof(get)) {
c9d8fe13 1188 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
d924357c
PM
1189 return -EINVAL;
1190 }
1191 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1192 return -EFAULT;
1193 if (*len != sizeof(struct ip6t_get_entries) + get.size) {
c9d8fe13
PM
1194 duprintf("get_entries: %u != %zu\n",
1195 *len, sizeof(get) + get.size);
d924357c
PM
1196 return -EINVAL;
1197 }
1198
1199 t = xt_find_table_lock(AF_INET6, get.name);
6b7d31fc 1200 if (t && !IS_ERR(t)) {
2e4e6a17
HW
1201 struct xt_table_info *private = t->private;
1202 duprintf("t->private->number = %u\n", private->number);
d924357c 1203 if (get.size == private->size)
2e4e6a17 1204 ret = copy_entries_to_user(private->size,
1da177e4
LT
1205 t, uptr->entrytable);
1206 else {
1207 duprintf("get_entries: I've got %u not %u!\n",
9c547959 1208 private->size, get.size);
1da177e4
LT
1209 ret = -EINVAL;
1210 }
6b7d31fc 1211 module_put(t->me);
2e4e6a17 1212 xt_table_unlock(t);
1da177e4 1213 } else
6b7d31fc 1214 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1215
1216 return ret;
1217}
1218
1219static int
3bc3fe5e
PM
1220__do_replace(const char *name, unsigned int valid_hooks,
1221 struct xt_table_info *newinfo, unsigned int num_counters,
1222 void __user *counters_ptr)
1da177e4
LT
1223{
1224 int ret;
2e4e6a17 1225 struct xt_table *t;
3bc3fe5e 1226 struct xt_table_info *oldinfo;
2e4e6a17 1227 struct xt_counters *counters;
3bc3fe5e 1228 void *loc_cpu_old_entry;
1da177e4 1229
3bc3fe5e
PM
1230 ret = 0;
1231 counters = vmalloc_node(num_counters * sizeof(struct xt_counters),
3b84e92b 1232 numa_node_id());
1da177e4
LT
1233 if (!counters) {
1234 ret = -ENOMEM;
3bc3fe5e 1235 goto out;
1da177e4 1236 }
1da177e4 1237
3bc3fe5e
PM
1238 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1239 "ip6table_%s", name);
6b7d31fc
HW
1240 if (!t || IS_ERR(t)) {
1241 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1242 goto free_newinfo_counters_untrans;
6b7d31fc 1243 }
1da177e4
LT
1244
1245 /* You lied! */
3bc3fe5e 1246 if (valid_hooks != t->valid_hooks) {
1da177e4 1247 duprintf("Valid hook crap: %08X vs %08X\n",
3bc3fe5e 1248 valid_hooks, t->valid_hooks);
1da177e4 1249 ret = -EINVAL;
6b7d31fc 1250 goto put_module;
1da177e4
LT
1251 }
1252
3bc3fe5e 1253 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1da177e4
LT
1254 if (!oldinfo)
1255 goto put_module;
1256
1257 /* Update module usage count based on number of rules */
1258 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1259 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1260 if ((oldinfo->number > oldinfo->initial_entries) ||
1261 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1262 module_put(t->me);
1263 if ((oldinfo->number > oldinfo->initial_entries) &&
1264 (newinfo->number <= oldinfo->initial_entries))
1265 module_put(t->me);
1266
1267 /* Get the old counters. */
1268 get_counters(oldinfo, counters);
1269 /* Decrease module usage counts and free resource */
31836064 1270 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
3bc3fe5e
PM
1271 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
1272 NULL);
2e4e6a17 1273 xt_free_table_info(oldinfo);
3bc3fe5e
PM
1274 if (copy_to_user(counters_ptr, counters,
1275 sizeof(struct xt_counters) * num_counters) != 0)
1da177e4
LT
1276 ret = -EFAULT;
1277 vfree(counters);
2e4e6a17 1278 xt_table_unlock(t);
1da177e4
LT
1279 return ret;
1280
1281 put_module:
1282 module_put(t->me);
2e4e6a17 1283 xt_table_unlock(t);
1da177e4 1284 free_newinfo_counters_untrans:
1da177e4 1285 vfree(counters);
3bc3fe5e
PM
1286 out:
1287 return ret;
1288}
1289
1290static int
1291do_replace(void __user *user, unsigned int len)
1292{
1293 int ret;
1294 struct ip6t_replace tmp;
1295 struct xt_table_info *newinfo;
1296 void *loc_cpu_entry;
1297
1298 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1299 return -EFAULT;
1300
1301 /* overflow check */
1302 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1303 return -ENOMEM;
1304
1305 newinfo = xt_alloc_table_info(tmp.size);
1306 if (!newinfo)
1307 return -ENOMEM;
1308
1309 /* choose the copy that is on our node/cpu */
1310 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1311 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1312 tmp.size) != 0) {
1313 ret = -EFAULT;
1314 goto free_newinfo;
1315 }
1316
1317 ret = translate_table(tmp.name, tmp.valid_hooks,
1318 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1319 tmp.hook_entry, tmp.underflow);
1320 if (ret != 0)
1321 goto free_newinfo;
1322
1323 duprintf("ip_tables: Translated table\n");
1324
1325 ret = __do_replace(tmp.name, tmp.valid_hooks, newinfo,
1326 tmp.num_counters, tmp.counters);
1327 if (ret)
1328 goto free_newinfo_untrans;
1329 return 0;
1330
1331 free_newinfo_untrans:
1332 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
1da177e4 1333 free_newinfo:
2e4e6a17 1334 xt_free_table_info(newinfo);
1da177e4
LT
1335 return ret;
1336}
1337
1338/* We're lazy, and add to the first CPU; overflow works its fey magic
1339 * and everything is OK. */
1340static inline int
1341add_counter_to_entry(struct ip6t_entry *e,
2e4e6a17 1342 const struct xt_counters addme[],
1da177e4
LT
1343 unsigned int *i)
1344{
1345#if 0
1346 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1347 *i,
1348 (long unsigned int)e->counters.pcnt,
1349 (long unsigned int)e->counters.bcnt,
1350 (long unsigned int)addme[*i].pcnt,
1351 (long unsigned int)addme[*i].bcnt);
1352#endif
1353
1354 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1355
1356 (*i)++;
1357 return 0;
1358}
1359
1360static int
3bc3fe5e 1361do_add_counters(void __user *user, unsigned int len, int compat)
1da177e4
LT
1362{
1363 unsigned int i;
3bc3fe5e
PM
1364 struct xt_counters_info tmp;
1365 struct xt_counters *paddc;
1366 unsigned int num_counters;
1367 char *name;
1368 int size;
1369 void *ptmp;
2e4e6a17 1370 struct xt_table *t;
9c547959 1371 struct xt_table_info *private;
6b7d31fc 1372 int ret = 0;
31836064 1373 void *loc_cpu_entry;
3bc3fe5e
PM
1374#ifdef CONFIG_COMPAT
1375 struct compat_xt_counters_info compat_tmp;
1da177e4 1376
3bc3fe5e
PM
1377 if (compat) {
1378 ptmp = &compat_tmp;
1379 size = sizeof(struct compat_xt_counters_info);
1380 } else
1381#endif
1382 {
1383 ptmp = &tmp;
1384 size = sizeof(struct xt_counters_info);
1385 }
1386
1387 if (copy_from_user(ptmp, user, size) != 0)
1da177e4
LT
1388 return -EFAULT;
1389
3bc3fe5e
PM
1390#ifdef CONFIG_COMPAT
1391 if (compat) {
1392 num_counters = compat_tmp.num_counters;
1393 name = compat_tmp.name;
1394 } else
1395#endif
1396 {
1397 num_counters = tmp.num_counters;
1398 name = tmp.name;
1399 }
1400
1401 if (len != size + num_counters * sizeof(struct xt_counters))
1da177e4
LT
1402 return -EINVAL;
1403
3bc3fe5e 1404 paddc = vmalloc_node(len - size, numa_node_id());
1da177e4
LT
1405 if (!paddc)
1406 return -ENOMEM;
1407
3bc3fe5e 1408 if (copy_from_user(paddc, user + size, len - size) != 0) {
1da177e4
LT
1409 ret = -EFAULT;
1410 goto free;
1411 }
1412
3bc3fe5e 1413 t = xt_find_table_lock(AF_INET6, name);
6b7d31fc
HW
1414 if (!t || IS_ERR(t)) {
1415 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1416 goto free;
6b7d31fc 1417 }
1da177e4
LT
1418
1419 write_lock_bh(&t->lock);
2e4e6a17 1420 private = t->private;
3bc3fe5e 1421 if (private->number != num_counters) {
1da177e4
LT
1422 ret = -EINVAL;
1423 goto unlock_up_free;
1424 }
1425
1426 i = 0;
31836064 1427 /* Choose the copy that is on our node */
da4d0f6b 1428 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 1429 IP6T_ENTRY_ITERATE(loc_cpu_entry,
2e4e6a17 1430 private->size,
1da177e4 1431 add_counter_to_entry,
3bc3fe5e 1432 paddc,
1da177e4
LT
1433 &i);
1434 unlock_up_free:
1435 write_unlock_bh(&t->lock);
2e4e6a17 1436 xt_table_unlock(t);
6b7d31fc 1437 module_put(t->me);
1da177e4
LT
1438 free:
1439 vfree(paddc);
1440
1441 return ret;
1442}
1443
3bc3fe5e
PM
1444#ifdef CONFIG_COMPAT
1445struct compat_ip6t_replace {
1446 char name[IP6T_TABLE_MAXNAMELEN];
1447 u32 valid_hooks;
1448 u32 num_entries;
1449 u32 size;
1450 u32 hook_entry[NF_INET_NUMHOOKS];
1451 u32 underflow[NF_INET_NUMHOOKS];
1452 u32 num_counters;
1453 compat_uptr_t counters; /* struct ip6t_counters * */
1454 struct compat_ip6t_entry entries[0];
1455};
1456
1457static int
1458compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
1459 compat_uint_t *size, struct xt_counters *counters,
1460 unsigned int *i)
1461{
1462 struct ip6t_entry_target *t;
1463 struct compat_ip6t_entry __user *ce;
1464 u_int16_t target_offset, next_offset;
1465 compat_uint_t origsize;
1466 int ret;
1467
1468 ret = -EFAULT;
1469 origsize = *size;
1470 ce = (struct compat_ip6t_entry __user *)*dstptr;
1471 if (copy_to_user(ce, e, sizeof(struct ip6t_entry)))
1472 goto out;
1473
1474 if (copy_to_user(&ce->counters, &counters[*i], sizeof(counters[*i])))
1475 goto out;
1476
1477 *dstptr += sizeof(struct compat_ip6t_entry);
1478 *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1479
1480 ret = IP6T_MATCH_ITERATE(e, xt_compat_match_to_user, dstptr, size);
1481 target_offset = e->target_offset - (origsize - *size);
1482 if (ret)
1483 goto out;
1484 t = ip6t_get_target(e);
1485 ret = xt_compat_target_to_user(t, dstptr, size);
1486 if (ret)
1487 goto out;
1488 ret = -EFAULT;
1489 next_offset = e->next_offset - (origsize - *size);
1490 if (put_user(target_offset, &ce->target_offset))
1491 goto out;
1492 if (put_user(next_offset, &ce->next_offset))
1493 goto out;
1494
1495 (*i)++;
1496 return 0;
1497out:
1498 return ret;
1499}
1500
022748a9 1501static int
3bc3fe5e
PM
1502compat_find_calc_match(struct ip6t_entry_match *m,
1503 const char *name,
1504 const struct ip6t_ip6 *ipv6,
1505 unsigned int hookmask,
1506 int *size, int *i)
1507{
1508 struct xt_match *match;
1509
1510 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
1511 m->u.user.revision),
1512 "ip6t_%s", m->u.user.name);
1513 if (IS_ERR(match) || !match) {
1514 duprintf("compat_check_calc_match: `%s' not found\n",
1515 m->u.user.name);
1516 return match ? PTR_ERR(match) : -ENOENT;
1517 }
1518 m->u.kernel.match = match;
1519 *size += xt_compat_match_offset(match);
1520
1521 (*i)++;
1522 return 0;
1523}
1524
022748a9 1525static int
3bc3fe5e
PM
1526compat_release_match(struct ip6t_entry_match *m, unsigned int *i)
1527{
1528 if (i && (*i)-- == 0)
1529 return 1;
1530
1531 module_put(m->u.kernel.match->me);
1532 return 0;
1533}
1534
022748a9 1535static int
3bc3fe5e
PM
1536compat_release_entry(struct compat_ip6t_entry *e, unsigned int *i)
1537{
1538 struct ip6t_entry_target *t;
1539
1540 if (i && (*i)-- == 0)
1541 return 1;
1542
1543 /* Cleanup all matches */
1544 COMPAT_IP6T_MATCH_ITERATE(e, compat_release_match, NULL);
1545 t = compat_ip6t_get_target(e);
1546 module_put(t->u.kernel.target->me);
1547 return 0;
1548}
1549
022748a9 1550static int
3bc3fe5e
PM
1551check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1552 struct xt_table_info *newinfo,
1553 unsigned int *size,
1554 unsigned char *base,
1555 unsigned char *limit,
1556 unsigned int *hook_entries,
1557 unsigned int *underflows,
1558 unsigned int *i,
1559 const char *name)
1560{
1561 struct ip6t_entry_target *t;
1562 struct xt_target *target;
1563 unsigned int entry_offset;
1564 int ret, off, h, j;
1565
1566 duprintf("check_compat_entry_size_and_hooks %p\n", e);
1567 if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0
1568 || (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
1569 duprintf("Bad offset %p, limit = %p\n", e, limit);
1570 return -EINVAL;
1571 }
1572
1573 if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1574 sizeof(struct compat_xt_entry_target)) {
1575 duprintf("checking: element %p size %u\n",
1576 e, e->next_offset);
1577 return -EINVAL;
1578 }
1579
1580 /* For purposes of check_entry casting the compat entry is fine */
1581 ret = check_entry((struct ip6t_entry *)e, name);
1582 if (ret)
1583 return ret;
1584
1585 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1586 entry_offset = (void *)e - (void *)base;
1587 j = 0;
1588 ret = COMPAT_IP6T_MATCH_ITERATE(e, compat_find_calc_match, name,
1589 &e->ipv6, e->comefrom, &off, &j);
1590 if (ret != 0)
1591 goto release_matches;
1592
1593 t = compat_ip6t_get_target(e);
1594 target = try_then_request_module(xt_find_target(AF_INET6,
1595 t->u.user.name,
1596 t->u.user.revision),
1597 "ip6t_%s", t->u.user.name);
1598 if (IS_ERR(target) || !target) {
1599 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1600 t->u.user.name);
1601 ret = target ? PTR_ERR(target) : -ENOENT;
1602 goto release_matches;
1603 }
1604 t->u.kernel.target = target;
1605
1606 off += xt_compat_target_offset(target);
1607 *size += off;
1608 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1609 if (ret)
1610 goto out;
1611
1612 /* Check hooks & underflows */
1613 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1614 if ((unsigned char *)e - base == hook_entries[h])
1615 newinfo->hook_entry[h] = hook_entries[h];
1616 if ((unsigned char *)e - base == underflows[h])
1617 newinfo->underflow[h] = underflows[h];
1618 }
1619
1620 /* Clear counters and comefrom */
1621 memset(&e->counters, 0, sizeof(e->counters));
1622 e->comefrom = 0;
1623
1624 (*i)++;
1625 return 0;
1626
1627out:
1628 module_put(t->u.kernel.target->me);
1629release_matches:
1630 IP6T_MATCH_ITERATE(e, compat_release_match, &j);
1631 return ret;
1632}
1633
1634static int
1635compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1636 unsigned int *size, const char *name,
1637 struct xt_table_info *newinfo, unsigned char *base)
1638{
1639 struct ip6t_entry_target *t;
1640 struct xt_target *target;
1641 struct ip6t_entry *de;
1642 unsigned int origsize;
1643 int ret, h;
1644
1645 ret = 0;
1646 origsize = *size;
1647 de = (struct ip6t_entry *)*dstptr;
1648 memcpy(de, e, sizeof(struct ip6t_entry));
1649 memcpy(&de->counters, &e->counters, sizeof(e->counters));
1650
1651 *dstptr += sizeof(struct ip6t_entry);
1652 *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1653
1654 ret = COMPAT_IP6T_MATCH_ITERATE(e, xt_compat_match_from_user,
1655 dstptr, size);
1656 if (ret)
1657 return ret;
1658 de->target_offset = e->target_offset - (origsize - *size);
1659 t = compat_ip6t_get_target(e);
1660 target = t->u.kernel.target;
1661 xt_compat_target_from_user(t, dstptr, size);
1662
1663 de->next_offset = e->next_offset - (origsize - *size);
1664 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1665 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1666 newinfo->hook_entry[h] -= origsize - *size;
1667 if ((unsigned char *)de - base < newinfo->underflow[h])
1668 newinfo->underflow[h] -= origsize - *size;
1669 }
1670 return ret;
1671}
1672
022748a9 1673static int compat_check_entry(struct ip6t_entry *e, const char *name,
3bc3fe5e
PM
1674 unsigned int *i)
1675{
1676 int j, ret;
1677
1678 j = 0;
1679 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6,
1680 e->comefrom, &j);
1681 if (ret)
1682 goto cleanup_matches;
1683
1684 ret = check_target(e, name);
1685 if (ret)
1686 goto cleanup_matches;
1687
1688 (*i)++;
1689 return 0;
1690
1691 cleanup_matches:
1692 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
1693 return ret;
1694}
1695
1696static int
1697translate_compat_table(const char *name,
1698 unsigned int valid_hooks,
1699 struct xt_table_info **pinfo,
1700 void **pentry0,
1701 unsigned int total_size,
1702 unsigned int number,
1703 unsigned int *hook_entries,
1704 unsigned int *underflows)
1705{
1706 unsigned int i, j;
1707 struct xt_table_info *newinfo, *info;
1708 void *pos, *entry0, *entry1;
1709 unsigned int size;
1710 int ret;
1711
1712 info = *pinfo;
1713 entry0 = *pentry0;
1714 size = total_size;
1715 info->number = number;
1716
1717 /* Init all hooks to impossible value. */
1718 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1719 info->hook_entry[i] = 0xFFFFFFFF;
1720 info->underflow[i] = 0xFFFFFFFF;
1721 }
1722
1723 duprintf("translate_compat_table: size %u\n", info->size);
1724 j = 0;
1725 xt_compat_lock(AF_INET6);
1726 /* Walk through entries, checking offsets. */
1727 ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1728 check_compat_entry_size_and_hooks,
1729 info, &size, entry0,
1730 entry0 + total_size,
1731 hook_entries, underflows, &j, name);
1732 if (ret != 0)
1733 goto out_unlock;
1734
1735 ret = -EINVAL;
1736 if (j != number) {
1737 duprintf("translate_compat_table: %u not %u entries\n",
1738 j, number);
1739 goto out_unlock;
1740 }
1741
1742 /* Check hooks all assigned */
1743 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1744 /* Only hooks which are valid */
1745 if (!(valid_hooks & (1 << i)))
1746 continue;
1747 if (info->hook_entry[i] == 0xFFFFFFFF) {
1748 duprintf("Invalid hook entry %u %u\n",
1749 i, hook_entries[i]);
1750 goto out_unlock;
1751 }
1752 if (info->underflow[i] == 0xFFFFFFFF) {
1753 duprintf("Invalid underflow %u %u\n",
1754 i, underflows[i]);
1755 goto out_unlock;
1756 }
1757 }
1758
1759 ret = -ENOMEM;
1760 newinfo = xt_alloc_table_info(size);
1761 if (!newinfo)
1762 goto out_unlock;
1763
1764 newinfo->number = number;
1765 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1766 newinfo->hook_entry[i] = info->hook_entry[i];
1767 newinfo->underflow[i] = info->underflow[i];
1768 }
1769 entry1 = newinfo->entries[raw_smp_processor_id()];
1770 pos = entry1;
1771 size = total_size;
1772 ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1773 compat_copy_entry_from_user,
1774 &pos, &size, name, newinfo, entry1);
1775 xt_compat_flush_offsets(AF_INET6);
1776 xt_compat_unlock(AF_INET6);
1777 if (ret)
1778 goto free_newinfo;
1779
1780 ret = -ELOOP;
1781 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1782 goto free_newinfo;
1783
1784 i = 0;
1785 ret = IP6T_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
1786 name, &i);
1787 if (ret) {
1788 j -= i;
1789 COMPAT_IP6T_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
1790 compat_release_entry, &j);
1791 IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, &i);
1792 xt_free_table_info(newinfo);
1793 return ret;
1794 }
1795
1796 /* And one copy for every other CPU */
1797 for_each_possible_cpu(i)
1798 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1799 memcpy(newinfo->entries[i], entry1, newinfo->size);
1800
1801 *pinfo = newinfo;
1802 *pentry0 = entry1;
1803 xt_free_table_info(info);
1804 return 0;
1805
1806free_newinfo:
1807 xt_free_table_info(newinfo);
1808out:
1809 COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size, compat_release_entry, &j);
1810 return ret;
1811out_unlock:
1812 xt_compat_flush_offsets(AF_INET6);
1813 xt_compat_unlock(AF_INET6);
1814 goto out;
1815}
1816
1817static int
1818compat_do_replace(void __user *user, unsigned int len)
1819{
1820 int ret;
1821 struct compat_ip6t_replace tmp;
1822 struct xt_table_info *newinfo;
1823 void *loc_cpu_entry;
1824
1825 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1826 return -EFAULT;
1827
1828 /* overflow check */
1829 if (tmp.size >= INT_MAX / num_possible_cpus())
1830 return -ENOMEM;
1831 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1832 return -ENOMEM;
1833
1834 newinfo = xt_alloc_table_info(tmp.size);
1835 if (!newinfo)
1836 return -ENOMEM;
1837
9c547959 1838 /* choose the copy that is on our node/cpu */
3bc3fe5e
PM
1839 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1840 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1841 tmp.size) != 0) {
1842 ret = -EFAULT;
1843 goto free_newinfo;
1844 }
1845
1846 ret = translate_compat_table(tmp.name, tmp.valid_hooks,
1847 &newinfo, &loc_cpu_entry, tmp.size,
1848 tmp.num_entries, tmp.hook_entry,
1849 tmp.underflow);
1850 if (ret != 0)
1851 goto free_newinfo;
1852
1853 duprintf("compat_do_replace: Translated table\n");
1854
1855 ret = __do_replace(tmp.name, tmp.valid_hooks, newinfo,
1856 tmp.num_counters, compat_ptr(tmp.counters));
1857 if (ret)
1858 goto free_newinfo_untrans;
1859 return 0;
1860
1861 free_newinfo_untrans:
1862 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
1863 free_newinfo:
1864 xt_free_table_info(newinfo);
1865 return ret;
1866}
1867
1868static int
1869compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1870 unsigned int len)
1871{
1872 int ret;
1873
1874 if (!capable(CAP_NET_ADMIN))
1875 return -EPERM;
1876
1877 switch (cmd) {
1878 case IP6T_SO_SET_REPLACE:
1879 ret = compat_do_replace(user, len);
1880 break;
1881
1882 case IP6T_SO_SET_ADD_COUNTERS:
1883 ret = do_add_counters(user, len, 1);
1884 break;
1885
1886 default:
1887 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1888 ret = -EINVAL;
1889 }
1890
1891 return ret;
1892}
1893
1894struct compat_ip6t_get_entries {
1895 char name[IP6T_TABLE_MAXNAMELEN];
1896 compat_uint_t size;
1897 struct compat_ip6t_entry entrytable[0];
1898};
1899
1900static int
1901compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1902 void __user *userptr)
1903{
1904 struct xt_counters *counters;
1905 struct xt_table_info *private = table->private;
1906 void __user *pos;
1907 unsigned int size;
1908 int ret = 0;
1909 void *loc_cpu_entry;
1910 unsigned int i = 0;
1911
1912 counters = alloc_counters(table);
1913 if (IS_ERR(counters))
1914 return PTR_ERR(counters);
1915
1916 /* choose the copy that is on our node/cpu, ...
1917 * This choice is lazy (because current thread is
1918 * allowed to migrate to another cpu)
1919 */
1920 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1921 pos = userptr;
1922 size = total_size;
1923 ret = IP6T_ENTRY_ITERATE(loc_cpu_entry, total_size,
1924 compat_copy_entry_to_user,
1925 &pos, &size, counters, &i);
1926
1927 vfree(counters);
1928 return ret;
1929}
1930
1931static int
1932compat_get_entries(struct compat_ip6t_get_entries __user *uptr, int *len)
1933{
1934 int ret;
1935 struct compat_ip6t_get_entries get;
1936 struct xt_table *t;
1937
1938 if (*len < sizeof(get)) {
c9d8fe13 1939 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
3bc3fe5e
PM
1940 return -EINVAL;
1941 }
1942
1943 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1944 return -EFAULT;
1945
1946 if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
c9d8fe13
PM
1947 duprintf("compat_get_entries: %u != %zu\n",
1948 *len, sizeof(get) + get.size);
3bc3fe5e
PM
1949 return -EINVAL;
1950 }
1951
1952 xt_compat_lock(AF_INET6);
1953 t = xt_find_table_lock(AF_INET6, get.name);
1954 if (t && !IS_ERR(t)) {
1955 struct xt_table_info *private = t->private;
1956 struct xt_table_info info;
9c547959 1957 duprintf("t->private->number = %u\n", private->number);
3bc3fe5e
PM
1958 ret = compat_table_info(private, &info);
1959 if (!ret && get.size == info.size) {
1960 ret = compat_copy_entries_to_user(private->size,
1961 t, uptr->entrytable);
1962 } else if (!ret) {
1963 duprintf("compat_get_entries: I've got %u not %u!\n",
9c547959 1964 private->size, get.size);
3bc3fe5e
PM
1965 ret = -EINVAL;
1966 }
1967 xt_compat_flush_offsets(AF_INET6);
1968 module_put(t->me);
1969 xt_table_unlock(t);
1970 } else
1971 ret = t ? PTR_ERR(t) : -ENOENT;
1972
1973 xt_compat_unlock(AF_INET6);
1974 return ret;
1975}
1976
1977static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1978
1979static int
1980compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1981{
1982 int ret;
1983
1984 if (!capable(CAP_NET_ADMIN))
1985 return -EPERM;
1986
1987 switch (cmd) {
1988 case IP6T_SO_GET_INFO:
1989 ret = get_info(user, len, 1);
1990 break;
1991 case IP6T_SO_GET_ENTRIES:
1992 ret = compat_get_entries(user, len);
1993 break;
1994 default:
1995 ret = do_ip6t_get_ctl(sk, cmd, user, len);
1996 }
1997 return ret;
1998}
1999#endif
2000
1da177e4
LT
2001static int
2002do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2003{
2004 int ret;
2005
2006 if (!capable(CAP_NET_ADMIN))
2007 return -EPERM;
2008
2009 switch (cmd) {
2010 case IP6T_SO_SET_REPLACE:
2011 ret = do_replace(user, len);
2012 break;
2013
2014 case IP6T_SO_SET_ADD_COUNTERS:
3bc3fe5e 2015 ret = do_add_counters(user, len, 0);
1da177e4
LT
2016 break;
2017
2018 default:
2019 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
2020 ret = -EINVAL;
2021 }
2022
2023 return ret;
2024}
2025
2026static int
2027do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2028{
2029 int ret;
2030
2031 if (!capable(CAP_NET_ADMIN))
2032 return -EPERM;
2033
2034 switch (cmd) {
433665c9 2035 case IP6T_SO_GET_INFO:
3bc3fe5e 2036 ret = get_info(user, len, 0);
433665c9 2037 break;
1da177e4 2038
d924357c
PM
2039 case IP6T_SO_GET_ENTRIES:
2040 ret = get_entries(user, len);
1da177e4 2041 break;
1da177e4 2042
6b7d31fc
HW
2043 case IP6T_SO_GET_REVISION_MATCH:
2044 case IP6T_SO_GET_REVISION_TARGET: {
2045 struct ip6t_get_revision rev;
2e4e6a17 2046 int target;
6b7d31fc
HW
2047
2048 if (*len != sizeof(rev)) {
2049 ret = -EINVAL;
2050 break;
2051 }
2052 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2053 ret = -EFAULT;
2054 break;
2055 }
2056
2057 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 2058 target = 1;
6b7d31fc 2059 else
2e4e6a17 2060 target = 0;
6b7d31fc 2061
2e4e6a17
HW
2062 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2063 rev.revision,
2064 target, &ret),
6b7d31fc
HW
2065 "ip6t_%s", rev.name);
2066 break;
2067 }
2068
1da177e4
LT
2069 default:
2070 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2071 ret = -EINVAL;
2072 }
2073
2074 return ret;
2075}
2076
9c547959 2077int ip6t_register_table(struct xt_table *table, const struct ip6t_replace *repl)
1da177e4
LT
2078{
2079 int ret;
2e4e6a17 2080 struct xt_table_info *newinfo;
259d4e41 2081 struct xt_table_info bootstrap
1da177e4 2082 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 2083 void *loc_cpu_entry;
1da177e4 2084
2e4e6a17 2085 newinfo = xt_alloc_table_info(repl->size);
1da177e4
LT
2086 if (!newinfo)
2087 return -ENOMEM;
2088
9c547959 2089 /* choose the copy on our node/cpu, but dont care about preemption */
31836064
ED
2090 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2091 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4
LT
2092
2093 ret = translate_table(table->name, table->valid_hooks,
31836064 2094 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
2095 repl->num_entries,
2096 repl->hook_entry,
2097 repl->underflow);
2098 if (ret != 0) {
2e4e6a17 2099 xt_free_table_info(newinfo);
1da177e4
LT
2100 return ret;
2101 }
2102
da298d3a
PM
2103 ret = xt_register_table(table, &bootstrap, newinfo);
2104 if (ret != 0) {
2e4e6a17 2105 xt_free_table_info(newinfo);
1da177e4
LT
2106 return ret;
2107 }
2108
2e4e6a17 2109 return 0;
1da177e4
LT
2110}
2111
2e4e6a17 2112void ip6t_unregister_table(struct xt_table *table)
1da177e4 2113{
2e4e6a17 2114 struct xt_table_info *private;
31836064
ED
2115 void *loc_cpu_entry;
2116
2e4e6a17 2117 private = xt_unregister_table(table);
1da177e4
LT
2118
2119 /* Decrease module usage counts and free resources */
2e4e6a17
HW
2120 loc_cpu_entry = private->entries[raw_smp_processor_id()];
2121 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
2122 xt_free_table_info(private);
1da177e4
LT
2123}
2124
2125/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 2126static inline bool
1da177e4
LT
2127icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2128 u_int8_t type, u_int8_t code,
ccb79bdc 2129 bool invert)
1da177e4
LT
2130{
2131 return (type == test_type && code >= min_code && code <= max_code)
2132 ^ invert;
2133}
2134
1d93a9cb 2135static bool
1da177e4
LT
2136icmp6_match(const struct sk_buff *skb,
2137 const struct net_device *in,
2138 const struct net_device *out,
c4986734 2139 const struct xt_match *match,
1da177e4
LT
2140 const void *matchinfo,
2141 int offset,
2142 unsigned int protoff,
cff533ac 2143 bool *hotdrop)
1da177e4 2144{
9c547959 2145 struct icmp6hdr _icmph, *ic;
1da177e4
LT
2146 const struct ip6t_icmp *icmpinfo = matchinfo;
2147
2148 /* Must not be a fragment. */
2149 if (offset)
1d93a9cb 2150 return false;
1da177e4 2151
9c547959 2152 ic = skb_header_pointer(skb, protoff, sizeof(_icmph), &_icmph);
1da177e4
LT
2153 if (ic == NULL) {
2154 /* We've been asked to examine this packet, and we
9c547959
PM
2155 * can't. Hence, no choice but to drop.
2156 */
1da177e4 2157 duprintf("Dropping evil ICMP tinygram.\n");
cff533ac 2158 *hotdrop = true;
1d93a9cb 2159 return false;
1da177e4
LT
2160 }
2161
2162 return icmp6_type_code_match(icmpinfo->type,
2163 icmpinfo->code[0],
2164 icmpinfo->code[1],
2165 ic->icmp6_type, ic->icmp6_code,
2166 !!(icmpinfo->invflags&IP6T_ICMP_INV));
2167}
2168
2169/* Called when user tries to insert an entry of this type. */
ccb79bdc 2170static bool
1da177e4 2171icmp6_checkentry(const char *tablename,
2e4e6a17 2172 const void *entry,
c4986734 2173 const struct xt_match *match,
1da177e4 2174 void *matchinfo,
1da177e4
LT
2175 unsigned int hook_mask)
2176{
2177 const struct ip6t_icmp *icmpinfo = matchinfo;
2178
7f939713
PM
2179 /* Must specify no unknown invflags */
2180 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1da177e4
LT
2181}
2182
2183/* The built-in targets: standard (NULL) and error. */
9f15c530 2184static struct xt_target ip6t_standard_target __read_mostly = {
1da177e4 2185 .name = IP6T_STANDARD_TARGET,
7f939713 2186 .targetsize = sizeof(int),
a45049c5 2187 .family = AF_INET6,
3bc3fe5e
PM
2188#ifdef CONFIG_COMPAT
2189 .compatsize = sizeof(compat_int_t),
2190 .compat_from_user = compat_standard_from_user,
2191 .compat_to_user = compat_standard_to_user,
2192#endif
1da177e4
LT
2193};
2194
9f15c530 2195static struct xt_target ip6t_error_target __read_mostly = {
1da177e4
LT
2196 .name = IP6T_ERROR_TARGET,
2197 .target = ip6t_error,
7f939713 2198 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
a45049c5 2199 .family = AF_INET6,
1da177e4
LT
2200};
2201
2202static struct nf_sockopt_ops ip6t_sockopts = {
2203 .pf = PF_INET6,
2204 .set_optmin = IP6T_BASE_CTL,
2205 .set_optmax = IP6T_SO_SET_MAX+1,
2206 .set = do_ip6t_set_ctl,
3bc3fe5e
PM
2207#ifdef CONFIG_COMPAT
2208 .compat_set = compat_do_ip6t_set_ctl,
2209#endif
1da177e4
LT
2210 .get_optmin = IP6T_BASE_CTL,
2211 .get_optmax = IP6T_SO_GET_MAX+1,
2212 .get = do_ip6t_get_ctl,
3bc3fe5e
PM
2213#ifdef CONFIG_COMPAT
2214 .compat_get = compat_do_ip6t_get_ctl,
2215#endif
16fcec35 2216 .owner = THIS_MODULE,
1da177e4
LT
2217};
2218
9f15c530 2219static struct xt_match icmp6_matchstruct __read_mostly = {
1da177e4 2220 .name = "icmp6",
9c547959 2221 .match = icmp6_match,
7f939713
PM
2222 .matchsize = sizeof(struct ip6t_icmp),
2223 .checkentry = icmp6_checkentry,
2224 .proto = IPPROTO_ICMPV6,
a45049c5 2225 .family = AF_INET6,
1da177e4
LT
2226};
2227
65b4b4e8 2228static int __init ip6_tables_init(void)
1da177e4
LT
2229{
2230 int ret;
2231
0eff66e6
PM
2232 ret = xt_proto_init(AF_INET6);
2233 if (ret < 0)
2234 goto err1;
2e4e6a17 2235
1da177e4 2236 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
2237 ret = xt_register_target(&ip6t_standard_target);
2238 if (ret < 0)
2239 goto err2;
2240 ret = xt_register_target(&ip6t_error_target);
2241 if (ret < 0)
2242 goto err3;
2243 ret = xt_register_match(&icmp6_matchstruct);
2244 if (ret < 0)
2245 goto err4;
1da177e4
LT
2246
2247 /* Register setsockopt */
2248 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
2249 if (ret < 0)
2250 goto err5;
1da177e4 2251
a887c1c1 2252 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 2253 return 0;
0eff66e6
PM
2254
2255err5:
2256 xt_unregister_match(&icmp6_matchstruct);
2257err4:
2258 xt_unregister_target(&ip6t_error_target);
2259err3:
2260 xt_unregister_target(&ip6t_standard_target);
2261err2:
2262 xt_proto_fini(AF_INET6);
2263err1:
2264 return ret;
1da177e4
LT
2265}
2266
65b4b4e8 2267static void __exit ip6_tables_fini(void)
1da177e4
LT
2268{
2269 nf_unregister_sockopt(&ip6t_sockopts);
9c547959 2270
a45049c5
PNA
2271 xt_unregister_match(&icmp6_matchstruct);
2272 xt_unregister_target(&ip6t_error_target);
2273 xt_unregister_target(&ip6t_standard_target);
2e4e6a17 2274 xt_proto_fini(AF_INET6);
1da177e4
LT
2275}
2276
e674d0f3 2277/*
b777e0ce
PM
2278 * find the offset to specified header or the protocol number of last header
2279 * if target < 0. "last header" is transport protocol header, ESP, or
2280 * "No next header".
2281 *
2282 * If target header is found, its offset is set in *offset and return protocol
2283 * number. Otherwise, return -1.
2284 *
6d381634
PM
2285 * If the first fragment doesn't contain the final protocol header or
2286 * NEXTHDR_NONE it is considered invalid.
2287 *
b777e0ce
PM
2288 * Note that non-1st fragment is special case that "the protocol number
2289 * of last header" is "next header" field in Fragment header. In this case,
2290 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
2291 * isn't NULL.
e674d0f3 2292 *
e674d0f3 2293 */
b777e0ce
PM
2294int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2295 int target, unsigned short *fragoff)
e674d0f3 2296{
6b88dd96 2297 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0660e03f 2298 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
e674d0f3
YK
2299 unsigned int len = skb->len - start;
2300
b777e0ce
PM
2301 if (fragoff)
2302 *fragoff = 0;
2303
e674d0f3
YK
2304 while (nexthdr != target) {
2305 struct ipv6_opt_hdr _hdr, *hp;
2306 unsigned int hdrlen;
2307
b777e0ce
PM
2308 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2309 if (target < 0)
2310 break;
6d381634 2311 return -ENOENT;
b777e0ce
PM
2312 }
2313
e674d0f3
YK
2314 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2315 if (hp == NULL)
6d381634 2316 return -EBADMSG;
e674d0f3 2317 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
2318 unsigned short _frag_off;
2319 __be16 *fp;
e674d0f3
YK
2320 fp = skb_header_pointer(skb,
2321 start+offsetof(struct frag_hdr,
2322 frag_off),
2323 sizeof(_frag_off),
2324 &_frag_off);
2325 if (fp == NULL)
6d381634 2326 return -EBADMSG;
e674d0f3 2327
b777e0ce
PM
2328 _frag_off = ntohs(*fp) & ~0x7;
2329 if (_frag_off) {
2330 if (target < 0 &&
2331 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 2332 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
2333 if (fragoff)
2334 *fragoff = _frag_off;
2335 return hp->nexthdr;
2336 }
6d381634 2337 return -ENOENT;
b777e0ce 2338 }
e674d0f3
YK
2339 hdrlen = 8;
2340 } else if (nexthdr == NEXTHDR_AUTH)
1ab1457c 2341 hdrlen = (hp->hdrlen + 2) << 2;
e674d0f3 2342 else
1ab1457c 2343 hdrlen = ipv6_optlen(hp);
e674d0f3
YK
2344
2345 nexthdr = hp->nexthdr;
2346 len -= hdrlen;
2347 start += hdrlen;
2348 }
2349
2350 *offset = start;
b777e0ce 2351 return nexthdr;
e674d0f3
YK
2352}
2353
1da177e4
LT
2354EXPORT_SYMBOL(ip6t_register_table);
2355EXPORT_SYMBOL(ip6t_unregister_table);
2356EXPORT_SYMBOL(ip6t_do_table);
1da177e4 2357EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 2358EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 2359
65b4b4e8
AM
2360module_init(ip6_tables_init);
2361module_exit(ip6_tables_fini);