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