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