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