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