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