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