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