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