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