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