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