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