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