]> bbs.cooldavid.org Git - net-next-2.6.git/blame - net/ipv6/netfilter/ip6_tables.c
netfilter: iptables: use skb->len for accounting
[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;
31836064
ED
900 unsigned int curcpu;
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
SH
909 local_bh_disable();
910 curcpu = smp_processor_id();
31836064
ED
911
912 i = 0;
0559518b
JE
913 xt_entry_foreach(iter, t->entries[curcpu], t->size) {
914 SET_COUNTER(counters[i], iter->counters.bcnt,
6b4ff2d7 915 iter->counters.pcnt);
0559518b
JE
916 ++i;
917 }
1da177e4 918
6f912042 919 for_each_possible_cpu(cpu) {
31836064
ED
920 if (cpu == curcpu)
921 continue;
1da177e4 922 i = 0;
942e4a2b 923 xt_info_wrlock(cpu);
0559518b
JE
924 xt_entry_foreach(iter, t->entries[cpu], t->size) {
925 ADD_COUNTER(counters[i], iter->counters.bcnt,
6b4ff2d7 926 iter->counters.pcnt);
0559518b
JE
927 ++i;
928 }
942e4a2b 929 xt_info_wrunlock(cpu);
1da177e4 930 }
78454473
SH
931 local_bh_enable();
932}
933
d5d1baa1 934static struct xt_counters *alloc_counters(const struct xt_table *table)
1da177e4 935{
ed1a6f5e 936 unsigned int countersize;
2e4e6a17 937 struct xt_counters *counters;
d5d1baa1 938 const struct xt_table_info *private = table->private;
1da177e4
LT
939
940 /* We need atomic snapshot of counters: rest doesn't change
941 (other than comefrom, which userspace doesn't care
942 about). */
2e4e6a17 943 countersize = sizeof(struct xt_counters) * private->number;
e12f8e29 944 counters = vmalloc(countersize);
1da177e4
LT
945
946 if (counters == NULL)
942e4a2b 947 return ERR_PTR(-ENOMEM);
78454473 948
942e4a2b 949 get_counters(private, counters);
78454473 950
49a88d18 951 return counters;
ed1a6f5e
PM
952}
953
954static int
955copy_entries_to_user(unsigned int total_size,
d5d1baa1 956 const struct xt_table *table,
ed1a6f5e
PM
957 void __user *userptr)
958{
959 unsigned int off, num;
d5d1baa1 960 const struct ip6t_entry *e;
ed1a6f5e 961 struct xt_counters *counters;
5452e425 962 const struct xt_table_info *private = table->private;
ed1a6f5e 963 int ret = 0;
5452e425 964 const void *loc_cpu_entry;
ed1a6f5e
PM
965
966 counters = alloc_counters(table);
967 if (IS_ERR(counters))
968 return PTR_ERR(counters);
969
9c547959
PM
970 /* choose the copy that is on our node/cpu, ...
971 * This choice is lazy (because current thread is
972 * allowed to migrate to another cpu)
973 */
2e4e6a17 974 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 975 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
976 ret = -EFAULT;
977 goto free_counters;
978 }
979
980 /* FIXME: use iterator macros --RR */
981 /* ... then go back and fix counters and names */
982 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
983 unsigned int i;
5452e425
JE
984 const struct ip6t_entry_match *m;
985 const struct ip6t_entry_target *t;
1da177e4 986
31836064 987 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
988 if (copy_to_user(userptr + off
989 + offsetof(struct ip6t_entry, counters),
990 &counters[num],
991 sizeof(counters[num])) != 0) {
992 ret = -EFAULT;
993 goto free_counters;
994 }
995
996 for (i = sizeof(struct ip6t_entry);
997 i < e->target_offset;
998 i += m->u.match_size) {
999 m = (void *)e + i;
1000
1001 if (copy_to_user(userptr + off + i
1002 + offsetof(struct ip6t_entry_match,
1003 u.user.name),
1004 m->u.kernel.match->name,
1005 strlen(m->u.kernel.match->name)+1)
1006 != 0) {
1007 ret = -EFAULT;
1008 goto free_counters;
1009 }
1010 }
1011
d5d1baa1 1012 t = ip6t_get_target_c(e);
1da177e4
LT
1013 if (copy_to_user(userptr + off + e->target_offset
1014 + offsetof(struct ip6t_entry_target,
1015 u.user.name),
1016 t->u.kernel.target->name,
1017 strlen(t->u.kernel.target->name)+1) != 0) {
1018 ret = -EFAULT;
1019 goto free_counters;
1020 }
1021 }
1022
1023 free_counters:
1024 vfree(counters);
1025 return ret;
1026}
1027
3bc3fe5e 1028#ifdef CONFIG_COMPAT
739674fb 1029static void compat_standard_from_user(void *dst, const void *src)
3bc3fe5e
PM
1030{
1031 int v = *(compat_int_t *)src;
1032
1033 if (v > 0)
1034 v += xt_compat_calc_jump(AF_INET6, v);
1035 memcpy(dst, &v, sizeof(v));
1036}
1037
739674fb 1038static int compat_standard_to_user(void __user *dst, const void *src)
3bc3fe5e
PM
1039{
1040 compat_int_t cv = *(int *)src;
1041
1042 if (cv > 0)
1043 cv -= xt_compat_calc_jump(AF_INET6, cv);
1044 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1045}
1046
d5d1baa1 1047static int compat_calc_entry(const struct ip6t_entry *e,
3bc3fe5e 1048 const struct xt_table_info *info,
d5d1baa1 1049 const void *base, struct xt_table_info *newinfo)
3bc3fe5e 1050{
dcea992a 1051 const struct xt_entry_match *ematch;
d5d1baa1 1052 const struct ip6t_entry_target *t;
3bc3fe5e
PM
1053 unsigned int entry_offset;
1054 int off, i, ret;
1055
1056 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1057 entry_offset = (void *)e - base;
dcea992a 1058 xt_ematch_foreach(ematch, e)
6bdb331b 1059 off += xt_compat_match_offset(ematch->u.kernel.match);
d5d1baa1 1060 t = ip6t_get_target_c(e);
3bc3fe5e
PM
1061 off += xt_compat_target_offset(t->u.kernel.target);
1062 newinfo->size -= off;
1063 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1064 if (ret)
1065 return ret;
1066
1067 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1068 if (info->hook_entry[i] &&
1069 (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1070 newinfo->hook_entry[i] -= off;
1071 if (info->underflow[i] &&
1072 (e < (struct ip6t_entry *)(base + info->underflow[i])))
1073 newinfo->underflow[i] -= off;
1074 }
1075 return 0;
1076}
1077
1078static int compat_table_info(const struct xt_table_info *info,
1079 struct xt_table_info *newinfo)
1080{
72b2b1dd 1081 struct ip6t_entry *iter;
3bc3fe5e 1082 void *loc_cpu_entry;
0559518b 1083 int ret;
3bc3fe5e
PM
1084
1085 if (!newinfo || !info)
1086 return -EINVAL;
1087
1088 /* we dont care about newinfo->entries[] */
1089 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1090 newinfo->initial_entries = 0;
1091 loc_cpu_entry = info->entries[raw_smp_processor_id()];
72b2b1dd
JE
1092 xt_entry_foreach(iter, loc_cpu_entry, info->size) {
1093 ret = compat_calc_entry(iter, info, loc_cpu_entry, newinfo);
1094 if (ret != 0)
0559518b 1095 return ret;
72b2b1dd 1096 }
0559518b 1097 return 0;
3bc3fe5e
PM
1098}
1099#endif
1100
d5d1baa1
JE
1101static int get_info(struct net *net, void __user *user,
1102 const int *len, int compat)
433665c9
PM
1103{
1104 char name[IP6T_TABLE_MAXNAMELEN];
1105 struct xt_table *t;
1106 int ret;
1107
1108 if (*len != sizeof(struct ip6t_getinfo)) {
c9d8fe13 1109 duprintf("length %u != %zu\n", *len,
433665c9
PM
1110 sizeof(struct ip6t_getinfo));
1111 return -EINVAL;
1112 }
1113
1114 if (copy_from_user(name, user, sizeof(name)) != 0)
1115 return -EFAULT;
1116
1117 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
3bc3fe5e
PM
1118#ifdef CONFIG_COMPAT
1119 if (compat)
1120 xt_compat_lock(AF_INET6);
1121#endif
336b517f 1122 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
433665c9
PM
1123 "ip6table_%s", name);
1124 if (t && !IS_ERR(t)) {
1125 struct ip6t_getinfo info;
5452e425 1126 const struct xt_table_info *private = t->private;
3bc3fe5e 1127#ifdef CONFIG_COMPAT
14c7dbe0
AD
1128 struct xt_table_info tmp;
1129
3bc3fe5e 1130 if (compat) {
3bc3fe5e
PM
1131 ret = compat_table_info(private, &tmp);
1132 xt_compat_flush_offsets(AF_INET6);
1133 private = &tmp;
1134 }
1135#endif
433665c9
PM
1136 info.valid_hooks = t->valid_hooks;
1137 memcpy(info.hook_entry, private->hook_entry,
1138 sizeof(info.hook_entry));
1139 memcpy(info.underflow, private->underflow,
1140 sizeof(info.underflow));
1141 info.num_entries = private->number;
1142 info.size = private->size;
b5dd674b 1143 strcpy(info.name, name);
433665c9
PM
1144
1145 if (copy_to_user(user, &info, *len) != 0)
1146 ret = -EFAULT;
1147 else
1148 ret = 0;
1149
1150 xt_table_unlock(t);
1151 module_put(t->me);
1152 } else
1153 ret = t ? PTR_ERR(t) : -ENOENT;
3bc3fe5e
PM
1154#ifdef CONFIG_COMPAT
1155 if (compat)
1156 xt_compat_unlock(AF_INET6);
1157#endif
433665c9
PM
1158 return ret;
1159}
1160
1da177e4 1161static int
d5d1baa1
JE
1162get_entries(struct net *net, struct ip6t_get_entries __user *uptr,
1163 const int *len)
1da177e4
LT
1164{
1165 int ret;
d924357c 1166 struct ip6t_get_entries get;
2e4e6a17 1167 struct xt_table *t;
1da177e4 1168
d924357c 1169 if (*len < sizeof(get)) {
c9d8fe13 1170 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
d924357c
PM
1171 return -EINVAL;
1172 }
1173 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1174 return -EFAULT;
1175 if (*len != sizeof(struct ip6t_get_entries) + get.size) {
c9d8fe13
PM
1176 duprintf("get_entries: %u != %zu\n",
1177 *len, sizeof(get) + get.size);
d924357c
PM
1178 return -EINVAL;
1179 }
1180
336b517f 1181 t = xt_find_table_lock(net, AF_INET6, get.name);
6b7d31fc 1182 if (t && !IS_ERR(t)) {
2e4e6a17
HW
1183 struct xt_table_info *private = t->private;
1184 duprintf("t->private->number = %u\n", private->number);
d924357c 1185 if (get.size == private->size)
2e4e6a17 1186 ret = copy_entries_to_user(private->size,
1da177e4
LT
1187 t, uptr->entrytable);
1188 else {
1189 duprintf("get_entries: I've got %u not %u!\n",
9c547959 1190 private->size, get.size);
544473c1 1191 ret = -EAGAIN;
1da177e4 1192 }
6b7d31fc 1193 module_put(t->me);
2e4e6a17 1194 xt_table_unlock(t);
1da177e4 1195 } else
6b7d31fc 1196 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1197
1198 return ret;
1199}
1200
1201static int
336b517f 1202__do_replace(struct net *net, const char *name, unsigned int valid_hooks,
3bc3fe5e
PM
1203 struct xt_table_info *newinfo, unsigned int num_counters,
1204 void __user *counters_ptr)
1da177e4
LT
1205{
1206 int ret;
2e4e6a17 1207 struct xt_table *t;
3bc3fe5e 1208 struct xt_table_info *oldinfo;
2e4e6a17 1209 struct xt_counters *counters;
5452e425 1210 const void *loc_cpu_old_entry;
72b2b1dd 1211 struct ip6t_entry *iter;
1da177e4 1212
3bc3fe5e 1213 ret = 0;
e12f8e29 1214 counters = vmalloc(num_counters * sizeof(struct xt_counters));
1da177e4
LT
1215 if (!counters) {
1216 ret = -ENOMEM;
3bc3fe5e 1217 goto out;
1da177e4 1218 }
1da177e4 1219
336b517f 1220 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
3bc3fe5e 1221 "ip6table_%s", name);
6b7d31fc
HW
1222 if (!t || IS_ERR(t)) {
1223 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1224 goto free_newinfo_counters_untrans;
6b7d31fc 1225 }
1da177e4
LT
1226
1227 /* You lied! */
3bc3fe5e 1228 if (valid_hooks != t->valid_hooks) {
1da177e4 1229 duprintf("Valid hook crap: %08X vs %08X\n",
3bc3fe5e 1230 valid_hooks, t->valid_hooks);
1da177e4 1231 ret = -EINVAL;
6b7d31fc 1232 goto put_module;
1da177e4
LT
1233 }
1234
3bc3fe5e 1235 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1da177e4
LT
1236 if (!oldinfo)
1237 goto put_module;
1238
1239 /* Update module usage count based on number of rules */
1240 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1241 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1242 if ((oldinfo->number > oldinfo->initial_entries) ||
1243 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1244 module_put(t->me);
1245 if ((oldinfo->number > oldinfo->initial_entries) &&
1246 (newinfo->number <= oldinfo->initial_entries))
1247 module_put(t->me);
1248
942e4a2b 1249 /* Get the old counters, and synchronize with replace */
1da177e4 1250 get_counters(oldinfo, counters);
942e4a2b 1251
1da177e4 1252 /* Decrease module usage counts and free resource */
31836064 1253 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
72b2b1dd 1254 xt_entry_foreach(iter, loc_cpu_old_entry, oldinfo->size)
0559518b 1255 cleanup_entry(iter, net);
72b2b1dd 1256
2e4e6a17 1257 xt_free_table_info(oldinfo);
3bc3fe5e
PM
1258 if (copy_to_user(counters_ptr, counters,
1259 sizeof(struct xt_counters) * num_counters) != 0)
1da177e4
LT
1260 ret = -EFAULT;
1261 vfree(counters);
2e4e6a17 1262 xt_table_unlock(t);
1da177e4
LT
1263 return ret;
1264
1265 put_module:
1266 module_put(t->me);
2e4e6a17 1267 xt_table_unlock(t);
1da177e4 1268 free_newinfo_counters_untrans:
1da177e4 1269 vfree(counters);
3bc3fe5e
PM
1270 out:
1271 return ret;
1272}
1273
1274static int
d5d1baa1 1275do_replace(struct net *net, const void __user *user, unsigned int len)
3bc3fe5e
PM
1276{
1277 int ret;
1278 struct ip6t_replace tmp;
1279 struct xt_table_info *newinfo;
1280 void *loc_cpu_entry;
72b2b1dd 1281 struct ip6t_entry *iter;
3bc3fe5e
PM
1282
1283 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1284 return -EFAULT;
1285
1286 /* overflow check */
1287 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1288 return -ENOMEM;
1289
1290 newinfo = xt_alloc_table_info(tmp.size);
1291 if (!newinfo)
1292 return -ENOMEM;
1293
1294 /* choose the copy that is on our node/cpu */
1295 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1296 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1297 tmp.size) != 0) {
1298 ret = -EFAULT;
1299 goto free_newinfo;
1300 }
1301
0f234214 1302 ret = translate_table(net, newinfo, loc_cpu_entry, &tmp);
3bc3fe5e
PM
1303 if (ret != 0)
1304 goto free_newinfo;
1305
1306 duprintf("ip_tables: Translated table\n");
1307
336b517f 1308 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
3bc3fe5e
PM
1309 tmp.num_counters, tmp.counters);
1310 if (ret)
1311 goto free_newinfo_untrans;
1312 return 0;
1313
1314 free_newinfo_untrans:
72b2b1dd 1315 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
0559518b 1316 cleanup_entry(iter, net);
1da177e4 1317 free_newinfo:
2e4e6a17 1318 xt_free_table_info(newinfo);
1da177e4
LT
1319 return ret;
1320}
1321
1da177e4 1322static int
d5d1baa1 1323do_add_counters(struct net *net, const void __user *user, unsigned int len,
336b517f 1324 int compat)
1da177e4 1325{
942e4a2b 1326 unsigned int i, curcpu;
3bc3fe5e
PM
1327 struct xt_counters_info tmp;
1328 struct xt_counters *paddc;
1329 unsigned int num_counters;
1330 char *name;
1331 int size;
1332 void *ptmp;
2e4e6a17 1333 struct xt_table *t;
5452e425 1334 const struct xt_table_info *private;
6b7d31fc 1335 int ret = 0;
5452e425 1336 const void *loc_cpu_entry;
72b2b1dd 1337 struct ip6t_entry *iter;
3bc3fe5e
PM
1338#ifdef CONFIG_COMPAT
1339 struct compat_xt_counters_info compat_tmp;
1da177e4 1340
3bc3fe5e
PM
1341 if (compat) {
1342 ptmp = &compat_tmp;
1343 size = sizeof(struct compat_xt_counters_info);
1344 } else
1345#endif
1346 {
1347 ptmp = &tmp;
1348 size = sizeof(struct xt_counters_info);
1349 }
1350
1351 if (copy_from_user(ptmp, user, size) != 0)
1da177e4
LT
1352 return -EFAULT;
1353
3bc3fe5e
PM
1354#ifdef CONFIG_COMPAT
1355 if (compat) {
1356 num_counters = compat_tmp.num_counters;
1357 name = compat_tmp.name;
1358 } else
1359#endif
1360 {
1361 num_counters = tmp.num_counters;
1362 name = tmp.name;
1363 }
1364
1365 if (len != size + num_counters * sizeof(struct xt_counters))
1da177e4
LT
1366 return -EINVAL;
1367
e12f8e29 1368 paddc = vmalloc(len - size);
1da177e4
LT
1369 if (!paddc)
1370 return -ENOMEM;
1371
3bc3fe5e 1372 if (copy_from_user(paddc, user + size, len - size) != 0) {
1da177e4
LT
1373 ret = -EFAULT;
1374 goto free;
1375 }
1376
336b517f 1377 t = xt_find_table_lock(net, AF_INET6, name);
6b7d31fc
HW
1378 if (!t || IS_ERR(t)) {
1379 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1380 goto free;
6b7d31fc 1381 }
1da177e4 1382
942e4a2b
SH
1383
1384 local_bh_disable();
2e4e6a17 1385 private = t->private;
3bc3fe5e 1386 if (private->number != num_counters) {
1da177e4
LT
1387 ret = -EINVAL;
1388 goto unlock_up_free;
1389 }
1390
1391 i = 0;
31836064 1392 /* Choose the copy that is on our node */
942e4a2b
SH
1393 curcpu = smp_processor_id();
1394 xt_info_wrlock(curcpu);
1395 loc_cpu_entry = private->entries[curcpu];
0559518b
JE
1396 xt_entry_foreach(iter, loc_cpu_entry, private->size) {
1397 ADD_COUNTER(iter->counters, paddc[i].bcnt, paddc[i].pcnt);
1398 ++i;
1399 }
942e4a2b
SH
1400 xt_info_wrunlock(curcpu);
1401
1da177e4 1402 unlock_up_free:
942e4a2b 1403 local_bh_enable();
2e4e6a17 1404 xt_table_unlock(t);
6b7d31fc 1405 module_put(t->me);
1da177e4
LT
1406 free:
1407 vfree(paddc);
1408
1409 return ret;
1410}
1411
3bc3fe5e
PM
1412#ifdef CONFIG_COMPAT
1413struct compat_ip6t_replace {
1414 char name[IP6T_TABLE_MAXNAMELEN];
1415 u32 valid_hooks;
1416 u32 num_entries;
1417 u32 size;
1418 u32 hook_entry[NF_INET_NUMHOOKS];
1419 u32 underflow[NF_INET_NUMHOOKS];
1420 u32 num_counters;
1421 compat_uptr_t counters; /* struct ip6t_counters * */
1422 struct compat_ip6t_entry entries[0];
1423};
1424
1425static int
1426compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
b0a6363c 1427 unsigned int *size, struct xt_counters *counters,
0559518b 1428 unsigned int i)
3bc3fe5e
PM
1429{
1430 struct ip6t_entry_target *t;
1431 struct compat_ip6t_entry __user *ce;
1432 u_int16_t target_offset, next_offset;
1433 compat_uint_t origsize;
dcea992a
JE
1434 const struct xt_entry_match *ematch;
1435 int ret = 0;
3bc3fe5e 1436
3bc3fe5e
PM
1437 origsize = *size;
1438 ce = (struct compat_ip6t_entry __user *)*dstptr;
0559518b
JE
1439 if (copy_to_user(ce, e, sizeof(struct ip6t_entry)) != 0 ||
1440 copy_to_user(&ce->counters, &counters[i],
1441 sizeof(counters[i])) != 0)
1442 return -EFAULT;
3bc3fe5e
PM
1443
1444 *dstptr += sizeof(struct compat_ip6t_entry);
1445 *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1446
dcea992a
JE
1447 xt_ematch_foreach(ematch, e) {
1448 ret = xt_compat_match_to_user(ematch, dstptr, size);
1449 if (ret != 0)
6bdb331b 1450 return ret;
dcea992a 1451 }
3bc3fe5e 1452 target_offset = e->target_offset - (origsize - *size);
3bc3fe5e
PM
1453 t = ip6t_get_target(e);
1454 ret = xt_compat_target_to_user(t, dstptr, size);
1455 if (ret)
0559518b 1456 return ret;
3bc3fe5e 1457 next_offset = e->next_offset - (origsize - *size);
0559518b
JE
1458 if (put_user(target_offset, &ce->target_offset) != 0 ||
1459 put_user(next_offset, &ce->next_offset) != 0)
1460 return -EFAULT;
3bc3fe5e 1461 return 0;
3bc3fe5e
PM
1462}
1463
022748a9 1464static int
3bc3fe5e
PM
1465compat_find_calc_match(struct ip6t_entry_match *m,
1466 const char *name,
1467 const struct ip6t_ip6 *ipv6,
1468 unsigned int hookmask,
6bdb331b 1469 int *size)
3bc3fe5e
PM
1470{
1471 struct xt_match *match;
1472
fd0ec0e6
JE
1473 match = xt_request_find_match(NFPROTO_IPV6, m->u.user.name,
1474 m->u.user.revision);
1475 if (IS_ERR(match)) {
3bc3fe5e
PM
1476 duprintf("compat_check_calc_match: `%s' not found\n",
1477 m->u.user.name);
fd0ec0e6 1478 return PTR_ERR(match);
3bc3fe5e
PM
1479 }
1480 m->u.kernel.match = match;
1481 *size += xt_compat_match_offset(match);
3bc3fe5e
PM
1482 return 0;
1483}
1484
0559518b 1485static void compat_release_entry(struct compat_ip6t_entry *e)
3bc3fe5e
PM
1486{
1487 struct ip6t_entry_target *t;
dcea992a 1488 struct xt_entry_match *ematch;
3bc3fe5e 1489
3bc3fe5e 1490 /* Cleanup all matches */
dcea992a 1491 xt_ematch_foreach(ematch, e)
6bdb331b 1492 module_put(ematch->u.kernel.match->me);
3bc3fe5e
PM
1493 t = compat_ip6t_get_target(e);
1494 module_put(t->u.kernel.target->me);
3bc3fe5e
PM
1495}
1496
022748a9 1497static int
3bc3fe5e
PM
1498check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1499 struct xt_table_info *newinfo,
1500 unsigned int *size,
d5d1baa1
JE
1501 const unsigned char *base,
1502 const unsigned char *limit,
1503 const unsigned int *hook_entries,
1504 const unsigned int *underflows,
3bc3fe5e
PM
1505 const char *name)
1506{
dcea992a 1507 struct xt_entry_match *ematch;
3bc3fe5e
PM
1508 struct ip6t_entry_target *t;
1509 struct xt_target *target;
1510 unsigned int entry_offset;
b0a6363c
PM
1511 unsigned int j;
1512 int ret, off, h;
3bc3fe5e
PM
1513
1514 duprintf("check_compat_entry_size_and_hooks %p\n", e);
3666ed1c
JP
1515 if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
1516 (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
3bc3fe5e
PM
1517 duprintf("Bad offset %p, limit = %p\n", e, limit);
1518 return -EINVAL;
1519 }
1520
1521 if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1522 sizeof(struct compat_xt_entry_target)) {
1523 duprintf("checking: element %p size %u\n",
1524 e, e->next_offset);
1525 return -EINVAL;
1526 }
1527
1528 /* For purposes of check_entry casting the compat entry is fine */
1529 ret = check_entry((struct ip6t_entry *)e, name);
1530 if (ret)
1531 return ret;
1532
1533 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1534 entry_offset = (void *)e - (void *)base;
1535 j = 0;
dcea992a
JE
1536 xt_ematch_foreach(ematch, e) {
1537 ret = compat_find_calc_match(ematch, name,
6b4ff2d7 1538 &e->ipv6, e->comefrom, &off);
dcea992a 1539 if (ret != 0)
6bdb331b
JE
1540 goto release_matches;
1541 ++j;
dcea992a 1542 }
3bc3fe5e
PM
1543
1544 t = compat_ip6t_get_target(e);
d2a7b6ba
JE
1545 target = xt_request_find_target(NFPROTO_IPV6, t->u.user.name,
1546 t->u.user.revision);
1547 if (IS_ERR(target)) {
3bc3fe5e
PM
1548 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1549 t->u.user.name);
d2a7b6ba 1550 ret = PTR_ERR(target);
3bc3fe5e
PM
1551 goto release_matches;
1552 }
1553 t->u.kernel.target = target;
1554
1555 off += xt_compat_target_offset(target);
1556 *size += off;
1557 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1558 if (ret)
1559 goto out;
1560
1561 /* Check hooks & underflows */
1562 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1563 if ((unsigned char *)e - base == hook_entries[h])
1564 newinfo->hook_entry[h] = hook_entries[h];
1565 if ((unsigned char *)e - base == underflows[h])
1566 newinfo->underflow[h] = underflows[h];
1567 }
1568
1569 /* Clear counters and comefrom */
1570 memset(&e->counters, 0, sizeof(e->counters));
1571 e->comefrom = 0;
3bc3fe5e
PM
1572 return 0;
1573
1574out:
1575 module_put(t->u.kernel.target->me);
1576release_matches:
6bdb331b
JE
1577 xt_ematch_foreach(ematch, e) {
1578 if (j-- == 0)
dcea992a 1579 break;
6bdb331b
JE
1580 module_put(ematch->u.kernel.match->me);
1581 }
3bc3fe5e
PM
1582 return ret;
1583}
1584
1585static int
1586compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1587 unsigned int *size, const char *name,
1588 struct xt_table_info *newinfo, unsigned char *base)
1589{
1590 struct ip6t_entry_target *t;
1591 struct xt_target *target;
1592 struct ip6t_entry *de;
1593 unsigned int origsize;
1594 int ret, h;
dcea992a 1595 struct xt_entry_match *ematch;
3bc3fe5e
PM
1596
1597 ret = 0;
1598 origsize = *size;
1599 de = (struct ip6t_entry *)*dstptr;
1600 memcpy(de, e, sizeof(struct ip6t_entry));
1601 memcpy(&de->counters, &e->counters, sizeof(e->counters));
1602
1603 *dstptr += sizeof(struct ip6t_entry);
1604 *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1605
dcea992a
JE
1606 xt_ematch_foreach(ematch, e) {
1607 ret = xt_compat_match_from_user(ematch, dstptr, size);
1608 if (ret != 0)
6bdb331b 1609 return ret;
dcea992a 1610 }
3bc3fe5e
PM
1611 de->target_offset = e->target_offset - (origsize - *size);
1612 t = compat_ip6t_get_target(e);
1613 target = t->u.kernel.target;
1614 xt_compat_target_from_user(t, dstptr, size);
1615
1616 de->next_offset = e->next_offset - (origsize - *size);
1617 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1618 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1619 newinfo->hook_entry[h] -= origsize - *size;
1620 if ((unsigned char *)de - base < newinfo->underflow[h])
1621 newinfo->underflow[h] -= origsize - *size;
1622 }
1623 return ret;
1624}
1625
f54e9367 1626static int compat_check_entry(struct ip6t_entry *e, struct net *net,
0559518b 1627 const char *name)
3bc3fe5e 1628{
b0a6363c 1629 unsigned int j;
dcea992a 1630 int ret = 0;
9b4fce7a 1631 struct xt_mtchk_param mtpar;
dcea992a 1632 struct xt_entry_match *ematch;
3bc3fe5e
PM
1633
1634 j = 0;
f54e9367 1635 mtpar.net = net;
9b4fce7a
JE
1636 mtpar.table = name;
1637 mtpar.entryinfo = &e->ipv6;
1638 mtpar.hook_mask = e->comefrom;
916a917d 1639 mtpar.family = NFPROTO_IPV6;
dcea992a 1640 xt_ematch_foreach(ematch, e) {
6bdb331b 1641 ret = check_match(ematch, &mtpar);
dcea992a 1642 if (ret != 0)
6bdb331b
JE
1643 goto cleanup_matches;
1644 ++j;
dcea992a 1645 }
3bc3fe5e 1646
add67461 1647 ret = check_target(e, net, name);
3bc3fe5e
PM
1648 if (ret)
1649 goto cleanup_matches;
3bc3fe5e
PM
1650 return 0;
1651
1652 cleanup_matches:
6bdb331b
JE
1653 xt_ematch_foreach(ematch, e) {
1654 if (j-- == 0)
dcea992a 1655 break;
6bdb331b
JE
1656 cleanup_match(ematch, net);
1657 }
3bc3fe5e
PM
1658 return ret;
1659}
1660
1661static int
f54e9367
AD
1662translate_compat_table(struct net *net,
1663 const char *name,
3bc3fe5e
PM
1664 unsigned int valid_hooks,
1665 struct xt_table_info **pinfo,
1666 void **pentry0,
1667 unsigned int total_size,
1668 unsigned int number,
1669 unsigned int *hook_entries,
1670 unsigned int *underflows)
1671{
1672 unsigned int i, j;
1673 struct xt_table_info *newinfo, *info;
1674 void *pos, *entry0, *entry1;
72b2b1dd
JE
1675 struct compat_ip6t_entry *iter0;
1676 struct ip6t_entry *iter1;
3bc3fe5e 1677 unsigned int size;
72b2b1dd 1678 int ret = 0;
3bc3fe5e
PM
1679
1680 info = *pinfo;
1681 entry0 = *pentry0;
1682 size = total_size;
1683 info->number = number;
1684
1685 /* Init all hooks to impossible value. */
1686 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1687 info->hook_entry[i] = 0xFFFFFFFF;
1688 info->underflow[i] = 0xFFFFFFFF;
1689 }
1690
1691 duprintf("translate_compat_table: size %u\n", info->size);
1692 j = 0;
1693 xt_compat_lock(AF_INET6);
1694 /* Walk through entries, checking offsets. */
72b2b1dd
JE
1695 xt_entry_foreach(iter0, entry0, total_size) {
1696 ret = check_compat_entry_size_and_hooks(iter0, info, &size,
6b4ff2d7
JE
1697 entry0,
1698 entry0 + total_size,
1699 hook_entries,
1700 underflows,
1701 name);
72b2b1dd 1702 if (ret != 0)
0559518b
JE
1703 goto out_unlock;
1704 ++j;
72b2b1dd 1705 }
3bc3fe5e
PM
1706
1707 ret = -EINVAL;
1708 if (j != number) {
1709 duprintf("translate_compat_table: %u not %u entries\n",
1710 j, number);
1711 goto out_unlock;
1712 }
1713
1714 /* Check hooks all assigned */
1715 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1716 /* Only hooks which are valid */
1717 if (!(valid_hooks & (1 << i)))
1718 continue;
1719 if (info->hook_entry[i] == 0xFFFFFFFF) {
1720 duprintf("Invalid hook entry %u %u\n",
1721 i, hook_entries[i]);
1722 goto out_unlock;
1723 }
1724 if (info->underflow[i] == 0xFFFFFFFF) {
1725 duprintf("Invalid underflow %u %u\n",
1726 i, underflows[i]);
1727 goto out_unlock;
1728 }
1729 }
1730
1731 ret = -ENOMEM;
1732 newinfo = xt_alloc_table_info(size);
1733 if (!newinfo)
1734 goto out_unlock;
1735
1736 newinfo->number = number;
1737 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1738 newinfo->hook_entry[i] = info->hook_entry[i];
1739 newinfo->underflow[i] = info->underflow[i];
1740 }
1741 entry1 = newinfo->entries[raw_smp_processor_id()];
1742 pos = entry1;
1743 size = total_size;
72b2b1dd 1744 xt_entry_foreach(iter0, entry0, total_size) {
6b4ff2d7
JE
1745 ret = compat_copy_entry_from_user(iter0, &pos, &size,
1746 name, newinfo, entry1);
72b2b1dd
JE
1747 if (ret != 0)
1748 break;
1749 }
3bc3fe5e
PM
1750 xt_compat_flush_offsets(AF_INET6);
1751 xt_compat_unlock(AF_INET6);
1752 if (ret)
1753 goto free_newinfo;
1754
1755 ret = -ELOOP;
1756 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1757 goto free_newinfo;
1758
1759 i = 0;
72b2b1dd 1760 xt_entry_foreach(iter1, entry1, newinfo->size) {
0559518b 1761 ret = compat_check_entry(iter1, net, name);
72b2b1dd
JE
1762 if (ret != 0)
1763 break;
0559518b 1764 ++i;
72b2b1dd 1765 }
3bc3fe5e 1766 if (ret) {
72b2b1dd
JE
1767 /*
1768 * The first i matches need cleanup_entry (calls ->destroy)
1769 * because they had called ->check already. The other j-i
1770 * entries need only release.
1771 */
1772 int skip = i;
3bc3fe5e 1773 j -= i;
72b2b1dd
JE
1774 xt_entry_foreach(iter0, entry0, newinfo->size) {
1775 if (skip-- > 0)
1776 continue;
0559518b 1777 if (j-- == 0)
72b2b1dd 1778 break;
0559518b 1779 compat_release_entry(iter0);
72b2b1dd 1780 }
0559518b
JE
1781 xt_entry_foreach(iter1, entry1, newinfo->size) {
1782 if (i-- == 0)
72b2b1dd 1783 break;
0559518b
JE
1784 cleanup_entry(iter1, net);
1785 }
3bc3fe5e
PM
1786 xt_free_table_info(newinfo);
1787 return ret;
1788 }
1789
1790 /* And one copy for every other CPU */
1791 for_each_possible_cpu(i)
1792 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1793 memcpy(newinfo->entries[i], entry1, newinfo->size);
1794
1795 *pinfo = newinfo;
1796 *pentry0 = entry1;
1797 xt_free_table_info(info);
1798 return 0;
1799
1800free_newinfo:
1801 xt_free_table_info(newinfo);
1802out:
0559518b
JE
1803 xt_entry_foreach(iter0, entry0, total_size) {
1804 if (j-- == 0)
72b2b1dd 1805 break;
0559518b
JE
1806 compat_release_entry(iter0);
1807 }
3bc3fe5e
PM
1808 return ret;
1809out_unlock:
1810 xt_compat_flush_offsets(AF_INET6);
1811 xt_compat_unlock(AF_INET6);
1812 goto out;
1813}
1814
1815static int
336b517f 1816compat_do_replace(struct net *net, void __user *user, unsigned int len)
3bc3fe5e
PM
1817{
1818 int ret;
1819 struct compat_ip6t_replace tmp;
1820 struct xt_table_info *newinfo;
1821 void *loc_cpu_entry;
72b2b1dd 1822 struct ip6t_entry *iter;
3bc3fe5e
PM
1823
1824 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1825 return -EFAULT;
1826
1827 /* overflow check */
1828 if (tmp.size >= INT_MAX / num_possible_cpus())
1829 return -ENOMEM;
1830 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1831 return -ENOMEM;
1832
1833 newinfo = xt_alloc_table_info(tmp.size);
1834 if (!newinfo)
1835 return -ENOMEM;
1836
9c547959 1837 /* choose the copy that is on our node/cpu */
3bc3fe5e
PM
1838 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1839 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1840 tmp.size) != 0) {
1841 ret = -EFAULT;
1842 goto free_newinfo;
1843 }
1844
f54e9367 1845 ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
3bc3fe5e
PM
1846 &newinfo, &loc_cpu_entry, tmp.size,
1847 tmp.num_entries, tmp.hook_entry,
1848 tmp.underflow);
1849 if (ret != 0)
1850 goto free_newinfo;
1851
1852 duprintf("compat_do_replace: Translated table\n");
1853
336b517f 1854 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
3bc3fe5e
PM
1855 tmp.num_counters, compat_ptr(tmp.counters));
1856 if (ret)
1857 goto free_newinfo_untrans;
1858 return 0;
1859
1860 free_newinfo_untrans:
72b2b1dd 1861 xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
0559518b 1862 cleanup_entry(iter, net);
3bc3fe5e
PM
1863 free_newinfo:
1864 xt_free_table_info(newinfo);
1865 return ret;
1866}
1867
1868static int
1869compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1870 unsigned int len)
1871{
1872 int ret;
1873
1874 if (!capable(CAP_NET_ADMIN))
1875 return -EPERM;
1876
1877 switch (cmd) {
1878 case IP6T_SO_SET_REPLACE:
3b1e0a65 1879 ret = compat_do_replace(sock_net(sk), user, len);
3bc3fe5e
PM
1880 break;
1881
1882 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 1883 ret = do_add_counters(sock_net(sk), user, len, 1);
3bc3fe5e
PM
1884 break;
1885
1886 default:
1887 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1888 ret = -EINVAL;
1889 }
1890
1891 return ret;
1892}
1893
1894struct compat_ip6t_get_entries {
1895 char name[IP6T_TABLE_MAXNAMELEN];
1896 compat_uint_t size;
1897 struct compat_ip6t_entry entrytable[0];
1898};
1899
1900static int
1901compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1902 void __user *userptr)
1903{
1904 struct xt_counters *counters;
5452e425 1905 const struct xt_table_info *private = table->private;
3bc3fe5e
PM
1906 void __user *pos;
1907 unsigned int size;
1908 int ret = 0;
5452e425 1909 const void *loc_cpu_entry;
3bc3fe5e 1910 unsigned int i = 0;
72b2b1dd 1911 struct ip6t_entry *iter;
3bc3fe5e
PM
1912
1913 counters = alloc_counters(table);
1914 if (IS_ERR(counters))
1915 return PTR_ERR(counters);
1916
1917 /* choose the copy that is on our node/cpu, ...
1918 * This choice is lazy (because current thread is
1919 * allowed to migrate to another cpu)
1920 */
1921 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1922 pos = userptr;
1923 size = total_size;
72b2b1dd
JE
1924 xt_entry_foreach(iter, loc_cpu_entry, total_size) {
1925 ret = compat_copy_entry_to_user(iter, &pos,
6b4ff2d7 1926 &size, counters, i++);
72b2b1dd
JE
1927 if (ret != 0)
1928 break;
1929 }
3bc3fe5e
PM
1930
1931 vfree(counters);
1932 return ret;
1933}
1934
1935static int
336b517f
AD
1936compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1937 int *len)
3bc3fe5e
PM
1938{
1939 int ret;
1940 struct compat_ip6t_get_entries get;
1941 struct xt_table *t;
1942
1943 if (*len < sizeof(get)) {
c9d8fe13 1944 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
3bc3fe5e
PM
1945 return -EINVAL;
1946 }
1947
1948 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1949 return -EFAULT;
1950
1951 if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
c9d8fe13
PM
1952 duprintf("compat_get_entries: %u != %zu\n",
1953 *len, sizeof(get) + get.size);
3bc3fe5e
PM
1954 return -EINVAL;
1955 }
1956
1957 xt_compat_lock(AF_INET6);
336b517f 1958 t = xt_find_table_lock(net, AF_INET6, get.name);
3bc3fe5e 1959 if (t && !IS_ERR(t)) {
5452e425 1960 const struct xt_table_info *private = t->private;
3bc3fe5e 1961 struct xt_table_info info;
9c547959 1962 duprintf("t->private->number = %u\n", private->number);
3bc3fe5e
PM
1963 ret = compat_table_info(private, &info);
1964 if (!ret && get.size == info.size) {
1965 ret = compat_copy_entries_to_user(private->size,
1966 t, uptr->entrytable);
1967 } else if (!ret) {
1968 duprintf("compat_get_entries: I've got %u not %u!\n",
9c547959 1969 private->size, get.size);
544473c1 1970 ret = -EAGAIN;
3bc3fe5e
PM
1971 }
1972 xt_compat_flush_offsets(AF_INET6);
1973 module_put(t->me);
1974 xt_table_unlock(t);
1975 } else
1976 ret = t ? PTR_ERR(t) : -ENOENT;
1977
1978 xt_compat_unlock(AF_INET6);
1979 return ret;
1980}
1981
1982static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1983
1984static int
1985compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1986{
1987 int ret;
1988
1989 if (!capable(CAP_NET_ADMIN))
1990 return -EPERM;
1991
1992 switch (cmd) {
1993 case IP6T_SO_GET_INFO:
3b1e0a65 1994 ret = get_info(sock_net(sk), user, len, 1);
3bc3fe5e
PM
1995 break;
1996 case IP6T_SO_GET_ENTRIES:
3b1e0a65 1997 ret = compat_get_entries(sock_net(sk), user, len);
3bc3fe5e
PM
1998 break;
1999 default:
2000 ret = do_ip6t_get_ctl(sk, cmd, user, len);
2001 }
2002 return ret;
2003}
2004#endif
2005
1da177e4
LT
2006static int
2007do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2008{
2009 int ret;
2010
2011 if (!capable(CAP_NET_ADMIN))
2012 return -EPERM;
2013
2014 switch (cmd) {
2015 case IP6T_SO_SET_REPLACE:
3b1e0a65 2016 ret = do_replace(sock_net(sk), user, len);
1da177e4
LT
2017 break;
2018
2019 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 2020 ret = do_add_counters(sock_net(sk), user, len, 0);
1da177e4
LT
2021 break;
2022
2023 default:
2024 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
2025 ret = -EINVAL;
2026 }
2027
2028 return ret;
2029}
2030
2031static int
2032do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2033{
2034 int ret;
2035
2036 if (!capable(CAP_NET_ADMIN))
2037 return -EPERM;
2038
2039 switch (cmd) {
433665c9 2040 case IP6T_SO_GET_INFO:
3b1e0a65 2041 ret = get_info(sock_net(sk), user, len, 0);
433665c9 2042 break;
1da177e4 2043
d924357c 2044 case IP6T_SO_GET_ENTRIES:
3b1e0a65 2045 ret = get_entries(sock_net(sk), user, len);
1da177e4 2046 break;
1da177e4 2047
6b7d31fc
HW
2048 case IP6T_SO_GET_REVISION_MATCH:
2049 case IP6T_SO_GET_REVISION_TARGET: {
2050 struct ip6t_get_revision rev;
2e4e6a17 2051 int target;
6b7d31fc
HW
2052
2053 if (*len != sizeof(rev)) {
2054 ret = -EINVAL;
2055 break;
2056 }
2057 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2058 ret = -EFAULT;
2059 break;
2060 }
2061
2062 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 2063 target = 1;
6b7d31fc 2064 else
2e4e6a17 2065 target = 0;
6b7d31fc 2066
2e4e6a17
HW
2067 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2068 rev.revision,
2069 target, &ret),
6b7d31fc
HW
2070 "ip6t_%s", rev.name);
2071 break;
2072 }
2073
1da177e4
LT
2074 default:
2075 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2076 ret = -EINVAL;
2077 }
2078
2079 return ret;
2080}
2081
35aad0ff
JE
2082struct xt_table *ip6t_register_table(struct net *net,
2083 const struct xt_table *table,
336b517f 2084 const struct ip6t_replace *repl)
1da177e4
LT
2085{
2086 int ret;
2e4e6a17 2087 struct xt_table_info *newinfo;
f3c5c1bf 2088 struct xt_table_info bootstrap = {0};
31836064 2089 void *loc_cpu_entry;
a98da11d 2090 struct xt_table *new_table;
1da177e4 2091
2e4e6a17 2092 newinfo = xt_alloc_table_info(repl->size);
44d34e72
AD
2093 if (!newinfo) {
2094 ret = -ENOMEM;
2095 goto out;
2096 }
1da177e4 2097
9c547959 2098 /* choose the copy on our node/cpu, but dont care about preemption */
31836064
ED
2099 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2100 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4 2101
0f234214 2102 ret = translate_table(net, newinfo, loc_cpu_entry, repl);
44d34e72
AD
2103 if (ret != 0)
2104 goto out_free;
1da177e4 2105
336b517f 2106 new_table = xt_register_table(net, table, &bootstrap, newinfo);
a98da11d 2107 if (IS_ERR(new_table)) {
44d34e72
AD
2108 ret = PTR_ERR(new_table);
2109 goto out_free;
1da177e4 2110 }
44d34e72 2111 return new_table;
1da177e4 2112
44d34e72
AD
2113out_free:
2114 xt_free_table_info(newinfo);
2115out:
2116 return ERR_PTR(ret);
1da177e4
LT
2117}
2118
f54e9367 2119void ip6t_unregister_table(struct net *net, struct xt_table *table)
1da177e4 2120{
2e4e6a17 2121 struct xt_table_info *private;
31836064 2122 void *loc_cpu_entry;
df200969 2123 struct module *table_owner = table->me;
72b2b1dd 2124 struct ip6t_entry *iter;
31836064 2125
2e4e6a17 2126 private = xt_unregister_table(table);
1da177e4
LT
2127
2128 /* Decrease module usage counts and free resources */
2e4e6a17 2129 loc_cpu_entry = private->entries[raw_smp_processor_id()];
72b2b1dd 2130 xt_entry_foreach(iter, loc_cpu_entry, private->size)
0559518b 2131 cleanup_entry(iter, net);
df200969
AD
2132 if (private->number > private->initial_entries)
2133 module_put(table_owner);
2e4e6a17 2134 xt_free_table_info(private);
1da177e4
LT
2135}
2136
2137/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 2138static inline bool
1da177e4
LT
2139icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2140 u_int8_t type, u_int8_t code,
ccb79bdc 2141 bool invert)
1da177e4
LT
2142{
2143 return (type == test_type && code >= min_code && code <= max_code)
2144 ^ invert;
2145}
2146
1d93a9cb 2147static bool
62fc8051 2148icmp6_match(const struct sk_buff *skb, struct xt_action_param *par)
1da177e4 2149{
5452e425
JE
2150 const struct icmp6hdr *ic;
2151 struct icmp6hdr _icmph;
f7108a20 2152 const struct ip6t_icmp *icmpinfo = par->matchinfo;
1da177e4
LT
2153
2154 /* Must not be a fragment. */
f7108a20 2155 if (par->fragoff != 0)
1d93a9cb 2156 return false;
1da177e4 2157
f7108a20 2158 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
1da177e4
LT
2159 if (ic == NULL) {
2160 /* We've been asked to examine this packet, and we
9c547959
PM
2161 * can't. Hence, no choice but to drop.
2162 */
1da177e4 2163 duprintf("Dropping evil ICMP tinygram.\n");
b4ba2611 2164 par->hotdrop = true;
1d93a9cb 2165 return false;
1da177e4
LT
2166 }
2167
2168 return icmp6_type_code_match(icmpinfo->type,
2169 icmpinfo->code[0],
2170 icmpinfo->code[1],
2171 ic->icmp6_type, ic->icmp6_code,
2172 !!(icmpinfo->invflags&IP6T_ICMP_INV));
2173}
2174
2175/* Called when user tries to insert an entry of this type. */
b0f38452 2176static int icmp6_checkentry(const struct xt_mtchk_param *par)
1da177e4 2177{
9b4fce7a 2178 const struct ip6t_icmp *icmpinfo = par->matchinfo;
1da177e4 2179
7f939713 2180 /* Must specify no unknown invflags */
bd414ee6 2181 return (icmpinfo->invflags & ~IP6T_ICMP_INV) ? -EINVAL : 0;
1da177e4
LT
2182}
2183
2184/* The built-in targets: standard (NULL) and error. */
4538506b
JE
2185static struct xt_target ip6t_builtin_tg[] __read_mostly = {
2186 {
2187 .name = IP6T_STANDARD_TARGET,
2188 .targetsize = sizeof(int),
2189 .family = NFPROTO_IPV6,
3bc3fe5e 2190#ifdef CONFIG_COMPAT
4538506b
JE
2191 .compatsize = sizeof(compat_int_t),
2192 .compat_from_user = compat_standard_from_user,
2193 .compat_to_user = compat_standard_to_user,
3bc3fe5e 2194#endif
4538506b
JE
2195 },
2196 {
2197 .name = IP6T_ERROR_TARGET,
2198 .target = ip6t_error,
2199 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
2200 .family = NFPROTO_IPV6,
2201 },
1da177e4
LT
2202};
2203
2204static struct nf_sockopt_ops ip6t_sockopts = {
2205 .pf = PF_INET6,
2206 .set_optmin = IP6T_BASE_CTL,
2207 .set_optmax = IP6T_SO_SET_MAX+1,
2208 .set = do_ip6t_set_ctl,
3bc3fe5e
PM
2209#ifdef CONFIG_COMPAT
2210 .compat_set = compat_do_ip6t_set_ctl,
2211#endif
1da177e4
LT
2212 .get_optmin = IP6T_BASE_CTL,
2213 .get_optmax = IP6T_SO_GET_MAX+1,
2214 .get = do_ip6t_get_ctl,
3bc3fe5e
PM
2215#ifdef CONFIG_COMPAT
2216 .compat_get = compat_do_ip6t_get_ctl,
2217#endif
16fcec35 2218 .owner = THIS_MODULE,
1da177e4
LT
2219};
2220
4538506b
JE
2221static struct xt_match ip6t_builtin_mt[] __read_mostly = {
2222 {
2223 .name = "icmp6",
2224 .match = icmp6_match,
2225 .matchsize = sizeof(struct ip6t_icmp),
2226 .checkentry = icmp6_checkentry,
2227 .proto = IPPROTO_ICMPV6,
2228 .family = NFPROTO_IPV6,
2229 },
1da177e4
LT
2230};
2231
3cb609d5
AD
2232static int __net_init ip6_tables_net_init(struct net *net)
2233{
383ca5b8 2234 return xt_proto_init(net, NFPROTO_IPV6);
3cb609d5
AD
2235}
2236
2237static void __net_exit ip6_tables_net_exit(struct net *net)
2238{
383ca5b8 2239 xt_proto_fini(net, NFPROTO_IPV6);
3cb609d5
AD
2240}
2241
2242static struct pernet_operations ip6_tables_net_ops = {
2243 .init = ip6_tables_net_init,
2244 .exit = ip6_tables_net_exit,
2245};
2246
65b4b4e8 2247static int __init ip6_tables_init(void)
1da177e4
LT
2248{
2249 int ret;
2250
3cb609d5 2251 ret = register_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2252 if (ret < 0)
2253 goto err1;
2e4e6a17 2254
1da177e4 2255 /* Noone else will be downing sem now, so we won't sleep */
4538506b 2256 ret = xt_register_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
0eff66e6
PM
2257 if (ret < 0)
2258 goto err2;
4538506b 2259 ret = xt_register_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
0eff66e6
PM
2260 if (ret < 0)
2261 goto err4;
1da177e4
LT
2262
2263 /* Register setsockopt */
2264 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
2265 if (ret < 0)
2266 goto err5;
1da177e4 2267
ff67e4e4 2268 pr_info("(C) 2000-2006 Netfilter Core Team\n");
1da177e4 2269 return 0;
0eff66e6
PM
2270
2271err5:
4538506b 2272 xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
0eff66e6 2273err4:
4538506b 2274 xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
0eff66e6 2275err2:
3cb609d5 2276 unregister_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2277err1:
2278 return ret;
1da177e4
LT
2279}
2280
65b4b4e8 2281static void __exit ip6_tables_fini(void)
1da177e4
LT
2282{
2283 nf_unregister_sockopt(&ip6t_sockopts);
9c547959 2284
4538506b
JE
2285 xt_unregister_matches(ip6t_builtin_mt, ARRAY_SIZE(ip6t_builtin_mt));
2286 xt_unregister_targets(ip6t_builtin_tg, ARRAY_SIZE(ip6t_builtin_tg));
3cb609d5 2287 unregister_pernet_subsys(&ip6_tables_net_ops);
1da177e4
LT
2288}
2289
e674d0f3 2290/*
b777e0ce
PM
2291 * find the offset to specified header or the protocol number of last header
2292 * if target < 0. "last header" is transport protocol header, ESP, or
2293 * "No next header".
2294 *
2295 * If target header is found, its offset is set in *offset and return protocol
2296 * number. Otherwise, return -1.
2297 *
6d381634
PM
2298 * If the first fragment doesn't contain the final protocol header or
2299 * NEXTHDR_NONE it is considered invalid.
2300 *
b777e0ce
PM
2301 * Note that non-1st fragment is special case that "the protocol number
2302 * of last header" is "next header" field in Fragment header. In this case,
2303 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
2304 * isn't NULL.
e674d0f3 2305 *
e674d0f3 2306 */
b777e0ce
PM
2307int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2308 int target, unsigned short *fragoff)
e674d0f3 2309{
6b88dd96 2310 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0660e03f 2311 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
e674d0f3
YK
2312 unsigned int len = skb->len - start;
2313
b777e0ce
PM
2314 if (fragoff)
2315 *fragoff = 0;
2316
e674d0f3
YK
2317 while (nexthdr != target) {
2318 struct ipv6_opt_hdr _hdr, *hp;
2319 unsigned int hdrlen;
2320
b777e0ce
PM
2321 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2322 if (target < 0)
2323 break;
6d381634 2324 return -ENOENT;
b777e0ce
PM
2325 }
2326
e674d0f3
YK
2327 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2328 if (hp == NULL)
6d381634 2329 return -EBADMSG;
e674d0f3 2330 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
2331 unsigned short _frag_off;
2332 __be16 *fp;
e674d0f3
YK
2333 fp = skb_header_pointer(skb,
2334 start+offsetof(struct frag_hdr,
2335 frag_off),
2336 sizeof(_frag_off),
2337 &_frag_off);
2338 if (fp == NULL)
6d381634 2339 return -EBADMSG;
e674d0f3 2340
b777e0ce
PM
2341 _frag_off = ntohs(*fp) & ~0x7;
2342 if (_frag_off) {
2343 if (target < 0 &&
2344 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 2345 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
2346 if (fragoff)
2347 *fragoff = _frag_off;
2348 return hp->nexthdr;
2349 }
6d381634 2350 return -ENOENT;
b777e0ce 2351 }
e674d0f3
YK
2352 hdrlen = 8;
2353 } else if (nexthdr == NEXTHDR_AUTH)
1ab1457c 2354 hdrlen = (hp->hdrlen + 2) << 2;
e674d0f3 2355 else
1ab1457c 2356 hdrlen = ipv6_optlen(hp);
e674d0f3
YK
2357
2358 nexthdr = hp->nexthdr;
2359 len -= hdrlen;
2360 start += hdrlen;
2361 }
2362
2363 *offset = start;
b777e0ce 2364 return nexthdr;
e674d0f3
YK
2365}
2366
1da177e4
LT
2367EXPORT_SYMBOL(ip6t_register_table);
2368EXPORT_SYMBOL(ip6t_unregister_table);
2369EXPORT_SYMBOL(ip6t_do_table);
1da177e4 2370EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 2371EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 2372
65b4b4e8
AM
2373module_init(ip6_tables_init);
2374module_exit(ip6_tables_fini);