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