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