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