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