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