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