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