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