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