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