[NETFILTER]: ip6_tables: move IP6T_SO_GET_INFO handling to seperate function
[linux-2.6-block.git] / net / ipv6 / netfilter / ip6_tables.c
CommitLineData
1da177e4
LT
1/*
2 * Packet matching code.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
6b7d31fc 5 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
1da177e4
LT
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
1da177e4 10 */
4fc268d2
RD
11
12#include <linux/capability.h>
14c85021 13#include <linux/in.h>
1da177e4
LT
14#include <linux/skbuff.h>
15#include <linux/kmod.h>
16#include <linux/vmalloc.h>
17#include <linux/netdevice.h>
18#include <linux/module.h>
4bdbf6c0 19#include <linux/poison.h>
1da177e4 20#include <linux/icmpv6.h>
1da177e4
LT
21#include <net/ipv6.h>
22#include <asm/uaccess.h>
57b47a53 23#include <linux/mutex.h>
1da177e4 24#include <linux/proc_fs.h>
c8923c6b 25#include <linux/cpumask.h>
1da177e4
LT
26
27#include <linux/netfilter_ipv6/ip6_tables.h>
2e4e6a17 28#include <linux/netfilter/x_tables.h>
1da177e4
LT
29
30MODULE_LICENSE("GPL");
31MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
32MODULE_DESCRIPTION("IPv6 packet filter");
33
1da177e4
LT
34/*#define DEBUG_IP_FIREWALL*/
35/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
36/*#define DEBUG_IP_FIREWALL_USER*/
37
38#ifdef DEBUG_IP_FIREWALL
39#define dprintf(format, args...) printk(format , ## args)
40#else
41#define dprintf(format, args...)
42#endif
43
44#ifdef DEBUG_IP_FIREWALL_USER
45#define duprintf(format, args...) printk(format , ## args)
46#else
47#define duprintf(format, args...)
48#endif
49
50#ifdef CONFIG_NETFILTER_DEBUG
51#define IP_NF_ASSERT(x) \
52do { \
53 if (!(x)) \
54 printk("IP_NF_ASSERT: %s:%s:%u\n", \
55 __FUNCTION__, __FILE__, __LINE__); \
56} while(0)
57#else
58#define IP_NF_ASSERT(x)
59#endif
1da177e4 60
1da177e4
LT
61#if 0
62/* All the better to debug you with... */
63#define static
64#define inline
65#endif
66
6b7d31fc 67/*
1da177e4 68 We keep a set of rules for each CPU, so we can avoid write-locking
6b7d31fc
HW
69 them in the softirq when updating the counters and therefore
70 only need to read-lock in the softirq; doing a write_lock_bh() in user
71 context stops packets coming through and allows user context to read
72 the counters or update the rules.
1da177e4 73
1da177e4
LT
74 Hence the start of any table is given by get_table() below. */
75
1da177e4 76/* Check for an extension */
1ab1457c 77int
1da177e4
LT
78ip6t_ext_hdr(u8 nexthdr)
79{
1ab1457c
YH
80 return ( (nexthdr == IPPROTO_HOPOPTS) ||
81 (nexthdr == IPPROTO_ROUTING) ||
82 (nexthdr == IPPROTO_FRAGMENT) ||
83 (nexthdr == IPPROTO_ESP) ||
84 (nexthdr == IPPROTO_AH) ||
85 (nexthdr == IPPROTO_NONE) ||
86 (nexthdr == IPPROTO_DSTOPTS) );
1da177e4
LT
87}
88
89/* Returns whether matches rule or not. */
1d93a9cb 90static inline bool
1da177e4
LT
91ip6_packet_match(const struct sk_buff *skb,
92 const char *indev,
93 const char *outdev,
94 const struct ip6t_ip6 *ip6info,
95 unsigned int *protoff,
cff533ac 96 int *fragoff, bool *hotdrop)
1da177e4
LT
97{
98 size_t i;
99 unsigned long ret;
0660e03f 100 const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
1da177e4
LT
101
102#define FWINV(bool,invflg) ((bool) ^ !!(ip6info->invflags & invflg))
103
f2ffd9ee 104 if (FWINV(ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
1ab1457c 105 &ip6info->src), IP6T_INV_SRCIP)
f2ffd9ee 106 || FWINV(ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
1ab1457c 107 &ip6info->dst), IP6T_INV_DSTIP)) {
1da177e4
LT
108 dprintf("Source or dest mismatch.\n");
109/*
110 dprintf("SRC: %u. Mask: %u. Target: %u.%s\n", ip->saddr,
111 ipinfo->smsk.s_addr, ipinfo->src.s_addr,
112 ipinfo->invflags & IP6T_INV_SRCIP ? " (INV)" : "");
113 dprintf("DST: %u. Mask: %u. Target: %u.%s\n", ip->daddr,
114 ipinfo->dmsk.s_addr, ipinfo->dst.s_addr,
115 ipinfo->invflags & IP6T_INV_DSTIP ? " (INV)" : "");*/
1d93a9cb 116 return false;
1da177e4
LT
117 }
118
119 /* Look for ifname matches; this should unroll nicely. */
120 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
121 ret |= (((const unsigned long *)indev)[i]
122 ^ ((const unsigned long *)ip6info->iniface)[i])
123 & ((const unsigned long *)ip6info->iniface_mask)[i];
124 }
125
126 if (FWINV(ret != 0, IP6T_INV_VIA_IN)) {
127 dprintf("VIA in mismatch (%s vs %s).%s\n",
128 indev, ip6info->iniface,
129 ip6info->invflags&IP6T_INV_VIA_IN ?" (INV)":"");
1d93a9cb 130 return false;
1da177e4
LT
131 }
132
133 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
134 ret |= (((const unsigned long *)outdev)[i]
135 ^ ((const unsigned long *)ip6info->outiface)[i])
136 & ((const unsigned long *)ip6info->outiface_mask)[i];
137 }
138
139 if (FWINV(ret != 0, IP6T_INV_VIA_OUT)) {
140 dprintf("VIA out mismatch (%s vs %s).%s\n",
141 outdev, ip6info->outiface,
142 ip6info->invflags&IP6T_INV_VIA_OUT ?" (INV)":"");
1d93a9cb 143 return false;
1da177e4
LT
144 }
145
146/* ... might want to do something with class and flowlabel here ... */
147
148 /* look for the desired protocol header */
149 if((ip6info->flags & IP6T_F_PROTO)) {
b777e0ce
PM
150 int protohdr;
151 unsigned short _frag_off;
1da177e4 152
b777e0ce 153 protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off);
51d8b1a6
PM
154 if (protohdr < 0) {
155 if (_frag_off == 0)
cff533ac 156 *hotdrop = true;
1d93a9cb 157 return false;
51d8b1a6 158 }
b777e0ce 159 *fragoff = _frag_off;
1da177e4
LT
160
161 dprintf("Packet protocol %hi ?= %s%hi.\n",
1ab1457c 162 protohdr,
1da177e4
LT
163 ip6info->invflags & IP6T_INV_PROTO ? "!":"",
164 ip6info->proto);
165
b777e0ce 166 if (ip6info->proto == protohdr) {
1da177e4 167 if(ip6info->invflags & IP6T_INV_PROTO) {
1d93a9cb 168 return false;
1da177e4 169 }
1d93a9cb 170 return true;
1da177e4
LT
171 }
172
173 /* We need match for the '-p all', too! */
174 if ((ip6info->proto != 0) &&
175 !(ip6info->invflags & IP6T_INV_PROTO))
1d93a9cb 176 return false;
1da177e4 177 }
1d93a9cb 178 return true;
1da177e4
LT
179}
180
181/* should be ip6 safe */
ccb79bdc 182static inline bool
1da177e4
LT
183ip6_checkentry(const struct ip6t_ip6 *ipv6)
184{
185 if (ipv6->flags & ~IP6T_F_MASK) {
186 duprintf("Unknown flag bits set: %08X\n",
187 ipv6->flags & ~IP6T_F_MASK);
ccb79bdc 188 return false;
1da177e4
LT
189 }
190 if (ipv6->invflags & ~IP6T_INV_MASK) {
191 duprintf("Unknown invflag bits set: %08X\n",
192 ipv6->invflags & ~IP6T_INV_MASK);
ccb79bdc 193 return false;
1da177e4 194 }
ccb79bdc 195 return true;
1da177e4
LT
196}
197
198static unsigned int
3db05fea 199ip6t_error(struct sk_buff *skb,
1da177e4
LT
200 const struct net_device *in,
201 const struct net_device *out,
202 unsigned int hooknum,
c4986734 203 const struct xt_target *target,
fe1cb108 204 const void *targinfo)
1da177e4
LT
205{
206 if (net_ratelimit())
207 printk("ip6_tables: error: `%s'\n", (char *)targinfo);
208
209 return NF_DROP;
210}
211
212static inline
1d93a9cb
JE
213bool do_match(struct ip6t_entry_match *m,
214 const struct sk_buff *skb,
215 const struct net_device *in,
216 const struct net_device *out,
217 int offset,
218 unsigned int protoff,
219 bool *hotdrop)
1da177e4
LT
220{
221 /* Stop iteration if it doesn't match */
1c524830 222 if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
1da177e4 223 offset, protoff, hotdrop))
1d93a9cb 224 return true;
1da177e4 225 else
1d93a9cb 226 return false;
1da177e4
LT
227}
228
229static inline struct ip6t_entry *
230get_entry(void *base, unsigned int offset)
231{
232 return (struct ip6t_entry *)(base + offset);
233}
234
ba9dda3a
JK
235/* All zeroes == unconditional rule. */
236static inline int
237unconditional(const struct ip6t_ip6 *ipv6)
238{
239 unsigned int i;
240
241 for (i = 0; i < sizeof(*ipv6); i++)
242 if (((char *)ipv6)[i])
243 break;
244
245 return (i == sizeof(*ipv6));
246}
247
248#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
249 defined(CONFIG_NETFILTER_XT_TARGET_TRACE_MODULE)
250/* This cries for unification! */
251static const char *hooknames[] = {
6e23ae2a
PM
252 [NF_INET_PRE_ROUTING] = "PREROUTING",
253 [NF_INET_LOCAL_IN] = "INPUT",
254 [NF_INET_FORWARD] = "FORWARD",
255 [NF_INET_LOCAL_OUT] = "OUTPUT",
256 [NF_INET_POST_ROUTING] = "POSTROUTING",
ba9dda3a
JK
257};
258
259enum nf_ip_trace_comments {
260 NF_IP6_TRACE_COMMENT_RULE,
261 NF_IP6_TRACE_COMMENT_RETURN,
262 NF_IP6_TRACE_COMMENT_POLICY,
263};
264
265static const char *comments[] = {
266 [NF_IP6_TRACE_COMMENT_RULE] = "rule",
267 [NF_IP6_TRACE_COMMENT_RETURN] = "return",
268 [NF_IP6_TRACE_COMMENT_POLICY] = "policy",
269};
270
271static struct nf_loginfo trace_loginfo = {
272 .type = NF_LOG_TYPE_LOG,
273 .u = {
274 .log = {
275 .level = 4,
276 .logflags = NF_LOG_MASK,
277 },
278 },
279};
280
281static inline int
282get_chainname_rulenum(struct ip6t_entry *s, struct ip6t_entry *e,
283 char *hookname, char **chainname,
284 char **comment, unsigned int *rulenum)
285{
286 struct ip6t_standard_target *t = (void *)ip6t_get_target(s);
287
288 if (strcmp(t->target.u.kernel.target->name, IP6T_ERROR_TARGET) == 0) {
289 /* Head of user chain: ERROR target with chainname */
290 *chainname = t->target.data;
291 (*rulenum) = 0;
292 } else if (s == e) {
293 (*rulenum)++;
294
295 if (s->target_offset == sizeof(struct ip6t_entry)
296 && strcmp(t->target.u.kernel.target->name,
297 IP6T_STANDARD_TARGET) == 0
298 && t->verdict < 0
299 && unconditional(&s->ipv6)) {
300 /* Tail of chains: STANDARD target (return/policy) */
301 *comment = *chainname == hookname
302 ? (char *)comments[NF_IP6_TRACE_COMMENT_POLICY]
303 : (char *)comments[NF_IP6_TRACE_COMMENT_RETURN];
304 }
305 return 1;
306 } else
307 (*rulenum)++;
308
309 return 0;
310}
311
312static void trace_packet(struct sk_buff *skb,
313 unsigned int hook,
314 const struct net_device *in,
315 const struct net_device *out,
316 char *tablename,
317 struct xt_table_info *private,
318 struct ip6t_entry *e)
319{
320 void *table_base;
321 struct ip6t_entry *root;
322 char *hookname, *chainname, *comment;
323 unsigned int rulenum = 0;
324
325 table_base = (void *)private->entries[smp_processor_id()];
326 root = get_entry(table_base, private->hook_entry[hook]);
327
328 hookname = chainname = (char *)hooknames[hook];
329 comment = (char *)comments[NF_IP6_TRACE_COMMENT_RULE];
330
331 IP6T_ENTRY_ITERATE(root,
332 private->size - private->hook_entry[hook],
333 get_chainname_rulenum,
334 e, hookname, &chainname, &comment, &rulenum);
335
336 nf_log_packet(AF_INET6, hook, skb, in, out, &trace_loginfo,
337 "TRACE: %s:%s:%s:%u ",
338 tablename, chainname, comment, rulenum);
339}
340#endif
341
1da177e4
LT
342/* Returns one of the generic firewall policies, like NF_ACCEPT. */
343unsigned int
3db05fea 344ip6t_do_table(struct sk_buff *skb,
1da177e4
LT
345 unsigned int hook,
346 const struct net_device *in,
347 const struct net_device *out,
fe1cb108 348 struct xt_table *table)
1da177e4 349{
6b7d31fc 350 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
1da177e4
LT
351 int offset = 0;
352 unsigned int protoff = 0;
cff533ac 353 bool hotdrop = false;
1da177e4
LT
354 /* Initializing verdict to NF_DROP keeps gcc happy. */
355 unsigned int verdict = NF_DROP;
356 const char *indev, *outdev;
357 void *table_base;
358 struct ip6t_entry *e, *back;
2e4e6a17 359 struct xt_table_info *private;
1da177e4
LT
360
361 /* Initialization */
362 indev = in ? in->name : nulldevname;
363 outdev = out ? out->name : nulldevname;
1da177e4
LT
364 /* We handle fragments by dealing with the first fragment as
365 * if it was a normal packet. All other fragments are treated
366 * normally, except that they will NEVER match rules that ask
367 * things we don't know, ie. tcp syn flag or ports). If the
368 * rule is also a fragment-specific rule, non-fragments won't
369 * match it. */
370
371 read_lock_bh(&table->lock);
2e4e6a17 372 private = table->private;
1da177e4 373 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
2e4e6a17
HW
374 table_base = (void *)private->entries[smp_processor_id()];
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 {
381 IP_NF_ASSERT(e);
382 IP_NF_ASSERT(back);
3db05fea 383 if (ip6_packet_match(skb, indev, outdev, &e->ipv6,
51d8b1a6 384 &protoff, &offset, &hotdrop)) {
1da177e4
LT
385 struct ip6t_entry_target *t;
386
387 if (IP6T_MATCH_ITERATE(e, do_match,
3db05fea 388 skb, in, out,
1da177e4
LT
389 offset, protoff, &hotdrop) != 0)
390 goto no_match;
391
392 ADD_COUNTER(e->counters,
72f36ec1
PM
393 ntohs(ipv6_hdr(skb)->payload_len) +
394 sizeof(struct ipv6hdr), 1);
1da177e4
LT
395
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)
401 /* The packet is traced: log it */
3db05fea
HX
402 if (unlikely(skb->nf_trace))
403 trace_packet(skb, hook, in, out,
ba9dda3a
JK
404 table->name, private, e);
405#endif
1da177e4
LT
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;
416 }
417 e = back;
418 back = get_entry(table_base,
419 back->comefrom);
420 continue;
421 }
05465343
PM
422 if (table_base + v != (void *)e + e->next_offset
423 && !(e->ipv6.flags & IP6T_F_GOTO)) {
1da177e4
LT
424 /* Save old back ptr in next entry */
425 struct ip6t_entry *next
426 = (void *)e + e->next_offset;
427 next->comefrom
428 = (void *)back - table_base;
429 /* set back pointer to next entry */
430 back = next;
431 }
432
433 e = get_entry(table_base, v);
434 } else {
435 /* Targets which reenter must return
1ab1457c 436 abs. verdicts */
1da177e4
LT
437#ifdef CONFIG_NETFILTER_DEBUG
438 ((struct ip6t_entry *)table_base)->comefrom
439 = 0xeeeeeeec;
440#endif
3db05fea 441 verdict = t->u.kernel.target->target(skb,
1da177e4
LT
442 in, out,
443 hook,
1c524830 444 t->u.kernel.target,
fe1cb108 445 t->data);
1da177e4
LT
446
447#ifdef CONFIG_NETFILTER_DEBUG
448 if (((struct ip6t_entry *)table_base)->comefrom
449 != 0xeeeeeeec
450 && verdict == IP6T_CONTINUE) {
451 printk("Target %s reentered!\n",
452 t->u.kernel.target->name);
453 verdict = NF_DROP;
454 }
455 ((struct ip6t_entry *)table_base)->comefrom
456 = 0x57acc001;
457#endif
458 if (verdict == IP6T_CONTINUE)
459 e = (void *)e + e->next_offset;
460 else
461 /* Verdict */
462 break;
463 }
464 } else {
465
466 no_match:
467 e = (void *)e + e->next_offset;
468 }
469 } while (!hotdrop);
470
471#ifdef CONFIG_NETFILTER_DEBUG
4bdbf6c0 472 ((struct ip6t_entry *)table_base)->comefrom = NETFILTER_LINK_POISON;
1da177e4
LT
473#endif
474 read_unlock_bh(&table->lock);
475
476#ifdef DEBUG_ALLOW_ALL
477 return NF_ACCEPT;
478#else
479 if (hotdrop)
480 return NF_DROP;
481 else return verdict;
482#endif
483}
484
1da177e4
LT
485/* Figures out from what hook each rule can be called: returns 0 if
486 there are loops. Puts hook bitmask in comefrom. */
487static int
2e4e6a17 488mark_source_chains(struct xt_table_info *newinfo,
31836064 489 unsigned int valid_hooks, void *entry0)
1da177e4
LT
490{
491 unsigned int hook;
492
493 /* No recursion; use packet counter to save back ptrs (reset
494 to 0 as we leave), and comefrom to save source hook bitmask */
6e23ae2a 495 for (hook = 0; hook < NF_INET_NUMHOOKS; hook++) {
1da177e4
LT
496 unsigned int pos = newinfo->hook_entry[hook];
497 struct ip6t_entry *e
31836064 498 = (struct ip6t_entry *)(entry0 + pos);
e1b4b9f3 499 int visited = e->comefrom & (1 << hook);
1da177e4
LT
500
501 if (!(valid_hooks & (1 << hook)))
502 continue;
503
504 /* Set initial back pointer. */
505 e->counters.pcnt = pos;
506
507 for (;;) {
508 struct ip6t_standard_target *t
509 = (void *)ip6t_get_target(e);
510
6e23ae2a 511 if (e->comefrom & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
512 printk("iptables: loop hook %u pos %u %08X.\n",
513 hook, pos, e->comefrom);
514 return 0;
515 }
516 e->comefrom
6e23ae2a 517 |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
1da177e4
LT
518
519 /* Unconditional return/END. */
e1b4b9f3 520 if ((e->target_offset == sizeof(struct ip6t_entry)
1da177e4
LT
521 && (strcmp(t->target.u.user.name,
522 IP6T_STANDARD_TARGET) == 0)
523 && t->verdict < 0
e1b4b9f3 524 && unconditional(&e->ipv6)) || visited) {
1da177e4
LT
525 unsigned int oldpos, size;
526
74c9c0c1
DM
527 if (t->verdict < -NF_MAX_VERDICT - 1) {
528 duprintf("mark_source_chains: bad "
529 "negative verdict (%i)\n",
530 t->verdict);
531 return 0;
532 }
533
1da177e4
LT
534 /* Return: backtrack through the last
535 big jump. */
536 do {
6e23ae2a 537 e->comefrom ^= (1<<NF_INET_NUMHOOKS);
1da177e4
LT
538#ifdef DEBUG_IP_FIREWALL_USER
539 if (e->comefrom
6e23ae2a 540 & (1 << NF_INET_NUMHOOKS)) {
1da177e4
LT
541 duprintf("Back unset "
542 "on hook %u "
543 "rule %u\n",
544 hook, pos);
545 }
546#endif
547 oldpos = pos;
548 pos = e->counters.pcnt;
549 e->counters.pcnt = 0;
550
551 /* We're at the start. */
552 if (pos == oldpos)
553 goto next;
554
555 e = (struct ip6t_entry *)
31836064 556 (entry0 + pos);
1da177e4
LT
557 } while (oldpos == pos + e->next_offset);
558
559 /* Move along one */
560 size = e->next_offset;
561 e = (struct ip6t_entry *)
31836064 562 (entry0 + pos + size);
1da177e4
LT
563 e->counters.pcnt = pos;
564 pos += size;
565 } else {
566 int newpos = t->verdict;
567
568 if (strcmp(t->target.u.user.name,
569 IP6T_STANDARD_TARGET) == 0
570 && newpos >= 0) {
74c9c0c1
DM
571 if (newpos > newinfo->size -
572 sizeof(struct ip6t_entry)) {
573 duprintf("mark_source_chains: "
574 "bad verdict (%i)\n",
575 newpos);
576 return 0;
577 }
1da177e4
LT
578 /* This a jump; chase it. */
579 duprintf("Jump rule %u -> %u\n",
580 pos, newpos);
581 } else {
582 /* ... this is a fallthru */
583 newpos = pos + e->next_offset;
584 }
585 e = (struct ip6t_entry *)
31836064 586 (entry0 + newpos);
1da177e4
LT
587 e->counters.pcnt = pos;
588 pos = newpos;
589 }
590 }
591 next:
592 duprintf("Finished chain %u\n", hook);
593 }
594 return 1;
595}
596
597static inline int
598cleanup_match(struct ip6t_entry_match *m, unsigned int *i)
599{
600 if (i && (*i)-- == 0)
601 return 1;
602
603 if (m->u.kernel.match->destroy)
efa74165 604 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
1da177e4
LT
605 module_put(m->u.kernel.match->me);
606 return 0;
607}
608
1da177e4 609static inline int
f173c8a1
PM
610check_entry(struct ip6t_entry *e, const char *name)
611{
612 struct ip6t_entry_target *t;
613
614 if (!ip6_checkentry(&e->ipv6)) {
615 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
616 return -EINVAL;
617 }
618
619 if (e->target_offset + sizeof(struct ip6t_entry_target) >
620 e->next_offset)
621 return -EINVAL;
622
623 t = ip6t_get_target(e);
624 if (e->target_offset + t->u.target_size > e->next_offset)
625 return -EINVAL;
626
627 return 0;
628}
629
630static inline int check_match(struct ip6t_entry_match *m, const char *name,
631 const struct ip6t_ip6 *ipv6,
632 unsigned int hookmask, unsigned int *i)
633{
634 struct xt_match *match;
635 int ret;
636
637 match = m->u.kernel.match;
638 ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
639 name, hookmask, ipv6->proto,
640 ipv6->invflags & IP6T_INV_PROTO);
641 if (!ret && m->u.kernel.match->checkentry
642 && !m->u.kernel.match->checkentry(name, ipv6, match, m->data,
643 hookmask)) {
644 duprintf("ip_tables: check failed for `%s'.\n",
645 m->u.kernel.match->name);
646 ret = -EINVAL;
647 }
648 if (!ret)
649 (*i)++;
650 return ret;
651}
652
653static inline int
654find_check_match(struct ip6t_entry_match *m,
655 const char *name,
656 const struct ip6t_ip6 *ipv6,
657 unsigned int hookmask,
658 unsigned int *i)
1da177e4 659{
6709dbbb 660 struct xt_match *match;
3cdc7c95 661 int ret;
1da177e4 662
2e4e6a17 663 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
1ab1457c 664 m->u.user.revision),
6b7d31fc
HW
665 "ip6t_%s", m->u.user.name);
666 if (IS_ERR(match) || !match) {
f173c8a1 667 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
6b7d31fc 668 return match ? PTR_ERR(match) : -ENOENT;
1da177e4
LT
669 }
670 m->u.kernel.match = match;
1da177e4 671
f173c8a1 672 ret = check_match(m, name, ipv6, hookmask, i);
3cdc7c95
PM
673 if (ret)
674 goto err;
675
1da177e4 676 return 0;
3cdc7c95
PM
677err:
678 module_put(m->u.kernel.match->me);
679 return ret;
1da177e4
LT
680}
681
f173c8a1 682static inline int check_target(struct ip6t_entry *e, const char *name)
1da177e4
LT
683{
684 struct ip6t_entry_target *t;
6709dbbb 685 struct xt_target *target;
1da177e4 686 int ret;
1da177e4 687
f173c8a1
PM
688 t = ip6t_get_target(e);
689 target = t->u.kernel.target;
690 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
691 name, e->comefrom, e->ipv6.proto,
692 e->ipv6.invflags & IP6T_INV_PROTO);
693 if (!ret && t->u.kernel.target->checkentry
694 && !t->u.kernel.target->checkentry(name, e, target, t->data,
695 e->comefrom)) {
696 duprintf("ip_tables: check failed for `%s'.\n",
697 t->u.kernel.target->name);
698 ret = -EINVAL;
1da177e4 699 }
f173c8a1
PM
700 return ret;
701}
1da177e4 702
f173c8a1
PM
703static inline int
704find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
705 unsigned int *i)
706{
707 struct ip6t_entry_target *t;
708 struct xt_target *target;
709 int ret;
710 unsigned int j;
711
712 ret = check_entry(e, name);
713 if (ret)
714 return ret;
590bdf7f 715
1da177e4 716 j = 0;
f173c8a1
PM
717 ret = IP6T_MATCH_ITERATE(e, find_check_match, name, &e->ipv6,
718 e->comefrom, &j);
1da177e4
LT
719 if (ret != 0)
720 goto cleanup_matches;
721
722 t = ip6t_get_target(e);
2e4e6a17
HW
723 target = try_then_request_module(xt_find_target(AF_INET6,
724 t->u.user.name,
725 t->u.user.revision),
6b7d31fc
HW
726 "ip6t_%s", t->u.user.name);
727 if (IS_ERR(target) || !target) {
f173c8a1 728 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
6b7d31fc 729 ret = target ? PTR_ERR(target) : -ENOENT;
1da177e4
LT
730 goto cleanup_matches;
731 }
732 t->u.kernel.target = target;
6b7d31fc 733
f173c8a1 734 ret = check_target(e, name);
3cdc7c95
PM
735 if (ret)
736 goto err;
737
1da177e4
LT
738 (*i)++;
739 return 0;
3cdc7c95
PM
740 err:
741 module_put(t->u.kernel.target->me);
1da177e4
LT
742 cleanup_matches:
743 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
744 return ret;
745}
746
747static inline int
748check_entry_size_and_hooks(struct ip6t_entry *e,
2e4e6a17 749 struct xt_table_info *newinfo,
1da177e4
LT
750 unsigned char *base,
751 unsigned char *limit,
752 const unsigned int *hook_entries,
753 const unsigned int *underflows,
754 unsigned int *i)
755{
756 unsigned int h;
757
758 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
759 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
760 duprintf("Bad offset %p\n", e);
761 return -EINVAL;
762 }
763
764 if (e->next_offset
765 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
766 duprintf("checking: element %p size %u\n",
767 e, e->next_offset);
768 return -EINVAL;
769 }
770
771 /* Check hooks & underflows */
6e23ae2a 772 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1da177e4
LT
773 if ((unsigned char *)e - base == hook_entries[h])
774 newinfo->hook_entry[h] = hook_entries[h];
775 if ((unsigned char *)e - base == underflows[h])
776 newinfo->underflow[h] = underflows[h];
777 }
778
779 /* FIXME: underflows must be unconditional, standard verdicts
1ab1457c 780 < 0 (not IP6T_RETURN). --RR */
1da177e4
LT
781
782 /* Clear counters and comefrom */
2e4e6a17 783 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4
LT
784 e->comefrom = 0;
785
786 (*i)++;
787 return 0;
788}
789
790static inline int
791cleanup_entry(struct ip6t_entry *e, unsigned int *i)
792{
793 struct ip6t_entry_target *t;
794
795 if (i && (*i)-- == 0)
796 return 1;
797
798 /* Cleanup all matches */
799 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
800 t = ip6t_get_target(e);
801 if (t->u.kernel.target->destroy)
efa74165 802 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
1da177e4
LT
803 module_put(t->u.kernel.target->me);
804 return 0;
805}
806
807/* Checks and translates the user-supplied table segment (held in
808 newinfo) */
809static int
810translate_table(const char *name,
811 unsigned int valid_hooks,
2e4e6a17 812 struct xt_table_info *newinfo,
31836064 813 void *entry0,
1da177e4
LT
814 unsigned int size,
815 unsigned int number,
816 const unsigned int *hook_entries,
817 const unsigned int *underflows)
818{
819 unsigned int i;
820 int ret;
821
822 newinfo->size = size;
823 newinfo->number = number;
824
825 /* Init all hooks to impossible value. */
6e23ae2a 826 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
827 newinfo->hook_entry[i] = 0xFFFFFFFF;
828 newinfo->underflow[i] = 0xFFFFFFFF;
829 }
830
831 duprintf("translate_table: size %u\n", newinfo->size);
832 i = 0;
833 /* Walk through entries, checking offsets. */
31836064 834 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
835 check_entry_size_and_hooks,
836 newinfo,
31836064
ED
837 entry0,
838 entry0 + size,
1da177e4
LT
839 hook_entries, underflows, &i);
840 if (ret != 0)
841 return ret;
842
843 if (i != number) {
844 duprintf("translate_table: %u not %u entries\n",
845 i, number);
846 return -EINVAL;
847 }
848
849 /* Check hooks all assigned */
6e23ae2a 850 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
851 /* Only hooks which are valid */
852 if (!(valid_hooks & (1 << i)))
853 continue;
854 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
855 duprintf("Invalid hook entry %u %u\n",
856 i, hook_entries[i]);
857 return -EINVAL;
858 }
859 if (newinfo->underflow[i] == 0xFFFFFFFF) {
860 duprintf("Invalid underflow %u %u\n",
861 i, underflows[i]);
862 return -EINVAL;
863 }
864 }
865
74c9c0c1
DM
866 if (!mark_source_chains(newinfo, valid_hooks, entry0))
867 return -ELOOP;
868
1da177e4
LT
869 /* Finally, each sanity check must pass */
870 i = 0;
31836064 871 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
f173c8a1 872 find_check_entry, name, size, &i);
1da177e4 873
74c9c0c1
DM
874 if (ret != 0) {
875 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
876 cleanup_entry, &i);
877 return ret;
878 }
1da177e4
LT
879
880 /* And one copy for every other CPU */
6f912042 881 for_each_possible_cpu(i) {
31836064
ED
882 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
883 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
884 }
885
590bdf7f 886 return 0;
1da177e4
LT
887}
888
1da177e4
LT
889/* Gets counters. */
890static inline int
891add_entry_to_counter(const struct ip6t_entry *e,
2e4e6a17 892 struct xt_counters total[],
1da177e4
LT
893 unsigned int *i)
894{
895 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
896
897 (*i)++;
898 return 0;
899}
900
31836064
ED
901static inline int
902set_entry_to_counter(const struct ip6t_entry *e,
903 struct ip6t_counters total[],
904 unsigned int *i)
905{
906 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
907
908 (*i)++;
909 return 0;
910}
911
1da177e4 912static void
2e4e6a17
HW
913get_counters(const struct xt_table_info *t,
914 struct xt_counters counters[])
1da177e4
LT
915{
916 unsigned int cpu;
917 unsigned int i;
31836064
ED
918 unsigned int curcpu;
919
920 /* Instead of clearing (by a previous call to memset())
921 * the counters and using adds, we set the counters
922 * with data used by 'current' CPU
923 * We dont care about preemption here.
924 */
925 curcpu = raw_smp_processor_id();
926
927 i = 0;
928 IP6T_ENTRY_ITERATE(t->entries[curcpu],
929 t->size,
930 set_entry_to_counter,
931 counters,
932 &i);
1da177e4 933
6f912042 934 for_each_possible_cpu(cpu) {
31836064
ED
935 if (cpu == curcpu)
936 continue;
1da177e4 937 i = 0;
31836064 938 IP6T_ENTRY_ITERATE(t->entries[cpu],
1da177e4
LT
939 t->size,
940 add_entry_to_counter,
941 counters,
942 &i);
943 }
944}
945
ed1a6f5e 946static inline struct xt_counters *alloc_counters(struct xt_table *table)
1da177e4 947{
ed1a6f5e 948 unsigned int countersize;
2e4e6a17
HW
949 struct xt_counters *counters;
950 struct xt_table_info *private = table->private;
1da177e4
LT
951
952 /* We need atomic snapshot of counters: rest doesn't change
953 (other than comefrom, which userspace doesn't care
954 about). */
2e4e6a17 955 countersize = sizeof(struct xt_counters) * private->number;
3b84e92b 956 counters = vmalloc_node(countersize, numa_node_id());
1da177e4
LT
957
958 if (counters == NULL)
ed1a6f5e 959 return ERR_PTR(-ENOMEM);
1da177e4
LT
960
961 /* First, sum counters... */
1da177e4 962 write_lock_bh(&table->lock);
2e4e6a17 963 get_counters(private, counters);
1da177e4
LT
964 write_unlock_bh(&table->lock);
965
ed1a6f5e
PM
966 return counters;
967}
968
969static int
970copy_entries_to_user(unsigned int total_size,
971 struct xt_table *table,
972 void __user *userptr)
973{
974 unsigned int off, num;
975 struct ip6t_entry *e;
976 struct xt_counters *counters;
977 struct xt_table_info *private = table->private;
978 int ret = 0;
979 void *loc_cpu_entry;
980
981 counters = alloc_counters(table);
982 if (IS_ERR(counters))
983 return PTR_ERR(counters);
984
31836064 985 /* choose the copy that is on ourc node/cpu */
2e4e6a17 986 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 987 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
988 ret = -EFAULT;
989 goto free_counters;
990 }
991
992 /* FIXME: use iterator macros --RR */
993 /* ... then go back and fix counters and names */
994 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
995 unsigned int i;
996 struct ip6t_entry_match *m;
997 struct ip6t_entry_target *t;
998
31836064 999 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
1000 if (copy_to_user(userptr + off
1001 + offsetof(struct ip6t_entry, counters),
1002 &counters[num],
1003 sizeof(counters[num])) != 0) {
1004 ret = -EFAULT;
1005 goto free_counters;
1006 }
1007
1008 for (i = sizeof(struct ip6t_entry);
1009 i < e->target_offset;
1010 i += m->u.match_size) {
1011 m = (void *)e + i;
1012
1013 if (copy_to_user(userptr + off + i
1014 + offsetof(struct ip6t_entry_match,
1015 u.user.name),
1016 m->u.kernel.match->name,
1017 strlen(m->u.kernel.match->name)+1)
1018 != 0) {
1019 ret = -EFAULT;
1020 goto free_counters;
1021 }
1022 }
1023
1024 t = ip6t_get_target(e);
1025 if (copy_to_user(userptr + off + e->target_offset
1026 + offsetof(struct ip6t_entry_target,
1027 u.user.name),
1028 t->u.kernel.target->name,
1029 strlen(t->u.kernel.target->name)+1) != 0) {
1030 ret = -EFAULT;
1031 goto free_counters;
1032 }
1033 }
1034
1035 free_counters:
1036 vfree(counters);
1037 return ret;
1038}
1039
433665c9
PM
1040static int get_info(void __user *user, int *len)
1041{
1042 char name[IP6T_TABLE_MAXNAMELEN];
1043 struct xt_table *t;
1044 int ret;
1045
1046 if (*len != sizeof(struct ip6t_getinfo)) {
1047 duprintf("length %u != %u\n", *len,
1048 sizeof(struct ip6t_getinfo));
1049 return -EINVAL;
1050 }
1051
1052 if (copy_from_user(name, user, sizeof(name)) != 0)
1053 return -EFAULT;
1054
1055 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
1056
1057 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
1058 "ip6table_%s", name);
1059 if (t && !IS_ERR(t)) {
1060 struct ip6t_getinfo info;
1061 struct xt_table_info *private = t->private;
1062
1063 info.valid_hooks = t->valid_hooks;
1064 memcpy(info.hook_entry, private->hook_entry,
1065 sizeof(info.hook_entry));
1066 memcpy(info.underflow, private->underflow,
1067 sizeof(info.underflow));
1068 info.num_entries = private->number;
1069 info.size = private->size;
1070 memcpy(info.name, name, sizeof(info.name));
1071
1072 if (copy_to_user(user, &info, *len) != 0)
1073 ret = -EFAULT;
1074 else
1075 ret = 0;
1076
1077 xt_table_unlock(t);
1078 module_put(t->me);
1079 } else
1080 ret = t ? PTR_ERR(t) : -ENOENT;
1081 return ret;
1082}
1083
1da177e4
LT
1084static int
1085get_entries(const struct ip6t_get_entries *entries,
1086 struct ip6t_get_entries __user *uptr)
1087{
1088 int ret;
2e4e6a17 1089 struct xt_table *t;
1da177e4 1090
2e4e6a17 1091 t = xt_find_table_lock(AF_INET6, entries->name);
6b7d31fc 1092 if (t && !IS_ERR(t)) {
2e4e6a17
HW
1093 struct xt_table_info *private = t->private;
1094 duprintf("t->private->number = %u\n", private->number);
1095 if (entries->size == private->size)
1096 ret = copy_entries_to_user(private->size,
1da177e4
LT
1097 t, uptr->entrytable);
1098 else {
1099 duprintf("get_entries: I've got %u not %u!\n",
2e4e6a17 1100 private->size, entries->size);
1da177e4
LT
1101 ret = -EINVAL;
1102 }
6b7d31fc 1103 module_put(t->me);
2e4e6a17 1104 xt_table_unlock(t);
1da177e4 1105 } else
6b7d31fc 1106 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1107
1108 return ret;
1109}
1110
1111static int
1112do_replace(void __user *user, unsigned int len)
1113{
1114 int ret;
1115 struct ip6t_replace tmp;
2e4e6a17
HW
1116 struct xt_table *t;
1117 struct xt_table_info *newinfo, *oldinfo;
1118 struct xt_counters *counters;
31836064 1119 void *loc_cpu_entry, *loc_cpu_old_entry;
1da177e4
LT
1120
1121 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1122 return -EFAULT;
1123
ee4bb818 1124 /* overflow check */
ee4bb818
KK
1125 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1126 return -ENOMEM;
1127
2e4e6a17 1128 newinfo = xt_alloc_table_info(tmp.size);
1da177e4
LT
1129 if (!newinfo)
1130 return -ENOMEM;
1131
31836064
ED
1132 /* choose the copy that is on our node/cpu */
1133 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1134 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1da177e4
LT
1135 tmp.size) != 0) {
1136 ret = -EFAULT;
1137 goto free_newinfo;
1138 }
1139
3b84e92b
PM
1140 counters = vmalloc_node(tmp.num_counters * sizeof(struct xt_counters),
1141 numa_node_id());
1da177e4
LT
1142 if (!counters) {
1143 ret = -ENOMEM;
1144 goto free_newinfo;
1145 }
1da177e4
LT
1146
1147 ret = translate_table(tmp.name, tmp.valid_hooks,
31836064 1148 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1da177e4
LT
1149 tmp.hook_entry, tmp.underflow);
1150 if (ret != 0)
1151 goto free_newinfo_counters;
1152
1153 duprintf("ip_tables: Translated table\n");
1154
2e4e6a17 1155 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
6b7d31fc
HW
1156 "ip6table_%s", tmp.name);
1157 if (!t || IS_ERR(t)) {
1158 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1159 goto free_newinfo_counters_untrans;
6b7d31fc 1160 }
1da177e4
LT
1161
1162 /* You lied! */
1163 if (tmp.valid_hooks != t->valid_hooks) {
1164 duprintf("Valid hook crap: %08X vs %08X\n",
1165 tmp.valid_hooks, t->valid_hooks);
1166 ret = -EINVAL;
6b7d31fc 1167 goto put_module;
1da177e4
LT
1168 }
1169
2e4e6a17 1170 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1da177e4
LT
1171 if (!oldinfo)
1172 goto put_module;
1173
1174 /* Update module usage count based on number of rules */
1175 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1176 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1177 if ((oldinfo->number > oldinfo->initial_entries) ||
1178 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1179 module_put(t->me);
1180 if ((oldinfo->number > oldinfo->initial_entries) &&
1181 (newinfo->number <= oldinfo->initial_entries))
1182 module_put(t->me);
1183
1184 /* Get the old counters. */
1185 get_counters(oldinfo, counters);
1186 /* Decrease module usage counts and free resource */
31836064
ED
1187 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1188 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
2e4e6a17 1189 xt_free_table_info(oldinfo);
1da177e4 1190 if (copy_to_user(tmp.counters, counters,
2e4e6a17 1191 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1da177e4
LT
1192 ret = -EFAULT;
1193 vfree(counters);
2e4e6a17 1194 xt_table_unlock(t);
1da177e4
LT
1195 return ret;
1196
1197 put_module:
1198 module_put(t->me);
2e4e6a17 1199 xt_table_unlock(t);
1da177e4 1200 free_newinfo_counters_untrans:
31836064 1201 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1da177e4
LT
1202 free_newinfo_counters:
1203 vfree(counters);
1204 free_newinfo:
2e4e6a17 1205 xt_free_table_info(newinfo);
1da177e4
LT
1206 return ret;
1207}
1208
1209/* We're lazy, and add to the first CPU; overflow works its fey magic
1210 * and everything is OK. */
1211static inline int
1212add_counter_to_entry(struct ip6t_entry *e,
2e4e6a17 1213 const struct xt_counters addme[],
1da177e4
LT
1214 unsigned int *i)
1215{
1216#if 0
1217 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1218 *i,
1219 (long unsigned int)e->counters.pcnt,
1220 (long unsigned int)e->counters.bcnt,
1221 (long unsigned int)addme[*i].pcnt,
1222 (long unsigned int)addme[*i].bcnt);
1223#endif
1224
1225 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1226
1227 (*i)++;
1228 return 0;
1229}
1230
1231static int
1232do_add_counters(void __user *user, unsigned int len)
1233{
1234 unsigned int i;
2e4e6a17
HW
1235 struct xt_counters_info tmp, *paddc;
1236 struct xt_table_info *private;
1237 struct xt_table *t;
6b7d31fc 1238 int ret = 0;
31836064 1239 void *loc_cpu_entry;
1da177e4
LT
1240
1241 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1242 return -EFAULT;
1243
2e4e6a17 1244 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1da177e4
LT
1245 return -EINVAL;
1246
3b84e92b 1247 paddc = vmalloc_node(len, numa_node_id());
1da177e4
LT
1248 if (!paddc)
1249 return -ENOMEM;
1250
1251 if (copy_from_user(paddc, user, len) != 0) {
1252 ret = -EFAULT;
1253 goto free;
1254 }
1255
2e4e6a17 1256 t = xt_find_table_lock(AF_INET6, tmp.name);
6b7d31fc
HW
1257 if (!t || IS_ERR(t)) {
1258 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1259 goto free;
6b7d31fc 1260 }
1da177e4
LT
1261
1262 write_lock_bh(&t->lock);
2e4e6a17 1263 private = t->private;
2c8ac66b 1264 if (private->number != tmp.num_counters) {
1da177e4
LT
1265 ret = -EINVAL;
1266 goto unlock_up_free;
1267 }
1268
1269 i = 0;
31836064 1270 /* Choose the copy that is on our node */
2e4e6a17 1271 loc_cpu_entry = private->entries[smp_processor_id()];
31836064 1272 IP6T_ENTRY_ITERATE(loc_cpu_entry,
2e4e6a17 1273 private->size,
1da177e4
LT
1274 add_counter_to_entry,
1275 paddc->counters,
1276 &i);
1277 unlock_up_free:
1278 write_unlock_bh(&t->lock);
2e4e6a17 1279 xt_table_unlock(t);
6b7d31fc 1280 module_put(t->me);
1da177e4
LT
1281 free:
1282 vfree(paddc);
1283
1284 return ret;
1285}
1286
1287static int
1288do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1289{
1290 int ret;
1291
1292 if (!capable(CAP_NET_ADMIN))
1293 return -EPERM;
1294
1295 switch (cmd) {
1296 case IP6T_SO_SET_REPLACE:
1297 ret = do_replace(user, len);
1298 break;
1299
1300 case IP6T_SO_SET_ADD_COUNTERS:
1301 ret = do_add_counters(user, len);
1302 break;
1303
1304 default:
1305 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1306 ret = -EINVAL;
1307 }
1308
1309 return ret;
1310}
1311
1312static int
1313do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1314{
1315 int ret;
1316
1317 if (!capable(CAP_NET_ADMIN))
1318 return -EPERM;
1319
1320 switch (cmd) {
433665c9
PM
1321 case IP6T_SO_GET_INFO:
1322 ret = get_info(user, len);
1323 break;
1da177e4
LT
1324
1325 case IP6T_SO_GET_ENTRIES: {
1326 struct ip6t_get_entries get;
1327
1328 if (*len < sizeof(get)) {
1329 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1330 ret = -EINVAL;
1331 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1332 ret = -EFAULT;
1333 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1334 duprintf("get_entries: %u != %u\n", *len,
1335 sizeof(struct ip6t_get_entries) + get.size);
1336 ret = -EINVAL;
1337 } else
1338 ret = get_entries(&get, user);
1339 break;
1340 }
1341
6b7d31fc
HW
1342 case IP6T_SO_GET_REVISION_MATCH:
1343 case IP6T_SO_GET_REVISION_TARGET: {
1344 struct ip6t_get_revision rev;
2e4e6a17 1345 int target;
6b7d31fc
HW
1346
1347 if (*len != sizeof(rev)) {
1348 ret = -EINVAL;
1349 break;
1350 }
1351 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1352 ret = -EFAULT;
1353 break;
1354 }
1355
1356 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 1357 target = 1;
6b7d31fc 1358 else
2e4e6a17 1359 target = 0;
6b7d31fc 1360
2e4e6a17
HW
1361 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1362 rev.revision,
1363 target, &ret),
6b7d31fc
HW
1364 "ip6t_%s", rev.name);
1365 break;
1366 }
1367
1da177e4
LT
1368 default:
1369 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1370 ret = -EINVAL;
1371 }
1372
1373 return ret;
1374}
1375
2e4e6a17 1376int ip6t_register_table(struct xt_table *table,
1da177e4
LT
1377 const struct ip6t_replace *repl)
1378{
1379 int ret;
2e4e6a17 1380 struct xt_table_info *newinfo;
259d4e41 1381 struct xt_table_info bootstrap
1da177e4 1382 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 1383 void *loc_cpu_entry;
1da177e4 1384
2e4e6a17 1385 newinfo = xt_alloc_table_info(repl->size);
1da177e4
LT
1386 if (!newinfo)
1387 return -ENOMEM;
1388
31836064
ED
1389 /* choose the copy on our node/cpu */
1390 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1391 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4
LT
1392
1393 ret = translate_table(table->name, table->valid_hooks,
31836064 1394 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
1395 repl->num_entries,
1396 repl->hook_entry,
1397 repl->underflow);
1398 if (ret != 0) {
2e4e6a17 1399 xt_free_table_info(newinfo);
1da177e4
LT
1400 return ret;
1401 }
1402
da298d3a
PM
1403 ret = xt_register_table(table, &bootstrap, newinfo);
1404 if (ret != 0) {
2e4e6a17 1405 xt_free_table_info(newinfo);
1da177e4
LT
1406 return ret;
1407 }
1408
2e4e6a17 1409 return 0;
1da177e4
LT
1410}
1411
2e4e6a17 1412void ip6t_unregister_table(struct xt_table *table)
1da177e4 1413{
2e4e6a17 1414 struct xt_table_info *private;
31836064
ED
1415 void *loc_cpu_entry;
1416
2e4e6a17 1417 private = xt_unregister_table(table);
1da177e4
LT
1418
1419 /* Decrease module usage counts and free resources */
2e4e6a17
HW
1420 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1421 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1422 xt_free_table_info(private);
1da177e4
LT
1423}
1424
1425/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 1426static inline bool
1da177e4
LT
1427icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1428 u_int8_t type, u_int8_t code,
ccb79bdc 1429 bool invert)
1da177e4
LT
1430{
1431 return (type == test_type && code >= min_code && code <= max_code)
1432 ^ invert;
1433}
1434
1d93a9cb 1435static bool
1da177e4
LT
1436icmp6_match(const struct sk_buff *skb,
1437 const struct net_device *in,
1438 const struct net_device *out,
c4986734 1439 const struct xt_match *match,
1da177e4
LT
1440 const void *matchinfo,
1441 int offset,
1442 unsigned int protoff,
cff533ac 1443 bool *hotdrop)
1da177e4
LT
1444{
1445 struct icmp6hdr _icmp, *ic;
1446 const struct ip6t_icmp *icmpinfo = matchinfo;
1447
1448 /* Must not be a fragment. */
1449 if (offset)
1d93a9cb 1450 return false;
1da177e4
LT
1451
1452 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1453 if (ic == NULL) {
1454 /* We've been asked to examine this packet, and we
1455 can't. Hence, no choice but to drop. */
1456 duprintf("Dropping evil ICMP tinygram.\n");
cff533ac 1457 *hotdrop = true;
1d93a9cb 1458 return false;
1da177e4
LT
1459 }
1460
1461 return icmp6_type_code_match(icmpinfo->type,
1462 icmpinfo->code[0],
1463 icmpinfo->code[1],
1464 ic->icmp6_type, ic->icmp6_code,
1465 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1466}
1467
1468/* Called when user tries to insert an entry of this type. */
ccb79bdc 1469static bool
1da177e4 1470icmp6_checkentry(const char *tablename,
2e4e6a17 1471 const void *entry,
c4986734 1472 const struct xt_match *match,
1da177e4 1473 void *matchinfo,
1da177e4
LT
1474 unsigned int hook_mask)
1475{
1476 const struct ip6t_icmp *icmpinfo = matchinfo;
1477
7f939713
PM
1478 /* Must specify no unknown invflags */
1479 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1da177e4
LT
1480}
1481
1482/* The built-in targets: standard (NULL) and error. */
9f15c530 1483static struct xt_target ip6t_standard_target __read_mostly = {
1da177e4 1484 .name = IP6T_STANDARD_TARGET,
7f939713 1485 .targetsize = sizeof(int),
a45049c5 1486 .family = AF_INET6,
1da177e4
LT
1487};
1488
9f15c530 1489static struct xt_target ip6t_error_target __read_mostly = {
1da177e4
LT
1490 .name = IP6T_ERROR_TARGET,
1491 .target = ip6t_error,
7f939713 1492 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
a45049c5 1493 .family = AF_INET6,
1da177e4
LT
1494};
1495
1496static struct nf_sockopt_ops ip6t_sockopts = {
1497 .pf = PF_INET6,
1498 .set_optmin = IP6T_BASE_CTL,
1499 .set_optmax = IP6T_SO_SET_MAX+1,
1500 .set = do_ip6t_set_ctl,
1501 .get_optmin = IP6T_BASE_CTL,
1502 .get_optmax = IP6T_SO_GET_MAX+1,
1503 .get = do_ip6t_get_ctl,
16fcec35 1504 .owner = THIS_MODULE,
1da177e4
LT
1505};
1506
9f15c530 1507static struct xt_match icmp6_matchstruct __read_mostly = {
1da177e4
LT
1508 .name = "icmp6",
1509 .match = &icmp6_match,
7f939713
PM
1510 .matchsize = sizeof(struct ip6t_icmp),
1511 .checkentry = icmp6_checkentry,
1512 .proto = IPPROTO_ICMPV6,
a45049c5 1513 .family = AF_INET6,
1da177e4
LT
1514};
1515
65b4b4e8 1516static int __init ip6_tables_init(void)
1da177e4
LT
1517{
1518 int ret;
1519
0eff66e6
PM
1520 ret = xt_proto_init(AF_INET6);
1521 if (ret < 0)
1522 goto err1;
2e4e6a17 1523
1da177e4 1524 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
1525 ret = xt_register_target(&ip6t_standard_target);
1526 if (ret < 0)
1527 goto err2;
1528 ret = xt_register_target(&ip6t_error_target);
1529 if (ret < 0)
1530 goto err3;
1531 ret = xt_register_match(&icmp6_matchstruct);
1532 if (ret < 0)
1533 goto err4;
1da177e4
LT
1534
1535 /* Register setsockopt */
1536 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
1537 if (ret < 0)
1538 goto err5;
1da177e4 1539
a887c1c1 1540 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 1541 return 0;
0eff66e6
PM
1542
1543err5:
1544 xt_unregister_match(&icmp6_matchstruct);
1545err4:
1546 xt_unregister_target(&ip6t_error_target);
1547err3:
1548 xt_unregister_target(&ip6t_standard_target);
1549err2:
1550 xt_proto_fini(AF_INET6);
1551err1:
1552 return ret;
1da177e4
LT
1553}
1554
65b4b4e8 1555static void __exit ip6_tables_fini(void)
1da177e4
LT
1556{
1557 nf_unregister_sockopt(&ip6t_sockopts);
a45049c5
PNA
1558 xt_unregister_match(&icmp6_matchstruct);
1559 xt_unregister_target(&ip6t_error_target);
1560 xt_unregister_target(&ip6t_standard_target);
2e4e6a17 1561 xt_proto_fini(AF_INET6);
1da177e4
LT
1562}
1563
e674d0f3 1564/*
b777e0ce
PM
1565 * find the offset to specified header or the protocol number of last header
1566 * if target < 0. "last header" is transport protocol header, ESP, or
1567 * "No next header".
1568 *
1569 * If target header is found, its offset is set in *offset and return protocol
1570 * number. Otherwise, return -1.
1571 *
6d381634
PM
1572 * If the first fragment doesn't contain the final protocol header or
1573 * NEXTHDR_NONE it is considered invalid.
1574 *
b777e0ce
PM
1575 * Note that non-1st fragment is special case that "the protocol number
1576 * of last header" is "next header" field in Fragment header. In this case,
1577 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1578 * isn't NULL.
e674d0f3 1579 *
e674d0f3 1580 */
b777e0ce
PM
1581int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1582 int target, unsigned short *fragoff)
e674d0f3 1583{
6b88dd96 1584 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0660e03f 1585 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
e674d0f3
YK
1586 unsigned int len = skb->len - start;
1587
b777e0ce
PM
1588 if (fragoff)
1589 *fragoff = 0;
1590
e674d0f3
YK
1591 while (nexthdr != target) {
1592 struct ipv6_opt_hdr _hdr, *hp;
1593 unsigned int hdrlen;
1594
b777e0ce
PM
1595 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1596 if (target < 0)
1597 break;
6d381634 1598 return -ENOENT;
b777e0ce
PM
1599 }
1600
e674d0f3
YK
1601 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1602 if (hp == NULL)
6d381634 1603 return -EBADMSG;
e674d0f3 1604 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
1605 unsigned short _frag_off;
1606 __be16 *fp;
e674d0f3
YK
1607 fp = skb_header_pointer(skb,
1608 start+offsetof(struct frag_hdr,
1609 frag_off),
1610 sizeof(_frag_off),
1611 &_frag_off);
1612 if (fp == NULL)
6d381634 1613 return -EBADMSG;
e674d0f3 1614
b777e0ce
PM
1615 _frag_off = ntohs(*fp) & ~0x7;
1616 if (_frag_off) {
1617 if (target < 0 &&
1618 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 1619 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
1620 if (fragoff)
1621 *fragoff = _frag_off;
1622 return hp->nexthdr;
1623 }
6d381634 1624 return -ENOENT;
b777e0ce 1625 }
e674d0f3
YK
1626 hdrlen = 8;
1627 } else if (nexthdr == NEXTHDR_AUTH)
1ab1457c 1628 hdrlen = (hp->hdrlen + 2) << 2;
e674d0f3 1629 else
1ab1457c 1630 hdrlen = ipv6_optlen(hp);
e674d0f3
YK
1631
1632 nexthdr = hp->nexthdr;
1633 len -= hdrlen;
1634 start += hdrlen;
1635 }
1636
1637 *offset = start;
b777e0ce 1638 return nexthdr;
e674d0f3
YK
1639}
1640
1da177e4
LT
1641EXPORT_SYMBOL(ip6t_register_table);
1642EXPORT_SYMBOL(ip6t_unregister_table);
1643EXPORT_SYMBOL(ip6t_do_table);
1da177e4 1644EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 1645EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 1646
65b4b4e8
AM
1647module_init(ip6_tables_init);
1648module_exit(ip6_tables_fini);