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