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