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