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