netfilter: ctnetlink: only assign helpers for matching protocols
[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
022748a9 672static int check_target(struct ip6t_entry *e, const char *name)
1da177e4 673{
af5d6dc2
JE
674 struct ip6t_entry_target *t = ip6t_get_target(e);
675 struct xt_tgchk_param par = {
676 .table = name,
677 .entryinfo = e,
678 .target = t->u.kernel.target,
679 .targinfo = t->data,
680 .hook_mask = e->comefrom,
916a917d 681 .family = NFPROTO_IPV6,
af5d6dc2 682 };
1da177e4 683 int ret;
1da177e4 684
f173c8a1 685 t = ip6t_get_target(e);
916a917d 686 ret = xt_check_target(&par, t->u.target_size - sizeof(*t),
af5d6dc2 687 e->ipv6.proto, e->ipv6.invflags & IP6T_INV_PROTO);
367c6790 688 if (ret < 0) {
f173c8a1
PM
689 duprintf("ip_tables: check failed for `%s'.\n",
690 t->u.kernel.target->name);
367c6790 691 return ret;
1da177e4 692 }
367c6790 693 return 0;
f173c8a1 694}
1da177e4 695
022748a9 696static int
a83d8e8d
AD
697find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
698 unsigned int size, unsigned int *i)
f173c8a1
PM
699{
700 struct ip6t_entry_target *t;
701 struct xt_target *target;
702 int ret;
703 unsigned int j;
9b4fce7a 704 struct xt_mtchk_param mtpar;
f173c8a1
PM
705
706 ret = check_entry(e, name);
707 if (ret)
708 return ret;
590bdf7f 709
1da177e4 710 j = 0;
a83d8e8d 711 mtpar.net = net;
9b4fce7a
JE
712 mtpar.table = name;
713 mtpar.entryinfo = &e->ipv6;
714 mtpar.hook_mask = e->comefrom;
916a917d 715 mtpar.family = NFPROTO_IPV6;
9b4fce7a 716 ret = IP6T_MATCH_ITERATE(e, find_check_match, &mtpar, &j);
1da177e4
LT
717 if (ret != 0)
718 goto cleanup_matches;
719
720 t = ip6t_get_target(e);
2e4e6a17
HW
721 target = try_then_request_module(xt_find_target(AF_INET6,
722 t->u.user.name,
723 t->u.user.revision),
6b7d31fc
HW
724 "ip6t_%s", t->u.user.name);
725 if (IS_ERR(target) || !target) {
f173c8a1 726 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
6b7d31fc 727 ret = target ? PTR_ERR(target) : -ENOENT;
1da177e4
LT
728 goto cleanup_matches;
729 }
730 t->u.kernel.target = target;
6b7d31fc 731
f173c8a1 732 ret = check_target(e, name);
3cdc7c95
PM
733 if (ret)
734 goto err;
735
1da177e4
LT
736 (*i)++;
737 return 0;
3cdc7c95
PM
738 err:
739 module_put(t->u.kernel.target->me);
1da177e4 740 cleanup_matches:
f54e9367 741 IP6T_MATCH_ITERATE(e, cleanup_match, net, &j);
1da177e4
LT
742 return ret;
743}
744
e2fe35c1
JE
745static bool check_underflow(struct ip6t_entry *e)
746{
747 const struct ip6t_entry_target *t;
748 unsigned int verdict;
749
750 if (!unconditional(&e->ipv6))
751 return false;
752 t = ip6t_get_target(e);
753 if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
754 return false;
755 verdict = ((struct ip6t_standard_target *)t)->verdict;
756 verdict = -verdict - 1;
757 return verdict == NF_DROP || verdict == NF_ACCEPT;
758}
759
022748a9 760static int
1da177e4 761check_entry_size_and_hooks(struct ip6t_entry *e,
2e4e6a17 762 struct xt_table_info *newinfo,
1da177e4
LT
763 unsigned char *base,
764 unsigned char *limit,
765 const unsigned int *hook_entries,
766 const unsigned int *underflows,
a7d51738 767 unsigned int valid_hooks,
1da177e4
LT
768 unsigned int *i)
769{
770 unsigned int h;
771
3666ed1c
JP
772 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
773 (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
1da177e4
LT
774 duprintf("Bad offset %p\n", e);
775 return -EINVAL;
776 }
777
778 if (e->next_offset
779 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
780 duprintf("checking: element %p size %u\n",
781 e, e->next_offset);
782 return -EINVAL;
783 }
784
785 /* Check hooks & underflows */
6e23ae2a 786 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
a7d51738
JE
787 if (!(valid_hooks & (1 << h)))
788 continue;
1da177e4
LT
789 if ((unsigned char *)e - base == hook_entries[h])
790 newinfo->hook_entry[h] = hook_entries[h];
90e7d4ab 791 if ((unsigned char *)e - base == underflows[h]) {
e2fe35c1
JE
792 if (!check_underflow(e)) {
793 pr_err("Underflows must be unconditional and "
794 "use the STANDARD target with "
795 "ACCEPT/DROP\n");
90e7d4ab
JE
796 return -EINVAL;
797 }
1da177e4 798 newinfo->underflow[h] = underflows[h];
90e7d4ab 799 }
1da177e4
LT
800 }
801
1da177e4 802 /* Clear counters and comefrom */
2e4e6a17 803 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4
LT
804 e->comefrom = 0;
805
806 (*i)++;
807 return 0;
808}
809
022748a9 810static int
f54e9367 811cleanup_entry(struct ip6t_entry *e, struct net *net, unsigned int *i)
1da177e4 812{
a2df1648 813 struct xt_tgdtor_param par;
1da177e4
LT
814 struct ip6t_entry_target *t;
815
816 if (i && (*i)-- == 0)
817 return 1;
818
819 /* Cleanup all matches */
f54e9367 820 IP6T_MATCH_ITERATE(e, cleanup_match, net, NULL);
1da177e4 821 t = ip6t_get_target(e);
a2df1648
JE
822
823 par.target = t->u.kernel.target;
824 par.targinfo = t->data;
916a917d 825 par.family = NFPROTO_IPV6;
a2df1648
JE
826 if (par.target->destroy != NULL)
827 par.target->destroy(&par);
828 module_put(par.target->me);
1da177e4
LT
829 return 0;
830}
831
832/* Checks and translates the user-supplied table segment (held in
833 newinfo) */
834static int
a83d8e8d
AD
835translate_table(struct net *net,
836 const char *name,
1da177e4 837 unsigned int valid_hooks,
2e4e6a17 838 struct xt_table_info *newinfo,
31836064 839 void *entry0,
1da177e4
LT
840 unsigned int size,
841 unsigned int number,
842 const unsigned int *hook_entries,
843 const unsigned int *underflows)
844{
845 unsigned int i;
846 int ret;
847
848 newinfo->size = size;
849 newinfo->number = number;
850
851 /* Init all hooks to impossible value. */
6e23ae2a 852 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
853 newinfo->hook_entry[i] = 0xFFFFFFFF;
854 newinfo->underflow[i] = 0xFFFFFFFF;
855 }
856
857 duprintf("translate_table: size %u\n", newinfo->size);
858 i = 0;
859 /* Walk through entries, checking offsets. */
31836064 860 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
861 check_entry_size_and_hooks,
862 newinfo,
31836064
ED
863 entry0,
864 entry0 + size,
a7d51738 865 hook_entries, underflows, valid_hooks, &i);
1da177e4
LT
866 if (ret != 0)
867 return ret;
868
869 if (i != number) {
870 duprintf("translate_table: %u not %u entries\n",
871 i, number);
872 return -EINVAL;
873 }
874
875 /* Check hooks all assigned */
6e23ae2a 876 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
877 /* Only hooks which are valid */
878 if (!(valid_hooks & (1 << i)))
879 continue;
880 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
881 duprintf("Invalid hook entry %u %u\n",
882 i, hook_entries[i]);
883 return -EINVAL;
884 }
885 if (newinfo->underflow[i] == 0xFFFFFFFF) {
886 duprintf("Invalid underflow %u %u\n",
887 i, underflows[i]);
888 return -EINVAL;
889 }
890 }
891
74c9c0c1
DM
892 if (!mark_source_chains(newinfo, valid_hooks, entry0))
893 return -ELOOP;
894
1da177e4
LT
895 /* Finally, each sanity check must pass */
896 i = 0;
31836064 897 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
a83d8e8d 898 find_check_entry, net, name, size, &i);
1da177e4 899
74c9c0c1
DM
900 if (ret != 0) {
901 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
f54e9367 902 cleanup_entry, net, &i);
74c9c0c1
DM
903 return ret;
904 }
1da177e4
LT
905
906 /* And one copy for every other CPU */
6f912042 907 for_each_possible_cpu(i) {
31836064
ED
908 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
909 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
910 }
911
9c547959 912 return ret;
1da177e4
LT
913}
914
1da177e4
LT
915/* Gets counters. */
916static inline int
917add_entry_to_counter(const struct ip6t_entry *e,
2e4e6a17 918 struct xt_counters total[],
1da177e4
LT
919 unsigned int *i)
920{
921 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
922
923 (*i)++;
924 return 0;
925}
926
31836064
ED
927static inline int
928set_entry_to_counter(const struct ip6t_entry *e,
929 struct ip6t_counters total[],
930 unsigned int *i)
931{
932 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
933
934 (*i)++;
935 return 0;
936}
937
1da177e4 938static void
2e4e6a17
HW
939get_counters(const struct xt_table_info *t,
940 struct xt_counters counters[])
1da177e4
LT
941{
942 unsigned int cpu;
943 unsigned int i;
31836064
ED
944 unsigned int curcpu;
945
946 /* Instead of clearing (by a previous call to memset())
947 * the counters and using adds, we set the counters
948 * with data used by 'current' CPU
942e4a2b
SH
949 *
950 * Bottom half has to be disabled to prevent deadlock
951 * if new softirq were to run and call ipt_do_table
31836064 952 */
942e4a2b
SH
953 local_bh_disable();
954 curcpu = smp_processor_id();
31836064
ED
955
956 i = 0;
957 IP6T_ENTRY_ITERATE(t->entries[curcpu],
958 t->size,
959 set_entry_to_counter,
960 counters,
961 &i);
1da177e4 962
6f912042 963 for_each_possible_cpu(cpu) {
31836064
ED
964 if (cpu == curcpu)
965 continue;
1da177e4 966 i = 0;
942e4a2b 967 xt_info_wrlock(cpu);
31836064 968 IP6T_ENTRY_ITERATE(t->entries[cpu],
1da177e4
LT
969 t->size,
970 add_entry_to_counter,
971 counters,
972 &i);
942e4a2b 973 xt_info_wrunlock(cpu);
1da177e4 974 }
78454473
SH
975 local_bh_enable();
976}
977
022748a9 978static struct xt_counters *alloc_counters(struct xt_table *table)
1da177e4 979{
ed1a6f5e 980 unsigned int countersize;
2e4e6a17 981 struct xt_counters *counters;
78454473 982 struct xt_table_info *private = table->private;
1da177e4
LT
983
984 /* We need atomic snapshot of counters: rest doesn't change
985 (other than comefrom, which userspace doesn't care
986 about). */
2e4e6a17 987 countersize = sizeof(struct xt_counters) * private->number;
3b84e92b 988 counters = vmalloc_node(countersize, numa_node_id());
1da177e4
LT
989
990 if (counters == NULL)
942e4a2b 991 return ERR_PTR(-ENOMEM);
78454473 992
942e4a2b 993 get_counters(private, counters);
78454473 994
49a88d18 995 return counters;
ed1a6f5e
PM
996}
997
998static int
999copy_entries_to_user(unsigned int total_size,
1000 struct xt_table *table,
1001 void __user *userptr)
1002{
1003 unsigned int off, num;
1004 struct ip6t_entry *e;
1005 struct xt_counters *counters;
5452e425 1006 const struct xt_table_info *private = table->private;
ed1a6f5e 1007 int ret = 0;
5452e425 1008 const void *loc_cpu_entry;
ed1a6f5e
PM
1009
1010 counters = alloc_counters(table);
1011 if (IS_ERR(counters))
1012 return PTR_ERR(counters);
1013
9c547959
PM
1014 /* choose the copy that is on our node/cpu, ...
1015 * This choice is lazy (because current thread is
1016 * allowed to migrate to another cpu)
1017 */
2e4e6a17 1018 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 1019 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
1020 ret = -EFAULT;
1021 goto free_counters;
1022 }
1023
1024 /* FIXME: use iterator macros --RR */
1025 /* ... then go back and fix counters and names */
1026 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
1027 unsigned int i;
5452e425
JE
1028 const struct ip6t_entry_match *m;
1029 const struct ip6t_entry_target *t;
1da177e4 1030
31836064 1031 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
1032 if (copy_to_user(userptr + off
1033 + offsetof(struct ip6t_entry, counters),
1034 &counters[num],
1035 sizeof(counters[num])) != 0) {
1036 ret = -EFAULT;
1037 goto free_counters;
1038 }
1039
1040 for (i = sizeof(struct ip6t_entry);
1041 i < e->target_offset;
1042 i += m->u.match_size) {
1043 m = (void *)e + i;
1044
1045 if (copy_to_user(userptr + off + i
1046 + offsetof(struct ip6t_entry_match,
1047 u.user.name),
1048 m->u.kernel.match->name,
1049 strlen(m->u.kernel.match->name)+1)
1050 != 0) {
1051 ret = -EFAULT;
1052 goto free_counters;
1053 }
1054 }
1055
1056 t = ip6t_get_target(e);
1057 if (copy_to_user(userptr + off + e->target_offset
1058 + offsetof(struct ip6t_entry_target,
1059 u.user.name),
1060 t->u.kernel.target->name,
1061 strlen(t->u.kernel.target->name)+1) != 0) {
1062 ret = -EFAULT;
1063 goto free_counters;
1064 }
1065 }
1066
1067 free_counters:
1068 vfree(counters);
1069 return ret;
1070}
1071
3bc3fe5e
PM
1072#ifdef CONFIG_COMPAT
1073static void compat_standard_from_user(void *dst, void *src)
1074{
1075 int v = *(compat_int_t *)src;
1076
1077 if (v > 0)
1078 v += xt_compat_calc_jump(AF_INET6, v);
1079 memcpy(dst, &v, sizeof(v));
1080}
1081
1082static int compat_standard_to_user(void __user *dst, void *src)
1083{
1084 compat_int_t cv = *(int *)src;
1085
1086 if (cv > 0)
1087 cv -= xt_compat_calc_jump(AF_INET6, cv);
1088 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1089}
1090
1091static inline int
1092compat_calc_match(struct ip6t_entry_match *m, int *size)
1093{
1094 *size += xt_compat_match_offset(m->u.kernel.match);
1095 return 0;
1096}
1097
1098static int compat_calc_entry(struct ip6t_entry *e,
1099 const struct xt_table_info *info,
1100 void *base, struct xt_table_info *newinfo)
1101{
1102 struct ip6t_entry_target *t;
1103 unsigned int entry_offset;
1104 int off, i, ret;
1105
1106 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1107 entry_offset = (void *)e - base;
1108 IP6T_MATCH_ITERATE(e, compat_calc_match, &off);
1109 t = ip6t_get_target(e);
1110 off += xt_compat_target_offset(t->u.kernel.target);
1111 newinfo->size -= off;
1112 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1113 if (ret)
1114 return ret;
1115
1116 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1117 if (info->hook_entry[i] &&
1118 (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1119 newinfo->hook_entry[i] -= off;
1120 if (info->underflow[i] &&
1121 (e < (struct ip6t_entry *)(base + info->underflow[i])))
1122 newinfo->underflow[i] -= off;
1123 }
1124 return 0;
1125}
1126
1127static int compat_table_info(const struct xt_table_info *info,
1128 struct xt_table_info *newinfo)
1129{
1130 void *loc_cpu_entry;
1131
1132 if (!newinfo || !info)
1133 return -EINVAL;
1134
1135 /* we dont care about newinfo->entries[] */
1136 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1137 newinfo->initial_entries = 0;
1138 loc_cpu_entry = info->entries[raw_smp_processor_id()];
1139 return IP6T_ENTRY_ITERATE(loc_cpu_entry, info->size,
1140 compat_calc_entry, info, loc_cpu_entry,
1141 newinfo);
1142}
1143#endif
1144
336b517f 1145static int get_info(struct net *net, void __user *user, int *len, int compat)
433665c9
PM
1146{
1147 char name[IP6T_TABLE_MAXNAMELEN];
1148 struct xt_table *t;
1149 int ret;
1150
1151 if (*len != sizeof(struct ip6t_getinfo)) {
c9d8fe13 1152 duprintf("length %u != %zu\n", *len,
433665c9
PM
1153 sizeof(struct ip6t_getinfo));
1154 return -EINVAL;
1155 }
1156
1157 if (copy_from_user(name, user, sizeof(name)) != 0)
1158 return -EFAULT;
1159
1160 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
3bc3fe5e
PM
1161#ifdef CONFIG_COMPAT
1162 if (compat)
1163 xt_compat_lock(AF_INET6);
1164#endif
336b517f 1165 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
433665c9
PM
1166 "ip6table_%s", name);
1167 if (t && !IS_ERR(t)) {
1168 struct ip6t_getinfo info;
5452e425 1169 const struct xt_table_info *private = t->private;
433665c9 1170
3bc3fe5e
PM
1171#ifdef CONFIG_COMPAT
1172 if (compat) {
1173 struct xt_table_info tmp;
1174 ret = compat_table_info(private, &tmp);
1175 xt_compat_flush_offsets(AF_INET6);
1176 private = &tmp;
1177 }
1178#endif
433665c9
PM
1179 info.valid_hooks = t->valid_hooks;
1180 memcpy(info.hook_entry, private->hook_entry,
1181 sizeof(info.hook_entry));
1182 memcpy(info.underflow, private->underflow,
1183 sizeof(info.underflow));
1184 info.num_entries = private->number;
1185 info.size = private->size;
b5dd674b 1186 strcpy(info.name, name);
433665c9
PM
1187
1188 if (copy_to_user(user, &info, *len) != 0)
1189 ret = -EFAULT;
1190 else
1191 ret = 0;
1192
1193 xt_table_unlock(t);
1194 module_put(t->me);
1195 } else
1196 ret = t ? PTR_ERR(t) : -ENOENT;
3bc3fe5e
PM
1197#ifdef CONFIG_COMPAT
1198 if (compat)
1199 xt_compat_unlock(AF_INET6);
1200#endif
433665c9
PM
1201 return ret;
1202}
1203
1da177e4 1204static int
336b517f 1205get_entries(struct net *net, struct ip6t_get_entries __user *uptr, int *len)
1da177e4
LT
1206{
1207 int ret;
d924357c 1208 struct ip6t_get_entries get;
2e4e6a17 1209 struct xt_table *t;
1da177e4 1210
d924357c 1211 if (*len < sizeof(get)) {
c9d8fe13 1212 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
d924357c
PM
1213 return -EINVAL;
1214 }
1215 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1216 return -EFAULT;
1217 if (*len != sizeof(struct ip6t_get_entries) + get.size) {
c9d8fe13
PM
1218 duprintf("get_entries: %u != %zu\n",
1219 *len, sizeof(get) + get.size);
d924357c
PM
1220 return -EINVAL;
1221 }
1222
336b517f 1223 t = xt_find_table_lock(net, AF_INET6, get.name);
6b7d31fc 1224 if (t && !IS_ERR(t)) {
2e4e6a17
HW
1225 struct xt_table_info *private = t->private;
1226 duprintf("t->private->number = %u\n", private->number);
d924357c 1227 if (get.size == private->size)
2e4e6a17 1228 ret = copy_entries_to_user(private->size,
1da177e4
LT
1229 t, uptr->entrytable);
1230 else {
1231 duprintf("get_entries: I've got %u not %u!\n",
9c547959 1232 private->size, get.size);
544473c1 1233 ret = -EAGAIN;
1da177e4 1234 }
6b7d31fc 1235 module_put(t->me);
2e4e6a17 1236 xt_table_unlock(t);
1da177e4 1237 } else
6b7d31fc 1238 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1239
1240 return ret;
1241}
1242
1243static int
336b517f 1244__do_replace(struct net *net, const char *name, unsigned int valid_hooks,
3bc3fe5e
PM
1245 struct xt_table_info *newinfo, unsigned int num_counters,
1246 void __user *counters_ptr)
1da177e4
LT
1247{
1248 int ret;
2e4e6a17 1249 struct xt_table *t;
3bc3fe5e 1250 struct xt_table_info *oldinfo;
2e4e6a17 1251 struct xt_counters *counters;
5452e425 1252 const void *loc_cpu_old_entry;
1da177e4 1253
3bc3fe5e
PM
1254 ret = 0;
1255 counters = vmalloc_node(num_counters * sizeof(struct xt_counters),
3b84e92b 1256 numa_node_id());
1da177e4
LT
1257 if (!counters) {
1258 ret = -ENOMEM;
3bc3fe5e 1259 goto out;
1da177e4 1260 }
1da177e4 1261
336b517f 1262 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
3bc3fe5e 1263 "ip6table_%s", name);
6b7d31fc
HW
1264 if (!t || IS_ERR(t)) {
1265 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1266 goto free_newinfo_counters_untrans;
6b7d31fc 1267 }
1da177e4
LT
1268
1269 /* You lied! */
3bc3fe5e 1270 if (valid_hooks != t->valid_hooks) {
1da177e4 1271 duprintf("Valid hook crap: %08X vs %08X\n",
3bc3fe5e 1272 valid_hooks, t->valid_hooks);
1da177e4 1273 ret = -EINVAL;
6b7d31fc 1274 goto put_module;
1da177e4
LT
1275 }
1276
3bc3fe5e 1277 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1da177e4
LT
1278 if (!oldinfo)
1279 goto put_module;
1280
1281 /* Update module usage count based on number of rules */
1282 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1283 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1284 if ((oldinfo->number > oldinfo->initial_entries) ||
1285 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1286 module_put(t->me);
1287 if ((oldinfo->number > oldinfo->initial_entries) &&
1288 (newinfo->number <= oldinfo->initial_entries))
1289 module_put(t->me);
1290
942e4a2b 1291 /* Get the old counters, and synchronize with replace */
1da177e4 1292 get_counters(oldinfo, counters);
942e4a2b 1293
1da177e4 1294 /* Decrease module usage counts and free resource */
31836064 1295 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
3bc3fe5e 1296 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
f54e9367 1297 net, NULL);
2e4e6a17 1298 xt_free_table_info(oldinfo);
3bc3fe5e
PM
1299 if (copy_to_user(counters_ptr, counters,
1300 sizeof(struct xt_counters) * num_counters) != 0)
1da177e4
LT
1301 ret = -EFAULT;
1302 vfree(counters);
2e4e6a17 1303 xt_table_unlock(t);
1da177e4
LT
1304 return ret;
1305
1306 put_module:
1307 module_put(t->me);
2e4e6a17 1308 xt_table_unlock(t);
1da177e4 1309 free_newinfo_counters_untrans:
1da177e4 1310 vfree(counters);
3bc3fe5e
PM
1311 out:
1312 return ret;
1313}
1314
1315static int
336b517f 1316do_replace(struct net *net, void __user *user, unsigned int len)
3bc3fe5e
PM
1317{
1318 int ret;
1319 struct ip6t_replace tmp;
1320 struct xt_table_info *newinfo;
1321 void *loc_cpu_entry;
1322
1323 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1324 return -EFAULT;
1325
1326 /* overflow check */
1327 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1328 return -ENOMEM;
1329
1330 newinfo = xt_alloc_table_info(tmp.size);
1331 if (!newinfo)
1332 return -ENOMEM;
1333
1334 /* choose the copy that is on our node/cpu */
1335 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1336 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1337 tmp.size) != 0) {
1338 ret = -EFAULT;
1339 goto free_newinfo;
1340 }
1341
a83d8e8d 1342 ret = translate_table(net, tmp.name, tmp.valid_hooks,
3bc3fe5e
PM
1343 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1344 tmp.hook_entry, tmp.underflow);
1345 if (ret != 0)
1346 goto free_newinfo;
1347
1348 duprintf("ip_tables: Translated table\n");
1349
336b517f 1350 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
3bc3fe5e
PM
1351 tmp.num_counters, tmp.counters);
1352 if (ret)
1353 goto free_newinfo_untrans;
1354 return 0;
1355
1356 free_newinfo_untrans:
f54e9367 1357 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
1da177e4 1358 free_newinfo:
2e4e6a17 1359 xt_free_table_info(newinfo);
1da177e4
LT
1360 return ret;
1361}
1362
942e4a2b
SH
1363/* We're lazy, and add to the first CPU; overflow works its fey magic
1364 * and everything is OK. */
1365static int
1366add_counter_to_entry(struct ip6t_entry *e,
1367 const struct xt_counters addme[],
1368 unsigned int *i)
1369{
1370 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1371
1372 (*i)++;
1373 return 0;
1374}
1375
1da177e4 1376static int
336b517f
AD
1377do_add_counters(struct net *net, void __user *user, unsigned int len,
1378 int compat)
1da177e4 1379{
942e4a2b 1380 unsigned int i, curcpu;
3bc3fe5e
PM
1381 struct xt_counters_info tmp;
1382 struct xt_counters *paddc;
1383 unsigned int num_counters;
1384 char *name;
1385 int size;
1386 void *ptmp;
2e4e6a17 1387 struct xt_table *t;
5452e425 1388 const struct xt_table_info *private;
6b7d31fc 1389 int ret = 0;
5452e425 1390 const void *loc_cpu_entry;
3bc3fe5e
PM
1391#ifdef CONFIG_COMPAT
1392 struct compat_xt_counters_info compat_tmp;
1da177e4 1393
3bc3fe5e
PM
1394 if (compat) {
1395 ptmp = &compat_tmp;
1396 size = sizeof(struct compat_xt_counters_info);
1397 } else
1398#endif
1399 {
1400 ptmp = &tmp;
1401 size = sizeof(struct xt_counters_info);
1402 }
1403
1404 if (copy_from_user(ptmp, user, size) != 0)
1da177e4
LT
1405 return -EFAULT;
1406
3bc3fe5e
PM
1407#ifdef CONFIG_COMPAT
1408 if (compat) {
1409 num_counters = compat_tmp.num_counters;
1410 name = compat_tmp.name;
1411 } else
1412#endif
1413 {
1414 num_counters = tmp.num_counters;
1415 name = tmp.name;
1416 }
1417
1418 if (len != size + num_counters * sizeof(struct xt_counters))
1da177e4
LT
1419 return -EINVAL;
1420
3bc3fe5e 1421 paddc = vmalloc_node(len - size, numa_node_id());
1da177e4
LT
1422 if (!paddc)
1423 return -ENOMEM;
1424
3bc3fe5e 1425 if (copy_from_user(paddc, user + size, len - size) != 0) {
1da177e4
LT
1426 ret = -EFAULT;
1427 goto free;
1428 }
1429
336b517f 1430 t = xt_find_table_lock(net, AF_INET6, name);
6b7d31fc
HW
1431 if (!t || IS_ERR(t)) {
1432 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1433 goto free;
6b7d31fc 1434 }
1da177e4 1435
942e4a2b
SH
1436
1437 local_bh_disable();
2e4e6a17 1438 private = t->private;
3bc3fe5e 1439 if (private->number != num_counters) {
1da177e4
LT
1440 ret = -EINVAL;
1441 goto unlock_up_free;
1442 }
1443
1444 i = 0;
31836064 1445 /* Choose the copy that is on our node */
942e4a2b
SH
1446 curcpu = smp_processor_id();
1447 xt_info_wrlock(curcpu);
1448 loc_cpu_entry = private->entries[curcpu];
31836064 1449 IP6T_ENTRY_ITERATE(loc_cpu_entry,
2e4e6a17 1450 private->size,
1da177e4 1451 add_counter_to_entry,
3bc3fe5e 1452 paddc,
1da177e4 1453 &i);
942e4a2b
SH
1454 xt_info_wrunlock(curcpu);
1455
1da177e4 1456 unlock_up_free:
942e4a2b 1457 local_bh_enable();
2e4e6a17 1458 xt_table_unlock(t);
6b7d31fc 1459 module_put(t->me);
1da177e4
LT
1460 free:
1461 vfree(paddc);
1462
1463 return ret;
1464}
1465
3bc3fe5e
PM
1466#ifdef CONFIG_COMPAT
1467struct compat_ip6t_replace {
1468 char name[IP6T_TABLE_MAXNAMELEN];
1469 u32 valid_hooks;
1470 u32 num_entries;
1471 u32 size;
1472 u32 hook_entry[NF_INET_NUMHOOKS];
1473 u32 underflow[NF_INET_NUMHOOKS];
1474 u32 num_counters;
1475 compat_uptr_t counters; /* struct ip6t_counters * */
1476 struct compat_ip6t_entry entries[0];
1477};
1478
1479static int
1480compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
b0a6363c 1481 unsigned int *size, struct xt_counters *counters,
3bc3fe5e
PM
1482 unsigned int *i)
1483{
1484 struct ip6t_entry_target *t;
1485 struct compat_ip6t_entry __user *ce;
1486 u_int16_t target_offset, next_offset;
1487 compat_uint_t origsize;
1488 int ret;
1489
1490 ret = -EFAULT;
1491 origsize = *size;
1492 ce = (struct compat_ip6t_entry __user *)*dstptr;
1493 if (copy_to_user(ce, e, sizeof(struct ip6t_entry)))
1494 goto out;
1495
1496 if (copy_to_user(&ce->counters, &counters[*i], sizeof(counters[*i])))
1497 goto out;
1498
1499 *dstptr += sizeof(struct compat_ip6t_entry);
1500 *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1501
1502 ret = IP6T_MATCH_ITERATE(e, xt_compat_match_to_user, dstptr, size);
1503 target_offset = e->target_offset - (origsize - *size);
1504 if (ret)
1505 goto out;
1506 t = ip6t_get_target(e);
1507 ret = xt_compat_target_to_user(t, dstptr, size);
1508 if (ret)
1509 goto out;
1510 ret = -EFAULT;
1511 next_offset = e->next_offset - (origsize - *size);
1512 if (put_user(target_offset, &ce->target_offset))
1513 goto out;
1514 if (put_user(next_offset, &ce->next_offset))
1515 goto out;
1516
1517 (*i)++;
1518 return 0;
1519out:
1520 return ret;
1521}
1522
022748a9 1523static int
3bc3fe5e
PM
1524compat_find_calc_match(struct ip6t_entry_match *m,
1525 const char *name,
1526 const struct ip6t_ip6 *ipv6,
1527 unsigned int hookmask,
b0a6363c 1528 int *size, unsigned int *i)
3bc3fe5e
PM
1529{
1530 struct xt_match *match;
1531
1532 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
1533 m->u.user.revision),
1534 "ip6t_%s", m->u.user.name);
1535 if (IS_ERR(match) || !match) {
1536 duprintf("compat_check_calc_match: `%s' not found\n",
1537 m->u.user.name);
1538 return match ? PTR_ERR(match) : -ENOENT;
1539 }
1540 m->u.kernel.match = match;
1541 *size += xt_compat_match_offset(match);
1542
1543 (*i)++;
1544 return 0;
1545}
1546
022748a9 1547static int
3bc3fe5e
PM
1548compat_release_match(struct ip6t_entry_match *m, unsigned int *i)
1549{
1550 if (i && (*i)-- == 0)
1551 return 1;
1552
1553 module_put(m->u.kernel.match->me);
1554 return 0;
1555}
1556
022748a9 1557static int
3bc3fe5e
PM
1558compat_release_entry(struct compat_ip6t_entry *e, unsigned int *i)
1559{
1560 struct ip6t_entry_target *t;
1561
1562 if (i && (*i)-- == 0)
1563 return 1;
1564
1565 /* Cleanup all matches */
1566 COMPAT_IP6T_MATCH_ITERATE(e, compat_release_match, NULL);
1567 t = compat_ip6t_get_target(e);
1568 module_put(t->u.kernel.target->me);
1569 return 0;
1570}
1571
022748a9 1572static int
3bc3fe5e
PM
1573check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1574 struct xt_table_info *newinfo,
1575 unsigned int *size,
1576 unsigned char *base,
1577 unsigned char *limit,
1578 unsigned int *hook_entries,
1579 unsigned int *underflows,
1580 unsigned int *i,
1581 const char *name)
1582{
1583 struct ip6t_entry_target *t;
1584 struct xt_target *target;
1585 unsigned int entry_offset;
b0a6363c
PM
1586 unsigned int j;
1587 int ret, off, h;
3bc3fe5e
PM
1588
1589 duprintf("check_compat_entry_size_and_hooks %p\n", e);
3666ed1c
JP
1590 if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
1591 (unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
3bc3fe5e
PM
1592 duprintf("Bad offset %p, limit = %p\n", e, limit);
1593 return -EINVAL;
1594 }
1595
1596 if (e->next_offset < sizeof(struct compat_ip6t_entry) +
1597 sizeof(struct compat_xt_entry_target)) {
1598 duprintf("checking: element %p size %u\n",
1599 e, e->next_offset);
1600 return -EINVAL;
1601 }
1602
1603 /* For purposes of check_entry casting the compat entry is fine */
1604 ret = check_entry((struct ip6t_entry *)e, name);
1605 if (ret)
1606 return ret;
1607
1608 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1609 entry_offset = (void *)e - (void *)base;
1610 j = 0;
1611 ret = COMPAT_IP6T_MATCH_ITERATE(e, compat_find_calc_match, name,
1612 &e->ipv6, e->comefrom, &off, &j);
1613 if (ret != 0)
1614 goto release_matches;
1615
1616 t = compat_ip6t_get_target(e);
1617 target = try_then_request_module(xt_find_target(AF_INET6,
1618 t->u.user.name,
1619 t->u.user.revision),
1620 "ip6t_%s", t->u.user.name);
1621 if (IS_ERR(target) || !target) {
1622 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1623 t->u.user.name);
1624 ret = target ? PTR_ERR(target) : -ENOENT;
1625 goto release_matches;
1626 }
1627 t->u.kernel.target = target;
1628
1629 off += xt_compat_target_offset(target);
1630 *size += off;
1631 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1632 if (ret)
1633 goto out;
1634
1635 /* Check hooks & underflows */
1636 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1637 if ((unsigned char *)e - base == hook_entries[h])
1638 newinfo->hook_entry[h] = hook_entries[h];
1639 if ((unsigned char *)e - base == underflows[h])
1640 newinfo->underflow[h] = underflows[h];
1641 }
1642
1643 /* Clear counters and comefrom */
1644 memset(&e->counters, 0, sizeof(e->counters));
1645 e->comefrom = 0;
1646
1647 (*i)++;
1648 return 0;
1649
1650out:
1651 module_put(t->u.kernel.target->me);
1652release_matches:
1653 IP6T_MATCH_ITERATE(e, compat_release_match, &j);
1654 return ret;
1655}
1656
1657static int
1658compat_copy_entry_from_user(struct compat_ip6t_entry *e, void **dstptr,
1659 unsigned int *size, const char *name,
1660 struct xt_table_info *newinfo, unsigned char *base)
1661{
1662 struct ip6t_entry_target *t;
1663 struct xt_target *target;
1664 struct ip6t_entry *de;
1665 unsigned int origsize;
1666 int ret, h;
1667
1668 ret = 0;
1669 origsize = *size;
1670 de = (struct ip6t_entry *)*dstptr;
1671 memcpy(de, e, sizeof(struct ip6t_entry));
1672 memcpy(&de->counters, &e->counters, sizeof(e->counters));
1673
1674 *dstptr += sizeof(struct ip6t_entry);
1675 *size += sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1676
1677 ret = COMPAT_IP6T_MATCH_ITERATE(e, xt_compat_match_from_user,
1678 dstptr, size);
1679 if (ret)
1680 return ret;
1681 de->target_offset = e->target_offset - (origsize - *size);
1682 t = compat_ip6t_get_target(e);
1683 target = t->u.kernel.target;
1684 xt_compat_target_from_user(t, dstptr, size);
1685
1686 de->next_offset = e->next_offset - (origsize - *size);
1687 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1688 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1689 newinfo->hook_entry[h] -= origsize - *size;
1690 if ((unsigned char *)de - base < newinfo->underflow[h])
1691 newinfo->underflow[h] -= origsize - *size;
1692 }
1693 return ret;
1694}
1695
f54e9367
AD
1696static int compat_check_entry(struct ip6t_entry *e, struct net *net,
1697 const char *name, unsigned int *i)
3bc3fe5e 1698{
b0a6363c
PM
1699 unsigned int j;
1700 int ret;
9b4fce7a 1701 struct xt_mtchk_param mtpar;
3bc3fe5e
PM
1702
1703 j = 0;
f54e9367 1704 mtpar.net = net;
9b4fce7a
JE
1705 mtpar.table = name;
1706 mtpar.entryinfo = &e->ipv6;
1707 mtpar.hook_mask = e->comefrom;
916a917d 1708 mtpar.family = NFPROTO_IPV6;
9b4fce7a 1709 ret = IP6T_MATCH_ITERATE(e, check_match, &mtpar, &j);
3bc3fe5e
PM
1710 if (ret)
1711 goto cleanup_matches;
1712
1713 ret = check_target(e, name);
1714 if (ret)
1715 goto cleanup_matches;
1716
1717 (*i)++;
1718 return 0;
1719
1720 cleanup_matches:
f54e9367 1721 IP6T_MATCH_ITERATE(e, cleanup_match, net, &j);
3bc3fe5e
PM
1722 return ret;
1723}
1724
1725static int
f54e9367
AD
1726translate_compat_table(struct net *net,
1727 const char *name,
3bc3fe5e
PM
1728 unsigned int valid_hooks,
1729 struct xt_table_info **pinfo,
1730 void **pentry0,
1731 unsigned int total_size,
1732 unsigned int number,
1733 unsigned int *hook_entries,
1734 unsigned int *underflows)
1735{
1736 unsigned int i, j;
1737 struct xt_table_info *newinfo, *info;
1738 void *pos, *entry0, *entry1;
1739 unsigned int size;
1740 int ret;
1741
1742 info = *pinfo;
1743 entry0 = *pentry0;
1744 size = total_size;
1745 info->number = number;
1746
1747 /* Init all hooks to impossible value. */
1748 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1749 info->hook_entry[i] = 0xFFFFFFFF;
1750 info->underflow[i] = 0xFFFFFFFF;
1751 }
1752
1753 duprintf("translate_compat_table: size %u\n", info->size);
1754 j = 0;
1755 xt_compat_lock(AF_INET6);
1756 /* Walk through entries, checking offsets. */
1757 ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1758 check_compat_entry_size_and_hooks,
1759 info, &size, entry0,
1760 entry0 + total_size,
1761 hook_entries, underflows, &j, name);
1762 if (ret != 0)
1763 goto out_unlock;
1764
1765 ret = -EINVAL;
1766 if (j != number) {
1767 duprintf("translate_compat_table: %u not %u entries\n",
1768 j, number);
1769 goto out_unlock;
1770 }
1771
1772 /* Check hooks all assigned */
1773 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1774 /* Only hooks which are valid */
1775 if (!(valid_hooks & (1 << i)))
1776 continue;
1777 if (info->hook_entry[i] == 0xFFFFFFFF) {
1778 duprintf("Invalid hook entry %u %u\n",
1779 i, hook_entries[i]);
1780 goto out_unlock;
1781 }
1782 if (info->underflow[i] == 0xFFFFFFFF) {
1783 duprintf("Invalid underflow %u %u\n",
1784 i, underflows[i]);
1785 goto out_unlock;
1786 }
1787 }
1788
1789 ret = -ENOMEM;
1790 newinfo = xt_alloc_table_info(size);
1791 if (!newinfo)
1792 goto out_unlock;
1793
1794 newinfo->number = number;
1795 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1796 newinfo->hook_entry[i] = info->hook_entry[i];
1797 newinfo->underflow[i] = info->underflow[i];
1798 }
1799 entry1 = newinfo->entries[raw_smp_processor_id()];
1800 pos = entry1;
1801 size = total_size;
1802 ret = COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size,
1803 compat_copy_entry_from_user,
1804 &pos, &size, name, newinfo, entry1);
1805 xt_compat_flush_offsets(AF_INET6);
1806 xt_compat_unlock(AF_INET6);
1807 if (ret)
1808 goto free_newinfo;
1809
1810 ret = -ELOOP;
1811 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1812 goto free_newinfo;
1813
1814 i = 0;
1815 ret = IP6T_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
f54e9367 1816 net, name, &i);
3bc3fe5e
PM
1817 if (ret) {
1818 j -= i;
1819 COMPAT_IP6T_ENTRY_ITERATE_CONTINUE(entry0, newinfo->size, i,
1820 compat_release_entry, &j);
f54e9367 1821 IP6T_ENTRY_ITERATE(entry1, newinfo->size, cleanup_entry, net, &i);
3bc3fe5e
PM
1822 xt_free_table_info(newinfo);
1823 return ret;
1824 }
1825
1826 /* And one copy for every other CPU */
1827 for_each_possible_cpu(i)
1828 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1829 memcpy(newinfo->entries[i], entry1, newinfo->size);
1830
1831 *pinfo = newinfo;
1832 *pentry0 = entry1;
1833 xt_free_table_info(info);
1834 return 0;
1835
1836free_newinfo:
1837 xt_free_table_info(newinfo);
1838out:
1839 COMPAT_IP6T_ENTRY_ITERATE(entry0, total_size, compat_release_entry, &j);
1840 return ret;
1841out_unlock:
1842 xt_compat_flush_offsets(AF_INET6);
1843 xt_compat_unlock(AF_INET6);
1844 goto out;
1845}
1846
1847static int
336b517f 1848compat_do_replace(struct net *net, void __user *user, unsigned int len)
3bc3fe5e
PM
1849{
1850 int ret;
1851 struct compat_ip6t_replace tmp;
1852 struct xt_table_info *newinfo;
1853 void *loc_cpu_entry;
1854
1855 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1856 return -EFAULT;
1857
1858 /* overflow check */
1859 if (tmp.size >= INT_MAX / num_possible_cpus())
1860 return -ENOMEM;
1861 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1862 return -ENOMEM;
1863
1864 newinfo = xt_alloc_table_info(tmp.size);
1865 if (!newinfo)
1866 return -ENOMEM;
1867
9c547959 1868 /* choose the copy that is on our node/cpu */
3bc3fe5e
PM
1869 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1870 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1871 tmp.size) != 0) {
1872 ret = -EFAULT;
1873 goto free_newinfo;
1874 }
1875
f54e9367 1876 ret = translate_compat_table(net, tmp.name, tmp.valid_hooks,
3bc3fe5e
PM
1877 &newinfo, &loc_cpu_entry, tmp.size,
1878 tmp.num_entries, tmp.hook_entry,
1879 tmp.underflow);
1880 if (ret != 0)
1881 goto free_newinfo;
1882
1883 duprintf("compat_do_replace: Translated table\n");
1884
336b517f 1885 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
3bc3fe5e
PM
1886 tmp.num_counters, compat_ptr(tmp.counters));
1887 if (ret)
1888 goto free_newinfo_untrans;
1889 return 0;
1890
1891 free_newinfo_untrans:
f54e9367 1892 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, net, NULL);
3bc3fe5e
PM
1893 free_newinfo:
1894 xt_free_table_info(newinfo);
1895 return ret;
1896}
1897
1898static int
1899compat_do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user,
1900 unsigned int len)
1901{
1902 int ret;
1903
1904 if (!capable(CAP_NET_ADMIN))
1905 return -EPERM;
1906
1907 switch (cmd) {
1908 case IP6T_SO_SET_REPLACE:
3b1e0a65 1909 ret = compat_do_replace(sock_net(sk), user, len);
3bc3fe5e
PM
1910 break;
1911
1912 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 1913 ret = do_add_counters(sock_net(sk), user, len, 1);
3bc3fe5e
PM
1914 break;
1915
1916 default:
1917 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1918 ret = -EINVAL;
1919 }
1920
1921 return ret;
1922}
1923
1924struct compat_ip6t_get_entries {
1925 char name[IP6T_TABLE_MAXNAMELEN];
1926 compat_uint_t size;
1927 struct compat_ip6t_entry entrytable[0];
1928};
1929
1930static int
1931compat_copy_entries_to_user(unsigned int total_size, struct xt_table *table,
1932 void __user *userptr)
1933{
1934 struct xt_counters *counters;
5452e425 1935 const struct xt_table_info *private = table->private;
3bc3fe5e
PM
1936 void __user *pos;
1937 unsigned int size;
1938 int ret = 0;
5452e425 1939 const void *loc_cpu_entry;
3bc3fe5e
PM
1940 unsigned int i = 0;
1941
1942 counters = alloc_counters(table);
1943 if (IS_ERR(counters))
1944 return PTR_ERR(counters);
1945
1946 /* choose the copy that is on our node/cpu, ...
1947 * This choice is lazy (because current thread is
1948 * allowed to migrate to another cpu)
1949 */
1950 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1951 pos = userptr;
1952 size = total_size;
1953 ret = IP6T_ENTRY_ITERATE(loc_cpu_entry, total_size,
1954 compat_copy_entry_to_user,
1955 &pos, &size, counters, &i);
1956
1957 vfree(counters);
1958 return ret;
1959}
1960
1961static int
336b517f
AD
1962compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1963 int *len)
3bc3fe5e
PM
1964{
1965 int ret;
1966 struct compat_ip6t_get_entries get;
1967 struct xt_table *t;
1968
1969 if (*len < sizeof(get)) {
c9d8fe13 1970 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
3bc3fe5e
PM
1971 return -EINVAL;
1972 }
1973
1974 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1975 return -EFAULT;
1976
1977 if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
c9d8fe13
PM
1978 duprintf("compat_get_entries: %u != %zu\n",
1979 *len, sizeof(get) + get.size);
3bc3fe5e
PM
1980 return -EINVAL;
1981 }
1982
1983 xt_compat_lock(AF_INET6);
336b517f 1984 t = xt_find_table_lock(net, AF_INET6, get.name);
3bc3fe5e 1985 if (t && !IS_ERR(t)) {
5452e425 1986 const struct xt_table_info *private = t->private;
3bc3fe5e 1987 struct xt_table_info info;
9c547959 1988 duprintf("t->private->number = %u\n", private->number);
3bc3fe5e
PM
1989 ret = compat_table_info(private, &info);
1990 if (!ret && get.size == info.size) {
1991 ret = compat_copy_entries_to_user(private->size,
1992 t, uptr->entrytable);
1993 } else if (!ret) {
1994 duprintf("compat_get_entries: I've got %u not %u!\n",
9c547959 1995 private->size, get.size);
544473c1 1996 ret = -EAGAIN;
3bc3fe5e
PM
1997 }
1998 xt_compat_flush_offsets(AF_INET6);
1999 module_put(t->me);
2000 xt_table_unlock(t);
2001 } else
2002 ret = t ? PTR_ERR(t) : -ENOENT;
2003
2004 xt_compat_unlock(AF_INET6);
2005 return ret;
2006}
2007
2008static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
2009
2010static int
2011compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2012{
2013 int ret;
2014
2015 if (!capable(CAP_NET_ADMIN))
2016 return -EPERM;
2017
2018 switch (cmd) {
2019 case IP6T_SO_GET_INFO:
3b1e0a65 2020 ret = get_info(sock_net(sk), user, len, 1);
3bc3fe5e
PM
2021 break;
2022 case IP6T_SO_GET_ENTRIES:
3b1e0a65 2023 ret = compat_get_entries(sock_net(sk), user, len);
3bc3fe5e
PM
2024 break;
2025 default:
2026 ret = do_ip6t_get_ctl(sk, cmd, user, len);
2027 }
2028 return ret;
2029}
2030#endif
2031
1da177e4
LT
2032static int
2033do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2034{
2035 int ret;
2036
2037 if (!capable(CAP_NET_ADMIN))
2038 return -EPERM;
2039
2040 switch (cmd) {
2041 case IP6T_SO_SET_REPLACE:
3b1e0a65 2042 ret = do_replace(sock_net(sk), user, len);
1da177e4
LT
2043 break;
2044
2045 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 2046 ret = do_add_counters(sock_net(sk), user, len, 0);
1da177e4
LT
2047 break;
2048
2049 default:
2050 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
2051 ret = -EINVAL;
2052 }
2053
2054 return ret;
2055}
2056
2057static int
2058do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2059{
2060 int ret;
2061
2062 if (!capable(CAP_NET_ADMIN))
2063 return -EPERM;
2064
2065 switch (cmd) {
433665c9 2066 case IP6T_SO_GET_INFO:
3b1e0a65 2067 ret = get_info(sock_net(sk), user, len, 0);
433665c9 2068 break;
1da177e4 2069
d924357c 2070 case IP6T_SO_GET_ENTRIES:
3b1e0a65 2071 ret = get_entries(sock_net(sk), user, len);
1da177e4 2072 break;
1da177e4 2073
6b7d31fc
HW
2074 case IP6T_SO_GET_REVISION_MATCH:
2075 case IP6T_SO_GET_REVISION_TARGET: {
2076 struct ip6t_get_revision rev;
2e4e6a17 2077 int target;
6b7d31fc
HW
2078
2079 if (*len != sizeof(rev)) {
2080 ret = -EINVAL;
2081 break;
2082 }
2083 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2084 ret = -EFAULT;
2085 break;
2086 }
2087
2088 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 2089 target = 1;
6b7d31fc 2090 else
2e4e6a17 2091 target = 0;
6b7d31fc 2092
2e4e6a17
HW
2093 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2094 rev.revision,
2095 target, &ret),
6b7d31fc
HW
2096 "ip6t_%s", rev.name);
2097 break;
2098 }
2099
1da177e4
LT
2100 default:
2101 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2102 ret = -EINVAL;
2103 }
2104
2105 return ret;
2106}
2107
35aad0ff
JE
2108struct xt_table *ip6t_register_table(struct net *net,
2109 const struct xt_table *table,
336b517f 2110 const struct ip6t_replace *repl)
1da177e4
LT
2111{
2112 int ret;
2e4e6a17 2113 struct xt_table_info *newinfo;
259d4e41 2114 struct xt_table_info bootstrap
1da177e4 2115 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 2116 void *loc_cpu_entry;
a98da11d 2117 struct xt_table *new_table;
1da177e4 2118
2e4e6a17 2119 newinfo = xt_alloc_table_info(repl->size);
44d34e72
AD
2120 if (!newinfo) {
2121 ret = -ENOMEM;
2122 goto out;
2123 }
1da177e4 2124
9c547959 2125 /* choose the copy on our node/cpu, but dont care about preemption */
31836064
ED
2126 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2127 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4 2128
a83d8e8d 2129 ret = translate_table(net, table->name, table->valid_hooks,
31836064 2130 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
2131 repl->num_entries,
2132 repl->hook_entry,
2133 repl->underflow);
44d34e72
AD
2134 if (ret != 0)
2135 goto out_free;
1da177e4 2136
336b517f 2137 new_table = xt_register_table(net, table, &bootstrap, newinfo);
a98da11d 2138 if (IS_ERR(new_table)) {
44d34e72
AD
2139 ret = PTR_ERR(new_table);
2140 goto out_free;
1da177e4 2141 }
44d34e72 2142 return new_table;
1da177e4 2143
44d34e72
AD
2144out_free:
2145 xt_free_table_info(newinfo);
2146out:
2147 return ERR_PTR(ret);
1da177e4
LT
2148}
2149
f54e9367 2150void ip6t_unregister_table(struct net *net, struct xt_table *table)
1da177e4 2151{
2e4e6a17 2152 struct xt_table_info *private;
31836064 2153 void *loc_cpu_entry;
df200969 2154 struct module *table_owner = table->me;
31836064 2155
2e4e6a17 2156 private = xt_unregister_table(table);
1da177e4
LT
2157
2158 /* Decrease module usage counts and free resources */
2e4e6a17 2159 loc_cpu_entry = private->entries[raw_smp_processor_id()];
f54e9367 2160 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, net, NULL);
df200969
AD
2161 if (private->number > private->initial_entries)
2162 module_put(table_owner);
2e4e6a17 2163 xt_free_table_info(private);
1da177e4
LT
2164}
2165
2166/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 2167static inline bool
1da177e4
LT
2168icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2169 u_int8_t type, u_int8_t code,
ccb79bdc 2170 bool invert)
1da177e4
LT
2171{
2172 return (type == test_type && code >= min_code && code <= max_code)
2173 ^ invert;
2174}
2175
1d93a9cb 2176static bool
f7108a20 2177icmp6_match(const struct sk_buff *skb, const struct xt_match_param *par)
1da177e4 2178{
5452e425
JE
2179 const struct icmp6hdr *ic;
2180 struct icmp6hdr _icmph;
f7108a20 2181 const struct ip6t_icmp *icmpinfo = par->matchinfo;
1da177e4
LT
2182
2183 /* Must not be a fragment. */
f7108a20 2184 if (par->fragoff != 0)
1d93a9cb 2185 return false;
1da177e4 2186
f7108a20 2187 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
1da177e4
LT
2188 if (ic == NULL) {
2189 /* We've been asked to examine this packet, and we
9c547959
PM
2190 * can't. Hence, no choice but to drop.
2191 */
1da177e4 2192 duprintf("Dropping evil ICMP tinygram.\n");
f7108a20 2193 *par->hotdrop = true;
1d93a9cb 2194 return false;
1da177e4
LT
2195 }
2196
2197 return icmp6_type_code_match(icmpinfo->type,
2198 icmpinfo->code[0],
2199 icmpinfo->code[1],
2200 ic->icmp6_type, ic->icmp6_code,
2201 !!(icmpinfo->invflags&IP6T_ICMP_INV));
2202}
2203
2204/* Called when user tries to insert an entry of this type. */
9b4fce7a 2205static bool icmp6_checkentry(const struct xt_mtchk_param *par)
1da177e4 2206{
9b4fce7a 2207 const struct ip6t_icmp *icmpinfo = par->matchinfo;
1da177e4 2208
7f939713
PM
2209 /* Must specify no unknown invflags */
2210 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1da177e4
LT
2211}
2212
2213/* The built-in targets: standard (NULL) and error. */
9f15c530 2214static struct xt_target ip6t_standard_target __read_mostly = {
1da177e4 2215 .name = IP6T_STANDARD_TARGET,
7f939713 2216 .targetsize = sizeof(int),
4ba351cf 2217 .family = NFPROTO_IPV6,
3bc3fe5e
PM
2218#ifdef CONFIG_COMPAT
2219 .compatsize = sizeof(compat_int_t),
2220 .compat_from_user = compat_standard_from_user,
2221 .compat_to_user = compat_standard_to_user,
2222#endif
1da177e4
LT
2223};
2224
9f15c530 2225static struct xt_target ip6t_error_target __read_mostly = {
1da177e4
LT
2226 .name = IP6T_ERROR_TARGET,
2227 .target = ip6t_error,
7f939713 2228 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
4ba351cf 2229 .family = NFPROTO_IPV6,
1da177e4
LT
2230};
2231
2232static struct nf_sockopt_ops ip6t_sockopts = {
2233 .pf = PF_INET6,
2234 .set_optmin = IP6T_BASE_CTL,
2235 .set_optmax = IP6T_SO_SET_MAX+1,
2236 .set = do_ip6t_set_ctl,
3bc3fe5e
PM
2237#ifdef CONFIG_COMPAT
2238 .compat_set = compat_do_ip6t_set_ctl,
2239#endif
1da177e4
LT
2240 .get_optmin = IP6T_BASE_CTL,
2241 .get_optmax = IP6T_SO_GET_MAX+1,
2242 .get = do_ip6t_get_ctl,
3bc3fe5e
PM
2243#ifdef CONFIG_COMPAT
2244 .compat_get = compat_do_ip6t_get_ctl,
2245#endif
16fcec35 2246 .owner = THIS_MODULE,
1da177e4
LT
2247};
2248
9f15c530 2249static struct xt_match icmp6_matchstruct __read_mostly = {
1da177e4 2250 .name = "icmp6",
9c547959 2251 .match = icmp6_match,
7f939713
PM
2252 .matchsize = sizeof(struct ip6t_icmp),
2253 .checkentry = icmp6_checkentry,
2254 .proto = IPPROTO_ICMPV6,
4ba351cf 2255 .family = NFPROTO_IPV6,
1da177e4
LT
2256};
2257
3cb609d5
AD
2258static int __net_init ip6_tables_net_init(struct net *net)
2259{
383ca5b8 2260 return xt_proto_init(net, NFPROTO_IPV6);
3cb609d5
AD
2261}
2262
2263static void __net_exit ip6_tables_net_exit(struct net *net)
2264{
383ca5b8 2265 xt_proto_fini(net, NFPROTO_IPV6);
3cb609d5
AD
2266}
2267
2268static struct pernet_operations ip6_tables_net_ops = {
2269 .init = ip6_tables_net_init,
2270 .exit = ip6_tables_net_exit,
2271};
2272
65b4b4e8 2273static int __init ip6_tables_init(void)
1da177e4
LT
2274{
2275 int ret;
2276
3cb609d5 2277 ret = register_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2278 if (ret < 0)
2279 goto err1;
2e4e6a17 2280
1da177e4 2281 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
2282 ret = xt_register_target(&ip6t_standard_target);
2283 if (ret < 0)
2284 goto err2;
2285 ret = xt_register_target(&ip6t_error_target);
2286 if (ret < 0)
2287 goto err3;
2288 ret = xt_register_match(&icmp6_matchstruct);
2289 if (ret < 0)
2290 goto err4;
1da177e4
LT
2291
2292 /* Register setsockopt */
2293 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
2294 if (ret < 0)
2295 goto err5;
1da177e4 2296
a887c1c1 2297 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 2298 return 0;
0eff66e6
PM
2299
2300err5:
2301 xt_unregister_match(&icmp6_matchstruct);
2302err4:
2303 xt_unregister_target(&ip6t_error_target);
2304err3:
2305 xt_unregister_target(&ip6t_standard_target);
2306err2:
3cb609d5 2307 unregister_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2308err1:
2309 return ret;
1da177e4
LT
2310}
2311
65b4b4e8 2312static void __exit ip6_tables_fini(void)
1da177e4
LT
2313{
2314 nf_unregister_sockopt(&ip6t_sockopts);
9c547959 2315
a45049c5
PNA
2316 xt_unregister_match(&icmp6_matchstruct);
2317 xt_unregister_target(&ip6t_error_target);
2318 xt_unregister_target(&ip6t_standard_target);
3cb609d5
AD
2319
2320 unregister_pernet_subsys(&ip6_tables_net_ops);
1da177e4
LT
2321}
2322
e674d0f3 2323/*
b777e0ce
PM
2324 * find the offset to specified header or the protocol number of last header
2325 * if target < 0. "last header" is transport protocol header, ESP, or
2326 * "No next header".
2327 *
2328 * If target header is found, its offset is set in *offset and return protocol
2329 * number. Otherwise, return -1.
2330 *
6d381634
PM
2331 * If the first fragment doesn't contain the final protocol header or
2332 * NEXTHDR_NONE it is considered invalid.
2333 *
b777e0ce
PM
2334 * Note that non-1st fragment is special case that "the protocol number
2335 * of last header" is "next header" field in Fragment header. In this case,
2336 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
2337 * isn't NULL.
e674d0f3 2338 *
e674d0f3 2339 */
b777e0ce
PM
2340int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2341 int target, unsigned short *fragoff)
e674d0f3 2342{
6b88dd96 2343 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0660e03f 2344 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
e674d0f3
YK
2345 unsigned int len = skb->len - start;
2346
b777e0ce
PM
2347 if (fragoff)
2348 *fragoff = 0;
2349
e674d0f3
YK
2350 while (nexthdr != target) {
2351 struct ipv6_opt_hdr _hdr, *hp;
2352 unsigned int hdrlen;
2353
b777e0ce
PM
2354 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2355 if (target < 0)
2356 break;
6d381634 2357 return -ENOENT;
b777e0ce
PM
2358 }
2359
e674d0f3
YK
2360 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2361 if (hp == NULL)
6d381634 2362 return -EBADMSG;
e674d0f3 2363 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
2364 unsigned short _frag_off;
2365 __be16 *fp;
e674d0f3
YK
2366 fp = skb_header_pointer(skb,
2367 start+offsetof(struct frag_hdr,
2368 frag_off),
2369 sizeof(_frag_off),
2370 &_frag_off);
2371 if (fp == NULL)
6d381634 2372 return -EBADMSG;
e674d0f3 2373
b777e0ce
PM
2374 _frag_off = ntohs(*fp) & ~0x7;
2375 if (_frag_off) {
2376 if (target < 0 &&
2377 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 2378 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
2379 if (fragoff)
2380 *fragoff = _frag_off;
2381 return hp->nexthdr;
2382 }
6d381634 2383 return -ENOENT;
b777e0ce 2384 }
e674d0f3
YK
2385 hdrlen = 8;
2386 } else if (nexthdr == NEXTHDR_AUTH)
1ab1457c 2387 hdrlen = (hp->hdrlen + 2) << 2;
e674d0f3 2388 else
1ab1457c 2389 hdrlen = ipv6_optlen(hp);
e674d0f3
YK
2390
2391 nexthdr = hp->nexthdr;
2392 len -= hdrlen;
2393 start += hdrlen;
2394 }
2395
2396 *offset = start;
b777e0ce 2397 return nexthdr;
e674d0f3
YK
2398}
2399
1da177e4
LT
2400EXPORT_SYMBOL(ip6t_register_table);
2401EXPORT_SYMBOL(ip6t_unregister_table);
2402EXPORT_SYMBOL(ip6t_do_table);
1da177e4 2403EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 2404EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 2405
65b4b4e8
AM
2406module_init(ip6_tables_init);
2407module_exit(ip6_tables_fini);