[NETFILTER]: ip6_tables: kill a few useless defines/forward declarations
[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
LT
609static inline int
610check_match(struct ip6t_entry_match *m,
611 const char *name,
612 const struct ip6t_ip6 *ipv6,
613 unsigned int hookmask,
614 unsigned int *i)
615{
6709dbbb 616 struct xt_match *match;
3cdc7c95 617 int ret;
1da177e4 618
2e4e6a17 619 match = try_then_request_module(xt_find_match(AF_INET6, m->u.user.name,
1ab1457c 620 m->u.user.revision),
6b7d31fc
HW
621 "ip6t_%s", m->u.user.name);
622 if (IS_ERR(match) || !match) {
1ab1457c 623 duprintf("check_match: `%s' not found\n", m->u.user.name);
6b7d31fc 624 return match ? PTR_ERR(match) : -ENOENT;
1da177e4
LT
625 }
626 m->u.kernel.match = match;
1da177e4 627
3cdc7c95
PM
628 ret = xt_check_match(match, AF_INET6, m->u.match_size - sizeof(*m),
629 name, hookmask, ipv6->proto,
630 ipv6->invflags & IP6T_INV_PROTO);
631 if (ret)
632 goto err;
633
1da177e4 634 if (m->u.kernel.match->checkentry
1c524830 635 && !m->u.kernel.match->checkentry(name, ipv6, match, m->data,
1da177e4 636 hookmask)) {
1da177e4
LT
637 duprintf("ip_tables: check failed for `%s'.\n",
638 m->u.kernel.match->name);
3cdc7c95
PM
639 ret = -EINVAL;
640 goto err;
1da177e4
LT
641 }
642
643 (*i)++;
644 return 0;
3cdc7c95
PM
645err:
646 module_put(m->u.kernel.match->me);
647 return ret;
1da177e4
LT
648}
649
1da177e4
LT
650static inline int
651check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
652 unsigned int *i)
653{
654 struct ip6t_entry_target *t;
6709dbbb 655 struct xt_target *target;
1da177e4
LT
656 int ret;
657 unsigned int j;
658
659 if (!ip6_checkentry(&e->ipv6)) {
660 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
661 return -EINVAL;
662 }
663
590bdf7f
DM
664 if (e->target_offset + sizeof(struct ip6t_entry_target) >
665 e->next_offset)
666 return -EINVAL;
667
1da177e4
LT
668 j = 0;
669 ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
670 if (ret != 0)
671 goto cleanup_matches;
672
673 t = ip6t_get_target(e);
590bdf7f
DM
674 ret = -EINVAL;
675 if (e->target_offset + t->u.target_size > e->next_offset)
676 goto cleanup_matches;
2e4e6a17
HW
677 target = try_then_request_module(xt_find_target(AF_INET6,
678 t->u.user.name,
679 t->u.user.revision),
6b7d31fc
HW
680 "ip6t_%s", t->u.user.name);
681 if (IS_ERR(target) || !target) {
1da177e4 682 duprintf("check_entry: `%s' not found\n", t->u.user.name);
6b7d31fc 683 ret = target ? PTR_ERR(target) : -ENOENT;
1da177e4
LT
684 goto cleanup_matches;
685 }
686 t->u.kernel.target = target;
6b7d31fc 687
3cdc7c95
PM
688 ret = xt_check_target(target, AF_INET6, t->u.target_size - sizeof(*t),
689 name, e->comefrom, e->ipv6.proto,
690 e->ipv6.invflags & IP6T_INV_PROTO);
691 if (ret)
692 goto err;
693
74c9c0c1 694 if (t->u.kernel.target->checkentry
1c524830 695 && !t->u.kernel.target->checkentry(name, e, target, t->data,
1da177e4 696 e->comefrom)) {
1da177e4
LT
697 duprintf("ip_tables: check failed for `%s'.\n",
698 t->u.kernel.target->name);
699 ret = -EINVAL;
3cdc7c95 700 goto err;
1da177e4
LT
701 }
702
703 (*i)++;
704 return 0;
3cdc7c95
PM
705 err:
706 module_put(t->u.kernel.target->me);
1da177e4
LT
707 cleanup_matches:
708 IP6T_MATCH_ITERATE(e, cleanup_match, &j);
709 return ret;
710}
711
712static inline int
713check_entry_size_and_hooks(struct ip6t_entry *e,
2e4e6a17 714 struct xt_table_info *newinfo,
1da177e4
LT
715 unsigned char *base,
716 unsigned char *limit,
717 const unsigned int *hook_entries,
718 const unsigned int *underflows,
719 unsigned int *i)
720{
721 unsigned int h;
722
723 if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0
724 || (unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
725 duprintf("Bad offset %p\n", e);
726 return -EINVAL;
727 }
728
729 if (e->next_offset
730 < sizeof(struct ip6t_entry) + sizeof(struct ip6t_entry_target)) {
731 duprintf("checking: element %p size %u\n",
732 e, e->next_offset);
733 return -EINVAL;
734 }
735
736 /* Check hooks & underflows */
6e23ae2a 737 for (h = 0; h < NF_INET_NUMHOOKS; h++) {
1da177e4
LT
738 if ((unsigned char *)e - base == hook_entries[h])
739 newinfo->hook_entry[h] = hook_entries[h];
740 if ((unsigned char *)e - base == underflows[h])
741 newinfo->underflow[h] = underflows[h];
742 }
743
744 /* FIXME: underflows must be unconditional, standard verdicts
1ab1457c 745 < 0 (not IP6T_RETURN). --RR */
1da177e4
LT
746
747 /* Clear counters and comefrom */
2e4e6a17 748 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4
LT
749 e->comefrom = 0;
750
751 (*i)++;
752 return 0;
753}
754
755static inline int
756cleanup_entry(struct ip6t_entry *e, unsigned int *i)
757{
758 struct ip6t_entry_target *t;
759
760 if (i && (*i)-- == 0)
761 return 1;
762
763 /* Cleanup all matches */
764 IP6T_MATCH_ITERATE(e, cleanup_match, NULL);
765 t = ip6t_get_target(e);
766 if (t->u.kernel.target->destroy)
efa74165 767 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
1da177e4
LT
768 module_put(t->u.kernel.target->me);
769 return 0;
770}
771
772/* Checks and translates the user-supplied table segment (held in
773 newinfo) */
774static int
775translate_table(const char *name,
776 unsigned int valid_hooks,
2e4e6a17 777 struct xt_table_info *newinfo,
31836064 778 void *entry0,
1da177e4
LT
779 unsigned int size,
780 unsigned int number,
781 const unsigned int *hook_entries,
782 const unsigned int *underflows)
783{
784 unsigned int i;
785 int ret;
786
787 newinfo->size = size;
788 newinfo->number = number;
789
790 /* Init all hooks to impossible value. */
6e23ae2a 791 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
792 newinfo->hook_entry[i] = 0xFFFFFFFF;
793 newinfo->underflow[i] = 0xFFFFFFFF;
794 }
795
796 duprintf("translate_table: size %u\n", newinfo->size);
797 i = 0;
798 /* Walk through entries, checking offsets. */
31836064 799 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
800 check_entry_size_and_hooks,
801 newinfo,
31836064
ED
802 entry0,
803 entry0 + size,
1da177e4
LT
804 hook_entries, underflows, &i);
805 if (ret != 0)
806 return ret;
807
808 if (i != number) {
809 duprintf("translate_table: %u not %u entries\n",
810 i, number);
811 return -EINVAL;
812 }
813
814 /* Check hooks all assigned */
6e23ae2a 815 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
1da177e4
LT
816 /* Only hooks which are valid */
817 if (!(valid_hooks & (1 << i)))
818 continue;
819 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
820 duprintf("Invalid hook entry %u %u\n",
821 i, hook_entries[i]);
822 return -EINVAL;
823 }
824 if (newinfo->underflow[i] == 0xFFFFFFFF) {
825 duprintf("Invalid underflow %u %u\n",
826 i, underflows[i]);
827 return -EINVAL;
828 }
829 }
830
74c9c0c1
DM
831 if (!mark_source_chains(newinfo, valid_hooks, entry0))
832 return -ELOOP;
833
1da177e4
LT
834 /* Finally, each sanity check must pass */
835 i = 0;
31836064 836 ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
837 check_entry, name, size, &i);
838
74c9c0c1
DM
839 if (ret != 0) {
840 IP6T_ENTRY_ITERATE(entry0, newinfo->size,
841 cleanup_entry, &i);
842 return ret;
843 }
1da177e4
LT
844
845 /* And one copy for every other CPU */
6f912042 846 for_each_possible_cpu(i) {
31836064
ED
847 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
848 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
849 }
850
590bdf7f 851 return 0;
1da177e4
LT
852}
853
1da177e4
LT
854/* Gets counters. */
855static inline int
856add_entry_to_counter(const struct ip6t_entry *e,
2e4e6a17 857 struct xt_counters total[],
1da177e4
LT
858 unsigned int *i)
859{
860 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
861
862 (*i)++;
863 return 0;
864}
865
31836064
ED
866static inline int
867set_entry_to_counter(const struct ip6t_entry *e,
868 struct ip6t_counters total[],
869 unsigned int *i)
870{
871 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
872
873 (*i)++;
874 return 0;
875}
876
1da177e4 877static void
2e4e6a17
HW
878get_counters(const struct xt_table_info *t,
879 struct xt_counters counters[])
1da177e4
LT
880{
881 unsigned int cpu;
882 unsigned int i;
31836064
ED
883 unsigned int curcpu;
884
885 /* Instead of clearing (by a previous call to memset())
886 * the counters and using adds, we set the counters
887 * with data used by 'current' CPU
888 * We dont care about preemption here.
889 */
890 curcpu = raw_smp_processor_id();
891
892 i = 0;
893 IP6T_ENTRY_ITERATE(t->entries[curcpu],
894 t->size,
895 set_entry_to_counter,
896 counters,
897 &i);
1da177e4 898
6f912042 899 for_each_possible_cpu(cpu) {
31836064
ED
900 if (cpu == curcpu)
901 continue;
1da177e4 902 i = 0;
31836064 903 IP6T_ENTRY_ITERATE(t->entries[cpu],
1da177e4
LT
904 t->size,
905 add_entry_to_counter,
906 counters,
907 &i);
908 }
909}
910
911static int
912copy_entries_to_user(unsigned int total_size,
2e4e6a17 913 struct xt_table *table,
1da177e4
LT
914 void __user *userptr)
915{
916 unsigned int off, num, countersize;
917 struct ip6t_entry *e;
2e4e6a17
HW
918 struct xt_counters *counters;
919 struct xt_table_info *private = table->private;
1da177e4 920 int ret = 0;
31836064 921 void *loc_cpu_entry;
1da177e4
LT
922
923 /* We need atomic snapshot of counters: rest doesn't change
924 (other than comefrom, which userspace doesn't care
925 about). */
2e4e6a17 926 countersize = sizeof(struct xt_counters) * private->number;
1da177e4
LT
927 counters = vmalloc(countersize);
928
929 if (counters == NULL)
930 return -ENOMEM;
931
932 /* First, sum counters... */
1da177e4 933 write_lock_bh(&table->lock);
2e4e6a17 934 get_counters(private, counters);
1da177e4
LT
935 write_unlock_bh(&table->lock);
936
31836064 937 /* choose the copy that is on ourc node/cpu */
2e4e6a17 938 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 939 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
940 ret = -EFAULT;
941 goto free_counters;
942 }
943
944 /* FIXME: use iterator macros --RR */
945 /* ... then go back and fix counters and names */
946 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
947 unsigned int i;
948 struct ip6t_entry_match *m;
949 struct ip6t_entry_target *t;
950
31836064 951 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
952 if (copy_to_user(userptr + off
953 + offsetof(struct ip6t_entry, counters),
954 &counters[num],
955 sizeof(counters[num])) != 0) {
956 ret = -EFAULT;
957 goto free_counters;
958 }
959
960 for (i = sizeof(struct ip6t_entry);
961 i < e->target_offset;
962 i += m->u.match_size) {
963 m = (void *)e + i;
964
965 if (copy_to_user(userptr + off + i
966 + offsetof(struct ip6t_entry_match,
967 u.user.name),
968 m->u.kernel.match->name,
969 strlen(m->u.kernel.match->name)+1)
970 != 0) {
971 ret = -EFAULT;
972 goto free_counters;
973 }
974 }
975
976 t = ip6t_get_target(e);
977 if (copy_to_user(userptr + off + e->target_offset
978 + offsetof(struct ip6t_entry_target,
979 u.user.name),
980 t->u.kernel.target->name,
981 strlen(t->u.kernel.target->name)+1) != 0) {
982 ret = -EFAULT;
983 goto free_counters;
984 }
985 }
986
987 free_counters:
988 vfree(counters);
989 return ret;
990}
991
992static int
993get_entries(const struct ip6t_get_entries *entries,
994 struct ip6t_get_entries __user *uptr)
995{
996 int ret;
2e4e6a17 997 struct xt_table *t;
1da177e4 998
2e4e6a17 999 t = xt_find_table_lock(AF_INET6, entries->name);
6b7d31fc 1000 if (t && !IS_ERR(t)) {
2e4e6a17
HW
1001 struct xt_table_info *private = t->private;
1002 duprintf("t->private->number = %u\n", private->number);
1003 if (entries->size == private->size)
1004 ret = copy_entries_to_user(private->size,
1da177e4
LT
1005 t, uptr->entrytable);
1006 else {
1007 duprintf("get_entries: I've got %u not %u!\n",
2e4e6a17 1008 private->size, entries->size);
1da177e4
LT
1009 ret = -EINVAL;
1010 }
6b7d31fc 1011 module_put(t->me);
2e4e6a17 1012 xt_table_unlock(t);
1da177e4 1013 } else
6b7d31fc 1014 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1015
1016 return ret;
1017}
1018
1019static int
1020do_replace(void __user *user, unsigned int len)
1021{
1022 int ret;
1023 struct ip6t_replace tmp;
2e4e6a17
HW
1024 struct xt_table *t;
1025 struct xt_table_info *newinfo, *oldinfo;
1026 struct xt_counters *counters;
31836064 1027 void *loc_cpu_entry, *loc_cpu_old_entry;
1da177e4
LT
1028
1029 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1030 return -EFAULT;
1031
ee4bb818 1032 /* overflow check */
ee4bb818
KK
1033 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1034 return -ENOMEM;
1035
2e4e6a17 1036 newinfo = xt_alloc_table_info(tmp.size);
1da177e4
LT
1037 if (!newinfo)
1038 return -ENOMEM;
1039
31836064
ED
1040 /* choose the copy that is on our node/cpu */
1041 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1042 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1da177e4
LT
1043 tmp.size) != 0) {
1044 ret = -EFAULT;
1045 goto free_newinfo;
1046 }
1047
2e4e6a17 1048 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
1da177e4
LT
1049 if (!counters) {
1050 ret = -ENOMEM;
1051 goto free_newinfo;
1052 }
1da177e4
LT
1053
1054 ret = translate_table(tmp.name, tmp.valid_hooks,
31836064 1055 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1da177e4
LT
1056 tmp.hook_entry, tmp.underflow);
1057 if (ret != 0)
1058 goto free_newinfo_counters;
1059
1060 duprintf("ip_tables: Translated table\n");
1061
2e4e6a17 1062 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
6b7d31fc
HW
1063 "ip6table_%s", tmp.name);
1064 if (!t || IS_ERR(t)) {
1065 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1066 goto free_newinfo_counters_untrans;
6b7d31fc 1067 }
1da177e4
LT
1068
1069 /* You lied! */
1070 if (tmp.valid_hooks != t->valid_hooks) {
1071 duprintf("Valid hook crap: %08X vs %08X\n",
1072 tmp.valid_hooks, t->valid_hooks);
1073 ret = -EINVAL;
6b7d31fc 1074 goto put_module;
1da177e4
LT
1075 }
1076
2e4e6a17 1077 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1da177e4
LT
1078 if (!oldinfo)
1079 goto put_module;
1080
1081 /* Update module usage count based on number of rules */
1082 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1083 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1084 if ((oldinfo->number > oldinfo->initial_entries) ||
1085 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1086 module_put(t->me);
1087 if ((oldinfo->number > oldinfo->initial_entries) &&
1088 (newinfo->number <= oldinfo->initial_entries))
1089 module_put(t->me);
1090
1091 /* Get the old counters. */
1092 get_counters(oldinfo, counters);
1093 /* Decrease module usage counts and free resource */
31836064
ED
1094 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1095 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
2e4e6a17 1096 xt_free_table_info(oldinfo);
1da177e4 1097 if (copy_to_user(tmp.counters, counters,
2e4e6a17 1098 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1da177e4
LT
1099 ret = -EFAULT;
1100 vfree(counters);
2e4e6a17 1101 xt_table_unlock(t);
1da177e4
LT
1102 return ret;
1103
1104 put_module:
1105 module_put(t->me);
2e4e6a17 1106 xt_table_unlock(t);
1da177e4 1107 free_newinfo_counters_untrans:
31836064 1108 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1da177e4
LT
1109 free_newinfo_counters:
1110 vfree(counters);
1111 free_newinfo:
2e4e6a17 1112 xt_free_table_info(newinfo);
1da177e4
LT
1113 return ret;
1114}
1115
1116/* We're lazy, and add to the first CPU; overflow works its fey magic
1117 * and everything is OK. */
1118static inline int
1119add_counter_to_entry(struct ip6t_entry *e,
2e4e6a17 1120 const struct xt_counters addme[],
1da177e4
LT
1121 unsigned int *i)
1122{
1123#if 0
1124 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1125 *i,
1126 (long unsigned int)e->counters.pcnt,
1127 (long unsigned int)e->counters.bcnt,
1128 (long unsigned int)addme[*i].pcnt,
1129 (long unsigned int)addme[*i].bcnt);
1130#endif
1131
1132 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1133
1134 (*i)++;
1135 return 0;
1136}
1137
1138static int
1139do_add_counters(void __user *user, unsigned int len)
1140{
1141 unsigned int i;
2e4e6a17
HW
1142 struct xt_counters_info tmp, *paddc;
1143 struct xt_table_info *private;
1144 struct xt_table *t;
6b7d31fc 1145 int ret = 0;
31836064 1146 void *loc_cpu_entry;
1da177e4
LT
1147
1148 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1149 return -EFAULT;
1150
2e4e6a17 1151 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1da177e4
LT
1152 return -EINVAL;
1153
1154 paddc = vmalloc(len);
1155 if (!paddc)
1156 return -ENOMEM;
1157
1158 if (copy_from_user(paddc, user, len) != 0) {
1159 ret = -EFAULT;
1160 goto free;
1161 }
1162
2e4e6a17 1163 t = xt_find_table_lock(AF_INET6, tmp.name);
6b7d31fc
HW
1164 if (!t || IS_ERR(t)) {
1165 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1166 goto free;
6b7d31fc 1167 }
1da177e4
LT
1168
1169 write_lock_bh(&t->lock);
2e4e6a17 1170 private = t->private;
2c8ac66b 1171 if (private->number != tmp.num_counters) {
1da177e4
LT
1172 ret = -EINVAL;
1173 goto unlock_up_free;
1174 }
1175
1176 i = 0;
31836064 1177 /* Choose the copy that is on our node */
2e4e6a17 1178 loc_cpu_entry = private->entries[smp_processor_id()];
31836064 1179 IP6T_ENTRY_ITERATE(loc_cpu_entry,
2e4e6a17 1180 private->size,
1da177e4
LT
1181 add_counter_to_entry,
1182 paddc->counters,
1183 &i);
1184 unlock_up_free:
1185 write_unlock_bh(&t->lock);
2e4e6a17 1186 xt_table_unlock(t);
6b7d31fc 1187 module_put(t->me);
1da177e4
LT
1188 free:
1189 vfree(paddc);
1190
1191 return ret;
1192}
1193
1194static int
1195do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1196{
1197 int ret;
1198
1199 if (!capable(CAP_NET_ADMIN))
1200 return -EPERM;
1201
1202 switch (cmd) {
1203 case IP6T_SO_SET_REPLACE:
1204 ret = do_replace(user, len);
1205 break;
1206
1207 case IP6T_SO_SET_ADD_COUNTERS:
1208 ret = do_add_counters(user, len);
1209 break;
1210
1211 default:
1212 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1213 ret = -EINVAL;
1214 }
1215
1216 return ret;
1217}
1218
1219static int
1220do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1221{
1222 int ret;
1223
1224 if (!capable(CAP_NET_ADMIN))
1225 return -EPERM;
1226
1227 switch (cmd) {
1228 case IP6T_SO_GET_INFO: {
1229 char name[IP6T_TABLE_MAXNAMELEN];
2e4e6a17 1230 struct xt_table *t;
1da177e4
LT
1231
1232 if (*len != sizeof(struct ip6t_getinfo)) {
1233 duprintf("length %u != %u\n", *len,
1234 sizeof(struct ip6t_getinfo));
1235 ret = -EINVAL;
1236 break;
1237 }
1238
1239 if (copy_from_user(name, user, sizeof(name)) != 0) {
1240 ret = -EFAULT;
1241 break;
1242 }
1243 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
6b7d31fc 1244
2e4e6a17 1245 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
6b7d31fc
HW
1246 "ip6table_%s", name);
1247 if (t && !IS_ERR(t)) {
1da177e4 1248 struct ip6t_getinfo info;
2e4e6a17 1249 struct xt_table_info *private = t->private;
1da177e4
LT
1250
1251 info.valid_hooks = t->valid_hooks;
2e4e6a17 1252 memcpy(info.hook_entry, private->hook_entry,
1da177e4 1253 sizeof(info.hook_entry));
2e4e6a17 1254 memcpy(info.underflow, private->underflow,
1da177e4 1255 sizeof(info.underflow));
2e4e6a17
HW
1256 info.num_entries = private->number;
1257 info.size = private->size;
1da177e4
LT
1258 memcpy(info.name, name, sizeof(info.name));
1259
1260 if (copy_to_user(user, &info, *len) != 0)
1261 ret = -EFAULT;
1262 else
1263 ret = 0;
2e4e6a17 1264 xt_table_unlock(t);
6b7d31fc
HW
1265 module_put(t->me);
1266 } else
1267 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1268 }
1269 break;
1270
1271 case IP6T_SO_GET_ENTRIES: {
1272 struct ip6t_get_entries get;
1273
1274 if (*len < sizeof(get)) {
1275 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1276 ret = -EINVAL;
1277 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1278 ret = -EFAULT;
1279 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1280 duprintf("get_entries: %u != %u\n", *len,
1281 sizeof(struct ip6t_get_entries) + get.size);
1282 ret = -EINVAL;
1283 } else
1284 ret = get_entries(&get, user);
1285 break;
1286 }
1287
6b7d31fc
HW
1288 case IP6T_SO_GET_REVISION_MATCH:
1289 case IP6T_SO_GET_REVISION_TARGET: {
1290 struct ip6t_get_revision rev;
2e4e6a17 1291 int target;
6b7d31fc
HW
1292
1293 if (*len != sizeof(rev)) {
1294 ret = -EINVAL;
1295 break;
1296 }
1297 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1298 ret = -EFAULT;
1299 break;
1300 }
1301
1302 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 1303 target = 1;
6b7d31fc 1304 else
2e4e6a17 1305 target = 0;
6b7d31fc 1306
2e4e6a17
HW
1307 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1308 rev.revision,
1309 target, &ret),
6b7d31fc
HW
1310 "ip6t_%s", rev.name);
1311 break;
1312 }
1313
1da177e4
LT
1314 default:
1315 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1316 ret = -EINVAL;
1317 }
1318
1319 return ret;
1320}
1321
2e4e6a17 1322int ip6t_register_table(struct xt_table *table,
1da177e4
LT
1323 const struct ip6t_replace *repl)
1324{
1325 int ret;
2e4e6a17 1326 struct xt_table_info *newinfo;
259d4e41 1327 struct xt_table_info bootstrap
1da177e4 1328 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 1329 void *loc_cpu_entry;
1da177e4 1330
2e4e6a17 1331 newinfo = xt_alloc_table_info(repl->size);
1da177e4
LT
1332 if (!newinfo)
1333 return -ENOMEM;
1334
31836064
ED
1335 /* choose the copy on our node/cpu */
1336 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1337 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4
LT
1338
1339 ret = translate_table(table->name, table->valid_hooks,
31836064 1340 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
1341 repl->num_entries,
1342 repl->hook_entry,
1343 repl->underflow);
1344 if (ret != 0) {
2e4e6a17 1345 xt_free_table_info(newinfo);
1da177e4
LT
1346 return ret;
1347 }
1348
da298d3a
PM
1349 ret = xt_register_table(table, &bootstrap, newinfo);
1350 if (ret != 0) {
2e4e6a17 1351 xt_free_table_info(newinfo);
1da177e4
LT
1352 return ret;
1353 }
1354
2e4e6a17 1355 return 0;
1da177e4
LT
1356}
1357
2e4e6a17 1358void ip6t_unregister_table(struct xt_table *table)
1da177e4 1359{
2e4e6a17 1360 struct xt_table_info *private;
31836064
ED
1361 void *loc_cpu_entry;
1362
2e4e6a17 1363 private = xt_unregister_table(table);
1da177e4
LT
1364
1365 /* Decrease module usage counts and free resources */
2e4e6a17
HW
1366 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1367 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1368 xt_free_table_info(private);
1da177e4
LT
1369}
1370
1371/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 1372static inline bool
1da177e4
LT
1373icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1374 u_int8_t type, u_int8_t code,
ccb79bdc 1375 bool invert)
1da177e4
LT
1376{
1377 return (type == test_type && code >= min_code && code <= max_code)
1378 ^ invert;
1379}
1380
1d93a9cb 1381static bool
1da177e4
LT
1382icmp6_match(const struct sk_buff *skb,
1383 const struct net_device *in,
1384 const struct net_device *out,
c4986734 1385 const struct xt_match *match,
1da177e4
LT
1386 const void *matchinfo,
1387 int offset,
1388 unsigned int protoff,
cff533ac 1389 bool *hotdrop)
1da177e4
LT
1390{
1391 struct icmp6hdr _icmp, *ic;
1392 const struct ip6t_icmp *icmpinfo = matchinfo;
1393
1394 /* Must not be a fragment. */
1395 if (offset)
1d93a9cb 1396 return false;
1da177e4
LT
1397
1398 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1399 if (ic == NULL) {
1400 /* We've been asked to examine this packet, and we
1401 can't. Hence, no choice but to drop. */
1402 duprintf("Dropping evil ICMP tinygram.\n");
cff533ac 1403 *hotdrop = true;
1d93a9cb 1404 return false;
1da177e4
LT
1405 }
1406
1407 return icmp6_type_code_match(icmpinfo->type,
1408 icmpinfo->code[0],
1409 icmpinfo->code[1],
1410 ic->icmp6_type, ic->icmp6_code,
1411 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1412}
1413
1414/* Called when user tries to insert an entry of this type. */
ccb79bdc 1415static bool
1da177e4 1416icmp6_checkentry(const char *tablename,
2e4e6a17 1417 const void *entry,
c4986734 1418 const struct xt_match *match,
1da177e4 1419 void *matchinfo,
1da177e4
LT
1420 unsigned int hook_mask)
1421{
1422 const struct ip6t_icmp *icmpinfo = matchinfo;
1423
7f939713
PM
1424 /* Must specify no unknown invflags */
1425 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1da177e4
LT
1426}
1427
1428/* The built-in targets: standard (NULL) and error. */
9f15c530 1429static struct xt_target ip6t_standard_target __read_mostly = {
1da177e4 1430 .name = IP6T_STANDARD_TARGET,
7f939713 1431 .targetsize = sizeof(int),
a45049c5 1432 .family = AF_INET6,
1da177e4
LT
1433};
1434
9f15c530 1435static struct xt_target ip6t_error_target __read_mostly = {
1da177e4
LT
1436 .name = IP6T_ERROR_TARGET,
1437 .target = ip6t_error,
7f939713 1438 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
a45049c5 1439 .family = AF_INET6,
1da177e4
LT
1440};
1441
1442static struct nf_sockopt_ops ip6t_sockopts = {
1443 .pf = PF_INET6,
1444 .set_optmin = IP6T_BASE_CTL,
1445 .set_optmax = IP6T_SO_SET_MAX+1,
1446 .set = do_ip6t_set_ctl,
1447 .get_optmin = IP6T_BASE_CTL,
1448 .get_optmax = IP6T_SO_GET_MAX+1,
1449 .get = do_ip6t_get_ctl,
16fcec35 1450 .owner = THIS_MODULE,
1da177e4
LT
1451};
1452
9f15c530 1453static struct xt_match icmp6_matchstruct __read_mostly = {
1da177e4
LT
1454 .name = "icmp6",
1455 .match = &icmp6_match,
7f939713
PM
1456 .matchsize = sizeof(struct ip6t_icmp),
1457 .checkentry = icmp6_checkentry,
1458 .proto = IPPROTO_ICMPV6,
a45049c5 1459 .family = AF_INET6,
1da177e4
LT
1460};
1461
65b4b4e8 1462static int __init ip6_tables_init(void)
1da177e4
LT
1463{
1464 int ret;
1465
0eff66e6
PM
1466 ret = xt_proto_init(AF_INET6);
1467 if (ret < 0)
1468 goto err1;
2e4e6a17 1469
1da177e4 1470 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
1471 ret = xt_register_target(&ip6t_standard_target);
1472 if (ret < 0)
1473 goto err2;
1474 ret = xt_register_target(&ip6t_error_target);
1475 if (ret < 0)
1476 goto err3;
1477 ret = xt_register_match(&icmp6_matchstruct);
1478 if (ret < 0)
1479 goto err4;
1da177e4
LT
1480
1481 /* Register setsockopt */
1482 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
1483 if (ret < 0)
1484 goto err5;
1da177e4 1485
a887c1c1 1486 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 1487 return 0;
0eff66e6
PM
1488
1489err5:
1490 xt_unregister_match(&icmp6_matchstruct);
1491err4:
1492 xt_unregister_target(&ip6t_error_target);
1493err3:
1494 xt_unregister_target(&ip6t_standard_target);
1495err2:
1496 xt_proto_fini(AF_INET6);
1497err1:
1498 return ret;
1da177e4
LT
1499}
1500
65b4b4e8 1501static void __exit ip6_tables_fini(void)
1da177e4
LT
1502{
1503 nf_unregister_sockopt(&ip6t_sockopts);
a45049c5
PNA
1504 xt_unregister_match(&icmp6_matchstruct);
1505 xt_unregister_target(&ip6t_error_target);
1506 xt_unregister_target(&ip6t_standard_target);
2e4e6a17 1507 xt_proto_fini(AF_INET6);
1da177e4
LT
1508}
1509
e674d0f3 1510/*
b777e0ce
PM
1511 * find the offset to specified header or the protocol number of last header
1512 * if target < 0. "last header" is transport protocol header, ESP, or
1513 * "No next header".
1514 *
1515 * If target header is found, its offset is set in *offset and return protocol
1516 * number. Otherwise, return -1.
1517 *
6d381634
PM
1518 * If the first fragment doesn't contain the final protocol header or
1519 * NEXTHDR_NONE it is considered invalid.
1520 *
b777e0ce
PM
1521 * Note that non-1st fragment is special case that "the protocol number
1522 * of last header" is "next header" field in Fragment header. In this case,
1523 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1524 * isn't NULL.
e674d0f3 1525 *
e674d0f3 1526 */
b777e0ce
PM
1527int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1528 int target, unsigned short *fragoff)
e674d0f3 1529{
6b88dd96 1530 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0660e03f 1531 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
e674d0f3
YK
1532 unsigned int len = skb->len - start;
1533
b777e0ce
PM
1534 if (fragoff)
1535 *fragoff = 0;
1536
e674d0f3
YK
1537 while (nexthdr != target) {
1538 struct ipv6_opt_hdr _hdr, *hp;
1539 unsigned int hdrlen;
1540
b777e0ce
PM
1541 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1542 if (target < 0)
1543 break;
6d381634 1544 return -ENOENT;
b777e0ce
PM
1545 }
1546
e674d0f3
YK
1547 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1548 if (hp == NULL)
6d381634 1549 return -EBADMSG;
e674d0f3 1550 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
1551 unsigned short _frag_off;
1552 __be16 *fp;
e674d0f3
YK
1553 fp = skb_header_pointer(skb,
1554 start+offsetof(struct frag_hdr,
1555 frag_off),
1556 sizeof(_frag_off),
1557 &_frag_off);
1558 if (fp == NULL)
6d381634 1559 return -EBADMSG;
e674d0f3 1560
b777e0ce
PM
1561 _frag_off = ntohs(*fp) & ~0x7;
1562 if (_frag_off) {
1563 if (target < 0 &&
1564 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 1565 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
1566 if (fragoff)
1567 *fragoff = _frag_off;
1568 return hp->nexthdr;
1569 }
6d381634 1570 return -ENOENT;
b777e0ce 1571 }
e674d0f3
YK
1572 hdrlen = 8;
1573 } else if (nexthdr == NEXTHDR_AUTH)
1ab1457c 1574 hdrlen = (hp->hdrlen + 2) << 2;
e674d0f3 1575 else
1ab1457c 1576 hdrlen = ipv6_optlen(hp);
e674d0f3
YK
1577
1578 nexthdr = hp->nexthdr;
1579 len -= hdrlen;
1580 start += hdrlen;
1581 }
1582
1583 *offset = start;
b777e0ce 1584 return nexthdr;
e674d0f3
YK
1585}
1586
1da177e4
LT
1587EXPORT_SYMBOL(ip6t_register_table);
1588EXPORT_SYMBOL(ip6t_unregister_table);
1589EXPORT_SYMBOL(ip6t_do_table);
1da177e4 1590EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 1591EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 1592
65b4b4e8
AM
1593module_init(ip6_tables_init);
1594module_exit(ip6_tables_fini);