netfilter: x_tables: validate e->target_offset early
[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) */
47901dc2 201static inline bool unconditional(const struct ip6t_ip6 *ipv6)
ba9dda3a 202{
47901dc2 203 static const struct ip6t_ip6 uncond;
ba9dda3a 204
47901dc2 205 return memcmp(ipv6, &uncond, sizeof(uncond)) == 0;
ba9dda3a
JK
206}
207
87a2e70d 208static inline const struct xt_entry_target *
d5d1baa1
JE
209ip6t_get_target_c(const struct ip6t_entry *e)
210{
211 return ip6t_get_target((struct ip6t_entry *)e);
212}
213
07a93626 214#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE)
ba9dda3a 215/* This cries for unification! */
022748a9 216static const char *const hooknames[] = {
6e23ae2a
PM
217 [NF_INET_PRE_ROUTING] = "PREROUTING",
218 [NF_INET_LOCAL_IN] = "INPUT",
219 [NF_INET_FORWARD] = "FORWARD",
220 [NF_INET_LOCAL_OUT] = "OUTPUT",
221 [NF_INET_POST_ROUTING] = "POSTROUTING",
ba9dda3a
JK
222};
223
224enum nf_ip_trace_comments {
225 NF_IP6_TRACE_COMMENT_RULE,
226 NF_IP6_TRACE_COMMENT_RETURN,
227 NF_IP6_TRACE_COMMENT_POLICY,
228};
229
022748a9 230static const char *const comments[] = {
ba9dda3a
JK
231 [NF_IP6_TRACE_COMMENT_RULE] = "rule",
232 [NF_IP6_TRACE_COMMENT_RETURN] = "return",
233 [NF_IP6_TRACE_COMMENT_POLICY] = "policy",
234};
235
236static struct nf_loginfo trace_loginfo = {
237 .type = NF_LOG_TYPE_LOG,
238 .u = {
239 .log = {
a81b2ce8 240 .level = LOGLEVEL_WARNING,
ba9dda3a
JK
241 .logflags = NF_LOG_MASK,
242 },
243 },
244};
245
022748a9 246/* Mildly perf critical (only if packet tracing is on) */
ba9dda3a 247static inline int
d5d1baa1 248get_chainname_rulenum(const struct ip6t_entry *s, const struct ip6t_entry *e,
4f2f6f23
JE
249 const char *hookname, const char **chainname,
250 const char **comment, unsigned int *rulenum)
ba9dda3a 251{
87a2e70d 252 const struct xt_standard_target *t = (void *)ip6t_get_target_c(s);
ba9dda3a 253
243bf6e2 254 if (strcmp(t->target.u.kernel.target->name, XT_ERROR_TARGET) == 0) {
ba9dda3a
JK
255 /* Head of user chain: ERROR target with chainname */
256 *chainname = t->target.data;
257 (*rulenum) = 0;
258 } else if (s == e) {
259 (*rulenum)++;
260
3666ed1c
JP
261 if (s->target_offset == sizeof(struct ip6t_entry) &&
262 strcmp(t->target.u.kernel.target->name,
243bf6e2 263 XT_STANDARD_TARGET) == 0 &&
3666ed1c
JP
264 t->verdict < 0 &&
265 unconditional(&s->ipv6)) {
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. */
3666ed1c
JP
491 if ((e->target_offset == sizeof(struct ip6t_entry) &&
492 (strcmp(t->target.u.user.name,
243bf6e2 493 XT_STANDARD_TARGET) == 0) &&
3666ed1c
JP
494 t->verdict < 0 &&
495 unconditional(&e->ipv6)) || visited) {
1da177e4
LT
496 unsigned int oldpos, size;
497
1f9352ae 498 if ((strcmp(t->target.u.user.name,
243bf6e2 499 XT_STANDARD_TARGET) == 0) &&
1f9352ae 500 t->verdict < -NF_MAX_VERDICT - 1) {
74c9c0c1
DM
501 duprintf("mark_source_chains: bad "
502 "negative verdict (%i)\n",
503 t->verdict);
504 return 0;
505 }
506
1da177e4
LT
507 /* Return: backtrack through the last
508 big jump. */
509 do {
6e23ae2a 510 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
1da177e4
LT
511#ifdef DEBUG_IP_FIREWALL_USER
512 if (e->comefrom
6e23ae2a 513 & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
514 duprintf("Back unset "
515 "on hook %u "
516 "rule %u\n",
517 hook, pos);
518 }
519#endif
520 oldpos = pos;
521 pos = e->counters.pcnt;
522 e->counters.pcnt = 0;
523
524 /* We're at the start. */
525 if (pos == oldpos)
526 goto next;
527
528 e = (struct ip6t_entry *)
31836064 529 (entry0 + pos);
1da177e4
LT
530 } while (oldpos == pos + e->next_offset);
531
532 /* Move along one */
533 size = e->next_offset;
534 e = (struct ip6t_entry *)
31836064 535 (entry0 + pos + size);
1da177e4
LT
536 e->counters.pcnt = pos;
537 pos += size;
538 } else {
539 int newpos = t->verdict;
540
541 if (strcmp(t->target.u.user.name,
243bf6e2 542 XT_STANDARD_TARGET) == 0 &&
3666ed1c 543 newpos >= 0) {
74c9c0c1
DM
544 if (newpos > newinfo->size -
545 sizeof(struct ip6t_entry)) {
546 duprintf("mark_source_chains: "
547 "bad verdict (%i)\n",
548 newpos);
549 return 0;
550 }
1da177e4
LT
551 /* This a jump; chase it. */
552 duprintf("Jump rule %u -> %u\n",
553 pos, newpos);
554 } else {
555 /* ... this is a fallthru */
556 newpos = pos + e->next_offset;
557 }
558 e = (struct ip6t_entry *)
31836064 559 (entry0 + newpos);
1da177e4
LT
560 e->counters.pcnt = pos;
561 pos = newpos;
562 }
563 }
6ac94619 564next:
1da177e4
LT
565 duprintf("Finished chain %u\n", hook);
566 }
567 return 1;
568}
569
87a2e70d 570static void cleanup_match(struct xt_entry_match *m, struct net *net)
1da177e4 571{
6be3d859
JE
572 struct xt_mtdtor_param par;
573
f54e9367 574 par.net = net;
6be3d859
JE
575 par.match = m->u.kernel.match;
576 par.matchinfo = m->data;
916a917d 577 par.family = NFPROTO_IPV6;
6be3d859
JE
578 if (par.match->destroy != NULL)
579 par.match->destroy(&par);
580 module_put(par.match->me);
1da177e4
LT
581}
582
022748a9 583static int
bdf533de 584check_entry(const struct ip6t_entry *e)
f173c8a1 585{
87a2e70d 586 const struct xt_entry_target *t;
f173c8a1 587
bdf533de 588 if (!ip6_checkentry(&e->ipv6))
f173c8a1 589 return -EINVAL;
f173c8a1 590
87a2e70d 591 if (e->target_offset + sizeof(struct xt_entry_target) >
f173c8a1
PM
592 e->next_offset)
593 return -EINVAL;
594
d5d1baa1 595 t = ip6t_get_target_c(e);
f173c8a1
PM
596 if (e->target_offset + t->u.target_size > e->next_offset)
597 return -EINVAL;
598
599 return 0;
600}
601
87a2e70d 602static int check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
f173c8a1 603{
9b4fce7a 604 const struct ip6t_ip6 *ipv6 = par->entryinfo;
f173c8a1
PM
605 int ret;
606
9b4fce7a
JE
607 par->match = m->u.kernel.match;
608 par->matchinfo = m->data;
609
916a917d 610 ret = xt_check_match(par, m->u.match_size - sizeof(*m),
9b4fce7a 611 ipv6->proto, ipv6->invflags & IP6T_INV_PROTO);
367c6790 612 if (ret < 0) {
f173c8a1 613 duprintf("ip_tables: check failed for `%s'.\n",
9b4fce7a 614 par.match->name);
367c6790 615 return ret;
f173c8a1 616 }
367c6790 617 return 0;
f173c8a1
PM
618}
619
022748a9 620static int
87a2e70d 621find_check_match(struct xt_entry_match *m, struct xt_mtchk_param *par)
1da177e4 622{
6709dbbb 623 struct xt_match *match;
3cdc7c95 624 int ret;
1da177e4 625
fd0ec0e6
JE
626 match = xt_request_find_match(NFPROTO_IPV6, m->u.user.name,
627 m->u.user.revision);
628 if (IS_ERR(match)) {
f173c8a1 629 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
fd0ec0e6 630 return PTR_ERR(match);
1da177e4
LT
631 }
632 m->u.kernel.match = match;
1da177e4 633
6bdb331b 634 ret = check_match(m, par);
3cdc7c95
PM
635 if (ret)
636 goto err;
637
1da177e4 638 return 0;
3cdc7c95
PM
639err:
640 module_put(m->u.kernel.match->me);
641 return ret;
1da177e4
LT
642}
643
add67461 644static int check_target(struct ip6t_entry *e, struct net *net, const char *name)
1da177e4 645{
87a2e70d 646 struct xt_entry_target *t = ip6t_get_target(e);
af5d6dc2 647 struct xt_tgchk_param par = {
add67461 648 .net = net,
af5d6dc2
JE
649 .table = name,
650 .entryinfo = e,
651 .target = t->u.kernel.target,
652 .targinfo = t->data,
653 .hook_mask = e->comefrom,
916a917d 654 .family = NFPROTO_IPV6,
af5d6dc2 655 };
1da177e4 656 int ret;
1da177e4 657
f173c8a1 658 t = ip6t_get_target(e);
916a917d 659 ret = xt_check_target(&par, t->u.target_size - sizeof(*t),
af5d6dc2 660 e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
367c6790 661 if (ret < 0) {
f173c8a1
PM
662 duprintf("ip_tables: check failed for `%s'.\n",
663 t->u.kernel.target->name);
367c6790 664 return ret;
1da177e4 665 }
367c6790 666 return 0;
f173c8a1 667}
1da177e4 668
022748a9 669static int
a83d8e8d 670find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
0559518b 671 unsigned int size)
f173c8a1 672{
87a2e70d 673 struct xt_entry_target *t;
f173c8a1
PM
674 struct xt_target *target;
675 int ret;
676 unsigned int j;
9b4fce7a 677 struct xt_mtchk_param mtpar;
dcea992a 678 struct xt_entry_match *ematch;
f173c8a1 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
730 if (!unconditional(&e->ipv6))
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
JP
752 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
753 (unsigned char *)e + sizeof(struct ip6t_entry) >= 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
JE
776 if (!check_underflow(e)) {
777 pr_err("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
JP
1506 if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
1507 (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
3bc3fe5e
PM
1508 duprintf("Bad offset %p, limit = %p\n", e, limit);
1509 return -EINVAL;
1510 }
1511
1512 if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1513 sizeof(struct compat_xt_entry_target)) {
1514 duprintf("checking: element %p size %u\n",
1515 e, e->next_offset);
1516 return -EINVAL;
1517 }
1518
1519 /* For purposes of check_entry casting the compat entry is fine */
bdf533de 1520 ret = check_entry((struct ip6t_entry *)e);
3bc3fe5e
PM
1521 if (ret)
1522 return ret;
1523
1524 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1525 entry_offset = (void *)e - (void *)base;
1526 j = 0;
dcea992a 1527 xt_ematch_foreach(ematch, e) {
2f06550b 1528 ret = compat_find_calc_match(ematch, name, &e->ipv6, &off);
dcea992a 1529 if (ret != 0)
6bdb331b
JE
1530 goto release_matches;
1531 ++j;
dcea992a 1532 }
3bc3fe5e
PM
1533
1534 t = compat_ip6t_get_target(e);
d2a7b6ba
JE
1535 target = xt_request_find_target(NFPROTO_IPV6, t->u.user.name,
1536 t->u.user.revision);
1537 if (IS_ERR(target)) {
3bc3fe5e
PM
1538 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1539 t->u.user.name);
d2a7b6ba 1540 ret = PTR_ERR(target);
3bc3fe5e
PM
1541 goto release_matches;
1542 }
1543 t->u.kernel.target = target;
1544
1545 off += xt_compat_target_offset(target);
1546 *size += off;
1547 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1548 if (ret)
1549 goto out;
1550
1551 /* Check hooks & underflows */
1552 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1553 if ((unsigned char *)e - base == hook_entries[h])
1554 newinfo->hook_entry[h] = hook_entries[h];
1555 if ((unsigned char *)e - base == underflows[h])
1556 newinfo->underflow[h] = underflows[h];
1557 }
1558
1559 /* Clear counters and comefrom */
1560 memset(&e->counters, 0, sizeof(e->counters));
1561 e->comefrom = 0;
3bc3fe5e
PM
1562 return 0;
1563
1564out:
1565 module_put(t->u.kernel.target->me);
1566release_matches:
6bdb331b
JE
1567 xt_ematch_foreach(ematch, e) {
1568 if (j-- == 0)
dcea992a 1569 break;
6bdb331b
JE
1570 module_put(ematch->u.kernel.match->me);
1571 }
3bc3fe5e
PM
1572 return ret;
1573}
1574
1575static int
1576compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1577 unsigned int *size, const char *name,
1578 struct xt_table_info *newinfo, unsigned char *base)
1579{
87a2e70d 1580 struct xt_entry_target *t;
3bc3fe5e
PM
1581 struct ip6t_entry *de;
1582 unsigned int origsize;
1583 int ret, h;
dcea992a 1584 struct xt_entry_match *ematch;
3bc3fe5e
PM
1585
1586 ret = 0;
1587 origsize = *size;
1588 de = (struct ip6t_entry *)*dstptr;
1589 memcpy(de, e, sizeof(struct ip6t_entry));
1590 memcpy(&de->counters, &e->counters, sizeof(e->counters));
1591
1592 *dstptr += sizeof(struct ip6t_entry);
1593 *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1594
dcea992a
JE
1595 xt_ematch_foreach(ematch, e) {
1596 ret = xt_compat_match_from_user(ematch, dstptr, size);
1597 if (ret != 0)
6bdb331b 1598 return ret;
dcea992a 1599 }
3bc3fe5e
PM
1600 de->target_offset = e->target_offset - (origsize - *size);
1601 t = compat_ip6t_get_target(e);
3bc3fe5e
PM
1602 xt_compat_target_from_user(t, dstptr, size);
1603
1604 de->next_offset = e->next_offset - (origsize - *size);
1605 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1606 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1607 newinfo->hook_entry[h] -= origsize - *size;
1608 if ((unsigned char *)de - base < newinfo->underflow[h])
1609 newinfo->underflow[h] -= origsize - *size;
1610 }
1611 return ret;
1612}
1613
f54e9367 1614static int compat_check_entry(struct ip6t_entry *e, struct net *net,
0559518b 1615 const char *name)
3bc3fe5e 1616{
b0a6363c 1617 unsigned int j;
dcea992a 1618 int ret = 0;
9b4fce7a 1619 struct xt_mtchk_param mtpar;
dcea992a 1620 struct xt_entry_match *ematch;
3bc3fe5e 1621
71ae0dff
FW
1622 e->counters.pcnt = xt_percpu_counter_alloc();
1623 if (IS_ERR_VALUE(e->counters.pcnt))
1624 return -ENOMEM;
3bc3fe5e 1625 j = 0;
f54e9367 1626 mtpar.net = net;
9b4fce7a
JE
1627 mtpar.table = name;
1628 mtpar.entryinfo = &e->ipv6;
1629 mtpar.hook_mask = e->comefrom;
916a917d 1630 mtpar.family = NFPROTO_IPV6;
dcea992a 1631 xt_ematch_foreach(ematch, e) {
6bdb331b 1632 ret = check_match(ematch, &mtpar);
dcea992a 1633 if (ret != 0)
6bdb331b
JE
1634 goto cleanup_matches;
1635 ++j;
dcea992a 1636 }
3bc3fe5e 1637
add67461 1638 ret = check_target(e, net, name);
3bc3fe5e
PM
1639 if (ret)
1640 goto cleanup_matches;
3bc3fe5e
PM
1641 return 0;
1642
1643 cleanup_matches:
6bdb331b
JE
1644 xt_ematch_foreach(ematch, e) {
1645 if (j-- == 0)
dcea992a 1646 break;
6bdb331b
JE
1647 cleanup_match(ematch, net);
1648 }
71ae0dff
FW
1649
1650 xt_percpu_counter_free(e->counters.pcnt);
1651
3bc3fe5e
PM
1652 return ret;
1653}
1654
1655static int
f54e9367
AD
1656translate_compat_table(struct net *net,
1657 const char *name,
3bc3fe5e
PM
1658 unsigned int valid_hooks,
1659 struct xt_table_info **pinfo,
1660 void **pentry0,
1661 unsigned int total_size,
1662 unsigned int number,
1663 unsigned int *hook_entries,
1664 unsigned int *underflows)
1665{
1666 unsigned int i, j;
1667 struct xt_table_info *newinfo, *info;
1668 void *pos, *entry0, *entry1;
72b2b1dd
JE
1669 struct compat_ip6t_entry *iter0;
1670 struct ip6t_entry *iter1;
3bc3fe5e 1671 unsigned int size;
72b2b1dd 1672 int ret = 0;
3bc3fe5e
PM
1673
1674 info = *pinfo;
1675 entry0 = *pentry0;
1676 size = total_size;
1677 info->number = number;
1678
1679 /* Init all hooks to impossible value. */
1680 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1681 info->hook_entry[i] = 0xFFFFFFFF;
1682 info->underflow[i] = 0xFFFFFFFF;
1683 }
1684
1685 duprintf("translate_compat_table: size %u\n", info->size);
1686 j = 0;
1687 xt_compat_lock(AF_INET6);
255d0dc3 1688 xt_compat_init_offsets(AF_INET6, number);
3bc3fe5e 1689 /* Walk through entries, checking offsets. */
72b2b1dd
JE
1690 xt_entry_foreach(iter0, entry0, total_size) {
1691 ret = check_compat_entry_size_and_hooks(iter0, info, &size,
6b4ff2d7
JE
1692 entry0,
1693 entry0 + total_size,
1694 hook_entries,
1695 underflows,
1696 name);
72b2b1dd 1697 if (ret != 0)
0559518b
JE
1698 goto out_unlock;
1699 ++j;
72b2b1dd 1700 }
3bc3fe5e
PM
1701
1702 ret = -EINVAL;
1703 if (j != number) {
1704 duprintf("translate_compat_table: %u not %u entries\n",
1705 j, number);
1706 goto out_unlock;
1707 }
1708
1709 /* Check hooks all assigned */
1710 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1711 /* Only hooks which are valid */
1712 if (!(valid_hooks & (1 << i)))
1713 continue;
1714 if (info->hook_entry[i] == 0xFFFFFFFF) {
1715 duprintf("Invalid hook entry %u %u\n",
1716 i, hook_entries[i]);
1717 goto out_unlock;
1718 }
1719 if (info->underflow[i] == 0xFFFFFFFF) {
1720 duprintf("Invalid underflow %u %u\n",
1721 i, underflows[i]);
1722 goto out_unlock;
1723 }
1724 }
1725
1726 ret = -ENOMEM;
1727 newinfo = xt_alloc_table_info(size);
1728 if (!newinfo)
1729 goto out_unlock;
1730
1731 newinfo->number = number;
1732 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1733 newinfo->hook_entry[i] = info->hook_entry[i];
1734 newinfo->underflow[i] = info->underflow[i];
1735 }
482cfc31 1736 entry1 = newinfo->entries;
3bc3fe5e
PM
1737 pos = entry1;
1738 size = total_size;
72b2b1dd 1739 xt_entry_foreach(iter0, entry0, total_size) {
6b4ff2d7
JE
1740 ret = compat_copy_entry_from_user(iter0, &pos, &size,
1741 name, newinfo, entry1);
72b2b1dd
JE
1742 if (ret != 0)
1743 break;
1744 }
3bc3fe5e
PM
1745 xt_compat_flush_offsets(AF_INET6);
1746 xt_compat_unlock(AF_INET6);
1747 if (ret)
1748 goto free_newinfo;
1749
1750 ret = -ELOOP;
1751 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1752 goto free_newinfo;
1753
1754 i = 0;
72b2b1dd 1755 xt_entry_foreach(iter1, entry1, newinfo->size) {
0559518b 1756 ret = compat_check_entry(iter1, net, name);
72b2b1dd
JE
1757 if (ret != 0)
1758 break;
0559518b 1759 ++i;
98dbbfc3
FW
1760 if (strcmp(ip6t_get_target(iter1)->u.user.name,
1761 XT_ERROR_TARGET) == 0)
1762 ++newinfo->stacksize;
72b2b1dd 1763 }
3bc3fe5e 1764 if (ret) {
72b2b1dd
JE
1765 /*
1766 * The first i matches need cleanup_entry (calls ->destroy)
1767 * because they had called ->check already. The other j-i
1768 * entries need only release.
1769 */
1770 int skip = i;
3bc3fe5e 1771 j -= i;
72b2b1dd
JE
1772 xt_entry_foreach(iter0, entry0, newinfo->size) {
1773 if (skip-- > 0)
1774 continue;
0559518b 1775 if (j-- == 0)
72b2b1dd 1776 break;
0559518b 1777 compat_release_entry(iter0);
72b2b1dd 1778 }
0559518b
JE
1779 xt_entry_foreach(iter1, entry1, newinfo->size) {
1780 if (i-- == 0)
72b2b1dd 1781 break;
0559518b
JE
1782 cleanup_entry(iter1, net);
1783 }
3bc3fe5e
PM
1784 xt_free_table_info(newinfo);
1785 return ret;
1786 }
1787
3bc3fe5e
PM
1788 *pinfo = newinfo;
1789 *pentry0 = entry1;
1790 xt_free_table_info(info);
1791 return 0;
1792
1793free_newinfo:
1794 xt_free_table_info(newinfo);
1795out:
0559518b
JE
1796 xt_entry_foreach(iter0, entry0, total_size) {
1797 if (j-- == 0)
72b2b1dd 1798 break;
0559518b
JE
1799 compat_release_entry(iter0);
1800 }
3bc3fe5e
PM
1801 return ret;
1802out_unlock:
1803 xt_compat_flush_offsets(AF_INET6);
1804 xt_compat_unlock(AF_INET6);
1805 goto out;
1806}
1807
1808static int
336b517f 1809compat_do_replace(struct net *net, void __user *user, unsigned int len)
3bc3fe5e
PM
1810{
1811 int ret;
1812 struct compat_ip6t_replace tmp;
1813 struct xt_table_info *newinfo;
1814 void *loc_cpu_entry;
72b2b1dd 1815 struct ip6t_entry *iter;
3bc3fe5e
PM
1816
1817 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1818 return -EFAULT;
1819
1820 /* overflow check */
1821 if (tmp.size >= INT_MAX / num_possible_cpus())
1822 return -ENOMEM;
1823 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1824 return -ENOMEM;
1086bbe9
DJ
1825 if (tmp.num_counters == 0)
1826 return -EINVAL;
1827
6a8ab060 1828 tmp.name[sizeof(tmp.name)-1] = 0;
3bc3fe5e
PM
1829
1830 newinfo = xt_alloc_table_info(tmp.size);
1831 if (!newinfo)
1832 return -ENOMEM;
1833
482cfc31 1834 loc_cpu_entry = newinfo->entries;
3bc3fe5e
PM
1835 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1836 tmp.size) != 0) {
1837 ret = -EFAULT;
1838 goto free_newinfo;
1839 }
1840
f54e9367 1841 ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
3bc3fe5e
PM
1842 &newinfo, &loc_cpu_entry, tmp.size,
1843 tmp.num_entries, tmp.hook_entry,
1844 tmp.underflow);
1845 if (ret != 0)
1846 goto free_newinfo;
1847
1848 duprintf("compat_do_replace: Translated table\n");
1849
336b517f 1850 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
3bc3fe5e
PM
1851 tmp.num_counters, compat_ptr(tmp.counters));
1852 if (ret)
1853 goto free_newinfo_untrans;
1854 return 0;
1855
1856 free_newinfo_untrans:
72b2b1dd 1857 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
0559518b 1858 cleanup_entry(iter, net);
3bc3fe5e
PM
1859 free_newinfo:
1860 xt_free_table_info(newinfo);
1861 return ret;
1862}
1863
1864static int
1865compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1866 unsigned int len)
1867{
1868 int ret;
1869
af31f412 1870 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
3bc3fe5e
PM
1871 return -EPERM;
1872
1873 switch (cmd) {
1874 case IP6T_SO_SET_REPLACE:
3b1e0a65 1875 ret = compat_do_replace(sock_net(sk), user, len);
3bc3fe5e
PM
1876 break;
1877
1878 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 1879 ret = do_add_counters(sock_net(sk), user, len, 1);
3bc3fe5e
PM
1880 break;
1881
1882 default:
1883 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1884 ret = -EINVAL;
1885 }
1886
1887 return ret;
1888}
1889
1890struct compat_ip6t_get_entries {
12b00c2c 1891 char name[XT_TABLE_MAXNAMELEN];
3bc3fe5e
PM
1892 compat_uint_t size;
1893 struct compat_ip6t_entry entrytable[0];
1894};
1895
1896static int
1897compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1898 void __user *userptr)
1899{
1900 struct xt_counters *counters;
5452e425 1901 const struct xt_table_info *private = table->private;
3bc3fe5e
PM
1902 void __user *pos;
1903 unsigned int size;
1904 int ret = 0;
3bc3fe5e 1905 unsigned int i = 0;
72b2b1dd 1906 struct ip6t_entry *iter;
3bc3fe5e
PM
1907
1908 counters = alloc_counters(table);
1909 if (IS_ERR(counters))
1910 return PTR_ERR(counters);
1911
3bc3fe5e
PM
1912 pos = userptr;
1913 size = total_size;
482cfc31 1914 xt_entry_foreach(iter, private->entries, total_size) {
72b2b1dd 1915 ret = compat_copy_entry_to_user(iter, &pos,
6b4ff2d7 1916 &size, counters, i++);
72b2b1dd
JE
1917 if (ret != 0)
1918 break;
1919 }
3bc3fe5e
PM
1920
1921 vfree(counters);
1922 return ret;
1923}
1924
1925static int
336b517f
AD
1926compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1927 int *len)
3bc3fe5e
PM
1928{
1929 int ret;
1930 struct compat_ip6t_get_entries get;
1931 struct xt_table *t;
1932
1933 if (*len < sizeof(get)) {
c9d8fe13 1934 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
3bc3fe5e
PM
1935 return -EINVAL;
1936 }
1937
1938 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1939 return -EFAULT;
1940
1941 if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
c9d8fe13
PM
1942 duprintf("compat_get_entries: %u != %zu\n",
1943 *len, sizeof(get) + get.size);
3bc3fe5e
PM
1944 return -EINVAL;
1945 }
1946
1947 xt_compat_lock(AF_INET6);
336b517f 1948 t = xt_find_table_lock(net, AF_INET6, get.name);
0cc8d8df 1949 if (!IS_ERR_OR_NULL(t)) {
5452e425 1950 const struct xt_table_info *private = t->private;
3bc3fe5e 1951 struct xt_table_info info;
9c547959 1952 duprintf("t->private->number = %u\n", private->number);
3bc3fe5e
PM
1953 ret = compat_table_info(private, &info);
1954 if (!ret && get.size == info.size) {
1955 ret = compat_copy_entries_to_user(private->size,
1956 t, uptr->entrytable);
1957 } else if (!ret) {
1958 duprintf("compat_get_entries: I've got %u not %u!\n",
9c547959 1959 private->size, get.size);
544473c1 1960 ret = -EAGAIN;
3bc3fe5e
PM
1961 }
1962 xt_compat_flush_offsets(AF_INET6);
1963 module_put(t->me);
1964 xt_table_unlock(t);
1965 } else
1966 ret = t ? PTR_ERR(t) : -ENOENT;
1967
1968 xt_compat_unlock(AF_INET6);
1969 return ret;
1970}
1971
1972static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1973
1974static int
1975compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1976{
1977 int ret;
1978
af31f412 1979 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
3bc3fe5e
PM
1980 return -EPERM;
1981
1982 switch (cmd) {
1983 case IP6T_SO_GET_INFO:
3b1e0a65 1984 ret = get_info(sock_net(sk), user, len, 1);
3bc3fe5e
PM
1985 break;
1986 case IP6T_SO_GET_ENTRIES:
3b1e0a65 1987 ret = compat_get_entries(sock_net(sk), user, len);
3bc3fe5e
PM
1988 break;
1989 default:
1990 ret = do_ip6t_get_ctl(sk, cmd, user, len);
1991 }
1992 return ret;
1993}
1994#endif
1995
1da177e4
LT
1996static int
1997do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1998{
1999 int ret;
2000
af31f412 2001 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
1da177e4
LT
2002 return -EPERM;
2003
2004 switch (cmd) {
2005 case IP6T_SO_SET_REPLACE:
3b1e0a65 2006 ret = do_replace(sock_net(sk), user, len);
1da177e4
LT
2007 break;
2008
2009 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 2010 ret = do_add_counters(sock_net(sk), user, len, 0);
1da177e4
LT
2011 break;
2012
2013 default:
2014 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
2015 ret = -EINVAL;
2016 }
2017
2018 return ret;
2019}
2020
2021static int
2022do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2023{
2024 int ret;
2025
af31f412 2026 if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
1da177e4
LT
2027 return -EPERM;
2028
2029 switch (cmd) {
433665c9 2030 case IP6T_SO_GET_INFO:
3b1e0a65 2031 ret = get_info(sock_net(sk), user, len, 0);
433665c9 2032 break;
1da177e4 2033
d924357c 2034 case IP6T_SO_GET_ENTRIES:
3b1e0a65 2035 ret = get_entries(sock_net(sk), user, len);
1da177e4 2036 break;
1da177e4 2037
6b7d31fc
HW
2038 case IP6T_SO_GET_REVISION_MATCH:
2039 case IP6T_SO_GET_REVISION_TARGET: {
12b00c2c 2040 struct xt_get_revision rev;
2e4e6a17 2041 int target;
6b7d31fc
HW
2042
2043 if (*len != sizeof(rev)) {
2044 ret = -EINVAL;
2045 break;
2046 }
2047 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2048 ret = -EFAULT;
2049 break;
2050 }
6a8ab060 2051 rev.name[sizeof(rev.name)-1] = 0;
6b7d31fc
HW
2052
2053 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 2054 target = 1;
6b7d31fc 2055 else
2e4e6a17 2056 target = 0;
6b7d31fc 2057
2e4e6a17
HW
2058 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2059 rev.revision,
2060 target, &ret),
6b7d31fc
HW
2061 "ip6t_%s", rev.name);
2062 break;
2063 }
2064
1da177e4
LT
2065 default:
2066 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2067 ret = -EINVAL;
2068 }
2069
2070 return ret;
2071}
2072
b9e69e12
FW
2073static void __ip6t_unregister_table(struct net *net, struct xt_table *table)
2074{
2075 struct xt_table_info *private;
2076 void *loc_cpu_entry;
2077 struct module *table_owner = table->me;
2078 struct ip6t_entry *iter;
2079
2080 private = xt_unregister_table(table);
2081
2082 /* Decrease module usage counts and free resources */
2083 loc_cpu_entry = private->entries;
2084 xt_entry_foreach(iter, loc_cpu_entry, private->size)
2085 cleanup_entry(iter, net);
2086 if (private->number > private->initial_entries)
2087 module_put(table_owner);
2088 xt_free_table_info(private);
2089}
2090
a67dd266
FW
2091int ip6t_register_table(struct net *net, const struct xt_table *table,
2092 const struct ip6t_replace *repl,
2093 const struct nf_hook_ops *ops,
2094 struct xt_table **res)
1da177e4
LT
2095{
2096 int ret;
2e4e6a17 2097 struct xt_table_info *newinfo;
f3c5c1bf 2098 struct xt_table_info bootstrap = {0};
31836064 2099 void *loc_cpu_entry;
a98da11d 2100 struct xt_table *new_table;
1da177e4 2101
2e4e6a17 2102 newinfo = xt_alloc_table_info(repl->size);
a67dd266
FW
2103 if (!newinfo)
2104 return -ENOMEM;
1da177e4 2105
482cfc31 2106 loc_cpu_entry = newinfo->entries;
31836064 2107 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4 2108
0f234214 2109 ret = translate_table(net, newinfo, loc_cpu_entry, repl);
44d34e72
AD
2110 if (ret != 0)
2111 goto out_free;
1da177e4 2112
336b517f 2113 new_table = xt_register_table(net, table, &bootstrap, newinfo);
a98da11d 2114 if (IS_ERR(new_table)) {
44d34e72
AD
2115 ret = PTR_ERR(new_table);
2116 goto out_free;
1da177e4 2117 }
a67dd266 2118
b9e69e12 2119 /* set res now, will see skbs right after nf_register_net_hooks */
a67dd266 2120 WRITE_ONCE(*res, new_table);
b9e69e12
FW
2121
2122 ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks));
2123 if (ret != 0) {
2124 __ip6t_unregister_table(net, new_table);
2125 *res = NULL;
2126 }
2127
a67dd266 2128 return ret;
1da177e4 2129
44d34e72
AD
2130out_free:
2131 xt_free_table_info(newinfo);
a67dd266 2132 return ret;
1da177e4
LT
2133}
2134
a67dd266
FW
2135void ip6t_unregister_table(struct net *net, struct xt_table *table,
2136 const struct nf_hook_ops *ops)
1da177e4 2137{
b9e69e12
FW
2138 nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks));
2139 __ip6t_unregister_table(net, table);
1da177e4
LT
2140}
2141
2142/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 2143static inline bool
1da177e4
LT
2144icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2145 u_int8_t type, u_int8_t code,
ccb79bdc 2146 bool invert)
1da177e4
LT
2147{
2148 return (type == test_type && code >= min_code && code <= max_code)
2149 ^ invert;
2150}
2151
1d93a9cb 2152static bool
62fc8051 2153icmp6_match(const struct sk_buff *skb, struct xt_action_param *par)
1da177e4 2154{
5452e425
JE
2155 const struct icmp6hdr *ic;
2156 struct icmp6hdr _icmph;
f7108a20 2157 const struct ip6t_icmp *icmpinfo = par->matchinfo;
1da177e4
LT
2158
2159 /* Must not be a fragment. */
f7108a20 2160 if (par->fragoff != 0)
1d93a9cb 2161 return false;
1da177e4 2162
f7108a20 2163 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
1da177e4
LT
2164 if (ic == NULL) {
2165 /* We've been asked to examine this packet, and we
9c547959
PM
2166 * can't. Hence, no choice but to drop.
2167 */
1da177e4 2168 duprintf("Dropping evil ICMP tinygram.\n");
b4ba2611 2169 par->hotdrop = true;
1d93a9cb 2170 return false;
1da177e4
LT
2171 }
2172
2173 return icmp6_type_code_match(icmpinfo->type,
2174 icmpinfo->code[0],
2175 icmpinfo->code[1],
2176 ic->icmp6_type, ic->icmp6_code,
2177 !!(icmpinfo->invflags&IP6T_ICMP_INV));
2178}
2179
2180/* Called when user tries to insert an entry of this type. */
b0f38452 2181static int icmp6_checkentry(const struct xt_mtchk_param *par)
1da177e4 2182{
9b4fce7a 2183 const struct ip6t_icmp *icmpinfo = par->matchinfo;
1da177e4 2184
7f939713 2185 /* Must specify no unknown invflags */
bd414ee6 2186 return (icmpinfo->invflags & ~IP6T_ICMP_INV) ? -EINVAL : 0;
1da177e4
LT
2187}
2188
2189/* The built-in targets: standard (NULL) and error. */
4538506b
JE
2190static struct xt_target ip6t_builtin_tg[] __read_mostly = {
2191 {
243bf6e2 2192 .name = XT_STANDARD_TARGET,
4538506b
JE
2193 .targetsize = sizeof(int),
2194 .family = NFPROTO_IPV6,
3bc3fe5e 2195#ifdef CONFIG_COMPAT
4538506b
JE
2196 .compatsize = sizeof(compat_int_t),
2197 .compat_from_user = compat_standard_from_user,
2198 .compat_to_user = compat_standard_to_user,
3bc3fe5e 2199#endif
4538506b
JE
2200 },
2201 {
243bf6e2 2202 .name = XT_ERROR_TARGET,
4538506b 2203 .target = ip6t_error,
12b00c2c 2204 .targetsize = XT_FUNCTION_MAXNAMELEN,
4538506b
JE
2205 .family = NFPROTO_IPV6,
2206 },
1da177e4
LT
2207};
2208
2209static struct nf_sockopt_ops ip6t_sockopts = {
2210 .pf = PF_INET6,
2211 .set_optmin = IP6T_BASE_CTL,
2212 .set_optmax = IP6T_SO_SET_MAX+1,
2213 .set = do_ip6t_set_ctl,
3bc3fe5e
PM
2214#ifdef CONFIG_COMPAT
2215 .compat_set = compat_do_ip6t_set_ctl,
2216#endif
1da177e4
LT
2217 .get_optmin = IP6T_BASE_CTL,
2218 .get_optmax = IP6T_SO_GET_MAX+1,
2219 .get = do_ip6t_get_ctl,
3bc3fe5e
PM
2220#ifdef CONFIG_COMPAT
2221 .compat_get = compat_do_ip6t_get_ctl,
2222#endif
16fcec35 2223 .owner = THIS_MODULE,
1da177e4
LT
2224};
2225
4538506b
JE
2226static struct xt_match ip6t_builtin_mt[] __read_mostly = {
2227 {
2228 .name = "icmp6",
2229 .match = icmp6_match,
2230 .matchsize = sizeof(struct ip6t_icmp),
2231 .checkentry = icmp6_checkentry,
2232 .proto = IPPROTO_ICMPV6,
2233 .family = NFPROTO_IPV6,
2234 },
1da177e4
LT
2235};
2236
3cb609d5
AD
2237static int __net_init ip6_tables_net_init(struct net *net)
2238{
383ca5b8 2239 return xt_proto_init(net, NFPROTO_IPV6);
3cb609d5
AD
2240}
2241
2242static void __net_exit ip6_tables_net_exit(struct net *net)
2243{
383ca5b8 2244 xt_proto_fini(net, NFPROTO_IPV6);
3cb609d5
AD
2245}
2246
2247static struct pernet_operations ip6_tables_net_ops = {
2248 .init = ip6_tables_net_init,
2249 .exit = ip6_tables_net_exit,
2250};
2251
65b4b4e8 2252static int __init ip6_tables_init(void)
1da177e4
LT
2253{
2254 int ret;
2255
3cb609d5 2256 ret = register_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2257 if (ret < 0)
2258 goto err1;
2e4e6a17 2259
25985edc 2260 /* No one else will be downing sem now, so we won't sleep */
4538506b 2261 ret = xt_register_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
0eff66e6
PM
2262 if (ret < 0)
2263 goto err2;
4538506b 2264 ret = xt_register_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
0eff66e6
PM
2265 if (ret < 0)
2266 goto err4;
1da177e4
LT
2267
2268 /* Register setsockopt */
2269 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
2270 if (ret < 0)
2271 goto err5;
1da177e4 2272
ff67e4e4 2273 pr_info("(C) 2000-2006 Netfilter Core Team\n");
1da177e4 2274 return 0;
0eff66e6
PM
2275
2276err5:
4538506b 2277 xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
0eff66e6 2278err4:
4538506b 2279 xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
0eff66e6 2280err2:
3cb609d5 2281 unregister_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2282err1:
2283 return ret;
1da177e4
LT
2284}
2285
65b4b4e8 2286static void __exit ip6_tables_fini(void)
1da177e4
LT
2287{
2288 nf_unregister_sockopt(&ip6t_sockopts);
9c547959 2289
4538506b
JE
2290 xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
2291 xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
3cb609d5 2292 unregister_pernet_subsys(&ip6_tables_net_ops);
1da177e4
LT
2293}
2294
2295EXPORT_SYMBOL(ip6t_register_table);
2296EXPORT_SYMBOL(ip6t_unregister_table);
2297EXPORT_SYMBOL(ip6t_do_table);
1da177e4 2298
65b4b4e8
AM
2299module_init(ip6_tables_init);
2300module_exit(ip6_tables_fini);