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