]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - net/ipv6/netfilter/ip6_tables.c
netfilter: ip_tables: simplify translate_compat_table args
[mirror_ubuntu-hirsute-kernel.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
36472341
FW
458static bool find_jump_target(const struct xt_table_info *t,
459 const struct ip6t_entry *target)
460{
461 struct ip6t_entry *iter;
462
463 xt_entry_foreach(iter, t->entries, t->size) {
464 if (iter == target)
465 return true;
466 }
467 return false;
468}
469
1da177e4 470/* Figures out from what hook each rule can be called: returns 0 if
98dbbfc3 471 there are loops. Puts hook bitmask in comefrom. */
1da177e4 472static int
98dbbfc3 473mark_source_chains(const struct xt_table_info *newinfo,
31836064 474 unsigned int valid_hooks, void *entry0)
1da177e4
LT
475{
476 unsigned int hook;
477
478 /* No recursion; use packet counter to save back ptrs (reset
479 to 0 as we leave), and comefrom to save source hook bitmask */
6e23ae2a 480 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
1da177e4 481 unsigned int pos = newinfo->hook_entry[hook];
9c547959 482 struct ip6t_entry *e = (struct ip6t_entry *)(entry0 + pos);
1da177e4
LT
483
484 if (!(valid_hooks & (1 << hook)))
485 continue;
486
487 /* Set initial back pointer. */
488 e->counters.pcnt = pos;
489
490 for (;;) {
87a2e70d 491 const struct xt_standard_target *t
d5d1baa1 492 = (void *)ip6t_get_target_c(e);
9c547959 493 int visited = e->comefrom & (1 << hook);
1da177e4 494
6e23ae2a 495 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
654d0fbd 496 pr_err("iptables: loop hook %u pos %u %08X.\n",
1da177e4
LT
497 hook, pos, e->comefrom);
498 return 0;
499 }
9c547959 500 e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
1da177e4
LT
501
502 /* Unconditional return/END. */
54d83fc7 503 if ((unconditional(e) &&
3666ed1c 504 (strcmp(t->target.u.user.name,
243bf6e2 505 XT_STANDARD_TARGET) == 0) &&
54d83fc7 506 t->verdict < 0) || visited) {
1da177e4
LT
507 unsigned int oldpos, size;
508
1f9352ae 509 if ((strcmp(t->target.u.user.name,
243bf6e2 510 XT_STANDARD_TARGET) == 0) &&
1f9352ae 511 t->verdict < -NF_MAX_VERDICT - 1) {
74c9c0c1
DM
512 duprintf("mark_source_chains: bad "
513 "negative verdict (%i)\n",
514 t->verdict);
515 return 0;
516 }
517
1da177e4
LT
518 /* Return: backtrack through the last
519 big jump. */
520 do {
6e23ae2a 521 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
1da177e4
LT
522#ifdef DEBUG_IP_FIREWALL_USER
523 if (e->comefrom
6e23ae2a 524 & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
525 duprintf("Back unset "
526 "on hook %u "
527 "rule %u\n",
528 hook, pos);
529 }
530#endif
531 oldpos = pos;
532 pos = e->counters.pcnt;
533 e->counters.pcnt = 0;
534
535 /* We're at the start. */
536 if (pos == oldpos)
537 goto next;
538
539 e = (struct ip6t_entry *)
31836064 540 (entry0 + pos);
1da177e4
LT
541 } while (oldpos == pos + e->next_offset);
542
543 /* Move along one */
544 size = e->next_offset;
545 e = (struct ip6t_entry *)
31836064 546 (entry0 + pos + size);
f24e230d
FW
547 if (pos + size >= newinfo->size)
548 return 0;
1da177e4
LT
549 e->counters.pcnt = pos;
550 pos += size;
551 } else {
552 int newpos = t->verdict;
553
554 if (strcmp(t->target.u.user.name,
243bf6e2 555 XT_STANDARD_TARGET) == 0 &&
3666ed1c 556 newpos >= 0) {
74c9c0c1
DM
557 if (newpos > newinfo->size -
558 sizeof(struct ip6t_entry)) {
559 duprintf("mark_source_chains: "
560 "bad verdict (%i)\n",
561 newpos);
562 return 0;
563 }
1da177e4
LT
564 /* This a jump; chase it. */
565 duprintf("Jump rule %u -> %u\n",
566 pos, newpos);
36472341
FW
567 e = (struct ip6t_entry *)
568 (entry0 + newpos);
569 if (!find_jump_target(newinfo, e))
570 return 0;
1da177e4
LT
571 } else {
572 /* ... this is a fallthru */
573 newpos = pos + e->next_offset;
f24e230d
FW
574 if (newpos >= newinfo->size)
575 return 0;
1da177e4
LT
576 }
577 e = (struct ip6t_entry *)
31836064 578 (entry0 + newpos);
1da177e4
LT
579 e->counters.pcnt = pos;
580 pos = newpos;
581 }
582 }
6ac94619 583next:
1da177e4
LT
584 duprintf("Finished chain %u\n", hook);
585 }
586 return 1;
587}
588
87a2e70d 589static void cleanup_match(struct xt_entry_match *m, struct net *net)
1da177e4 590{
6be3d859
JE
591 struct xt_mtdtor_param par;
592
f54e9367 593 par.net = net;
6be3d859
JE
594 par.match = m->u.kernel.match;
595 par.matchinfo = m->data;
916a917d 596 par.family = NFPROTO_IPV6;
6be3d859
JE
597 if (par.match->destroy != NULL)
598 par.match->destroy(&par);
599 module_put(par.match->me);
1da177e4
LT
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 679
71ae0dff
FW
680 e->counters.pcnt = xt_percpu_counter_alloc();
681 if (IS_ERR_VALUE(e->counters.pcnt))
682 return -ENOMEM;
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 }
71ae0dff
FW
719
720 xt_percpu_counter_free(e->counters.pcnt);
721
1da177e4
LT
722 return ret;
723}
724
d5d1baa1 725static bool check_underflow(const struct ip6t_entry *e)
e2fe35c1 726{
87a2e70d 727 const struct xt_entry_target *t;
e2fe35c1
JE
728 unsigned int verdict;
729
54d83fc7 730 if (!unconditional(e))
e2fe35c1 731 return false;
d5d1baa1 732 t = ip6t_get_target_c(e);
e2fe35c1
JE
733 if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
734 return false;
87a2e70d 735 verdict = ((struct xt_standard_target *)t)->verdict;
e2fe35c1
JE
736 verdict = -verdict - 1;
737 return verdict == NF_DROP || verdict == NF_ACCEPT;
738}
739
022748a9 740static int
1da177e4 741check_entry_size_and_hooks(struct ip6t_entry *e,
2e4e6a17 742 struct xt_table_info *newinfo,
d5d1baa1
JE
743 const unsigned char *base,
744 const unsigned char *limit,
1da177e4
LT
745 const unsigned int *hook_entries,
746 const unsigned int *underflows,
0559518b 747 unsigned int valid_hooks)
1da177e4
LT
748{
749 unsigned int h;
bdf533de 750 int err;
1da177e4 751
3666ed1c 752 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
6e94e0cf
FW
753 (unsigned char *)e + sizeof(struct ip6t_entry) >= limit ||
754 (unsigned char *)e + e->next_offset > limit) {
1da177e4
LT
755 duprintf("Bad offset %p\n", e);
756 return -EINVAL;
757 }
758
759 if (e->next_offset
87a2e70d 760 < sizeof(struct ip6t_entry) + sizeof(struct xt_entry_target)) {
1da177e4
LT
761 duprintf("checking: element %p size %u\n",
762 e, e->next_offset);
763 return -EINVAL;
764 }
765
aa412ba2
FW
766 if (!ip6_checkentry(&e->ipv6))
767 return -EINVAL;
768
ce683e5f
FW
769 err = xt_check_entry_offsets(e, e->elems, e->target_offset,
770 e->next_offset);
bdf533de
FW
771 if (err)
772 return err;
773
1da177e4 774 /* Check hooks & underflows */
6e23ae2a 775 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
a7d51738
JE
776 if (!(valid_hooks & (1 << h)))
777 continue;
1da177e4
LT
778 if ((unsigned char *)e - base == hook_entries[h])
779 newinfo->hook_entry[h] = hook_entries[h];
90e7d4ab 780 if ((unsigned char *)e - base == underflows[h]) {
e2fe35c1 781 if (!check_underflow(e)) {
54d83fc7
FW
782 pr_debug("Underflows must be unconditional and "
783 "use the STANDARD target with "
784 "ACCEPT/DROP\n");
90e7d4ab
JE
785 return -EINVAL;
786 }
1da177e4 787 newinfo->underflow[h] = underflows[h];
90e7d4ab 788 }
1da177e4
LT
789 }
790
1da177e4 791 /* Clear counters and comefrom */
2e4e6a17 792 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4 793 e->comefrom = 0;
1da177e4
LT
794 return 0;
795}
796
0559518b 797static void cleanup_entry(struct ip6t_entry *e, struct net *net)
1da177e4 798{
a2df1648 799 struct xt_tgdtor_param par;
87a2e70d 800 struct xt_entry_target *t;
dcea992a 801 struct xt_entry_match *ematch;
1da177e4 802
1da177e4 803 /* Cleanup all matches */
dcea992a 804 xt_ematch_foreach(ematch, e)
6bdb331b 805 cleanup_match(ematch, net);
1da177e4 806 t = ip6t_get_target(e);
a2df1648 807
add67461 808 par.net = net;
a2df1648
JE
809 par.target = t->u.kernel.target;
810 par.targinfo = t->data;
916a917d 811 par.family = NFPROTO_IPV6;
a2df1648
JE
812 if (par.target->destroy != NULL)
813 par.target->destroy(&par);
814 module_put(par.target->me);
71ae0dff
FW
815
816 xt_percpu_counter_free(e->counters.pcnt);
1da177e4
LT
817}
818
819/* Checks and translates the user-supplied table segment (held in
820 newinfo) */
821static int
0f234214 822translate_table(struct net *net, struct xt_table_info *newinfo, void *entry0,
cda219c6 823 const struct ip6t_replace *repl)
1da177e4 824{
72b2b1dd 825 struct ip6t_entry *iter;
1da177e4 826 unsigned int i;
72b2b1dd 827 int ret = 0;
1da177e4 828
0f234214
JE
829 newinfo->size = repl->size;
830 newinfo->number = repl->num_entries;
1da177e4
LT
831
832 /* Init all hooks to impossible value. */
6e23ae2a 833 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
834 newinfo->hook_entry[i] = 0xFFFFFFFF;
835 newinfo->underflow[i] = 0xFFFFFFFF;
836 }
837
838 duprintf("translate_table: size %u\n", newinfo->size);
839 i = 0;
840 /* Walk through entries, checking offsets. */
72b2b1dd
JE
841 xt_entry_foreach(iter, entry0, newinfo->size) {
842 ret = check_entry_size_and_hooks(iter, newinfo, entry0,
6b4ff2d7
JE
843 entry0 + repl->size,
844 repl->hook_entry,
845 repl->underflow,
846 repl->valid_hooks);
72b2b1dd 847 if (ret != 0)
0559518b
JE
848 return ret;
849 ++i;
98dbbfc3
FW
850 if (strcmp(ip6t_get_target(iter)->u.user.name,
851 XT_ERROR_TARGET) == 0)
852 ++newinfo->stacksize;
72b2b1dd 853 }
1da177e4 854
0f234214 855 if (i != repl->num_entries) {
1da177e4 856 duprintf("translate_table: %u not %u entries\n",
0f234214 857 i, repl->num_entries);
1da177e4
LT
858 return -EINVAL;
859 }
860
861 /* Check hooks all assigned */
6e23ae2a 862 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4 863 /* Only hooks which are valid */
0f234214 864 if (!(repl->valid_hooks & (1 << i)))
1da177e4
LT
865 continue;
866 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
867 duprintf("Invalid hook entry %u %u\n",
0f234214 868 i, repl->hook_entry[i]);
1da177e4
LT
869 return -EINVAL;
870 }
871 if (newinfo->underflow[i] == 0xFFFFFFFF) {
872 duprintf("Invalid underflow %u %u\n",
0f234214 873 i, repl->underflow[i]);
1da177e4
LT
874 return -EINVAL;
875 }
876 }
877
0f234214 878 if (!mark_source_chains(newinfo, repl->valid_hooks, entry0))
74c9c0c1
DM
879 return -ELOOP;
880
1da177e4
LT
881 /* Finally, each sanity check must pass */
882 i = 0;
72b2b1dd 883 xt_entry_foreach(iter, entry0, newinfo->size) {
0f234214 884 ret = find_check_entry(iter, net, repl->name, repl->size);
72b2b1dd
JE
885 if (ret != 0)
886 break;
0559518b 887 ++i;
72b2b1dd 888 }
1da177e4 889
74c9c0c1 890 if (ret != 0) {
0559518b
JE
891 xt_entry_foreach(iter, entry0, newinfo->size) {
892 if (i-- == 0)
72b2b1dd 893 break;
0559518b
JE
894 cleanup_entry(iter, net);
895 }
74c9c0c1
DM
896 return ret;
897 }
1da177e4 898
9c547959 899 return ret;
1da177e4
LT
900}
901
1da177e4 902static void
2e4e6a17
HW
903get_counters(const struct xt_table_info *t,
904 struct xt_counters counters[])
1da177e4 905{
72b2b1dd 906 struct ip6t_entry *iter;
1da177e4
LT
907 unsigned int cpu;
908 unsigned int i;
909
6f912042 910 for_each_possible_cpu(cpu) {
7f5c6d4f 911 seqcount_t *s = &per_cpu(xt_recseq, cpu);
83723d60 912
1da177e4 913 i = 0;
482cfc31 914 xt_entry_foreach(iter, t->entries, t->size) {
71ae0dff 915 struct xt_counters *tmp;
83723d60
ED
916 u64 bcnt, pcnt;
917 unsigned int start;
918
71ae0dff 919 tmp = xt_get_per_cpu_counter(&iter->counters, cpu);
83723d60 920 do {
7f5c6d4f 921 start = read_seqcount_begin(s);
71ae0dff
FW
922 bcnt = tmp->bcnt;
923 pcnt = tmp->pcnt;
7f5c6d4f 924 } while (read_seqcount_retry(s, start));
83723d60
ED
925
926 ADD_COUNTER(counters[i], bcnt, pcnt);
0559518b
JE
927 ++i;
928 }
1da177e4 929 }
78454473
SH
930}
931
d5d1baa1 932static struct xt_counters *alloc_counters(const struct xt_table *table)
1da177e4 933{
ed1a6f5e 934 unsigned int countersize;
2e4e6a17 935 struct xt_counters *counters;
d5d1baa1 936 const struct xt_table_info *private = table->private;
1da177e4
LT
937
938 /* We need atomic snapshot of counters: rest doesn't change
939 (other than comefrom, which userspace doesn't care
940 about). */
2e4e6a17 941 countersize = sizeof(struct xt_counters) * private->number;
83723d60 942 counters = vzalloc(countersize);
1da177e4
LT
943
944 if (counters == NULL)
942e4a2b 945 return ERR_PTR(-ENOMEM);
78454473 946
942e4a2b 947 get_counters(private, counters);
78454473 948
49a88d18 949 return counters;
ed1a6f5e
PM
950}
951
952static int
953copy_entries_to_user(unsigned int total_size,
d5d1baa1 954 const struct xt_table *table,
ed1a6f5e
PM
955 void __user *userptr)
956{
957 unsigned int off, num;
d5d1baa1 958 const struct ip6t_entry *e;
ed1a6f5e 959 struct xt_counters *counters;
5452e425 960 const struct xt_table_info *private = table->private;
ed1a6f5e 961 int ret = 0;
711bdde6 962 const void *loc_cpu_entry;
ed1a6f5e
PM
963
964 counters = alloc_counters(table);
965 if (IS_ERR(counters))
966 return PTR_ERR(counters);
967
482cfc31 968 loc_cpu_entry = private->entries;
31836064 969 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
970 ret = -EFAULT;
971 goto free_counters;
972 }
973
974 /* FIXME: use iterator macros --RR */
975 /* ... then go back and fix counters and names */
976 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
977 unsigned int i;
87a2e70d
JE
978 const struct xt_entry_match *m;
979 const struct xt_entry_target *t;
1da177e4 980
31836064 981 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
982 if (copy_to_user(userptr + off
983 + offsetof(struct ip6t_entry, counters),
984 &counters[num],
985 sizeof(counters[num])) != 0) {
986 ret = -EFAULT;
987 goto free_counters;
988 }
989
990 for (i = sizeof(struct ip6t_entry);
991 i < e->target_offset;
992 i += m->u.match_size) {
993 m = (void *)e + i;
994
995 if (copy_to_user(userptr + off + i
87a2e70d 996 + offsetof(struct xt_entry_match,
1da177e4
LT
997 u.user.name),
998 m->u.kernel.match->name,
999 strlen(m->u.kernel.match->name)+1)
1000 != 0) {
1001 ret = -EFAULT;
1002 goto free_counters;
1003 }
1004 }
1005
d5d1baa1 1006 t = ip6t_get_target_c(e);
1da177e4 1007 if (copy_to_user(userptr + off + e->target_offset
87a2e70d 1008 + offsetof(struct xt_entry_target,
1da177e4
LT
1009 u.user.name),
1010 t->u.kernel.target->name,
1011 strlen(t->u.kernel.target->name)+1) != 0) {
1012 ret = -EFAULT;
1013 goto free_counters;
1014 }
1015 }
1016
1017 free_counters:
1018 vfree(counters);
1019 return ret;
1020}
1021
3bc3fe5e 1022#ifdef CONFIG_COMPAT
739674fb 1023static void compat_standard_from_user(void *dst, const void *src)
3bc3fe5e
PM
1024{
1025 int v = *(compat_int_t *)src;
1026
1027 if (v > 0)
1028 v += xt_compat_calc_jump(AF_INET6, v);
1029 memcpy(dst, &v, sizeof(v));
1030}
1031
739674fb 1032static int compat_standard_to_user(void __user *dst, const void *src)
3bc3fe5e
PM
1033{
1034 compat_int_t cv = *(int *)src;
1035
1036 if (cv > 0)
1037 cv -= xt_compat_calc_jump(AF_INET6, cv);
1038 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1039}
1040
d5d1baa1 1041static int compat_calc_entry(const struct ip6t_entry *e,
3bc3fe5e 1042 const struct xt_table_info *info,
d5d1baa1 1043 const void *base, struct xt_table_info *newinfo)
3bc3fe5e 1044{
dcea992a 1045 const struct xt_entry_match *ematch;
87a2e70d 1046 const struct xt_entry_target *t;
3bc3fe5e
PM
1047 unsigned int entry_offset;
1048 int off, i, ret;
1049
1050 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1051 entry_offset = (void *)e - base;
dcea992a 1052 xt_ematch_foreach(ematch, e)
6bdb331b 1053 off += xt_compat_match_offset(ematch->u.kernel.match);
d5d1baa1 1054 t = ip6t_get_target_c(e);
3bc3fe5e
PM
1055 off += xt_compat_target_offset(t->u.kernel.target);
1056 newinfo->size -= off;
1057 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1058 if (ret)
1059 return ret;
1060
1061 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1062 if (info->hook_entry[i] &&
1063 (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1064 newinfo->hook_entry[i] -= off;
1065 if (info->underflow[i] &&
1066 (e < (struct ip6t_entry *)(base + info->underflow[i])))
1067 newinfo->underflow[i] -= off;
1068 }
1069 return 0;
1070}
1071
1072static int compat_table_info(const struct xt_table_info *info,
1073 struct xt_table_info *newinfo)
1074{
72b2b1dd 1075 struct ip6t_entry *iter;
711bdde6 1076 const void *loc_cpu_entry;
0559518b 1077 int ret;
3bc3fe5e
PM
1078
1079 if (!newinfo || !info)
1080 return -EINVAL;
1081
482cfc31 1082 /* we dont care about newinfo->entries */
3bc3fe5e
PM
1083 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1084 newinfo->initial_entries = 0;
482cfc31 1085 loc_cpu_entry = info->entries;
255d0dc3 1086 xt_compat_init_offsets(AF_INET6, info->number);
72b2b1dd
JE
1087 xt_entry_foreach(iter, loc_cpu_entry, info->size) {
1088 ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
1089 if (ret != 0)
0559518b 1090 return ret;
72b2b1dd 1091 }
0559518b 1092 return 0;
3bc3fe5e
PM
1093}
1094#endif
1095
d5d1baa1 1096static int get_info(struct net *net, void __user *user,
cda219c6 1097 const int *len, int compat)
433665c9 1098{
12b00c2c 1099 char name[XT_TABLE_MAXNAMELEN];
433665c9
PM
1100 struct xt_table *t;
1101 int ret;
1102
1103 if (*len != sizeof(struct ip6t_getinfo)) {
c9d8fe13 1104 duprintf("length %u != %zu\n", *len,
433665c9
PM
1105 sizeof(struct ip6t_getinfo));
1106 return -EINVAL;
1107 }
1108
1109 if (copy_from_user(name, user, sizeof(name)) != 0)
1110 return -EFAULT;
1111
12b00c2c 1112 name[XT_TABLE_MAXNAMELEN-1] = '\0';
3bc3fe5e
PM
1113#ifdef CONFIG_COMPAT
1114 if (compat)
1115 xt_compat_lock(AF_INET6);
1116#endif
336b517f 1117 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
433665c9 1118 "ip6table_%s", name);
0cc8d8df 1119 if (!IS_ERR_OR_NULL(t)) {
433665c9 1120 struct ip6t_getinfo info;
5452e425 1121 const struct xt_table_info *private = t->private;
3bc3fe5e 1122#ifdef CONFIG_COMPAT
14c7dbe0
AD
1123 struct xt_table_info tmp;
1124
3bc3fe5e 1125 if (compat) {
3bc3fe5e
PM
1126 ret = compat_table_info(private, &tmp);
1127 xt_compat_flush_offsets(AF_INET6);
1128 private = &tmp;
1129 }
1130#endif
cccbe5ef 1131 memset(&info, 0, sizeof(info));
433665c9
PM
1132 info.valid_hooks = t->valid_hooks;
1133 memcpy(info.hook_entry, private->hook_entry,
1134 sizeof(info.hook_entry));
1135 memcpy(info.underflow, private->underflow,
1136 sizeof(info.underflow));
1137 info.num_entries = private->number;
1138 info.size = private->size;
b5dd674b 1139 strcpy(info.name, name);
433665c9
PM
1140
1141 if (copy_to_user(user, &info, *len) != 0)
1142 ret = -EFAULT;
1143 else
1144 ret = 0;
1145
1146 xt_table_unlock(t);
1147 module_put(t->me);
1148 } else
1149 ret = t ? PTR_ERR(t) : -ENOENT;
3bc3fe5e
PM
1150#ifdef CONFIG_COMPAT
1151 if (compat)
1152 xt_compat_unlock(AF_INET6);
1153#endif
433665c9
PM
1154 return ret;
1155}
1156
1da177e4 1157static int
d5d1baa1 1158get_entries(struct net *net, struct ip6t_get_entries __user *uptr,
cda219c6 1159 const int *len)
1da177e4
LT
1160{
1161 int ret;
d924357c 1162 struct ip6t_get_entries get;
2e4e6a17 1163 struct xt_table *t;
1da177e4 1164
d924357c 1165 if (*len < sizeof(get)) {
c9d8fe13 1166 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
d924357c
PM
1167 return -EINVAL;
1168 }
1169 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1170 return -EFAULT;
1171 if (*len != sizeof(struct ip6t_get_entries) + get.size) {
c9d8fe13
PM
1172 duprintf("get_entries: %u != %zu\n",
1173 *len, sizeof(get) + get.size);
d924357c
PM
1174 return -EINVAL;
1175 }
b301f253 1176 get.name[sizeof(get.name) - 1] = '\0';
d924357c 1177
336b517f 1178 t = xt_find_table_lock(net, AF_INET6, get.name);
0cc8d8df 1179 if (!IS_ERR_OR_NULL(t)) {
2e4e6a17
HW
1180 struct xt_table_info *private = t->private;
1181 duprintf("t->private->number = %u\n", private->number);
d924357c 1182 if (get.size == private->size)
2e4e6a17 1183 ret = copy_entries_to_user(private->size,
1da177e4
LT
1184 t, uptr->entrytable);
1185 else {
1186 duprintf("get_entries: I've got %u not %u!\n",
9c547959 1187 private->size, get.size);
544473c1 1188 ret = -EAGAIN;
1da177e4 1189 }
6b7d31fc 1190 module_put(t->me);
2e4e6a17 1191 xt_table_unlock(t);
1da177e4 1192 } else
6b7d31fc 1193 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1194
1195 return ret;
1196}
1197
1198static int
336b517f 1199__do_replace(struct net *net, const char *name, unsigned int valid_hooks,
3bc3fe5e
PM
1200 struct xt_table_info *newinfo, unsigned int num_counters,
1201 void __user *counters_ptr)
1da177e4
LT
1202{
1203 int ret;
2e4e6a17 1204 struct xt_table *t;
3bc3fe5e 1205 struct xt_table_info *oldinfo;
2e4e6a17 1206 struct xt_counters *counters;
72b2b1dd 1207 struct ip6t_entry *iter;
1da177e4 1208
3bc3fe5e 1209 ret = 0;
83723d60 1210 counters = vzalloc(num_counters * sizeof(struct xt_counters));
1da177e4
LT
1211 if (!counters) {
1212 ret = -ENOMEM;
3bc3fe5e 1213 goto out;
1da177e4 1214 }
1da177e4 1215
336b517f 1216 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
3bc3fe5e 1217 "ip6table_%s", name);
0cc8d8df 1218 if (IS_ERR_OR_NULL(t)) {
6b7d31fc 1219 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1220 goto free_newinfo_counters_untrans;
6b7d31fc 1221 }
1da177e4
LT
1222
1223 /* You lied! */
3bc3fe5e 1224 if (valid_hooks != t->valid_hooks) {
1da177e4 1225 duprintf("Valid hook crap: %08X vs %08X\n",
3bc3fe5e 1226 valid_hooks, t->valid_hooks);
1da177e4 1227 ret = -EINVAL;
6b7d31fc 1228 goto put_module;
1da177e4
LT
1229 }
1230
3bc3fe5e 1231 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1da177e4
LT
1232 if (!oldinfo)
1233 goto put_module;
1234
1235 /* Update module usage count based on number of rules */
1236 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1237 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1238 if ((oldinfo->number > oldinfo->initial_entries) ||
1239 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1240 module_put(t->me);
1241 if ((oldinfo->number > oldinfo->initial_entries) &&
1242 (newinfo->number <= oldinfo->initial_entries))
1243 module_put(t->me);
1244
942e4a2b 1245 /* Get the old counters, and synchronize with replace */
1da177e4 1246 get_counters(oldinfo, counters);
942e4a2b 1247
1da177e4 1248 /* Decrease module usage counts and free resource */
482cfc31 1249 xt_entry_foreach(iter, oldinfo->entries, oldinfo->size)
0559518b 1250 cleanup_entry(iter, net);
72b2b1dd 1251
2e4e6a17 1252 xt_free_table_info(oldinfo);
3bc3fe5e 1253 if (copy_to_user(counters_ptr, counters,
c58dd2dd
TG
1254 sizeof(struct xt_counters) * num_counters) != 0) {
1255 /* Silent error, can't fail, new table is already in place */
1256 net_warn_ratelimited("ip6tables: counters copy to user failed while replacing table\n");
1257 }
1da177e4 1258 vfree(counters);
2e4e6a17 1259 xt_table_unlock(t);
1da177e4
LT
1260 return ret;
1261
1262 put_module:
1263 module_put(t->me);
2e4e6a17 1264 xt_table_unlock(t);
1da177e4 1265 free_newinfo_counters_untrans:
1da177e4 1266 vfree(counters);
3bc3fe5e
PM
1267 out:
1268 return ret;
1269}
1270
1271static int
d5d1baa1 1272do_replace(struct net *net, const void __user *user, unsigned int len)
3bc3fe5e
PM
1273{
1274 int ret;
1275 struct ip6t_replace tmp;
1276 struct xt_table_info *newinfo;
1277 void *loc_cpu_entry;
72b2b1dd 1278 struct ip6t_entry *iter;
3bc3fe5e
PM
1279
1280 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1281 return -EFAULT;
1282
1283 /* overflow check */
1284 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1285 return -ENOMEM;
1086bbe9
DJ
1286 if (tmp.num_counters == 0)
1287 return -EINVAL;
1288
6a8ab060 1289 tmp.name[sizeof(tmp.name)-1] = 0;
3bc3fe5e
PM
1290
1291 newinfo = xt_alloc_table_info(tmp.size);
1292 if (!newinfo)
1293 return -ENOMEM;
1294
482cfc31 1295 loc_cpu_entry = newinfo->entries;
3bc3fe5e
PM
1296 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1297 tmp.size) != 0) {
1298 ret = -EFAULT;
1299 goto free_newinfo;
1300 }
1301
0f234214 1302 ret = translate_table(net, newinfo, loc_cpu_entry, &tmp);
3bc3fe5e
PM
1303 if (ret != 0)
1304 goto free_newinfo;
1305
1306 duprintf("ip_tables: Translated table\n");
1307
336b517f 1308 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
3bc3fe5e
PM
1309 tmp.num_counters, tmp.counters);
1310 if (ret)
1311 goto free_newinfo_untrans;
1312 return 0;
1313
1314 free_newinfo_untrans:
72b2b1dd 1315 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
0559518b 1316 cleanup_entry(iter, net);
1da177e4 1317 free_newinfo:
2e4e6a17 1318 xt_free_table_info(newinfo);
1da177e4
LT
1319 return ret;
1320}
1321
1da177e4 1322static int
d5d1baa1 1323do_add_counters(struct net *net, const void __user *user, unsigned int len,
336b517f 1324 int compat)
1da177e4 1325{
482cfc31 1326 unsigned int i;
3bc3fe5e
PM
1327 struct xt_counters_info tmp;
1328 struct xt_counters *paddc;
1329 unsigned int num_counters;
1330 char *name;
1331 int size;
1332 void *ptmp;
2e4e6a17 1333 struct xt_table *t;
5452e425 1334 const struct xt_table_info *private;
6b7d31fc 1335 int ret = 0;
72b2b1dd 1336 struct ip6t_entry *iter;
7f5c6d4f 1337 unsigned int addend;
3bc3fe5e
PM
1338#ifdef CONFIG_COMPAT
1339 struct compat_xt_counters_info compat_tmp;
1da177e4 1340
3bc3fe5e
PM
1341 if (compat) {
1342 ptmp = &compat_tmp;
1343 size = sizeof(struct compat_xt_counters_info);
1344 } else
1345#endif
1346 {
1347 ptmp = &tmp;
1348 size = sizeof(struct xt_counters_info);
1349 }
1350
1351 if (copy_from_user(ptmp, user, size) != 0)
1da177e4
LT
1352 return -EFAULT;
1353
3bc3fe5e
PM
1354#ifdef CONFIG_COMPAT
1355 if (compat) {
1356 num_counters = compat_tmp.num_counters;
1357 name = compat_tmp.name;
1358 } else
1359#endif
1360 {
1361 num_counters = tmp.num_counters;
1362 name = tmp.name;
1363 }
1364
1365 if (len != size + num_counters * sizeof(struct xt_counters))
1da177e4
LT
1366 return -EINVAL;
1367
e12f8e29 1368 paddc = vmalloc(len - size);
1da177e4
LT
1369 if (!paddc)
1370 return -ENOMEM;
1371
3bc3fe5e 1372 if (copy_from_user(paddc, user + size, len - size) != 0) {
1da177e4
LT
1373 ret = -EFAULT;
1374 goto free;
1375 }
1376
336b517f 1377 t = xt_find_table_lock(net, AF_INET6, name);
0cc8d8df 1378 if (IS_ERR_OR_NULL(t)) {
6b7d31fc 1379 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1380 goto free;
6b7d31fc 1381 }
1da177e4 1382
942e4a2b 1383 local_bh_disable();
2e4e6a17 1384 private = t->private;
3bc3fe5e 1385 if (private->number != num_counters) {
1da177e4
LT
1386 ret = -EINVAL;
1387 goto unlock_up_free;
1388 }
1389
1390 i = 0;
7f5c6d4f 1391 addend = xt_write_recseq_begin();
482cfc31 1392 xt_entry_foreach(iter, private->entries, private->size) {
71ae0dff
FW
1393 struct xt_counters *tmp;
1394
1395 tmp = xt_get_this_cpu_counter(&iter->counters);
1396 ADD_COUNTER(*tmp, paddc[i].bcnt, paddc[i].pcnt);
0559518b
JE
1397 ++i;
1398 }
7f5c6d4f 1399 xt_write_recseq_end(addend);
1da177e4 1400 unlock_up_free:
942e4a2b 1401 local_bh_enable();
2e4e6a17 1402 xt_table_unlock(t);
6b7d31fc 1403 module_put(t->me);
1da177e4
LT
1404 free:
1405 vfree(paddc);
1406
1407 return ret;
1408}
1409
3bc3fe5e
PM
1410#ifdef CONFIG_COMPAT
1411struct compat_ip6t_replace {
12b00c2c 1412 char name[XT_TABLE_MAXNAMELEN];
3bc3fe5e
PM
1413 u32 valid_hooks;
1414 u32 num_entries;
1415 u32 size;
1416 u32 hook_entry[NF_INET_NUMHOOKS];
1417 u32 underflow[NF_INET_NUMHOOKS];
1418 u32 num_counters;
87a2e70d 1419 compat_uptr_t counters; /* struct xt_counters * */
3bc3fe5e
PM
1420 struct compat_ip6t_entry entries[0];
1421};
1422
1423static int
1424compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
b0a6363c 1425 unsigned int *size, struct xt_counters *counters,
0559518b 1426 unsigned int i)
3bc3fe5e 1427{
87a2e70d 1428 struct xt_entry_target *t;
3bc3fe5e
PM
1429 struct compat_ip6t_entry __user *ce;
1430 u_int16_t target_offset, next_offset;
1431 compat_uint_t origsize;
dcea992a
JE
1432 const struct xt_entry_match *ematch;
1433 int ret = 0;
3bc3fe5e 1434
3bc3fe5e
PM
1435 origsize = *size;
1436 ce = (struct compat_ip6t_entry __user *)*dstptr;
0559518b
JE
1437 if (copy_to_user(ce, e, sizeof(struct ip6t_entry)) != 0 ||
1438 copy_to_user(&ce->counters, &counters[i],
1439 sizeof(counters[i])) != 0)
1440 return -EFAULT;
3bc3fe5e
PM
1441
1442 *dstptr += sizeof(struct compat_ip6t_entry);
1443 *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1444
dcea992a
JE
1445 xt_ematch_foreach(ematch, e) {
1446 ret = xt_compat_match_to_user(ematch, dstptr, size);
1447 if (ret != 0)
6bdb331b 1448 return ret;
dcea992a 1449 }
3bc3fe5e 1450 target_offset = e->target_offset - (origsize - *size);
3bc3fe5e
PM
1451 t = ip6t_get_target(e);
1452 ret = xt_compat_target_to_user(t, dstptr, size);
1453 if (ret)
0559518b 1454 return ret;
3bc3fe5e 1455 next_offset = e->next_offset - (origsize - *size);
0559518b
JE
1456 if (put_user(target_offset, &ce->target_offset) != 0 ||
1457 put_user(next_offset, &ce->next_offset) != 0)
1458 return -EFAULT;
3bc3fe5e 1459 return 0;
3bc3fe5e
PM
1460}
1461
022748a9 1462static int
87a2e70d 1463compat_find_calc_match(struct xt_entry_match *m,
3bc3fe5e
PM
1464 const char *name,
1465 const struct ip6t_ip6 *ipv6,
6bdb331b 1466 int *size)
3bc3fe5e
PM
1467{
1468 struct xt_match *match;
1469
fd0ec0e6
JE
1470 match = xt_request_find_match(NFPROTO_IPV6, m->u.user.name,
1471 m->u.user.revision);
1472 if (IS_ERR(match)) {
3bc3fe5e
PM
1473 duprintf("compat_check_calc_match: `%s' not found\n",
1474 m->u.user.name);
fd0ec0e6 1475 return PTR_ERR(match);
3bc3fe5e
PM
1476 }
1477 m->u.kernel.match = match;
1478 *size += xt_compat_match_offset(match);
3bc3fe5e
PM
1479 return 0;
1480}
1481
0559518b 1482static void compat_release_entry(struct compat_ip6t_entry *e)
3bc3fe5e 1483{
87a2e70d 1484 struct xt_entry_target *t;
dcea992a 1485 struct xt_entry_match *ematch;
3bc3fe5e 1486
3bc3fe5e 1487 /* Cleanup all matches */
dcea992a 1488 xt_ematch_foreach(ematch, e)
6bdb331b 1489 module_put(ematch->u.kernel.match->me);
3bc3fe5e
PM
1490 t = compat_ip6t_get_target(e);
1491 module_put(t->u.kernel.target->me);
3bc3fe5e
PM
1492}
1493
022748a9 1494static int
3bc3fe5e
PM
1495check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1496 struct xt_table_info *newinfo,
1497 unsigned int *size,
d5d1baa1
JE
1498 const unsigned char *base,
1499 const unsigned char *limit,
1500 const unsigned int *hook_entries,
1501 const unsigned int *underflows,
3bc3fe5e
PM
1502 const char *name)
1503{
dcea992a 1504 struct xt_entry_match *ematch;
87a2e70d 1505 struct xt_entry_target *t;
3bc3fe5e
PM
1506 struct xt_target *target;
1507 unsigned int entry_offset;
b0a6363c
PM
1508 unsigned int j;
1509 int ret, off, h;
3bc3fe5e
PM
1510
1511 duprintf("check_compat_entry_size_and_hooks %p\n", e);
3666ed1c 1512 if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
6e94e0cf
FW
1513 (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit ||
1514 (unsigned char *)e + e->next_offset > limit) {
3bc3fe5e
PM
1515 duprintf("Bad offset %p, limit = %p\n", e, limit);
1516 return -EINVAL;
1517 }
1518
1519 if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1520 sizeof(struct compat_xt_entry_target)) {
1521 duprintf("checking: element %p size %u\n",
1522 e, e->next_offset);
1523 return -EINVAL;
1524 }
1525
aa412ba2
FW
1526 if (!ip6_checkentry(&e->ipv6))
1527 return -EINVAL;
1528
ce683e5f 1529 ret = xt_compat_check_entry_offsets(e, e->elems,
fc1221b3 1530 e->target_offset, e->next_offset);
3bc3fe5e
PM
1531 if (ret)
1532 return ret;
1533
1534 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1535 entry_offset = (void *)e - (void *)base;
1536 j = 0;
dcea992a 1537 xt_ematch_foreach(ematch, e) {
2f06550b 1538 ret = compat_find_calc_match(ematch, name, &e->ipv6, &off);
dcea992a 1539 if (ret != 0)
6bdb331b
JE
1540 goto release_matches;
1541 ++j;
dcea992a 1542 }
3bc3fe5e
PM
1543
1544 t = compat_ip6t_get_target(e);
d2a7b6ba
JE
1545 target = xt_request_find_target(NFPROTO_IPV6, t->u.user.name,
1546 t->u.user.revision);
1547 if (IS_ERR(target)) {
3bc3fe5e
PM
1548 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1549 t->u.user.name);
d2a7b6ba 1550 ret = PTR_ERR(target);
3bc3fe5e
PM
1551 goto release_matches;
1552 }
1553 t->u.kernel.target = target;
1554
1555 off += xt_compat_target_offset(target);
1556 *size += off;
1557 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1558 if (ret)
1559 goto out;
1560
1561 /* Check hooks & underflows */
1562 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1563 if ((unsigned char *)e - base == hook_entries[h])
1564 newinfo->hook_entry[h] = hook_entries[h];
1565 if ((unsigned char *)e - base == underflows[h])
1566 newinfo->underflow[h] = underflows[h];
1567 }
1568
1569 /* Clear counters and comefrom */
1570 memset(&e->counters, 0, sizeof(e->counters));
1571 e->comefrom = 0;
3bc3fe5e
PM
1572 return 0;
1573
1574out:
1575 module_put(t->u.kernel.target->me);
1576release_matches:
6bdb331b
JE
1577 xt_ematch_foreach(ematch, e) {
1578 if (j-- == 0)
dcea992a 1579 break;
6bdb331b
JE
1580 module_put(ematch->u.kernel.match->me);
1581 }
3bc3fe5e
PM
1582 return ret;
1583}
1584
1585static int
1586compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1587 unsigned int *size, const char *name,
1588 struct xt_table_info *newinfo, unsigned char *base)
1589{
87a2e70d 1590 struct xt_entry_target *t;
3bc3fe5e
PM
1591 struct ip6t_entry *de;
1592 unsigned int origsize;
1593 int ret, h;
dcea992a 1594 struct xt_entry_match *ematch;
3bc3fe5e
PM
1595
1596 ret = 0;
1597 origsize = *size;
1598 de = (struct ip6t_entry *)*dstptr;
1599 memcpy(de, e, sizeof(struct ip6t_entry));
1600 memcpy(&de->counters, &e->counters, sizeof(e->counters));
1601
1602 *dstptr += sizeof(struct ip6t_entry);
1603 *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1604
dcea992a
JE
1605 xt_ematch_foreach(ematch, e) {
1606 ret = xt_compat_match_from_user(ematch, dstptr, size);
1607 if (ret != 0)
6bdb331b 1608 return ret;
dcea992a 1609 }
3bc3fe5e
PM
1610 de->target_offset = e->target_offset - (origsize - *size);
1611 t = compat_ip6t_get_target(e);
3bc3fe5e
PM
1612 xt_compat_target_from_user(t, dstptr, size);
1613
1614 de->next_offset = e->next_offset - (origsize - *size);
1615 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1616 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1617 newinfo->hook_entry[h] -= origsize - *size;
1618 if ((unsigned char *)de - base < newinfo->underflow[h])
1619 newinfo->underflow[h] -= origsize - *size;
1620 }
1621 return ret;
1622}
1623
f54e9367 1624static int compat_check_entry(struct ip6t_entry *e, struct net *net,
0559518b 1625 const char *name)
3bc3fe5e 1626{
b0a6363c 1627 unsigned int j;
dcea992a 1628 int ret = 0;
9b4fce7a 1629 struct xt_mtchk_param mtpar;
dcea992a 1630 struct xt_entry_match *ematch;
3bc3fe5e 1631
71ae0dff
FW
1632 e->counters.pcnt = xt_percpu_counter_alloc();
1633 if (IS_ERR_VALUE(e->counters.pcnt))
1634 return -ENOMEM;
3bc3fe5e 1635 j = 0;
f54e9367 1636 mtpar.net = net;
9b4fce7a
JE
1637 mtpar.table = name;
1638 mtpar.entryinfo = &e->ipv6;
1639 mtpar.hook_mask = e->comefrom;
916a917d 1640 mtpar.family = NFPROTO_IPV6;
dcea992a 1641 xt_ematch_foreach(ematch, e) {
6bdb331b 1642 ret = check_match(ematch, &mtpar);
dcea992a 1643 if (ret != 0)
6bdb331b
JE
1644 goto cleanup_matches;
1645 ++j;
dcea992a 1646 }
3bc3fe5e 1647
add67461 1648 ret = check_target(e, net, name);
3bc3fe5e
PM
1649 if (ret)
1650 goto cleanup_matches;
3bc3fe5e
PM
1651 return 0;
1652
1653 cleanup_matches:
6bdb331b
JE
1654 xt_ematch_foreach(ematch, e) {
1655 if (j-- == 0)
dcea992a 1656 break;
6bdb331b
JE
1657 cleanup_match(ematch, net);
1658 }
71ae0dff
FW
1659
1660 xt_percpu_counter_free(e->counters.pcnt);
1661
3bc3fe5e
PM
1662 return ret;
1663}
1664
1665static int
f54e9367
AD
1666translate_compat_table(struct net *net,
1667 const char *name,
3bc3fe5e
PM
1668 unsigned int valid_hooks,
1669 struct xt_table_info **pinfo,
1670 void **pentry0,
1671 unsigned int total_size,
1672 unsigned int number,
1673 unsigned int *hook_entries,
1674 unsigned int *underflows)
1675{
1676 unsigned int i, j;
1677 struct xt_table_info *newinfo, *info;
1678 void *pos, *entry0, *entry1;
72b2b1dd
JE
1679 struct compat_ip6t_entry *iter0;
1680 struct ip6t_entry *iter1;
3bc3fe5e 1681 unsigned int size;
72b2b1dd 1682 int ret = 0;
3bc3fe5e
PM
1683
1684 info = *pinfo;
1685 entry0 = *pentry0;
1686 size = total_size;
1687 info->number = number;
1688
1689 /* Init all hooks to impossible value. */
1690 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1691 info->hook_entry[i] = 0xFFFFFFFF;
1692 info->underflow[i] = 0xFFFFFFFF;
1693 }
1694
1695 duprintf("translate_compat_table: size %u\n", info->size);
1696 j = 0;
1697 xt_compat_lock(AF_INET6);
255d0dc3 1698 xt_compat_init_offsets(AF_INET6, number);
3bc3fe5e 1699 /* Walk through entries, checking offsets. */
72b2b1dd
JE
1700 xt_entry_foreach(iter0, entry0, total_size) {
1701 ret = check_compat_entry_size_and_hooks(iter0, info, &size,
6b4ff2d7
JE
1702 entry0,
1703 entry0 + total_size,
1704 hook_entries,
1705 underflows,
1706 name);
72b2b1dd 1707 if (ret != 0)
0559518b
JE
1708 goto out_unlock;
1709 ++j;
72b2b1dd 1710 }
3bc3fe5e
PM
1711
1712 ret = -EINVAL;
1713 if (j != number) {
1714 duprintf("translate_compat_table: %u not %u entries\n",
1715 j, number);
1716 goto out_unlock;
1717 }
1718
1719 /* Check hooks all assigned */
1720 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1721 /* Only hooks which are valid */
1722 if (!(valid_hooks & (1 << i)))
1723 continue;
1724 if (info->hook_entry[i] == 0xFFFFFFFF) {
1725 duprintf("Invalid hook entry %u %u\n",
1726 i, hook_entries[i]);
1727 goto out_unlock;
1728 }
1729 if (info->underflow[i] == 0xFFFFFFFF) {
1730 duprintf("Invalid underflow %u %u\n",
1731 i, underflows[i]);
1732 goto out_unlock;
1733 }
1734 }
1735
1736 ret = -ENOMEM;
1737 newinfo = xt_alloc_table_info(size);
1738 if (!newinfo)
1739 goto out_unlock;
1740
1741 newinfo->number = number;
1742 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1743 newinfo->hook_entry[i] = info->hook_entry[i];
1744 newinfo->underflow[i] = info->underflow[i];
1745 }
482cfc31 1746 entry1 = newinfo->entries;
3bc3fe5e
PM
1747 pos = entry1;
1748 size = total_size;
72b2b1dd 1749 xt_entry_foreach(iter0, entry0, total_size) {
6b4ff2d7
JE
1750 ret = compat_copy_entry_from_user(iter0, &pos, &size,
1751 name, newinfo, entry1);
72b2b1dd
JE
1752 if (ret != 0)
1753 break;
1754 }
3bc3fe5e
PM
1755 xt_compat_flush_offsets(AF_INET6);
1756 xt_compat_unlock(AF_INET6);
1757 if (ret)
1758 goto free_newinfo;
1759
1760 ret = -ELOOP;
1761 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1762 goto free_newinfo;
1763
1764 i = 0;
72b2b1dd 1765 xt_entry_foreach(iter1, entry1, newinfo->size) {
0559518b 1766 ret = compat_check_entry(iter1, net, name);
72b2b1dd
JE
1767 if (ret != 0)
1768 break;
0559518b 1769 ++i;
98dbbfc3
FW
1770 if (strcmp(ip6t_get_target(iter1)->u.user.name,
1771 XT_ERROR_TARGET) == 0)
1772 ++newinfo->stacksize;
72b2b1dd 1773 }
3bc3fe5e 1774 if (ret) {
72b2b1dd
JE
1775 /*
1776 * The first i matches need cleanup_entry (calls ->destroy)
1777 * because they had called ->check already. The other j-i
1778 * entries need only release.
1779 */
1780 int skip = i;
3bc3fe5e 1781 j -= i;
72b2b1dd
JE
1782 xt_entry_foreach(iter0, entry0, newinfo->size) {
1783 if (skip-- > 0)
1784 continue;
0559518b 1785 if (j-- == 0)
72b2b1dd 1786 break;
0559518b 1787 compat_release_entry(iter0);
72b2b1dd 1788 }
0559518b
JE
1789 xt_entry_foreach(iter1, entry1, newinfo->size) {
1790 if (i-- == 0)
72b2b1dd 1791 break;
0559518b
JE
1792 cleanup_entry(iter1, net);
1793 }
3bc3fe5e
PM
1794 xt_free_table_info(newinfo);
1795 return ret;
1796 }
1797
3bc3fe5e
PM
1798 *pinfo = newinfo;
1799 *pentry0 = entry1;
1800 xt_free_table_info(info);
1801 return 0;
1802
1803free_newinfo:
1804 xt_free_table_info(newinfo);
1805out:
0559518b
JE
1806 xt_entry_foreach(iter0, entry0, total_size) {
1807 if (j-- == 0)
72b2b1dd 1808 break;
0559518b
JE
1809 compat_release_entry(iter0);
1810 }
3bc3fe5e
PM
1811 return ret;
1812out_unlock:
1813 xt_compat_flush_offsets(AF_INET6);
1814 xt_compat_unlock(AF_INET6);
1815 goto out;
1816}
1817
1818static int
336b517f 1819compat_do_replace(struct net *net, void __user *user, unsigned int len)
3bc3fe5e
PM
1820{
1821 int ret;
1822 struct compat_ip6t_replace tmp;
1823 struct xt_table_info *newinfo;
1824 void *loc_cpu_entry;
72b2b1dd 1825 struct ip6t_entry *iter;
3bc3fe5e
PM
1826
1827 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1828 return -EFAULT;
1829
1830 /* overflow check */
1831 if (tmp.size >= INT_MAX / num_possible_cpus())
1832 return -ENOMEM;
1833 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1834 return -ENOMEM;
1086bbe9
DJ
1835 if (tmp.num_counters == 0)
1836 return -EINVAL;
1837
6a8ab060 1838 tmp.name[sizeof(tmp.name)-1] = 0;
3bc3fe5e
PM
1839
1840 newinfo = xt_alloc_table_info(tmp.size);
1841 if (!newinfo)
1842 return -ENOMEM;
1843
482cfc31 1844 loc_cpu_entry = newinfo->entries;
3bc3fe5e
PM
1845 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1846 tmp.size) != 0) {
1847 ret = -EFAULT;
1848 goto free_newinfo;
1849 }
1850
f54e9367 1851 ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
3bc3fe5e
PM
1852 &newinfo, &loc_cpu_entry, tmp.size,
1853 tmp.num_entries, tmp.hook_entry,
1854 tmp.underflow);
1855 if (ret != 0)
1856 goto free_newinfo;
1857
1858 duprintf("compat_do_replace: Translated table\n");
1859
336b517f 1860 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
3bc3fe5e
PM
1861 tmp.num_counters, compat_ptr(tmp.counters));
1862 if (ret)
1863 goto free_newinfo_untrans;
1864 return 0;
1865
1866 free_newinfo_untrans:
72b2b1dd 1867 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
0559518b 1868 cleanup_entry(iter, net);
3bc3fe5e
PM
1869 free_newinfo:
1870 xt_free_table_info(newinfo);
1871 return ret;
1872}
1873
1874static int
1875compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1876 unsigned int len)
1877{
1878 int ret;
1879
af31f412 1880 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
3bc3fe5e
PM
1881 return -EPERM;
1882
1883 switch (cmd) {
1884 case IP6T_SO_SET_REPLACE:
3b1e0a65 1885 ret = compat_do_replace(sock_net(sk), user, len);
3bc3fe5e
PM
1886 break;
1887
1888 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 1889 ret = do_add_counters(sock_net(sk), user, len, 1);
3bc3fe5e
PM
1890 break;
1891
1892 default:
1893 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1894 ret = -EINVAL;
1895 }
1896
1897 return ret;
1898}
1899
1900struct compat_ip6t_get_entries {
12b00c2c 1901 char name[XT_TABLE_MAXNAMELEN];
3bc3fe5e
PM
1902 compat_uint_t size;
1903 struct compat_ip6t_entry entrytable[0];
1904};
1905
1906static int
1907compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1908 void __user *userptr)
1909{
1910 struct xt_counters *counters;
5452e425 1911 const struct xt_table_info *private = table->private;
3bc3fe5e
PM
1912 void __user *pos;
1913 unsigned int size;
1914 int ret = 0;
3bc3fe5e 1915 unsigned int i = 0;
72b2b1dd 1916 struct ip6t_entry *iter;
3bc3fe5e
PM
1917
1918 counters = alloc_counters(table);
1919 if (IS_ERR(counters))
1920 return PTR_ERR(counters);
1921
3bc3fe5e
PM
1922 pos = userptr;
1923 size = total_size;
482cfc31 1924 xt_entry_foreach(iter, private->entries, total_size) {
72b2b1dd 1925 ret = compat_copy_entry_to_user(iter, &pos,
6b4ff2d7 1926 &size, counters, i++);
72b2b1dd
JE
1927 if (ret != 0)
1928 break;
1929 }
3bc3fe5e
PM
1930
1931 vfree(counters);
1932 return ret;
1933}
1934
1935static int
336b517f
AD
1936compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1937 int *len)
3bc3fe5e
PM
1938{
1939 int ret;
1940 struct compat_ip6t_get_entries get;
1941 struct xt_table *t;
1942
1943 if (*len < sizeof(get)) {
c9d8fe13 1944 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
3bc3fe5e
PM
1945 return -EINVAL;
1946 }
1947
1948 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1949 return -EFAULT;
1950
1951 if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
c9d8fe13
PM
1952 duprintf("compat_get_entries: %u != %zu\n",
1953 *len, sizeof(get) + get.size);
3bc3fe5e
PM
1954 return -EINVAL;
1955 }
b301f253 1956 get.name[sizeof(get.name) - 1] = '\0';
3bc3fe5e
PM
1957
1958 xt_compat_lock(AF_INET6);
336b517f 1959 t = xt_find_table_lock(net, AF_INET6, get.name);
0cc8d8df 1960 if (!IS_ERR_OR_NULL(t)) {
5452e425 1961 const struct xt_table_info *private = t->private;
3bc3fe5e 1962 struct xt_table_info info;
9c547959 1963 duprintf("t->private->number = %u\n", private->number);
3bc3fe5e
PM
1964 ret = compat_table_info(private, &info);
1965 if (!ret && get.size == info.size) {
1966 ret = compat_copy_entries_to_user(private->size,
1967 t, uptr->entrytable);
1968 } else if (!ret) {
1969 duprintf("compat_get_entries: I've got %u not %u!\n",
9c547959 1970 private->size, get.size);
544473c1 1971 ret = -EAGAIN;
3bc3fe5e
PM
1972 }
1973 xt_compat_flush_offsets(AF_INET6);
1974 module_put(t->me);
1975 xt_table_unlock(t);
1976 } else
1977 ret = t ? PTR_ERR(t) : -ENOENT;
1978
1979 xt_compat_unlock(AF_INET6);
1980 return ret;
1981}
1982
1983static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1984
1985static int
1986compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1987{
1988 int ret;
1989
af31f412 1990 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
3bc3fe5e
PM
1991 return -EPERM;
1992
1993 switch (cmd) {
1994 case IP6T_SO_GET_INFO:
3b1e0a65 1995 ret = get_info(sock_net(sk), user, len, 1);
3bc3fe5e
PM
1996 break;
1997 case IP6T_SO_GET_ENTRIES:
3b1e0a65 1998 ret = compat_get_entries(sock_net(sk), user, len);
3bc3fe5e
PM
1999 break;
2000 default:
2001 ret = do_ip6t_get_ctl(sk, cmd, user, len);
2002 }
2003 return ret;
2004}
2005#endif
2006
1da177e4
LT
2007static int
2008do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2009{
2010 int ret;
2011
af31f412 2012 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
1da177e4
LT
2013 return -EPERM;
2014
2015 switch (cmd) {
2016 case IP6T_SO_SET_REPLACE:
3b1e0a65 2017 ret = do_replace(sock_net(sk), user, len);
1da177e4
LT
2018 break;
2019
2020 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 2021 ret = do_add_counters(sock_net(sk), user, len, 0);
1da177e4
LT
2022 break;
2023
2024 default:
2025 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
2026 ret = -EINVAL;
2027 }
2028
2029 return ret;
2030}
2031
2032static int
2033do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2034{
2035 int ret;
2036
af31f412 2037 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
1da177e4
LT
2038 return -EPERM;
2039
2040 switch (cmd) {
433665c9 2041 case IP6T_SO_GET_INFO:
3b1e0a65 2042 ret = get_info(sock_net(sk), user, len, 0);
433665c9 2043 break;
1da177e4 2044
d924357c 2045 case IP6T_SO_GET_ENTRIES:
3b1e0a65 2046 ret = get_entries(sock_net(sk), user, len);
1da177e4 2047 break;
1da177e4 2048
6b7d31fc
HW
2049 case IP6T_SO_GET_REVISION_MATCH:
2050 case IP6T_SO_GET_REVISION_TARGET: {
12b00c2c 2051 struct xt_get_revision rev;
2e4e6a17 2052 int target;
6b7d31fc
HW
2053
2054 if (*len != sizeof(rev)) {
2055 ret = -EINVAL;
2056 break;
2057 }
2058 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2059 ret = -EFAULT;
2060 break;
2061 }
6a8ab060 2062 rev.name[sizeof(rev.name)-1] = 0;
6b7d31fc
HW
2063
2064 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 2065 target = 1;
6b7d31fc 2066 else
2e4e6a17 2067 target = 0;
6b7d31fc 2068
2e4e6a17
HW
2069 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2070 rev.revision,
2071 target, &ret),
6b7d31fc
HW
2072 "ip6t_%s", rev.name);
2073 break;
2074 }
2075
1da177e4
LT
2076 default:
2077 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2078 ret = -EINVAL;
2079 }
2080
2081 return ret;
2082}
2083
b9e69e12
FW
2084static void __ip6t_unregister_table(struct net *net, struct xt_table *table)
2085{
2086 struct xt_table_info *private;
2087 void *loc_cpu_entry;
2088 struct module *table_owner = table->me;
2089 struct ip6t_entry *iter;
2090
2091 private = xt_unregister_table(table);
2092
2093 /* Decrease module usage counts and free resources */
2094 loc_cpu_entry = private->entries;
2095 xt_entry_foreach(iter, loc_cpu_entry, private->size)
2096 cleanup_entry(iter, net);
2097 if (private->number > private->initial_entries)
2098 module_put(table_owner);
2099 xt_free_table_info(private);
2100}
2101
a67dd266
FW
2102int ip6t_register_table(struct net *net, const struct xt_table *table,
2103 const struct ip6t_replace *repl,
2104 const struct nf_hook_ops *ops,
2105 struct xt_table **res)
1da177e4
LT
2106{
2107 int ret;
2e4e6a17 2108 struct xt_table_info *newinfo;
f3c5c1bf 2109 struct xt_table_info bootstrap = {0};
31836064 2110 void *loc_cpu_entry;
a98da11d 2111 struct xt_table *new_table;
1da177e4 2112
2e4e6a17 2113 newinfo = xt_alloc_table_info(repl->size);
a67dd266
FW
2114 if (!newinfo)
2115 return -ENOMEM;
1da177e4 2116
482cfc31 2117 loc_cpu_entry = newinfo->entries;
31836064 2118 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4 2119
0f234214 2120 ret = translate_table(net, newinfo, loc_cpu_entry, repl);
44d34e72
AD
2121 if (ret != 0)
2122 goto out_free;
1da177e4 2123
336b517f 2124 new_table = xt_register_table(net, table, &bootstrap, newinfo);
a98da11d 2125 if (IS_ERR(new_table)) {
44d34e72
AD
2126 ret = PTR_ERR(new_table);
2127 goto out_free;
1da177e4 2128 }
a67dd266 2129
b9e69e12 2130 /* set res now, will see skbs right after nf_register_net_hooks */
a67dd266 2131 WRITE_ONCE(*res, new_table);
b9e69e12
FW
2132
2133 ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
2134 if (ret != 0) {
2135 __ip6t_unregister_table(net, new_table);
2136 *res = NULL;
2137 }
2138
a67dd266 2139 return ret;
1da177e4 2140
44d34e72
AD
2141out_free:
2142 xt_free_table_info(newinfo);
a67dd266 2143 return ret;
1da177e4
LT
2144}
2145
a67dd266
FW
2146void ip6t_unregister_table(struct net *net, struct xt_table *table,
2147 const struct nf_hook_ops *ops)
1da177e4 2148{
b9e69e12
FW
2149 nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
2150 __ip6t_unregister_table(net, table);
1da177e4
LT
2151}
2152
2153/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 2154static inline bool
1da177e4
LT
2155icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2156 u_int8_t type, u_int8_t code,
ccb79bdc 2157 bool invert)
1da177e4
LT
2158{
2159 return (type == test_type && code >= min_code && code <= max_code)
2160 ^ invert;
2161}
2162
1d93a9cb 2163static bool
62fc8051 2164icmp6_match(const struct sk_buff *skb, struct xt_action_param *par)
1da177e4 2165{
5452e425
JE
2166 const struct icmp6hdr *ic;
2167 struct icmp6hdr _icmph;
f7108a20 2168 const struct ip6t_icmp *icmpinfo = par->matchinfo;
1da177e4
LT
2169
2170 /* Must not be a fragment. */
f7108a20 2171 if (par->fragoff != 0)
1d93a9cb 2172 return false;
1da177e4 2173
f7108a20 2174 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
1da177e4
LT
2175 if (ic == NULL) {
2176 /* We've been asked to examine this packet, and we
9c547959
PM
2177 * can't. Hence, no choice but to drop.
2178 */
1da177e4 2179 duprintf("Dropping evil ICMP tinygram.\n");
b4ba2611 2180 par->hotdrop = true;
1d93a9cb 2181 return false;
1da177e4
LT
2182 }
2183
2184 return icmp6_type_code_match(icmpinfo->type,
2185 icmpinfo->code[0],
2186 icmpinfo->code[1],
2187 ic->icmp6_type, ic->icmp6_code,
2188 !!(icmpinfo->invflags&IP6T_ICMP_INV));
2189}
2190
2191/* Called when user tries to insert an entry of this type. */
b0f38452 2192static int icmp6_checkentry(const struct xt_mtchk_param *par)
1da177e4 2193{
9b4fce7a 2194 const struct ip6t_icmp *icmpinfo = par->matchinfo;
1da177e4 2195
7f939713 2196 /* Must specify no unknown invflags */
bd414ee6 2197 return (icmpinfo->invflags & ~IP6T_ICMP_INV) ? -EINVAL : 0;
1da177e4
LT
2198}
2199
2200/* The built-in targets: standard (NULL) and error. */
4538506b
JE
2201static struct xt_target ip6t_builtin_tg[] __read_mostly = {
2202 {
243bf6e2 2203 .name = XT_STANDARD_TARGET,
4538506b
JE
2204 .targetsize = sizeof(int),
2205 .family = NFPROTO_IPV6,
3bc3fe5e 2206#ifdef CONFIG_COMPAT
4538506b
JE
2207 .compatsize = sizeof(compat_int_t),
2208 .compat_from_user = compat_standard_from_user,
2209 .compat_to_user = compat_standard_to_user,
3bc3fe5e 2210#endif
4538506b
JE
2211 },
2212 {
243bf6e2 2213 .name = XT_ERROR_TARGET,
4538506b 2214 .target = ip6t_error,
12b00c2c 2215 .targetsize = XT_FUNCTION_MAXNAMELEN,
4538506b
JE
2216 .family = NFPROTO_IPV6,
2217 },
1da177e4
LT
2218};
2219
2220static struct nf_sockopt_ops ip6t_sockopts = {
2221 .pf = PF_INET6,
2222 .set_optmin = IP6T_BASE_CTL,
2223 .set_optmax = IP6T_SO_SET_MAX+1,
2224 .set = do_ip6t_set_ctl,
3bc3fe5e
PM
2225#ifdef CONFIG_COMPAT
2226 .compat_set = compat_do_ip6t_set_ctl,
2227#endif
1da177e4
LT
2228 .get_optmin = IP6T_BASE_CTL,
2229 .get_optmax = IP6T_SO_GET_MAX+1,
2230 .get = do_ip6t_get_ctl,
3bc3fe5e
PM
2231#ifdef CONFIG_COMPAT
2232 .compat_get = compat_do_ip6t_get_ctl,
2233#endif
16fcec35 2234 .owner = THIS_MODULE,
1da177e4
LT
2235};
2236
4538506b
JE
2237static struct xt_match ip6t_builtin_mt[] __read_mostly = {
2238 {
2239 .name = "icmp6",
2240 .match = icmp6_match,
2241 .matchsize = sizeof(struct ip6t_icmp),
2242 .checkentry = icmp6_checkentry,
2243 .proto = IPPROTO_ICMPV6,
2244 .family = NFPROTO_IPV6,
2245 },
1da177e4
LT
2246};
2247
3cb609d5
AD
2248static int __net_init ip6_tables_net_init(struct net *net)
2249{
383ca5b8 2250 return xt_proto_init(net, NFPROTO_IPV6);
3cb609d5
AD
2251}
2252
2253static void __net_exit ip6_tables_net_exit(struct net *net)
2254{
383ca5b8 2255 xt_proto_fini(net, NFPROTO_IPV6);
3cb609d5
AD
2256}
2257
2258static struct pernet_operations ip6_tables_net_ops = {
2259 .init = ip6_tables_net_init,
2260 .exit = ip6_tables_net_exit,
2261};
2262
65b4b4e8 2263static int __init ip6_tables_init(void)
1da177e4
LT
2264{
2265 int ret;
2266
3cb609d5 2267 ret = register_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2268 if (ret < 0)
2269 goto err1;
2e4e6a17 2270
25985edc 2271 /* No one else will be downing sem now, so we won't sleep */
4538506b 2272 ret = xt_register_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
0eff66e6
PM
2273 if (ret < 0)
2274 goto err2;
4538506b 2275 ret = xt_register_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
0eff66e6
PM
2276 if (ret < 0)
2277 goto err4;
1da177e4
LT
2278
2279 /* Register setsockopt */
2280 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
2281 if (ret < 0)
2282 goto err5;
1da177e4 2283
ff67e4e4 2284 pr_info("(C) 2000-2006 Netfilter Core Team\n");
1da177e4 2285 return 0;
0eff66e6
PM
2286
2287err5:
4538506b 2288 xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
0eff66e6 2289err4:
4538506b 2290 xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
0eff66e6 2291err2:
3cb609d5 2292 unregister_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2293err1:
2294 return ret;
1da177e4
LT
2295}
2296
65b4b4e8 2297static void __exit ip6_tables_fini(void)
1da177e4
LT
2298{
2299 nf_unregister_sockopt(&ip6t_sockopts);
9c547959 2300
4538506b
JE
2301 xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
2302 xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
3cb609d5 2303 unregister_pernet_subsys(&ip6_tables_net_ops);
1da177e4
LT
2304}
2305
2306EXPORT_SYMBOL(ip6t_register_table);
2307EXPORT_SYMBOL(ip6t_unregister_table);
2308EXPORT_SYMBOL(ip6t_do_table);
1da177e4 2309
65b4b4e8
AM
2310module_init(ip6_tables_init);
2311module_exit(ip6_tables_fini);