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