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