netfilter: xtables: move extension arguments into compound structure (1/6)
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / net / ipv6 / netfilter / ip6_tables.c
CommitLineData
1da177e4
LT
1/*
2 * Packet matching code.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
6b7d31fc 5 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
1da177e4
LT
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
1da177e4 10 */
4fc268d2
RD
11
12#include <linux/capability.h>
14c85021 13#include <linux/in.h>
1da177e4
LT
14#include <linux/skbuff.h>
15#include <linux/kmod.h>
16#include <linux/vmalloc.h>
17#include <linux/netdevice.h>
18#include <linux/module.h>
4bdbf6c0 19#include <linux/poison.h>
1da177e4 20#include <linux/icmpv6.h>
1da177e4 21#include <net/ipv6.h>
3bc3fe5e 22#include <net/compat.h>
1da177e4 23#include <asm/uaccess.h>
57b47a53 24#include <linux/mutex.h>
1da177e4 25#include <linux/proc_fs.h>
3bc3fe5e 26#include <linux/err.h>
c8923c6b 27#include <linux/cpumask.h>
1da177e4
LT
28
29#include <linux/netfilter_ipv6/ip6_tables.h>
2e4e6a17 30#include <linux/netfilter/x_tables.h>
f01ffbd6 31#include <net/netfilter/nf_log.h>
1da177e4
LT
32
33MODULE_LICENSE("GPL");
34MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
35MODULE_DESCRIPTION("IPv6 packet filter");
36
1da177e4
LT
37/*#define DEBUG_IP_FIREWALL*/
38/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
39/*#define DEBUG_IP_FIREWALL_USER*/
40
41#ifdef DEBUG_IP_FIREWALL
42#define dprintf(format, args...) printk(format , ## args)
43#else
44#define dprintf(format, args...)
45#endif
46
47#ifdef DEBUG_IP_FIREWALL_USER
48#define duprintf(format, args...) printk(format , ## args)
49#else
50#define duprintf(format, args...)
51#endif
52
53#ifdef CONFIG_NETFILTER_DEBUG
54#define IP_NF_ASSERT(x) \
55do { \
56 if (!(x)) \
57 printk("IP_NF_ASSERT: %s:%s:%u\n", \
0dc47877 58 __func__, __FILE__, __LINE__); \
1da177e4
LT
59} while(0)
60#else
61#define IP_NF_ASSERT(x)
62#endif
1da177e4 63
1da177e4
LT
64#if 0
65/* All the better to debug you with... */
66#define static
67#define inline
68#endif
69
6b7d31fc 70/*
1da177e4 71 We keep a set of rules for each CPU, so we can avoid write-locking
6b7d31fc
HW
72 them in the softirq when updating the counters and therefore
73 only need to read-lock in the softirq; doing a write_lock_bh() in user
74 context stops packets coming through and allows user context to read
75 the counters or update the rules.
1da177e4 76
1da177e4
LT
77 Hence the start of any table is given by get_table() below. */
78
1da177e4 79/* Check for an extension */
1ab1457c 80int
1da177e4
LT
81ip6t_ext_hdr(u8 nexthdr)
82{
1ab1457c
YH
83 return ( (nexthdr == IPPROTO_HOPOPTS) ||
84 (nexthdr == IPPROTO_ROUTING) ||
85 (nexthdr == IPPROTO_FRAGMENT) ||
86 (nexthdr == IPPROTO_ESP) ||
87 (nexthdr == IPPROTO_AH) ||
88 (nexthdr == IPPROTO_NONE) ||
89 (nexthdr == IPPROTO_DSTOPTS) );
1da177e4
LT
90}
91
92/* Returns whether matches rule or not. */
022748a9 93/* Performance critical - called for every packet */
1d93a9cb 94static inline bool
1da177e4
LT
95ip6_packet_match(const struct sk_buff *skb,
96 const char *indev,
97 const char *outdev,
98 const struct ip6t_ip6 *ip6info,
99 unsigned int *protoff,
cff533ac 100 int *fragoff, bool *hotdrop)
1da177e4
LT
101{
102 size_t i;
103 unsigned long ret;
0660e03f 104 const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
1da177e4 105
e79ec50b 106#define FWINV(bool, invflg) ((bool) ^ !!(ip6info->invflags & (invflg)))
1da177e4 107
f2ffd9ee 108 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
1ab1457c 109 &ip6info->src), IP6T_INV_SRCIP)
f2ffd9ee 110 || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
1ab1457c 111 &ip6info->dst), IP6T_INV_DSTIP)) {
1da177e4
LT
112 dprintf("Source or dest mismatch.\n");
113/*
114 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
115 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
116 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
117 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
118 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
119 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
1d93a9cb 120 return false;
1da177e4
LT
121 }
122
123 /* Look for ifname matches; this should unroll nicely. */
124 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
125 ret |= (((const unsigned long *)indev)[i]
126 ^ ((const unsigned long *)ip6info->iniface)[i])
127 & ((const unsigned long *)ip6info->iniface_mask)[i];
128 }
129
130 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
131 dprintf("VIA in mismatch (%s vs %s).%s\n",
132 indev, ip6info->iniface,
133 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
1d93a9cb 134 return false;
1da177e4
LT
135 }
136
137 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
138 ret |= (((const unsigned long *)outdev)[i]
139 ^ ((const unsigned long *)ip6info->outiface)[i])
140 & ((const unsigned long *)ip6info->outiface_mask)[i];
141 }
142
143 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
144 dprintf("VIA out mismatch (%s vs %s).%s\n",
145 outdev, ip6info->outiface,
146 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
1d93a9cb 147 return false;
1da177e4
LT
148 }
149
150/* ... might want to do something with class and flowlabel here ... */
151
152 /* look for the desired protocol header */
153 if((ip6info->flags & IP6T_F_PROTO)) {
b777e0ce
PM
154 int protohdr;
155 unsigned short _frag_off;
1da177e4 156
b777e0ce 157 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
51d8b1a6
PM
158 if (protohdr < 0) {
159 if (_frag_off == 0)
cff533ac 160 *hotdrop = true;
1d93a9cb 161 return false;
51d8b1a6 162 }
b777e0ce 163 *fragoff = _frag_off;
1da177e4
LT
164
165 dprintf("Packet protocol %hi ?= %s%hi.\n",
1ab1457c 166 protohdr,
1da177e4
LT
167 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
168 ip6info->proto);
169
b777e0ce 170 if (ip6info->proto == protohdr) {
1da177e4 171 if(ip6info->invflags & IP6T_INV_PROTO) {
1d93a9cb 172 return false;
1da177e4 173 }
1d93a9cb 174 return true;
1da177e4
LT
175 }
176
177 /* We need match for the '-p all', too! */
178 if ((ip6info->proto != 0) &&
179 !(ip6info->invflags & IP6T_INV_PROTO))
1d93a9cb 180 return false;
1da177e4 181 }
1d93a9cb 182 return true;
1da177e4
LT
183}
184
185/* should be ip6 safe */
022748a9 186static bool
1da177e4
LT
187ip6_checkentry(const struct ip6t_ip6 *ipv6)
188{
189 if (ipv6->flags & ~IP6T_F_MASK) {
190 duprintf("Unknown flag bits set: %08X\n",
191 ipv6->flags & ~IP6T_F_MASK);
ccb79bdc 192 return false;
1da177e4
LT
193 }
194 if (ipv6->invflags & ~IP6T_INV_MASK) {
195 duprintf("Unknown invflag bits set: %08X\n",
196 ipv6->invflags & ~IP6T_INV_MASK);
ccb79bdc 197 return false;
1da177e4 198 }
ccb79bdc 199 return true;
1da177e4
LT
200}
201
202static unsigned int
3db05fea 203ip6t_error(struct sk_buff *skb,
1da177e4
LT
204 const struct net_device *in,
205 const struct net_device *out,
206 unsigned int hooknum,
c4986734 207 const struct xt_target *target,
fe1cb108 208 const void *targinfo)
1da177e4
LT
209{
210 if (net_ratelimit())
211 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
212
213 return NF_DROP;
214}
215
022748a9
DV
216/* Performance critical - called for every packet */
217static inline bool
f7108a20
JE
218do_match(struct ip6t_entry_match *m, const struct sk_buff *skb,
219 struct xt_match_param *par)
1da177e4 220{
f7108a20
JE
221 par->match = m->u.kernel.match;
222 par->matchinfo = m->data;
223
1da177e4 224 /* Stop iteration if it doesn't match */
f7108a20 225 if (!m->u.kernel.match->match(skb, par))
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 237/* All zeroes == unconditional rule. */
022748a9 238/* Mildly perf critical (only if packet tracing is on) */
ba9dda3a
JK
239static inline int
240unconditional(const struct ip6t_ip6 *ipv6)
241{
242 unsigned int i;
243
244 for (i = 0; i < sizeof(*ipv6); i++)
245 if (((char *)ipv6)[i])
246 break;
247
248 return (i == sizeof(*ipv6));
249}
250
251#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
252 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
253/* This cries for unification! */
022748a9 254static const char *const hooknames[] = {
6e23ae2a
PM
255 [NF_INET_PRE_ROUTING] = "PREROUTING",
256 [NF_INET_LOCAL_IN] = "INPUT",
257 [NF_INET_FORWARD] = "FORWARD",
258 [NF_INET_LOCAL_OUT] = "OUTPUT",
259 [NF_INET_POST_ROUTING] = "POSTROUTING",
ba9dda3a
JK
260};
261
262enum nf_ip_trace_comments {
263 NF_IP6_TRACE_COMMENT_RULE,
264 NF_IP6_TRACE_COMMENT_RETURN,
265 NF_IP6_TRACE_COMMENT_POLICY,
266};
267
022748a9 268static const char *const comments[] = {
ba9dda3a
JK
269 [NF_IP6_TRACE_COMMENT_RULE] = "rule",
270 [NF_IP6_TRACE_COMMENT_RETURN] = "return",
271 [NF_IP6_TRACE_COMMENT_POLICY] = "policy",
272};
273
274static struct nf_loginfo trace_loginfo = {
275 .type = NF_LOG_TYPE_LOG,
276 .u = {
277 .log = {
278 .level = 4,
279 .logflags = NF_LOG_MASK,
280 },
281 },
282};
283
022748a9 284/* Mildly perf critical (only if packet tracing is on) */
ba9dda3a
JK
285static inline int
286get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
287 char *hookname, char **chainname,
288 char **comment, unsigned int *rulenum)
289{
290 struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
291
292 if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
293 /* Head of user chain: ERROR target with chainname */
294 *chainname = t->target.data;
295 (*rulenum) = 0;
296 } else if (s == e) {
297 (*rulenum)++;
298
299 if (s->target_offset == sizeof(struct ip6t_entry)
300 && strcmp(t->target.u.kernel.target->name,
301 IP6T_STANDARD_TARGET) == 0
302 && t->verdict < 0
303 && unconditional(&s->ipv6)) {
304 /* Tail of chains: STANDARD target (return/policy) */
305 *comment = *chainname == hookname
306 ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
307 : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
308 }
309 return 1;
310 } else
311 (*rulenum)++;
312
313 return 0;
314}
315
316static void trace_packet(struct sk_buff *skb,
317 unsigned int hook,
318 const struct net_device *in,
319 const struct net_device *out,
ecb6f85e 320 const char *tablename,
ba9dda3a
JK
321 struct xt_table_info *private,
322 struct ip6t_entry *e)
323{
324 void *table_base;
5452e425 325 const struct ip6t_entry *root;
ba9dda3a
JK
326 char *hookname, *chainname, *comment;
327 unsigned int rulenum = 0;
328
329 table_base = (void *)private->entries[smp_processor_id()];
330 root = get_entry(table_base, private->hook_entry[hook]);
331
332 hookname = chainname = (char *)hooknames[hook];
333 comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
334
335 IP6T_ENTRY_ITERATE(root,
336 private->size - private->hook_entry[hook],
337 get_chainname_rulenum,
338 e, hookname, &chainname, &comment, &rulenum);
339
340 nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
341 "TRACE: %s:%s:%s:%u ",
342 tablename, chainname, comment, rulenum);
343}
344#endif
345
1da177e4
LT
346/* Returns one of the generic firewall policies, like NF_ACCEPT. */
347unsigned int
3db05fea 348ip6t_do_table(struct sk_buff *skb,
1da177e4
LT
349 unsigned int hook,
350 const struct net_device *in,
351 const struct net_device *out,
fe1cb108 352 struct xt_table *table)
1da177e4 353{
6b7d31fc 354 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
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;
f7108a20 362 struct xt_match_param mtpar;
1da177e4
LT
363
364 /* Initialization */
365 indev = in ? in->name : nulldevname;
366 outdev = out ? out->name : nulldevname;
1da177e4
LT
367 /* We handle fragments by dealing with the first fragment as
368 * if it was a normal packet. All other fragments are treated
369 * normally, except that they will NEVER match rules that ask
370 * things we don't know, ie. tcp syn flag or ports). If the
371 * rule is also a fragment-specific rule, non-fragments won't
372 * match it. */
f7108a20
JE
373 mtpar.hotdrop = &hotdrop;
374 mtpar.in = in;
375 mtpar.out = out;
1da177e4
LT
376
377 read_lock_bh(&table->lock);
378 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
9c547959 379 private = table->private;
2e4e6a17
HW
380 table_base = (void *)private->entries[smp_processor_id()];
381 e = get_entry(table_base, private->hook_entry[hook]);
1da177e4 382
1da177e4 383 /* For return from builtin chain */
2e4e6a17 384 back = get_entry(table_base, private->underflow[hook]);
1da177e4
LT
385
386 do {
387 IP_NF_ASSERT(e);
388 IP_NF_ASSERT(back);
3db05fea 389 if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
f7108a20 390 &mtpar.thoff, &mtpar.fragoff, &hotdrop)) {
1da177e4
LT
391 struct ip6t_entry_target *t;
392
f7108a20 393 if (IP6T_MATCH_ITERATE(e, do_match, skb, &mtpar) != 0)
1da177e4
LT
394 goto no_match;
395
396 ADD_COUNTER(e->counters,
72f36ec1
PM
397 ntohs(ipv6_hdr(skb)->payload_len) +
398 sizeof(struct ipv6hdr), 1);
1da177e4
LT
399
400 t = ip6t_get_target(e);
401 IP_NF_ASSERT(t->u.kernel.target);
ba9dda3a
JK
402
403#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
404 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
405 /* The packet is traced: log it */
3db05fea
HX
406 if (unlikely(skb->nf_trace))
407 trace_packet(skb, hook, in, out,
ba9dda3a
JK
408 table->name, private, e);
409#endif
1da177e4
LT
410 /* Standard target? */
411 if (!t->u.kernel.target->target) {
412 int v;
413
414 v = ((struct ip6t_standard_target *)t)->verdict;
415 if (v < 0) {
416 /* Pop from stack? */
417 if (v != IP6T_RETURN) {
418 verdict = (unsigned)(-v) - 1;
419 break;
420 }
421 e = back;
422 back = get_entry(table_base,
423 back->comefrom);
424 continue;
425 }
05465343
PM
426 if (table_base + v != (void *)e + e->next_offset
427 && !(e->ipv6.flags & IP6T_F_GOTO)) {
1da177e4
LT
428 /* Save old back ptr in next entry */
429 struct ip6t_entry *next
430 = (void *)e + e->next_offset;
431 next->comefrom
432 = (void *)back - table_base;
433 /* set back pointer to next entry */
434 back = next;
435 }
436
437 e = get_entry(table_base, v);
438 } else {
439 /* Targets which reenter must return
1ab1457c 440 abs. verdicts */
1da177e4
LT
441#ifdef CONFIG_NETFILTER_DEBUG
442 ((struct ip6t_entry *)table_base)->comefrom
443 = 0xeeeeeeec;
444#endif
3db05fea 445 verdict = t->u.kernel.target->target(skb,
1da177e4
LT
446 in, out,
447 hook,
1c524830 448 t->u.kernel.target,
fe1cb108 449 t->data);
1da177e4
LT
450
451#ifdef CONFIG_NETFILTER_DEBUG
452 if (((struct ip6t_entry *)table_base)->comefrom
453 != 0xeeeeeeec
454 && verdict == IP6T_CONTINUE) {
455 printk("Target %s reentered!\n",
456 t->u.kernel.target->name);
457 verdict = NF_DROP;
458 }
459 ((struct ip6t_entry *)table_base)->comefrom
460 = 0x57acc001;
461#endif
462 if (verdict == IP6T_CONTINUE)
463 e = (void *)e + e->next_offset;
464 else
465 /* Verdict */
466 break;
467 }
468 } else {
469
470 no_match:
471 e = (void *)e + e->next_offset;
472 }
473 } while (!hotdrop);
474
475#ifdef CONFIG_NETFILTER_DEBUG
4bdbf6c0 476 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
1da177e4
LT
477#endif
478 read_unlock_bh(&table->lock);
479
480#ifdef DEBUG_ALLOW_ALL
481 return NF_ACCEPT;
482#else
483 if (hotdrop)
484 return NF_DROP;
485 else return verdict;
486#endif
487}
488
1da177e4
LT
489/* Figures out from what hook each rule can be called: returns 0 if
490 there are loops. Puts hook bitmask in comefrom. */
491static int
2e4e6a17 492mark_source_chains(struct xt_table_info *newinfo,
31836064 493 unsigned int valid_hooks, void *entry0)
1da177e4
LT
494{
495 unsigned int hook;
496
497 /* No recursion; use packet counter to save back ptrs (reset
498 to 0 as we leave), and comefrom to save source hook bitmask */
6e23ae2a 499 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
1da177e4 500 unsigned int pos = newinfo->hook_entry[hook];
9c547959 501 struct ip6t_entry *e = (struct ip6t_entry *)(entry0 + pos);
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);
9c547959 512 int visited = e->comefrom & (1 << hook);
1da177e4 513
6e23ae2a 514 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
515 printk("iptables: loop hook %u pos %u %08X.\n",
516 hook, pos, e->comefrom);
517 return 0;
518 }
9c547959 519 e->comefrom |= ((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
022748a9 599static int
1da177e4
LT
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
022748a9 611static 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
022748a9 632static int check_match(struct ip6t_entry_match *m, const char *name,
f173c8a1
PM
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,
367c6790
JE
642 ipv6->invflags & IP6T_INV_PROTO, ipv6, m->data);
643 if (ret < 0) {
f173c8a1
PM
644 duprintf("ip_tables: check failed for `%s'.\n",
645 m->u.kernel.match->name);
367c6790 646 return ret;
f173c8a1 647 }
367c6790
JE
648 ++*i;
649 return 0;
f173c8a1
PM
650}
651
022748a9 652static int
f173c8a1
PM
653find_check_match(struct ip6t_entry_match *m,
654 const char *name,
655 const struct ip6t_ip6 *ipv6,
656 unsigned int hookmask,
657 unsigned int *i)
1da177e4 658{
6709dbbb 659 struct xt_match *match;
3cdc7c95 660 int ret;
1da177e4 661
2e4e6a17 662 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
9c547959 663 m->u.user.revision),
6b7d31fc
HW
664 "ip6t_%s", m->u.user.name);
665 if (IS_ERR(match) || !match) {
f173c8a1 666 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
6b7d31fc 667 return match ? PTR_ERR(match) : -ENOENT;
1da177e4
LT
668 }
669 m->u.kernel.match = match;
1da177e4 670
f173c8a1 671 ret = check_match(m, name, ipv6, hookmask, i);
3cdc7c95
PM
672 if (ret)
673 goto err;
674
1da177e4 675 return 0;
3cdc7c95
PM
676err:
677 module_put(m->u.kernel.match->me);
678 return ret;
1da177e4
LT
679}
680
022748a9 681static int check_target(struct ip6t_entry *e, const char *name)
1da177e4
LT
682{
683 struct ip6t_entry_target *t;
6709dbbb 684 struct xt_target *target;
1da177e4 685 int ret;
1da177e4 686
f173c8a1
PM
687 t = ip6t_get_target(e);
688 target = t->u.kernel.target;
689 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
690 name, e->comefrom, e->ipv6.proto,
367c6790
JE
691 e->ipv6.invflags & IP6T_INV_PROTO, e, t->data);
692 if (ret < 0) {
f173c8a1
PM
693 duprintf("ip_tables: check failed for `%s'.\n",
694 t->u.kernel.target->name);
367c6790 695 return ret;
1da177e4 696 }
367c6790 697 return 0;
f173c8a1 698}
1da177e4 699
022748a9 700static int
f173c8a1
PM
701find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
702 unsigned int *i)
703{
704 struct ip6t_entry_target *t;
705 struct xt_target *target;
706 int ret;
707 unsigned int j;
708
709 ret = check_entry(e, name);
710 if (ret)
711 return ret;
590bdf7f 712
1da177e4 713 j = 0;
f173c8a1
PM
714 ret = IP6T_MATCH_ITERATE(e, find_check_match, name, &e->ipv6,
715 e->comefrom, &j);
1da177e4
LT
716 if (ret != 0)
717 goto cleanup_matches;
718
719 t = ip6t_get_target(e);
2e4e6a17
HW
720 target = try_then_request_module(xt_find_target(AF_INET6,
721 t->u.user.name,
722 t->u.user.revision),
6b7d31fc
HW
723 "ip6t_%s", t->u.user.name);
724 if (IS_ERR(target) || !target) {
f173c8a1 725 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
6b7d31fc 726 ret = target ? PTR_ERR(target) : -ENOENT;
1da177e4
LT
727 goto cleanup_matches;
728 }
729 t->u.kernel.target = target;
6b7d31fc 730
f173c8a1 731 ret = check_target(e, name);
3cdc7c95
PM
732 if (ret)
733 goto err;
734
1da177e4
LT
735 (*i)++;
736 return 0;
3cdc7c95
PM
737 err:
738 module_put(t->u.kernel.target->me);
1da177e4
LT
739 cleanup_matches:
740 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
741 return ret;
742}
743
022748a9 744static int
1da177e4 745check_entry_size_and_hooks(struct ip6t_entry *e,
2e4e6a17 746 struct xt_table_info *newinfo,
1da177e4
LT
747 unsigned char *base,
748 unsigned char *limit,
749 const unsigned int *hook_entries,
750 const unsigned int *underflows,
751 unsigned int *i)
752{
753 unsigned int h;
754
755 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
756 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
757 duprintf("Bad offset %p\n", e);
758 return -EINVAL;
759 }
760
761 if (e->next_offset
762 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
763 duprintf("checking: element %p size %u\n",
764 e, e->next_offset);
765 return -EINVAL;
766 }
767
768 /* Check hooks & underflows */
6e23ae2a 769 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1da177e4
LT
770 if ((unsigned char *)e - base == hook_entries[h])
771 newinfo->hook_entry[h] = hook_entries[h];
772 if ((unsigned char *)e - base == underflows[h])
773 newinfo->underflow[h] = underflows[h];
774 }
775
776 /* FIXME: underflows must be unconditional, standard verdicts
1ab1457c 777 < 0 (not IP6T_RETURN). --RR */
1da177e4
LT
778
779 /* Clear counters and comefrom */
2e4e6a17 780 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4
LT
781 e->comefrom = 0;
782
783 (*i)++;
784 return 0;
785}
786
022748a9 787static int
1da177e4
LT
788cleanup_entry(struct ip6t_entry *e, unsigned int *i)
789{
790 struct ip6t_entry_target *t;
791
792 if (i && (*i)-- == 0)
793 return 1;
794
795 /* Cleanup all matches */
796 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
797 t = ip6t_get_target(e);
798 if (t->u.kernel.target->destroy)
efa74165 799 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
1da177e4
LT
800 module_put(t->u.kernel.target->me);
801 return 0;
802}
803
804/* Checks and translates the user-supplied table segment (held in
805 newinfo) */
806static int
807translate_table(const char *name,
808 unsigned int valid_hooks,
2e4e6a17 809 struct xt_table_info *newinfo,
31836064 810 void *entry0,
1da177e4
LT
811 unsigned int size,
812 unsigned int number,
813 const unsigned int *hook_entries,
814 const unsigned int *underflows)
815{
816 unsigned int i;
817 int ret;
818
819 newinfo->size = size;
820 newinfo->number = number;
821
822 /* Init all hooks to impossible value. */
6e23ae2a 823 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
824 newinfo->hook_entry[i] = 0xFFFFFFFF;
825 newinfo->underflow[i] = 0xFFFFFFFF;
826 }
827
828 duprintf("translate_table: size %u\n", newinfo->size);
829 i = 0;
830 /* Walk through entries, checking offsets. */
31836064 831 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
832 check_entry_size_and_hooks,
833 newinfo,
31836064
ED
834 entry0,
835 entry0 + size,
1da177e4
LT
836 hook_entries, underflows, &i);
837 if (ret != 0)
838 return ret;
839
840 if (i != number) {
841 duprintf("translate_table: %u not %u entries\n",
842 i, number);
843 return -EINVAL;
844 }
845
846 /* Check hooks all assigned */
6e23ae2a 847 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
848 /* Only hooks which are valid */
849 if (!(valid_hooks & (1 << i)))
850 continue;
851 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
852 duprintf("Invalid hook entry %u %u\n",
853 i, hook_entries[i]);
854 return -EINVAL;
855 }
856 if (newinfo->underflow[i] == 0xFFFFFFFF) {
857 duprintf("Invalid underflow %u %u\n",
858 i, underflows[i]);
859 return -EINVAL;
860 }
861 }
862
74c9c0c1
DM
863 if (!mark_source_chains(newinfo, valid_hooks, entry0))
864 return -ELOOP;
865
1da177e4
LT
866 /* Finally, each sanity check must pass */
867 i = 0;
31836064 868 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
f173c8a1 869 find_check_entry, name, size, &i);
1da177e4 870
74c9c0c1
DM
871 if (ret != 0) {
872 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
873 cleanup_entry, &i);
874 return ret;
875 }
1da177e4
LT
876
877 /* And one copy for every other CPU */
6f912042 878 for_each_possible_cpu(i) {
31836064
ED
879 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
880 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
881 }
882
9c547959 883 return ret;
1da177e4
LT
884}
885
1da177e4
LT
886/* Gets counters. */
887static inline int
888add_entry_to_counter(const struct ip6t_entry *e,
2e4e6a17 889 struct xt_counters total[],
1da177e4
LT
890 unsigned int *i)
891{
892 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
893
894 (*i)++;
895 return 0;
896}
897
31836064
ED
898static inline int
899set_entry_to_counter(const struct ip6t_entry *e,
900 struct ip6t_counters total[],
901 unsigned int *i)
902{
903 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
904
905 (*i)++;
906 return 0;
907}
908
1da177e4 909static void
2e4e6a17
HW
910get_counters(const struct xt_table_info *t,
911 struct xt_counters counters[])
1da177e4
LT
912{
913 unsigned int cpu;
914 unsigned int i;
31836064
ED
915 unsigned int curcpu;
916
917 /* Instead of clearing (by a previous call to memset())
918 * the counters and using adds, we set the counters
919 * with data used by 'current' CPU
920 * We dont care about preemption here.
921 */
922 curcpu = raw_smp_processor_id();
923
924 i = 0;
925 IP6T_ENTRY_ITERATE(t->entries[curcpu],
926 t->size,
927 set_entry_to_counter,
928 counters,
929 &i);
1da177e4 930
6f912042 931 for_each_possible_cpu(cpu) {
31836064
ED
932 if (cpu == curcpu)
933 continue;
1da177e4 934 i = 0;
31836064 935 IP6T_ENTRY_ITERATE(t->entries[cpu],
1da177e4
LT
936 t->size,
937 add_entry_to_counter,
938 counters,
939 &i);
940 }
941}
942
022748a9 943static struct xt_counters *alloc_counters(struct xt_table *table)
1da177e4 944{
ed1a6f5e 945 unsigned int countersize;
2e4e6a17 946 struct xt_counters *counters;
5452e425 947 const struct xt_table_info *private = table->private;
1da177e4
LT
948
949 /* We need atomic snapshot of counters: rest doesn't change
950 (other than comefrom, which userspace doesn't care
951 about). */
2e4e6a17 952 countersize = sizeof(struct xt_counters) * private->number;
3b84e92b 953 counters = vmalloc_node(countersize, numa_node_id());
1da177e4
LT
954
955 if (counters == NULL)
ed1a6f5e 956 return ERR_PTR(-ENOMEM);
1da177e4
LT
957
958 /* First, sum counters... */
1da177e4 959 write_lock_bh(&table->lock);
2e4e6a17 960 get_counters(private, counters);
1da177e4
LT
961 write_unlock_bh(&table->lock);
962
ed1a6f5e
PM
963 return counters;
964}
965
966static int
967copy_entries_to_user(unsigned int total_size,
968 struct xt_table *table,
969 void __user *userptr)
970{
971 unsigned int off, num;
972 struct ip6t_entry *e;
973 struct xt_counters *counters;
5452e425 974 const struct xt_table_info *private = table->private;
ed1a6f5e 975 int ret = 0;
5452e425 976 const void *loc_cpu_entry;
ed1a6f5e
PM
977
978 counters = alloc_counters(table);
979 if (IS_ERR(counters))
980 return PTR_ERR(counters);
981
9c547959
PM
982 /* choose the copy that is on our node/cpu, ...
983 * This choice is lazy (because current thread is
984 * allowed to migrate to another cpu)
985 */
2e4e6a17 986 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 987 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
988 ret = -EFAULT;
989 goto free_counters;
990 }
991
992 /* FIXME: use iterator macros --RR */
993 /* ... then go back and fix counters and names */
994 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
995 unsigned int i;
5452e425
JE
996 const struct ip6t_entry_match *m;
997 const struct ip6t_entry_target *t;
1da177e4 998
31836064 999 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
1000 if (copy_to_user(userptr + off
1001 + offsetof(struct ip6t_entry, counters),
1002 &counters[num],
1003 sizeof(counters[num])) != 0) {
1004 ret = -EFAULT;
1005 goto free_counters;
1006 }
1007
1008 for (i = sizeof(struct ip6t_entry);
1009 i < e->target_offset;
1010 i += m->u.match_size) {
1011 m = (void *)e + i;
1012
1013 if (copy_to_user(userptr + off + i
1014 + offsetof(struct ip6t_entry_match,
1015 u.user.name),
1016 m->u.kernel.match->name,
1017 strlen(m->u.kernel.match->name)+1)
1018 != 0) {
1019 ret = -EFAULT;
1020 goto free_counters;
1021 }
1022 }
1023
1024 t = ip6t_get_target(e);
1025 if (copy_to_user(userptr + off + e->target_offset
1026 + offsetof(struct ip6t_entry_target,
1027 u.user.name),
1028 t->u.kernel.target->name,
1029 strlen(t->u.kernel.target->name)+1) != 0) {
1030 ret = -EFAULT;
1031 goto free_counters;
1032 }
1033 }
1034
1035 free_counters:
1036 vfree(counters);
1037 return ret;
1038}
1039
3bc3fe5e
PM
1040#ifdef CONFIG_COMPAT
1041static void compat_standard_from_user(void *dst, void *src)
1042{
1043 int v = *(compat_int_t *)src;
1044
1045 if (v > 0)
1046 v += xt_compat_calc_jump(AF_INET6, v);
1047 memcpy(dst, &v, sizeof(v));
1048}
1049
1050static int compat_standard_to_user(void __user *dst, void *src)
1051{
1052 compat_int_t cv = *(int *)src;
1053
1054 if (cv > 0)
1055 cv -= xt_compat_calc_jump(AF_INET6, cv);
1056 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
1057}
1058
1059static inline int
1060compat_calc_match(struct ip6t_entry_match *m, int *size)
1061{
1062 *size += xt_compat_match_offset(m->u.kernel.match);
1063 return 0;
1064}
1065
1066static int compat_calc_entry(struct ip6t_entry *e,
1067 const struct xt_table_info *info,
1068 void *base, struct xt_table_info *newinfo)
1069{
1070 struct ip6t_entry_target *t;
1071 unsigned int entry_offset;
1072 int off, i, ret;
1073
1074 off = sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1075 entry_offset = (void *)e - base;
1076 IP6T_MATCH_ITERATE(e, compat_calc_match, &off);
1077 t = ip6t_get_target(e);
1078 off += xt_compat_target_offset(t->u.kernel.target);
1079 newinfo->size -= off;
1080 ret = xt_compat_add_offset(AF_INET6, entry_offset, off);
1081 if (ret)
1082 return ret;
1083
1084 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1085 if (info->hook_entry[i] &&
1086 (e < (struct ip6t_entry *)(base + info->hook_entry[i])))
1087 newinfo->hook_entry[i] -= off;
1088 if (info->underflow[i] &&
1089 (e < (struct ip6t_entry *)(base + info->underflow[i])))
1090 newinfo->underflow[i] -= off;
1091 }
1092 return 0;
1093}
1094
1095static int compat_table_info(const struct xt_table_info *info,
1096 struct xt_table_info *newinfo)
1097{
1098 void *loc_cpu_entry;
1099
1100 if (!newinfo || !info)
1101 return -EINVAL;
1102
1103 /* we dont care about newinfo->entries[] */
1104 memcpy(newinfo, info, offsetof(struct xt_table_info, entries));
1105 newinfo->initial_entries = 0;
1106 loc_cpu_entry = info->entries[raw_smp_processor_id()];
1107 return IP6T_ENTRY_ITERATE(loc_cpu_entry, info->size,
1108 compat_calc_entry, info, loc_cpu_entry,
1109 newinfo);
1110}
1111#endif
1112
336b517f 1113static int get_info(struct net *net, void __user *user, int *len, int compat)
433665c9
PM
1114{
1115 char name[IP6T_TABLE_MAXNAMELEN];
1116 struct xt_table *t;
1117 int ret;
1118
1119 if (*len != sizeof(struct ip6t_getinfo)) {
c9d8fe13 1120 duprintf("length %u != %zu\n", *len,
433665c9
PM
1121 sizeof(struct ip6t_getinfo));
1122 return -EINVAL;
1123 }
1124
1125 if (copy_from_user(name, user, sizeof(name)) != 0)
1126 return -EFAULT;
1127
1128 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
3bc3fe5e
PM
1129#ifdef CONFIG_COMPAT
1130 if (compat)
1131 xt_compat_lock(AF_INET6);
1132#endif
336b517f 1133 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
433665c9
PM
1134 "ip6table_%s", name);
1135 if (t && !IS_ERR(t)) {
1136 struct ip6t_getinfo info;
5452e425 1137 const struct xt_table_info *private = t->private;
433665c9 1138
3bc3fe5e
PM
1139#ifdef CONFIG_COMPAT
1140 if (compat) {
1141 struct xt_table_info tmp;
1142 ret = compat_table_info(private, &tmp);
1143 xt_compat_flush_offsets(AF_INET6);
1144 private = &tmp;
1145 }
1146#endif
433665c9
PM
1147 info.valid_hooks = t->valid_hooks;
1148 memcpy(info.hook_entry, private->hook_entry,
1149 sizeof(info.hook_entry));
1150 memcpy(info.underflow, private->underflow,
1151 sizeof(info.underflow));
1152 info.num_entries = private->number;
1153 info.size = private->size;
b5dd674b 1154 strcpy(info.name, name);
433665c9
PM
1155
1156 if (copy_to_user(user, &info, *len) != 0)
1157 ret = -EFAULT;
1158 else
1159 ret = 0;
1160
1161 xt_table_unlock(t);
1162 module_put(t->me);
1163 } else
1164 ret = t ? PTR_ERR(t) : -ENOENT;
3bc3fe5e
PM
1165#ifdef CONFIG_COMPAT
1166 if (compat)
1167 xt_compat_unlock(AF_INET6);
1168#endif
433665c9
PM
1169 return ret;
1170}
1171
1da177e4 1172static int
336b517f 1173get_entries(struct net *net, struct ip6t_get_entries __user *uptr, int *len)
1da177e4
LT
1174{
1175 int ret;
d924357c 1176 struct ip6t_get_entries get;
2e4e6a17 1177 struct xt_table *t;
1da177e4 1178
d924357c 1179 if (*len < sizeof(get)) {
c9d8fe13 1180 duprintf("get_entries: %u < %zu\n", *len, sizeof(get));
d924357c
PM
1181 return -EINVAL;
1182 }
1183 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1184 return -EFAULT;
1185 if (*len != sizeof(struct ip6t_get_entries) + get.size) {
c9d8fe13
PM
1186 duprintf("get_entries: %u != %zu\n",
1187 *len, sizeof(get) + get.size);
d924357c
PM
1188 return -EINVAL;
1189 }
1190
336b517f 1191 t = xt_find_table_lock(net, AF_INET6, get.name);
6b7d31fc 1192 if (t && !IS_ERR(t)) {
2e4e6a17
HW
1193 struct xt_table_info *private = t->private;
1194 duprintf("t->private->number = %u\n", private->number);
d924357c 1195 if (get.size == private->size)
2e4e6a17 1196 ret = copy_entries_to_user(private->size,
1da177e4
LT
1197 t, uptr->entrytable);
1198 else {
1199 duprintf("get_entries: I've got %u not %u!\n",
9c547959 1200 private->size, get.size);
544473c1 1201 ret = -EAGAIN;
1da177e4 1202 }
6b7d31fc 1203 module_put(t->me);
2e4e6a17 1204 xt_table_unlock(t);
1da177e4 1205 } else
6b7d31fc 1206 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1207
1208 return ret;
1209}
1210
1211static int
336b517f 1212__do_replace(struct net *net, const char *name, unsigned int valid_hooks,
3bc3fe5e
PM
1213 struct xt_table_info *newinfo, unsigned int num_counters,
1214 void __user *counters_ptr)
1da177e4
LT
1215{
1216 int ret;
2e4e6a17 1217 struct xt_table *t;
3bc3fe5e 1218 struct xt_table_info *oldinfo;
2e4e6a17 1219 struct xt_counters *counters;
5452e425 1220 const void *loc_cpu_old_entry;
1da177e4 1221
3bc3fe5e
PM
1222 ret = 0;
1223 counters = vmalloc_node(num_counters * sizeof(struct xt_counters),
3b84e92b 1224 numa_node_id());
1da177e4
LT
1225 if (!counters) {
1226 ret = -ENOMEM;
3bc3fe5e 1227 goto out;
1da177e4 1228 }
1da177e4 1229
336b517f 1230 t = try_then_request_module(xt_find_table_lock(net, AF_INET6, name),
3bc3fe5e 1231 "ip6table_%s", name);
6b7d31fc
HW
1232 if (!t || IS_ERR(t)) {
1233 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1234 goto free_newinfo_counters_untrans;
6b7d31fc 1235 }
1da177e4
LT
1236
1237 /* You lied! */
3bc3fe5e 1238 if (valid_hooks != t->valid_hooks) {
1da177e4 1239 duprintf("Valid hook crap: %08X vs %08X\n",
3bc3fe5e 1240 valid_hooks, t->valid_hooks);
1da177e4 1241 ret = -EINVAL;
6b7d31fc 1242 goto put_module;
1da177e4
LT
1243 }
1244
3bc3fe5e 1245 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1da177e4
LT
1246 if (!oldinfo)
1247 goto put_module;
1248
1249 /* Update module usage count based on number of rules */
1250 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1251 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1252 if ((oldinfo->number > oldinfo->initial_entries) ||
1253 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1254 module_put(t->me);
1255 if ((oldinfo->number > oldinfo->initial_entries) &&
1256 (newinfo->number <= oldinfo->initial_entries))
1257 module_put(t->me);
1258
1259 /* Get the old counters. */
1260 get_counters(oldinfo, counters);
1261 /* Decrease module usage counts and free resource */
31836064 1262 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
3bc3fe5e
PM
1263 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,
1264 NULL);
2e4e6a17 1265 xt_free_table_info(oldinfo);
3bc3fe5e
PM
1266 if (copy_to_user(counters_ptr, counters,
1267 sizeof(struct xt_counters) * num_counters) != 0)
1da177e4
LT
1268 ret = -EFAULT;
1269 vfree(counters);
2e4e6a17 1270 xt_table_unlock(t);
1da177e4
LT
1271 return ret;
1272
1273 put_module:
1274 module_put(t->me);
2e4e6a17 1275 xt_table_unlock(t);
1da177e4 1276 free_newinfo_counters_untrans:
1da177e4 1277 vfree(counters);
3bc3fe5e
PM
1278 out:
1279 return ret;
1280}
1281
1282static int
336b517f 1283do_replace(struct net *net, void __user *user, unsigned int len)
3bc3fe5e
PM
1284{
1285 int ret;
1286 struct ip6t_replace tmp;
1287 struct xt_table_info *newinfo;
1288 void *loc_cpu_entry;
1289
1290 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1291 return -EFAULT;
1292
1293 /* overflow check */
1294 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1295 return -ENOMEM;
1296
1297 newinfo = xt_alloc_table_info(tmp.size);
1298 if (!newinfo)
1299 return -ENOMEM;
1300
1301 /* choose the copy that is on our node/cpu */
1302 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1303 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1304 tmp.size) != 0) {
1305 ret = -EFAULT;
1306 goto free_newinfo;
1307 }
1308
1309 ret = translate_table(tmp.name, tmp.valid_hooks,
1310 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1311 tmp.hook_entry, tmp.underflow);
1312 if (ret != 0)
1313 goto free_newinfo;
1314
1315 duprintf("ip_tables: Translated table\n");
1316
336b517f 1317 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
3bc3fe5e
PM
1318 tmp.num_counters, tmp.counters);
1319 if (ret)
1320 goto free_newinfo_untrans;
1321 return 0;
1322
1323 free_newinfo_untrans:
1324 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry, NULL);
1da177e4 1325 free_newinfo:
2e4e6a17 1326 xt_free_table_info(newinfo);
1da177e4
LT
1327 return ret;
1328}
1329
1330/* We're lazy, and add to the first CPU; overflow works its fey magic
1331 * and everything is OK. */
1332static inline int
1333add_counter_to_entry(struct ip6t_entry *e,
2e4e6a17 1334 const struct xt_counters addme[],
1da177e4
LT
1335 unsigned int *i)
1336{
1337#if 0
1338 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1339 *i,
1340 (long unsigned int)e->counters.pcnt,
1341 (long unsigned int)e->counters.bcnt,
1342 (long unsigned int)addme[*i].pcnt,
1343 (long unsigned int)addme[*i].bcnt);
1344#endif
1345
1346 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1347
1348 (*i)++;
1349 return 0;
1350}
1351
1352static int
336b517f
AD
1353do_add_counters(struct net *net, void __user *user, unsigned int len,
1354 int compat)
1da177e4
LT
1355{
1356 unsigned int i;
3bc3fe5e
PM
1357 struct xt_counters_info tmp;
1358 struct xt_counters *paddc;
1359 unsigned int num_counters;
1360 char *name;
1361 int size;
1362 void *ptmp;
2e4e6a17 1363 struct xt_table *t;
5452e425 1364 const struct xt_table_info *private;
6b7d31fc 1365 int ret = 0;
5452e425 1366 const void *loc_cpu_entry;
3bc3fe5e
PM
1367#ifdef CONFIG_COMPAT
1368 struct compat_xt_counters_info compat_tmp;
1da177e4 1369
3bc3fe5e
PM
1370 if (compat) {
1371 ptmp = &compat_tmp;
1372 size = sizeof(struct compat_xt_counters_info);
1373 } else
1374#endif
1375 {
1376 ptmp = &tmp;
1377 size = sizeof(struct xt_counters_info);
1378 }
1379
1380 if (copy_from_user(ptmp, user, size) != 0)
1da177e4
LT
1381 return -EFAULT;
1382
3bc3fe5e
PM
1383#ifdef CONFIG_COMPAT
1384 if (compat) {
1385 num_counters = compat_tmp.num_counters;
1386 name = compat_tmp.name;
1387 } else
1388#endif
1389 {
1390 num_counters = tmp.num_counters;
1391 name = tmp.name;
1392 }
1393
1394 if (len != size + num_counters * sizeof(struct xt_counters))
1da177e4
LT
1395 return -EINVAL;
1396
3bc3fe5e 1397 paddc = vmalloc_node(len - size, numa_node_id());
1da177e4
LT
1398 if (!paddc)
1399 return -ENOMEM;
1400
3bc3fe5e 1401 if (copy_from_user(paddc, user + size, len - size) != 0) {
1da177e4
LT
1402 ret = -EFAULT;
1403 goto free;
1404 }
1405
336b517f 1406 t = xt_find_table_lock(net, AF_INET6, name);
6b7d31fc
HW
1407 if (!t || IS_ERR(t)) {
1408 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1409 goto free;
6b7d31fc 1410 }
1da177e4
LT
1411
1412 write_lock_bh(&t->lock);
2e4e6a17 1413 private = t->private;
3bc3fe5e 1414 if (private->number != num_counters) {
1da177e4
LT
1415 ret = -EINVAL;
1416 goto unlock_up_free;
1417 }
1418
1419 i = 0;
31836064 1420 /* Choose the copy that is on our node */
da4d0f6b 1421 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 1422 IP6T_ENTRY_ITERATE(loc_cpu_entry,
2e4e6a17 1423 private->size,
1da177e4 1424 add_counter_to_entry,
3bc3fe5e 1425 paddc,
1da177e4
LT
1426 &i);
1427 unlock_up_free:
1428 write_unlock_bh(&t->lock);
2e4e6a17 1429 xt_table_unlock(t);
6b7d31fc 1430 module_put(t->me);
1da177e4
LT
1431 free:
1432 vfree(paddc);
1433
1434 return ret;
1435}
1436
3bc3fe5e
PM
1437#ifdef CONFIG_COMPAT
1438struct compat_ip6t_replace {
1439 char name[IP6T_TABLE_MAXNAMELEN];
1440 u32 valid_hooks;
1441 u32 num_entries;
1442 u32 size;
1443 u32 hook_entry[NF_INET_NUMHOOKS];
1444 u32 underflow[NF_INET_NUMHOOKS];
1445 u32 num_counters;
1446 compat_uptr_t counters; /* struct ip6t_counters * */
1447 struct compat_ip6t_entry entries[0];
1448};
1449
1450static int
1451compat_copy_entry_to_user(struct ip6t_entry *e, void __user **dstptr,
b0a6363c 1452 unsigned int *size, struct xt_counters *counters,
3bc3fe5e
PM
1453 unsigned int *i)
1454{
1455 struct ip6t_entry_target *t;
1456 struct compat_ip6t_entry __user *ce;
1457 u_int16_t target_offset, next_offset;
1458 compat_uint_t origsize;
1459 int ret;
1460
1461 ret = -EFAULT;
1462 origsize = *size;
1463 ce = (struct compat_ip6t_entry __user *)*dstptr;
1464 if (copy_to_user(ce, e, sizeof(struct ip6t_entry)))
1465 goto out;
1466
1467 if (copy_to_user(&ce->counters, &counters[*i], sizeof(counters[*i])))
1468 goto out;
1469
1470 *dstptr += sizeof(struct compat_ip6t_entry);
1471 *size -= sizeof(struct ip6t_entry) - sizeof(struct compat_ip6t_entry);
1472
1473 ret = IP6T_MATCH_ITERATE(e, xt_compat_match_to_user, dstptr, size);
1474 target_offset = e->target_offset - (origsize - *size);
1475 if (ret)
1476 goto out;
1477 t = ip6t_get_target(e);
1478 ret = xt_compat_target_to_user(t, dstptr, size);
1479 if (ret)
1480 goto out;
1481 ret = -EFAULT;
1482 next_offset = e->next_offset - (origsize - *size);
1483 if (put_user(target_offset, &ce->target_offset))
1484 goto out;
1485 if (put_user(next_offset, &ce->next_offset))
1486 goto out;
1487
1488 (*i)++;
1489 return 0;
1490out:
1491 return ret;
1492}
1493
022748a9 1494static int
3bc3fe5e
PM
1495compat_find_calc_match(struct ip6t_entry_match *m,
1496 const char *name,
1497 const struct ip6t_ip6 *ipv6,
1498 unsigned int hookmask,
b0a6363c 1499 int *size, unsigned int *i)
3bc3fe5e
PM
1500{
1501 struct xt_match *match;
1502
1503 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
1504 m->u.user.revision),
1505 "ip6t_%s", m->u.user.name);
1506 if (IS_ERR(match) || !match) {
1507 duprintf("compat_check_calc_match: `%s' not found\n",
1508 m->u.user.name);
1509 return match ? PTR_ERR(match) : -ENOENT;
1510 }
1511 m->u.kernel.match = match;
1512 *size += xt_compat_match_offset(match);
1513
1514 (*i)++;
1515 return 0;
1516}
1517
022748a9 1518static int
3bc3fe5e
PM
1519compat_release_match(struct ip6t_entry_match *m, unsigned int *i)
1520{
1521 if (i && (*i)-- == 0)
1522 return 1;
1523
1524 module_put(m->u.kernel.match->me);
1525 return 0;
1526}
1527
022748a9 1528static int
3bc3fe5e
PM
1529compat_release_entry(struct compat_ip6t_entry *e, unsigned int *i)
1530{
1531 struct ip6t_entry_target *t;
1532
1533 if (i && (*i)-- == 0)
1534 return 1;
1535
1536 /* Cleanup all matches */
1537 COMPAT_IP6T_MATCH_ITERATE(e, compat_release_match, NULL);
1538 t = compat_ip6t_get_target(e);
1539 module_put(t->u.kernel.target->me);
1540 return 0;
1541}
1542
022748a9 1543static int
3bc3fe5e
PM
1544check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
1545 struct xt_table_info *newinfo,
1546 unsigned int *size,
1547 unsigned char *base,
1548 unsigned char *limit,
1549 unsigned int *hook_entries,
1550 unsigned int *underflows,
1551 unsigned int *i,
1552 const char *name)
1553{
1554 struct ip6t_entry_target *t;
1555 struct xt_target *target;
1556 unsigned int entry_offset;
b0a6363c
PM
1557 unsigned int j;
1558 int ret, off, h;
3bc3fe5e
PM
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
022748a9 1667static int compat_check_entry(struct ip6t_entry *e, const char *name,
3bc3fe5e
PM
1668 unsigned int *i)
1669{
b0a6363c
PM
1670 unsigned int j;
1671 int ret;
3bc3fe5e
PM
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
336b517f 1813compat_do_replace(struct net *net, void __user *user, unsigned int len)
3bc3fe5e
PM
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
336b517f 1850 ret = __do_replace(net, tmp.name, tmp.valid_hooks, newinfo,
3bc3fe5e
PM
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:
3b1e0a65 1874 ret = compat_do_replace(sock_net(sk), user, len);
3bc3fe5e
PM
1875 break;
1876
1877 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 1878 ret = do_add_counters(sock_net(sk), user, len, 1);
3bc3fe5e
PM
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;
5452e425 1900 const struct xt_table_info *private = table->private;
3bc3fe5e
PM
1901 void __user *pos;
1902 unsigned int size;
1903 int ret = 0;
5452e425 1904 const void *loc_cpu_entry;
3bc3fe5e
PM
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
336b517f
AD
1927compat_get_entries(struct net *net, struct compat_ip6t_get_entries __user *uptr,
1928 int *len)
3bc3fe5e
PM
1929{
1930 int ret;
1931 struct compat_ip6t_get_entries get;
1932 struct xt_table *t;
1933
1934 if (*len < sizeof(get)) {
c9d8fe13 1935 duprintf("compat_get_entries: %u < %zu\n", *len, sizeof(get));
3bc3fe5e
PM
1936 return -EINVAL;
1937 }
1938
1939 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1940 return -EFAULT;
1941
1942 if (*len != sizeof(struct compat_ip6t_get_entries) + get.size) {
c9d8fe13
PM
1943 duprintf("compat_get_entries: %u != %zu\n",
1944 *len, sizeof(get) + get.size);
3bc3fe5e
PM
1945 return -EINVAL;
1946 }
1947
1948 xt_compat_lock(AF_INET6);
336b517f 1949 t = xt_find_table_lock(net, AF_INET6, get.name);
3bc3fe5e 1950 if (t && !IS_ERR(t)) {
5452e425 1951 const struct xt_table_info *private = t->private;
3bc3fe5e 1952 struct xt_table_info info;
9c547959 1953 duprintf("t->private->number = %u\n", private->number);
3bc3fe5e
PM
1954 ret = compat_table_info(private, &info);
1955 if (!ret && get.size == info.size) {
1956 ret = compat_copy_entries_to_user(private->size,
1957 t, uptr->entrytable);
1958 } else if (!ret) {
1959 duprintf("compat_get_entries: I've got %u not %u!\n",
9c547959 1960 private->size, get.size);
544473c1 1961 ret = -EAGAIN;
3bc3fe5e
PM
1962 }
1963 xt_compat_flush_offsets(AF_INET6);
1964 module_put(t->me);
1965 xt_table_unlock(t);
1966 } else
1967 ret = t ? PTR_ERR(t) : -ENOENT;
1968
1969 xt_compat_unlock(AF_INET6);
1970 return ret;
1971}
1972
1973static int do_ip6t_get_ctl(struct sock *, int, void __user *, int *);
1974
1975static int
1976compat_do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1977{
1978 int ret;
1979
1980 if (!capable(CAP_NET_ADMIN))
1981 return -EPERM;
1982
1983 switch (cmd) {
1984 case IP6T_SO_GET_INFO:
3b1e0a65 1985 ret = get_info(sock_net(sk), user, len, 1);
3bc3fe5e
PM
1986 break;
1987 case IP6T_SO_GET_ENTRIES:
3b1e0a65 1988 ret = compat_get_entries(sock_net(sk), user, len);
3bc3fe5e
PM
1989 break;
1990 default:
1991 ret = do_ip6t_get_ctl(sk, cmd, user, len);
1992 }
1993 return ret;
1994}
1995#endif
1996
1da177e4
LT
1997static int
1998do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1999{
2000 int ret;
2001
2002 if (!capable(CAP_NET_ADMIN))
2003 return -EPERM;
2004
2005 switch (cmd) {
2006 case IP6T_SO_SET_REPLACE:
3b1e0a65 2007 ret = do_replace(sock_net(sk), user, len);
1da177e4
LT
2008 break;
2009
2010 case IP6T_SO_SET_ADD_COUNTERS:
3b1e0a65 2011 ret = do_add_counters(sock_net(sk), user, len, 0);
1da177e4
LT
2012 break;
2013
2014 default:
2015 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
2016 ret = -EINVAL;
2017 }
2018
2019 return ret;
2020}
2021
2022static int
2023do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2024{
2025 int ret;
2026
2027 if (!capable(CAP_NET_ADMIN))
2028 return -EPERM;
2029
2030 switch (cmd) {
433665c9 2031 case IP6T_SO_GET_INFO:
3b1e0a65 2032 ret = get_info(sock_net(sk), user, len, 0);
433665c9 2033 break;
1da177e4 2034
d924357c 2035 case IP6T_SO_GET_ENTRIES:
3b1e0a65 2036 ret = get_entries(sock_net(sk), user, len);
1da177e4 2037 break;
1da177e4 2038
6b7d31fc
HW
2039 case IP6T_SO_GET_REVISION_MATCH:
2040 case IP6T_SO_GET_REVISION_TARGET: {
2041 struct ip6t_get_revision rev;
2e4e6a17 2042 int target;
6b7d31fc
HW
2043
2044 if (*len != sizeof(rev)) {
2045 ret = -EINVAL;
2046 break;
2047 }
2048 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2049 ret = -EFAULT;
2050 break;
2051 }
2052
2053 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 2054 target = 1;
6b7d31fc 2055 else
2e4e6a17 2056 target = 0;
6b7d31fc 2057
2e4e6a17
HW
2058 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
2059 rev.revision,
2060 target, &ret),
6b7d31fc
HW
2061 "ip6t_%s", rev.name);
2062 break;
2063 }
2064
1da177e4
LT
2065 default:
2066 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
2067 ret = -EINVAL;
2068 }
2069
2070 return ret;
2071}
2072
336b517f
AD
2073struct xt_table *ip6t_register_table(struct net *net, struct xt_table *table,
2074 const struct ip6t_replace *repl)
1da177e4
LT
2075{
2076 int ret;
2e4e6a17 2077 struct xt_table_info *newinfo;
259d4e41 2078 struct xt_table_info bootstrap
1da177e4 2079 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 2080 void *loc_cpu_entry;
a98da11d 2081 struct xt_table *new_table;
1da177e4 2082
2e4e6a17 2083 newinfo = xt_alloc_table_info(repl->size);
44d34e72
AD
2084 if (!newinfo) {
2085 ret = -ENOMEM;
2086 goto out;
2087 }
1da177e4 2088
9c547959 2089 /* choose the copy on our node/cpu, but dont care about preemption */
31836064
ED
2090 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2091 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4
LT
2092
2093 ret = translate_table(table->name, table->valid_hooks,
31836064 2094 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
2095 repl->num_entries,
2096 repl->hook_entry,
2097 repl->underflow);
44d34e72
AD
2098 if (ret != 0)
2099 goto out_free;
1da177e4 2100
336b517f 2101 new_table = xt_register_table(net, table, &bootstrap, newinfo);
a98da11d 2102 if (IS_ERR(new_table)) {
44d34e72
AD
2103 ret = PTR_ERR(new_table);
2104 goto out_free;
1da177e4 2105 }
44d34e72 2106 return new_table;
1da177e4 2107
44d34e72
AD
2108out_free:
2109 xt_free_table_info(newinfo);
2110out:
2111 return ERR_PTR(ret);
1da177e4
LT
2112}
2113
2e4e6a17 2114void ip6t_unregister_table(struct xt_table *table)
1da177e4 2115{
2e4e6a17 2116 struct xt_table_info *private;
31836064 2117 void *loc_cpu_entry;
df200969 2118 struct module *table_owner = table->me;
31836064 2119
2e4e6a17 2120 private = xt_unregister_table(table);
1da177e4
LT
2121
2122 /* Decrease module usage counts and free resources */
2e4e6a17
HW
2123 loc_cpu_entry = private->entries[raw_smp_processor_id()];
2124 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
df200969
AD
2125 if (private->number > private->initial_entries)
2126 module_put(table_owner);
2e4e6a17 2127 xt_free_table_info(private);
1da177e4
LT
2128}
2129
2130/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 2131static inline bool
1da177e4
LT
2132icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2133 u_int8_t type, u_int8_t code,
ccb79bdc 2134 bool invert)
1da177e4
LT
2135{
2136 return (type == test_type && code >= min_code && code <= max_code)
2137 ^ invert;
2138}
2139
1d93a9cb 2140static bool
f7108a20 2141icmp6_match(const struct sk_buff *skb, const struct xt_match_param *par)
1da177e4 2142{
5452e425
JE
2143 const struct icmp6hdr *ic;
2144 struct icmp6hdr _icmph;
f7108a20 2145 const struct ip6t_icmp *icmpinfo = par->matchinfo;
1da177e4
LT
2146
2147 /* Must not be a fragment. */
f7108a20 2148 if (par->fragoff != 0)
1d93a9cb 2149 return false;
1da177e4 2150
f7108a20 2151 ic = skb_header_pointer(skb, par->thoff, sizeof(_icmph), &_icmph);
1da177e4
LT
2152 if (ic == NULL) {
2153 /* We've been asked to examine this packet, and we
9c547959
PM
2154 * can't. Hence, no choice but to drop.
2155 */
1da177e4 2156 duprintf("Dropping evil ICMP tinygram.\n");
f7108a20 2157 *par->hotdrop = true;
1d93a9cb 2158 return false;
1da177e4
LT
2159 }
2160
2161 return icmp6_type_code_match(icmpinfo->type,
2162 icmpinfo->code[0],
2163 icmpinfo->code[1],
2164 ic->icmp6_type, ic->icmp6_code,
2165 !!(icmpinfo->invflags&IP6T_ICMP_INV));
2166}
2167
2168/* Called when user tries to insert an entry of this type. */
ccb79bdc 2169static bool
1da177e4 2170icmp6_checkentry(const char *tablename,
2e4e6a17 2171 const void *entry,
c4986734 2172 const struct xt_match *match,
1da177e4 2173 void *matchinfo,
1da177e4
LT
2174 unsigned int hook_mask)
2175{
2176 const struct ip6t_icmp *icmpinfo = matchinfo;
2177
7f939713
PM
2178 /* Must specify no unknown invflags */
2179 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1da177e4
LT
2180}
2181
2182/* The built-in targets: standard (NULL) and error. */
9f15c530 2183static struct xt_target ip6t_standard_target __read_mostly = {
1da177e4 2184 .name = IP6T_STANDARD_TARGET,
7f939713 2185 .targetsize = sizeof(int),
a45049c5 2186 .family = AF_INET6,
3bc3fe5e
PM
2187#ifdef CONFIG_COMPAT
2188 .compatsize = sizeof(compat_int_t),
2189 .compat_from_user = compat_standard_from_user,
2190 .compat_to_user = compat_standard_to_user,
2191#endif
1da177e4
LT
2192};
2193
9f15c530 2194static struct xt_target ip6t_error_target __read_mostly = {
1da177e4
LT
2195 .name = IP6T_ERROR_TARGET,
2196 .target = ip6t_error,
7f939713 2197 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
a45049c5 2198 .family = AF_INET6,
1da177e4
LT
2199};
2200
2201static struct nf_sockopt_ops ip6t_sockopts = {
2202 .pf = PF_INET6,
2203 .set_optmin = IP6T_BASE_CTL,
2204 .set_optmax = IP6T_SO_SET_MAX+1,
2205 .set = do_ip6t_set_ctl,
3bc3fe5e
PM
2206#ifdef CONFIG_COMPAT
2207 .compat_set = compat_do_ip6t_set_ctl,
2208#endif
1da177e4
LT
2209 .get_optmin = IP6T_BASE_CTL,
2210 .get_optmax = IP6T_SO_GET_MAX+1,
2211 .get = do_ip6t_get_ctl,
3bc3fe5e
PM
2212#ifdef CONFIG_COMPAT
2213 .compat_get = compat_do_ip6t_get_ctl,
2214#endif
16fcec35 2215 .owner = THIS_MODULE,
1da177e4
LT
2216};
2217
9f15c530 2218static struct xt_match icmp6_matchstruct __read_mostly = {
1da177e4 2219 .name = "icmp6",
9c547959 2220 .match = icmp6_match,
7f939713
PM
2221 .matchsize = sizeof(struct ip6t_icmp),
2222 .checkentry = icmp6_checkentry,
2223 .proto = IPPROTO_ICMPV6,
a45049c5 2224 .family = AF_INET6,
1da177e4
LT
2225};
2226
3cb609d5
AD
2227static int __net_init ip6_tables_net_init(struct net *net)
2228{
2229 return xt_proto_init(net, AF_INET6);
2230}
2231
2232static void __net_exit ip6_tables_net_exit(struct net *net)
2233{
2234 xt_proto_fini(net, AF_INET6);
2235}
2236
2237static struct pernet_operations ip6_tables_net_ops = {
2238 .init = ip6_tables_net_init,
2239 .exit = ip6_tables_net_exit,
2240};
2241
65b4b4e8 2242static int __init ip6_tables_init(void)
1da177e4
LT
2243{
2244 int ret;
2245
3cb609d5 2246 ret = register_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2247 if (ret < 0)
2248 goto err1;
2e4e6a17 2249
1da177e4 2250 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
2251 ret = xt_register_target(&ip6t_standard_target);
2252 if (ret < 0)
2253 goto err2;
2254 ret = xt_register_target(&ip6t_error_target);
2255 if (ret < 0)
2256 goto err3;
2257 ret = xt_register_match(&icmp6_matchstruct);
2258 if (ret < 0)
2259 goto err4;
1da177e4
LT
2260
2261 /* Register setsockopt */
2262 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
2263 if (ret < 0)
2264 goto err5;
1da177e4 2265
a887c1c1 2266 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 2267 return 0;
0eff66e6
PM
2268
2269err5:
2270 xt_unregister_match(&icmp6_matchstruct);
2271err4:
2272 xt_unregister_target(&ip6t_error_target);
2273err3:
2274 xt_unregister_target(&ip6t_standard_target);
2275err2:
3cb609d5 2276 unregister_pernet_subsys(&ip6_tables_net_ops);
0eff66e6
PM
2277err1:
2278 return ret;
1da177e4
LT
2279}
2280
65b4b4e8 2281static void __exit ip6_tables_fini(void)
1da177e4
LT
2282{
2283 nf_unregister_sockopt(&ip6t_sockopts);
9c547959 2284
a45049c5
PNA
2285 xt_unregister_match(&icmp6_matchstruct);
2286 xt_unregister_target(&ip6t_error_target);
2287 xt_unregister_target(&ip6t_standard_target);
3cb609d5
AD
2288
2289 unregister_pernet_subsys(&ip6_tables_net_ops);
1da177e4
LT
2290}
2291
e674d0f3 2292/*
b777e0ce
PM
2293 * find the offset to specified header or the protocol number of last header
2294 * if target < 0. "last header" is transport protocol header, ESP, or
2295 * "No next header".
2296 *
2297 * If target header is found, its offset is set in *offset and return protocol
2298 * number. Otherwise, return -1.
2299 *
6d381634
PM
2300 * If the first fragment doesn't contain the final protocol header or
2301 * NEXTHDR_NONE it is considered invalid.
2302 *
b777e0ce
PM
2303 * Note that non-1st fragment is special case that "the protocol number
2304 * of last header" is "next header" field in Fragment header. In this case,
2305 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
2306 * isn't NULL.
e674d0f3 2307 *
e674d0f3 2308 */
b777e0ce
PM
2309int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
2310 int target, unsigned short *fragoff)
e674d0f3 2311{
6b88dd96 2312 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0660e03f 2313 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
e674d0f3
YK
2314 unsigned int len = skb->len - start;
2315
b777e0ce
PM
2316 if (fragoff)
2317 *fragoff = 0;
2318
e674d0f3
YK
2319 while (nexthdr != target) {
2320 struct ipv6_opt_hdr _hdr, *hp;
2321 unsigned int hdrlen;
2322
b777e0ce
PM
2323 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
2324 if (target < 0)
2325 break;
6d381634 2326 return -ENOENT;
b777e0ce
PM
2327 }
2328
e674d0f3
YK
2329 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
2330 if (hp == NULL)
6d381634 2331 return -EBADMSG;
e674d0f3 2332 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
2333 unsigned short _frag_off;
2334 __be16 *fp;
e674d0f3
YK
2335 fp = skb_header_pointer(skb,
2336 start+offsetof(struct frag_hdr,
2337 frag_off),
2338 sizeof(_frag_off),
2339 &_frag_off);
2340 if (fp == NULL)
6d381634 2341 return -EBADMSG;
e674d0f3 2342
b777e0ce
PM
2343 _frag_off = ntohs(*fp) & ~0x7;
2344 if (_frag_off) {
2345 if (target < 0 &&
2346 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 2347 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
2348 if (fragoff)
2349 *fragoff = _frag_off;
2350 return hp->nexthdr;
2351 }
6d381634 2352 return -ENOENT;
b777e0ce 2353 }
e674d0f3
YK
2354 hdrlen = 8;
2355 } else if (nexthdr == NEXTHDR_AUTH)
1ab1457c 2356 hdrlen = (hp->hdrlen + 2) << 2;
e674d0f3 2357 else
1ab1457c 2358 hdrlen = ipv6_optlen(hp);
e674d0f3
YK
2359
2360 nexthdr = hp->nexthdr;
2361 len -= hdrlen;
2362 start += hdrlen;
2363 }
2364
2365 *offset = start;
b777e0ce 2366 return nexthdr;
e674d0f3
YK
2367}
2368
1da177e4
LT
2369EXPORT_SYMBOL(ip6t_register_table);
2370EXPORT_SYMBOL(ip6t_unregister_table);
2371EXPORT_SYMBOL(ip6t_do_table);
1da177e4 2372EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 2373EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 2374
65b4b4e8
AM
2375module_init(ip6_tables_init);
2376module_exit(ip6_tables_fini);