[NETFILTER]: ip6_tables: use vmalloc_node()
[deliverable/linux.git] / net / ipv6 / netfilter / ip6_tables.c
CommitLineData
1da177e4
LT
1/*
2 * Packet matching code.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
6b7d31fc 5 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
1da177e4
LT
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
1da177e4 10 */
4fc268d2
RD
11
12#include <linux/capability.h>
14c85021 13#include <linux/in.h>
1da177e4
LT
14#include <linux/skbuff.h>
15#include <linux/kmod.h>
16#include <linux/vmalloc.h>
17#include <linux/netdevice.h>
18#include <linux/module.h>
4bdbf6c0 19#include <linux/poison.h>
1da177e4 20#include <linux/icmpv6.h>
1da177e4
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
946static int
947copy_entries_to_user(unsigned int total_size,
2e4e6a17 948 struct xt_table *table,
1da177e4
LT
949 void __user *userptr)
950{
951 unsigned int off, num, countersize;
952 struct ip6t_entry *e;
2e4e6a17
HW
953 struct xt_counters *counters;
954 struct xt_table_info *private = table->private;
1da177e4 955 int ret = 0;
31836064 956 void *loc_cpu_entry;
1da177e4
LT
957
958 /* We need atomic snapshot of counters: rest doesn't change
959 (other than comefrom, which userspace doesn't care
960 about). */
2e4e6a17 961 countersize = sizeof(struct xt_counters) * private->number;
3b84e92b 962 counters = vmalloc_node(countersize, numa_node_id());
1da177e4
LT
963
964 if (counters == NULL)
965 return -ENOMEM;
966
967 /* First, sum counters... */
1da177e4 968 write_lock_bh(&table->lock);
2e4e6a17 969 get_counters(private, counters);
1da177e4
LT
970 write_unlock_bh(&table->lock);
971
31836064 972 /* choose the copy that is on ourc node/cpu */
2e4e6a17 973 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064 974 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
975 ret = -EFAULT;
976 goto free_counters;
977 }
978
979 /* FIXME: use iterator macros --RR */
980 /* ... then go back and fix counters and names */
981 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
982 unsigned int i;
983 struct ip6t_entry_match *m;
984 struct ip6t_entry_target *t;
985
31836064 986 e = (struct ip6t_entry *)(loc_cpu_entry + off);
1da177e4
LT
987 if (copy_to_user(userptr + off
988 + offsetof(struct ip6t_entry, counters),
989 &counters[num],
990 sizeof(counters[num])) != 0) {
991 ret = -EFAULT;
992 goto free_counters;
993 }
994
995 for (i = sizeof(struct ip6t_entry);
996 i < e->target_offset;
997 i += m->u.match_size) {
998 m = (void *)e + i;
999
1000 if (copy_to_user(userptr + off + i
1001 + offsetof(struct ip6t_entry_match,
1002 u.user.name),
1003 m->u.kernel.match->name,
1004 strlen(m->u.kernel.match->name)+1)
1005 != 0) {
1006 ret = -EFAULT;
1007 goto free_counters;
1008 }
1009 }
1010
1011 t = ip6t_get_target(e);
1012 if (copy_to_user(userptr + off + e->target_offset
1013 + offsetof(struct ip6t_entry_target,
1014 u.user.name),
1015 t->u.kernel.target->name,
1016 strlen(t->u.kernel.target->name)+1) != 0) {
1017 ret = -EFAULT;
1018 goto free_counters;
1019 }
1020 }
1021
1022 free_counters:
1023 vfree(counters);
1024 return ret;
1025}
1026
1027static int
1028get_entries(const struct ip6t_get_entries *entries,
1029 struct ip6t_get_entries __user *uptr)
1030{
1031 int ret;
2e4e6a17 1032 struct xt_table *t;
1da177e4 1033
2e4e6a17 1034 t = xt_find_table_lock(AF_INET6, entries->name);
6b7d31fc 1035 if (t && !IS_ERR(t)) {
2e4e6a17
HW
1036 struct xt_table_info *private = t->private;
1037 duprintf("t->private->number = %u\n", private->number);
1038 if (entries->size == private->size)
1039 ret = copy_entries_to_user(private->size,
1da177e4
LT
1040 t, uptr->entrytable);
1041 else {
1042 duprintf("get_entries: I've got %u not %u!\n",
2e4e6a17 1043 private->size, entries->size);
1da177e4
LT
1044 ret = -EINVAL;
1045 }
6b7d31fc 1046 module_put(t->me);
2e4e6a17 1047 xt_table_unlock(t);
1da177e4 1048 } else
6b7d31fc 1049 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1050
1051 return ret;
1052}
1053
1054static int
1055do_replace(void __user *user, unsigned int len)
1056{
1057 int ret;
1058 struct ip6t_replace tmp;
2e4e6a17
HW
1059 struct xt_table *t;
1060 struct xt_table_info *newinfo, *oldinfo;
1061 struct xt_counters *counters;
31836064 1062 void *loc_cpu_entry, *loc_cpu_old_entry;
1da177e4
LT
1063
1064 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1065 return -EFAULT;
1066
ee4bb818 1067 /* overflow check */
ee4bb818
KK
1068 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1069 return -ENOMEM;
1070
2e4e6a17 1071 newinfo = xt_alloc_table_info(tmp.size);
1da177e4
LT
1072 if (!newinfo)
1073 return -ENOMEM;
1074
31836064
ED
1075 /* choose the copy that is on our node/cpu */
1076 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1077 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1da177e4
LT
1078 tmp.size) != 0) {
1079 ret = -EFAULT;
1080 goto free_newinfo;
1081 }
1082
3b84e92b
PM
1083 counters = vmalloc_node(tmp.num_counters * sizeof(struct xt_counters),
1084 numa_node_id());
1da177e4
LT
1085 if (!counters) {
1086 ret = -ENOMEM;
1087 goto free_newinfo;
1088 }
1da177e4
LT
1089
1090 ret = translate_table(tmp.name, tmp.valid_hooks,
31836064 1091 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1da177e4
LT
1092 tmp.hook_entry, tmp.underflow);
1093 if (ret != 0)
1094 goto free_newinfo_counters;
1095
1096 duprintf("ip_tables: Translated table\n");
1097
2e4e6a17 1098 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
6b7d31fc
HW
1099 "ip6table_%s", tmp.name);
1100 if (!t || IS_ERR(t)) {
1101 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1102 goto free_newinfo_counters_untrans;
6b7d31fc 1103 }
1da177e4
LT
1104
1105 /* You lied! */
1106 if (tmp.valid_hooks != t->valid_hooks) {
1107 duprintf("Valid hook crap: %08X vs %08X\n",
1108 tmp.valid_hooks, t->valid_hooks);
1109 ret = -EINVAL;
6b7d31fc 1110 goto put_module;
1da177e4
LT
1111 }
1112
2e4e6a17 1113 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1da177e4
LT
1114 if (!oldinfo)
1115 goto put_module;
1116
1117 /* Update module usage count based on number of rules */
1118 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1119 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1120 if ((oldinfo->number > oldinfo->initial_entries) ||
1121 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1122 module_put(t->me);
1123 if ((oldinfo->number > oldinfo->initial_entries) &&
1124 (newinfo->number <= oldinfo->initial_entries))
1125 module_put(t->me);
1126
1127 /* Get the old counters. */
1128 get_counters(oldinfo, counters);
1129 /* Decrease module usage counts and free resource */
31836064
ED
1130 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1131 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
2e4e6a17 1132 xt_free_table_info(oldinfo);
1da177e4 1133 if (copy_to_user(tmp.counters, counters,
2e4e6a17 1134 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1da177e4
LT
1135 ret = -EFAULT;
1136 vfree(counters);
2e4e6a17 1137 xt_table_unlock(t);
1da177e4
LT
1138 return ret;
1139
1140 put_module:
1141 module_put(t->me);
2e4e6a17 1142 xt_table_unlock(t);
1da177e4 1143 free_newinfo_counters_untrans:
31836064 1144 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1da177e4
LT
1145 free_newinfo_counters:
1146 vfree(counters);
1147 free_newinfo:
2e4e6a17 1148 xt_free_table_info(newinfo);
1da177e4
LT
1149 return ret;
1150}
1151
1152/* We're lazy, and add to the first CPU; overflow works its fey magic
1153 * and everything is OK. */
1154static inline int
1155add_counter_to_entry(struct ip6t_entry *e,
2e4e6a17 1156 const struct xt_counters addme[],
1da177e4
LT
1157 unsigned int *i)
1158{
1159#if 0
1160 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1161 *i,
1162 (long unsigned int)e->counters.pcnt,
1163 (long unsigned int)e->counters.bcnt,
1164 (long unsigned int)addme[*i].pcnt,
1165 (long unsigned int)addme[*i].bcnt);
1166#endif
1167
1168 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1169
1170 (*i)++;
1171 return 0;
1172}
1173
1174static int
1175do_add_counters(void __user *user, unsigned int len)
1176{
1177 unsigned int i;
2e4e6a17
HW
1178 struct xt_counters_info tmp, *paddc;
1179 struct xt_table_info *private;
1180 struct xt_table *t;
6b7d31fc 1181 int ret = 0;
31836064 1182 void *loc_cpu_entry;
1da177e4
LT
1183
1184 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1185 return -EFAULT;
1186
2e4e6a17 1187 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1da177e4
LT
1188 return -EINVAL;
1189
3b84e92b 1190 paddc = vmalloc_node(len, numa_node_id());
1da177e4
LT
1191 if (!paddc)
1192 return -ENOMEM;
1193
1194 if (copy_from_user(paddc, user, len) != 0) {
1195 ret = -EFAULT;
1196 goto free;
1197 }
1198
2e4e6a17 1199 t = xt_find_table_lock(AF_INET6, tmp.name);
6b7d31fc
HW
1200 if (!t || IS_ERR(t)) {
1201 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1202 goto free;
6b7d31fc 1203 }
1da177e4
LT
1204
1205 write_lock_bh(&t->lock);
2e4e6a17 1206 private = t->private;
2c8ac66b 1207 if (private->number != tmp.num_counters) {
1da177e4
LT
1208 ret = -EINVAL;
1209 goto unlock_up_free;
1210 }
1211
1212 i = 0;
31836064 1213 /* Choose the copy that is on our node */
2e4e6a17 1214 loc_cpu_entry = private->entries[smp_processor_id()];
31836064 1215 IP6T_ENTRY_ITERATE(loc_cpu_entry,
2e4e6a17 1216 private->size,
1da177e4
LT
1217 add_counter_to_entry,
1218 paddc->counters,
1219 &i);
1220 unlock_up_free:
1221 write_unlock_bh(&t->lock);
2e4e6a17 1222 xt_table_unlock(t);
6b7d31fc 1223 module_put(t->me);
1da177e4
LT
1224 free:
1225 vfree(paddc);
1226
1227 return ret;
1228}
1229
1230static int
1231do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1232{
1233 int ret;
1234
1235 if (!capable(CAP_NET_ADMIN))
1236 return -EPERM;
1237
1238 switch (cmd) {
1239 case IP6T_SO_SET_REPLACE:
1240 ret = do_replace(user, len);
1241 break;
1242
1243 case IP6T_SO_SET_ADD_COUNTERS:
1244 ret = do_add_counters(user, len);
1245 break;
1246
1247 default:
1248 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1249 ret = -EINVAL;
1250 }
1251
1252 return ret;
1253}
1254
1255static int
1256do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1257{
1258 int ret;
1259
1260 if (!capable(CAP_NET_ADMIN))
1261 return -EPERM;
1262
1263 switch (cmd) {
1264 case IP6T_SO_GET_INFO: {
1265 char name[IP6T_TABLE_MAXNAMELEN];
2e4e6a17 1266 struct xt_table *t;
1da177e4
LT
1267
1268 if (*len != sizeof(struct ip6t_getinfo)) {
1269 duprintf("length %u != %u\n", *len,
1270 sizeof(struct ip6t_getinfo));
1271 ret = -EINVAL;
1272 break;
1273 }
1274
1275 if (copy_from_user(name, user, sizeof(name)) != 0) {
1276 ret = -EFAULT;
1277 break;
1278 }
1279 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
6b7d31fc 1280
2e4e6a17 1281 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
6b7d31fc
HW
1282 "ip6table_%s", name);
1283 if (t && !IS_ERR(t)) {
1da177e4 1284 struct ip6t_getinfo info;
2e4e6a17 1285 struct xt_table_info *private = t->private;
1da177e4
LT
1286
1287 info.valid_hooks = t->valid_hooks;
2e4e6a17 1288 memcpy(info.hook_entry, private->hook_entry,
1da177e4 1289 sizeof(info.hook_entry));
2e4e6a17 1290 memcpy(info.underflow, private->underflow,
1da177e4 1291 sizeof(info.underflow));
2e4e6a17
HW
1292 info.num_entries = private->number;
1293 info.size = private->size;
1da177e4
LT
1294 memcpy(info.name, name, sizeof(info.name));
1295
1296 if (copy_to_user(user, &info, *len) != 0)
1297 ret = -EFAULT;
1298 else
1299 ret = 0;
2e4e6a17 1300 xt_table_unlock(t);
6b7d31fc
HW
1301 module_put(t->me);
1302 } else
1303 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1304 }
1305 break;
1306
1307 case IP6T_SO_GET_ENTRIES: {
1308 struct ip6t_get_entries get;
1309
1310 if (*len < sizeof(get)) {
1311 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1312 ret = -EINVAL;
1313 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1314 ret = -EFAULT;
1315 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1316 duprintf("get_entries: %u != %u\n", *len,
1317 sizeof(struct ip6t_get_entries) + get.size);
1318 ret = -EINVAL;
1319 } else
1320 ret = get_entries(&get, user);
1321 break;
1322 }
1323
6b7d31fc
HW
1324 case IP6T_SO_GET_REVISION_MATCH:
1325 case IP6T_SO_GET_REVISION_TARGET: {
1326 struct ip6t_get_revision rev;
2e4e6a17 1327 int target;
6b7d31fc
HW
1328
1329 if (*len != sizeof(rev)) {
1330 ret = -EINVAL;
1331 break;
1332 }
1333 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1334 ret = -EFAULT;
1335 break;
1336 }
1337
1338 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 1339 target = 1;
6b7d31fc 1340 else
2e4e6a17 1341 target = 0;
6b7d31fc 1342
2e4e6a17
HW
1343 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1344 rev.revision,
1345 target, &ret),
6b7d31fc
HW
1346 "ip6t_%s", rev.name);
1347 break;
1348 }
1349
1da177e4
LT
1350 default:
1351 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1352 ret = -EINVAL;
1353 }
1354
1355 return ret;
1356}
1357
2e4e6a17 1358int ip6t_register_table(struct xt_table *table,
1da177e4
LT
1359 const struct ip6t_replace *repl)
1360{
1361 int ret;
2e4e6a17 1362 struct xt_table_info *newinfo;
259d4e41 1363 struct xt_table_info bootstrap
1da177e4 1364 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 1365 void *loc_cpu_entry;
1da177e4 1366
2e4e6a17 1367 newinfo = xt_alloc_table_info(repl->size);
1da177e4
LT
1368 if (!newinfo)
1369 return -ENOMEM;
1370
31836064
ED
1371 /* choose the copy on our node/cpu */
1372 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1373 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4
LT
1374
1375 ret = translate_table(table->name, table->valid_hooks,
31836064 1376 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
1377 repl->num_entries,
1378 repl->hook_entry,
1379 repl->underflow);
1380 if (ret != 0) {
2e4e6a17 1381 xt_free_table_info(newinfo);
1da177e4
LT
1382 return ret;
1383 }
1384
da298d3a
PM
1385 ret = xt_register_table(table, &bootstrap, newinfo);
1386 if (ret != 0) {
2e4e6a17 1387 xt_free_table_info(newinfo);
1da177e4
LT
1388 return ret;
1389 }
1390
2e4e6a17 1391 return 0;
1da177e4
LT
1392}
1393
2e4e6a17 1394void ip6t_unregister_table(struct xt_table *table)
1da177e4 1395{
2e4e6a17 1396 struct xt_table_info *private;
31836064
ED
1397 void *loc_cpu_entry;
1398
2e4e6a17 1399 private = xt_unregister_table(table);
1da177e4
LT
1400
1401 /* Decrease module usage counts and free resources */
2e4e6a17
HW
1402 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1403 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1404 xt_free_table_info(private);
1da177e4
LT
1405}
1406
1407/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 1408static inline bool
1da177e4
LT
1409icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1410 u_int8_t type, u_int8_t code,
ccb79bdc 1411 bool invert)
1da177e4
LT
1412{
1413 return (type == test_type && code >= min_code && code <= max_code)
1414 ^ invert;
1415}
1416
1d93a9cb 1417static bool
1da177e4
LT
1418icmp6_match(const struct sk_buff *skb,
1419 const struct net_device *in,
1420 const struct net_device *out,
c4986734 1421 const struct xt_match *match,
1da177e4
LT
1422 const void *matchinfo,
1423 int offset,
1424 unsigned int protoff,
cff533ac 1425 bool *hotdrop)
1da177e4
LT
1426{
1427 struct icmp6hdr _icmp, *ic;
1428 const struct ip6t_icmp *icmpinfo = matchinfo;
1429
1430 /* Must not be a fragment. */
1431 if (offset)
1d93a9cb 1432 return false;
1da177e4
LT
1433
1434 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1435 if (ic == NULL) {
1436 /* We've been asked to examine this packet, and we
1437 can't. Hence, no choice but to drop. */
1438 duprintf("Dropping evil ICMP tinygram.\n");
cff533ac 1439 *hotdrop = true;
1d93a9cb 1440 return false;
1da177e4
LT
1441 }
1442
1443 return icmp6_type_code_match(icmpinfo->type,
1444 icmpinfo->code[0],
1445 icmpinfo->code[1],
1446 ic->icmp6_type, ic->icmp6_code,
1447 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1448}
1449
1450/* Called when user tries to insert an entry of this type. */
ccb79bdc 1451static bool
1da177e4 1452icmp6_checkentry(const char *tablename,
2e4e6a17 1453 const void *entry,
c4986734 1454 const struct xt_match *match,
1da177e4 1455 void *matchinfo,
1da177e4
LT
1456 unsigned int hook_mask)
1457{
1458 const struct ip6t_icmp *icmpinfo = matchinfo;
1459
7f939713
PM
1460 /* Must specify no unknown invflags */
1461 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1da177e4
LT
1462}
1463
1464/* The built-in targets: standard (NULL) and error. */
9f15c530 1465static struct xt_target ip6t_standard_target __read_mostly = {
1da177e4 1466 .name = IP6T_STANDARD_TARGET,
7f939713 1467 .targetsize = sizeof(int),
a45049c5 1468 .family = AF_INET6,
1da177e4
LT
1469};
1470
9f15c530 1471static struct xt_target ip6t_error_target __read_mostly = {
1da177e4
LT
1472 .name = IP6T_ERROR_TARGET,
1473 .target = ip6t_error,
7f939713 1474 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
a45049c5 1475 .family = AF_INET6,
1da177e4
LT
1476};
1477
1478static struct nf_sockopt_ops ip6t_sockopts = {
1479 .pf = PF_INET6,
1480 .set_optmin = IP6T_BASE_CTL,
1481 .set_optmax = IP6T_SO_SET_MAX+1,
1482 .set = do_ip6t_set_ctl,
1483 .get_optmin = IP6T_BASE_CTL,
1484 .get_optmax = IP6T_SO_GET_MAX+1,
1485 .get = do_ip6t_get_ctl,
16fcec35 1486 .owner = THIS_MODULE,
1da177e4
LT
1487};
1488
9f15c530 1489static struct xt_match icmp6_matchstruct __read_mostly = {
1da177e4
LT
1490 .name = "icmp6",
1491 .match = &icmp6_match,
7f939713
PM
1492 .matchsize = sizeof(struct ip6t_icmp),
1493 .checkentry = icmp6_checkentry,
1494 .proto = IPPROTO_ICMPV6,
a45049c5 1495 .family = AF_INET6,
1da177e4
LT
1496};
1497
65b4b4e8 1498static int __init ip6_tables_init(void)
1da177e4
LT
1499{
1500 int ret;
1501
0eff66e6
PM
1502 ret = xt_proto_init(AF_INET6);
1503 if (ret < 0)
1504 goto err1;
2e4e6a17 1505
1da177e4 1506 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
1507 ret = xt_register_target(&ip6t_standard_target);
1508 if (ret < 0)
1509 goto err2;
1510 ret = xt_register_target(&ip6t_error_target);
1511 if (ret < 0)
1512 goto err3;
1513 ret = xt_register_match(&icmp6_matchstruct);
1514 if (ret < 0)
1515 goto err4;
1da177e4
LT
1516
1517 /* Register setsockopt */
1518 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
1519 if (ret < 0)
1520 goto err5;
1da177e4 1521
a887c1c1 1522 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 1523 return 0;
0eff66e6
PM
1524
1525err5:
1526 xt_unregister_match(&icmp6_matchstruct);
1527err4:
1528 xt_unregister_target(&ip6t_error_target);
1529err3:
1530 xt_unregister_target(&ip6t_standard_target);
1531err2:
1532 xt_proto_fini(AF_INET6);
1533err1:
1534 return ret;
1da177e4
LT
1535}
1536
65b4b4e8 1537static void __exit ip6_tables_fini(void)
1da177e4
LT
1538{
1539 nf_unregister_sockopt(&ip6t_sockopts);
a45049c5
PNA
1540 xt_unregister_match(&icmp6_matchstruct);
1541 xt_unregister_target(&ip6t_error_target);
1542 xt_unregister_target(&ip6t_standard_target);
2e4e6a17 1543 xt_proto_fini(AF_INET6);
1da177e4
LT
1544}
1545
e674d0f3 1546/*
b777e0ce
PM
1547 * find the offset to specified header or the protocol number of last header
1548 * if target < 0. "last header" is transport protocol header, ESP, or
1549 * "No next header".
1550 *
1551 * If target header is found, its offset is set in *offset and return protocol
1552 * number. Otherwise, return -1.
1553 *
6d381634
PM
1554 * If the first fragment doesn't contain the final protocol header or
1555 * NEXTHDR_NONE it is considered invalid.
1556 *
b777e0ce
PM
1557 * Note that non-1st fragment is special case that "the protocol number
1558 * of last header" is "next header" field in Fragment header. In this case,
1559 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1560 * isn't NULL.
e674d0f3 1561 *
e674d0f3 1562 */
b777e0ce
PM
1563int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1564 int target, unsigned short *fragoff)
e674d0f3 1565{
6b88dd96 1566 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0660e03f 1567 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
e674d0f3
YK
1568 unsigned int len = skb->len - start;
1569
b777e0ce
PM
1570 if (fragoff)
1571 *fragoff = 0;
1572
e674d0f3
YK
1573 while (nexthdr != target) {
1574 struct ipv6_opt_hdr _hdr, *hp;
1575 unsigned int hdrlen;
1576
b777e0ce
PM
1577 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1578 if (target < 0)
1579 break;
6d381634 1580 return -ENOENT;
b777e0ce
PM
1581 }
1582
e674d0f3
YK
1583 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1584 if (hp == NULL)
6d381634 1585 return -EBADMSG;
e674d0f3 1586 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
1587 unsigned short _frag_off;
1588 __be16 *fp;
e674d0f3
YK
1589 fp = skb_header_pointer(skb,
1590 start+offsetof(struct frag_hdr,
1591 frag_off),
1592 sizeof(_frag_off),
1593 &_frag_off);
1594 if (fp == NULL)
6d381634 1595 return -EBADMSG;
e674d0f3 1596
b777e0ce
PM
1597 _frag_off = ntohs(*fp) & ~0x7;
1598 if (_frag_off) {
1599 if (target < 0 &&
1600 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 1601 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
1602 if (fragoff)
1603 *fragoff = _frag_off;
1604 return hp->nexthdr;
1605 }
6d381634 1606 return -ENOENT;
b777e0ce 1607 }
e674d0f3
YK
1608 hdrlen = 8;
1609 } else if (nexthdr == NEXTHDR_AUTH)
1ab1457c 1610 hdrlen = (hp->hdrlen + 2) << 2;
e674d0f3 1611 else
1ab1457c 1612 hdrlen = ipv6_optlen(hp);
e674d0f3
YK
1613
1614 nexthdr = hp->nexthdr;
1615 len -= hdrlen;
1616 start += hdrlen;
1617 }
1618
1619 *offset = start;
b777e0ce 1620 return nexthdr;
e674d0f3
YK
1621}
1622
1da177e4
LT
1623EXPORT_SYMBOL(ip6t_register_table);
1624EXPORT_SYMBOL(ip6t_unregister_table);
1625EXPORT_SYMBOL(ip6t_do_table);
1da177e4 1626EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 1627EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 1628
65b4b4e8
AM
1629module_init(ip6_tables_init);
1630module_exit(ip6_tables_fini);
This page took 0.538785 seconds and 5 git commands to generate.