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