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