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