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