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