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