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