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