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