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