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