[NETFILTER]: ip6_tables: move entry, match and target checks to seperate functions
[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;
1da177e4
LT
962 counters = vmalloc(countersize);
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
2e4e6a17 1083 counters = vmalloc(tmp.num_counters * sizeof(struct xt_counters));
1da177e4
LT
1084 if (!counters) {
1085 ret = -ENOMEM;
1086 goto free_newinfo;
1087 }
1da177e4
LT
1088
1089 ret = translate_table(tmp.name, tmp.valid_hooks,
31836064 1090 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1da177e4
LT
1091 tmp.hook_entry, tmp.underflow);
1092 if (ret != 0)
1093 goto free_newinfo_counters;
1094
1095 duprintf("ip_tables: Translated table\n");
1096
2e4e6a17 1097 t = try_then_request_module(xt_find_table_lock(AF_INET6, tmp.name),
6b7d31fc
HW
1098 "ip6table_%s", tmp.name);
1099 if (!t || IS_ERR(t)) {
1100 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1101 goto free_newinfo_counters_untrans;
6b7d31fc 1102 }
1da177e4
LT
1103
1104 /* You lied! */
1105 if (tmp.valid_hooks != t->valid_hooks) {
1106 duprintf("Valid hook crap: %08X vs %08X\n",
1107 tmp.valid_hooks, t->valid_hooks);
1108 ret = -EINVAL;
6b7d31fc 1109 goto put_module;
1da177e4
LT
1110 }
1111
2e4e6a17 1112 oldinfo = xt_replace_table(t, tmp.num_counters, newinfo, &ret);
1da177e4
LT
1113 if (!oldinfo)
1114 goto put_module;
1115
1116 /* Update module usage count based on number of rules */
1117 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1118 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1ab1457c
YH
1119 if ((oldinfo->number > oldinfo->initial_entries) ||
1120 (newinfo->number <= oldinfo->initial_entries))
1da177e4
LT
1121 module_put(t->me);
1122 if ((oldinfo->number > oldinfo->initial_entries) &&
1123 (newinfo->number <= oldinfo->initial_entries))
1124 module_put(t->me);
1125
1126 /* Get the old counters. */
1127 get_counters(oldinfo, counters);
1128 /* Decrease module usage counts and free resource */
31836064
ED
1129 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1130 IP6T_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
2e4e6a17 1131 xt_free_table_info(oldinfo);
1da177e4 1132 if (copy_to_user(tmp.counters, counters,
2e4e6a17 1133 sizeof(struct xt_counters) * tmp.num_counters) != 0)
1da177e4
LT
1134 ret = -EFAULT;
1135 vfree(counters);
2e4e6a17 1136 xt_table_unlock(t);
1da177e4
LT
1137 return ret;
1138
1139 put_module:
1140 module_put(t->me);
2e4e6a17 1141 xt_table_unlock(t);
1da177e4 1142 free_newinfo_counters_untrans:
31836064 1143 IP6T_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1da177e4
LT
1144 free_newinfo_counters:
1145 vfree(counters);
1146 free_newinfo:
2e4e6a17 1147 xt_free_table_info(newinfo);
1da177e4
LT
1148 return ret;
1149}
1150
1151/* We're lazy, and add to the first CPU; overflow works its fey magic
1152 * and everything is OK. */
1153static inline int
1154add_counter_to_entry(struct ip6t_entry *e,
2e4e6a17 1155 const struct xt_counters addme[],
1da177e4
LT
1156 unsigned int *i)
1157{
1158#if 0
1159 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1160 *i,
1161 (long unsigned int)e->counters.pcnt,
1162 (long unsigned int)e->counters.bcnt,
1163 (long unsigned int)addme[*i].pcnt,
1164 (long unsigned int)addme[*i].bcnt);
1165#endif
1166
1167 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1168
1169 (*i)++;
1170 return 0;
1171}
1172
1173static int
1174do_add_counters(void __user *user, unsigned int len)
1175{
1176 unsigned int i;
2e4e6a17
HW
1177 struct xt_counters_info tmp, *paddc;
1178 struct xt_table_info *private;
1179 struct xt_table *t;
6b7d31fc 1180 int ret = 0;
31836064 1181 void *loc_cpu_entry;
1da177e4
LT
1182
1183 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1184 return -EFAULT;
1185
2e4e6a17 1186 if (len != sizeof(tmp) + tmp.num_counters*sizeof(struct xt_counters))
1da177e4
LT
1187 return -EINVAL;
1188
1189 paddc = vmalloc(len);
1190 if (!paddc)
1191 return -ENOMEM;
1192
1193 if (copy_from_user(paddc, user, len) != 0) {
1194 ret = -EFAULT;
1195 goto free;
1196 }
1197
2e4e6a17 1198 t = xt_find_table_lock(AF_INET6, tmp.name);
6b7d31fc
HW
1199 if (!t || IS_ERR(t)) {
1200 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1201 goto free;
6b7d31fc 1202 }
1da177e4
LT
1203
1204 write_lock_bh(&t->lock);
2e4e6a17 1205 private = t->private;
2c8ac66b 1206 if (private->number != tmp.num_counters) {
1da177e4
LT
1207 ret = -EINVAL;
1208 goto unlock_up_free;
1209 }
1210
1211 i = 0;
31836064 1212 /* Choose the copy that is on our node */
2e4e6a17 1213 loc_cpu_entry = private->entries[smp_processor_id()];
31836064 1214 IP6T_ENTRY_ITERATE(loc_cpu_entry,
2e4e6a17 1215 private->size,
1da177e4
LT
1216 add_counter_to_entry,
1217 paddc->counters,
1218 &i);
1219 unlock_up_free:
1220 write_unlock_bh(&t->lock);
2e4e6a17 1221 xt_table_unlock(t);
6b7d31fc 1222 module_put(t->me);
1da177e4
LT
1223 free:
1224 vfree(paddc);
1225
1226 return ret;
1227}
1228
1229static int
1230do_ip6t_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1231{
1232 int ret;
1233
1234 if (!capable(CAP_NET_ADMIN))
1235 return -EPERM;
1236
1237 switch (cmd) {
1238 case IP6T_SO_SET_REPLACE:
1239 ret = do_replace(user, len);
1240 break;
1241
1242 case IP6T_SO_SET_ADD_COUNTERS:
1243 ret = do_add_counters(user, len);
1244 break;
1245
1246 default:
1247 duprintf("do_ip6t_set_ctl: unknown request %i\n", cmd);
1248 ret = -EINVAL;
1249 }
1250
1251 return ret;
1252}
1253
1254static int
1255do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1256{
1257 int ret;
1258
1259 if (!capable(CAP_NET_ADMIN))
1260 return -EPERM;
1261
1262 switch (cmd) {
1263 case IP6T_SO_GET_INFO: {
1264 char name[IP6T_TABLE_MAXNAMELEN];
2e4e6a17 1265 struct xt_table *t;
1da177e4
LT
1266
1267 if (*len != sizeof(struct ip6t_getinfo)) {
1268 duprintf("length %u != %u\n", *len,
1269 sizeof(struct ip6t_getinfo));
1270 ret = -EINVAL;
1271 break;
1272 }
1273
1274 if (copy_from_user(name, user, sizeof(name)) != 0) {
1275 ret = -EFAULT;
1276 break;
1277 }
1278 name[IP6T_TABLE_MAXNAMELEN-1] = '\0';
6b7d31fc 1279
2e4e6a17 1280 t = try_then_request_module(xt_find_table_lock(AF_INET6, name),
6b7d31fc
HW
1281 "ip6table_%s", name);
1282 if (t && !IS_ERR(t)) {
1da177e4 1283 struct ip6t_getinfo info;
2e4e6a17 1284 struct xt_table_info *private = t->private;
1da177e4
LT
1285
1286 info.valid_hooks = t->valid_hooks;
2e4e6a17 1287 memcpy(info.hook_entry, private->hook_entry,
1da177e4 1288 sizeof(info.hook_entry));
2e4e6a17 1289 memcpy(info.underflow, private->underflow,
1da177e4 1290 sizeof(info.underflow));
2e4e6a17
HW
1291 info.num_entries = private->number;
1292 info.size = private->size;
1da177e4
LT
1293 memcpy(info.name, name, sizeof(info.name));
1294
1295 if (copy_to_user(user, &info, *len) != 0)
1296 ret = -EFAULT;
1297 else
1298 ret = 0;
2e4e6a17 1299 xt_table_unlock(t);
6b7d31fc
HW
1300 module_put(t->me);
1301 } else
1302 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4
LT
1303 }
1304 break;
1305
1306 case IP6T_SO_GET_ENTRIES: {
1307 struct ip6t_get_entries get;
1308
1309 if (*len < sizeof(get)) {
1310 duprintf("get_entries: %u < %u\n", *len, sizeof(get));
1311 ret = -EINVAL;
1312 } else if (copy_from_user(&get, user, sizeof(get)) != 0) {
1313 ret = -EFAULT;
1314 } else if (*len != sizeof(struct ip6t_get_entries) + get.size) {
1315 duprintf("get_entries: %u != %u\n", *len,
1316 sizeof(struct ip6t_get_entries) + get.size);
1317 ret = -EINVAL;
1318 } else
1319 ret = get_entries(&get, user);
1320 break;
1321 }
1322
6b7d31fc
HW
1323 case IP6T_SO_GET_REVISION_MATCH:
1324 case IP6T_SO_GET_REVISION_TARGET: {
1325 struct ip6t_get_revision rev;
2e4e6a17 1326 int target;
6b7d31fc
HW
1327
1328 if (*len != sizeof(rev)) {
1329 ret = -EINVAL;
1330 break;
1331 }
1332 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1333 ret = -EFAULT;
1334 break;
1335 }
1336
1337 if (cmd == IP6T_SO_GET_REVISION_TARGET)
2e4e6a17 1338 target = 1;
6b7d31fc 1339 else
2e4e6a17 1340 target = 0;
6b7d31fc 1341
2e4e6a17
HW
1342 try_then_request_module(xt_find_revision(AF_INET6, rev.name,
1343 rev.revision,
1344 target, &ret),
6b7d31fc
HW
1345 "ip6t_%s", rev.name);
1346 break;
1347 }
1348
1da177e4
LT
1349 default:
1350 duprintf("do_ip6t_get_ctl: unknown request %i\n", cmd);
1351 ret = -EINVAL;
1352 }
1353
1354 return ret;
1355}
1356
2e4e6a17 1357int ip6t_register_table(struct xt_table *table,
1da177e4
LT
1358 const struct ip6t_replace *repl)
1359{
1360 int ret;
2e4e6a17 1361 struct xt_table_info *newinfo;
259d4e41 1362 struct xt_table_info bootstrap
1da177e4 1363 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 1364 void *loc_cpu_entry;
1da177e4 1365
2e4e6a17 1366 newinfo = xt_alloc_table_info(repl->size);
1da177e4
LT
1367 if (!newinfo)
1368 return -ENOMEM;
1369
31836064
ED
1370 /* choose the copy on our node/cpu */
1371 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1372 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4
LT
1373
1374 ret = translate_table(table->name, table->valid_hooks,
31836064 1375 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
1376 repl->num_entries,
1377 repl->hook_entry,
1378 repl->underflow);
1379 if (ret != 0) {
2e4e6a17 1380 xt_free_table_info(newinfo);
1da177e4
LT
1381 return ret;
1382 }
1383
da298d3a
PM
1384 ret = xt_register_table(table, &bootstrap, newinfo);
1385 if (ret != 0) {
2e4e6a17 1386 xt_free_table_info(newinfo);
1da177e4
LT
1387 return ret;
1388 }
1389
2e4e6a17 1390 return 0;
1da177e4
LT
1391}
1392
2e4e6a17 1393void ip6t_unregister_table(struct xt_table *table)
1da177e4 1394{
2e4e6a17 1395 struct xt_table_info *private;
31836064
ED
1396 void *loc_cpu_entry;
1397
2e4e6a17 1398 private = xt_unregister_table(table);
1da177e4
LT
1399
1400 /* Decrease module usage counts and free resources */
2e4e6a17
HW
1401 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1402 IP6T_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
1403 xt_free_table_info(private);
1da177e4
LT
1404}
1405
1406/* Returns 1 if the type and code is matched by the range, 0 otherwise */
ccb79bdc 1407static inline bool
1da177e4
LT
1408icmp6_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
1409 u_int8_t type, u_int8_t code,
ccb79bdc 1410 bool invert)
1da177e4
LT
1411{
1412 return (type == test_type && code >= min_code && code <= max_code)
1413 ^ invert;
1414}
1415
1d93a9cb 1416static bool
1da177e4
LT
1417icmp6_match(const struct sk_buff *skb,
1418 const struct net_device *in,
1419 const struct net_device *out,
c4986734 1420 const struct xt_match *match,
1da177e4
LT
1421 const void *matchinfo,
1422 int offset,
1423 unsigned int protoff,
cff533ac 1424 bool *hotdrop)
1da177e4
LT
1425{
1426 struct icmp6hdr _icmp, *ic;
1427 const struct ip6t_icmp *icmpinfo = matchinfo;
1428
1429 /* Must not be a fragment. */
1430 if (offset)
1d93a9cb 1431 return false;
1da177e4
LT
1432
1433 ic = skb_header_pointer(skb, protoff, sizeof(_icmp), &_icmp);
1434 if (ic == NULL) {
1435 /* We've been asked to examine this packet, and we
1436 can't. Hence, no choice but to drop. */
1437 duprintf("Dropping evil ICMP tinygram.\n");
cff533ac 1438 *hotdrop = true;
1d93a9cb 1439 return false;
1da177e4
LT
1440 }
1441
1442 return icmp6_type_code_match(icmpinfo->type,
1443 icmpinfo->code[0],
1444 icmpinfo->code[1],
1445 ic->icmp6_type, ic->icmp6_code,
1446 !!(icmpinfo->invflags&IP6T_ICMP_INV));
1447}
1448
1449/* Called when user tries to insert an entry of this type. */
ccb79bdc 1450static bool
1da177e4 1451icmp6_checkentry(const char *tablename,
2e4e6a17 1452 const void *entry,
c4986734 1453 const struct xt_match *match,
1da177e4 1454 void *matchinfo,
1da177e4
LT
1455 unsigned int hook_mask)
1456{
1457 const struct ip6t_icmp *icmpinfo = matchinfo;
1458
7f939713
PM
1459 /* Must specify no unknown invflags */
1460 return !(icmpinfo->invflags & ~IP6T_ICMP_INV);
1da177e4
LT
1461}
1462
1463/* The built-in targets: standard (NULL) and error. */
9f15c530 1464static struct xt_target ip6t_standard_target __read_mostly = {
1da177e4 1465 .name = IP6T_STANDARD_TARGET,
7f939713 1466 .targetsize = sizeof(int),
a45049c5 1467 .family = AF_INET6,
1da177e4
LT
1468};
1469
9f15c530 1470static struct xt_target ip6t_error_target __read_mostly = {
1da177e4
LT
1471 .name = IP6T_ERROR_TARGET,
1472 .target = ip6t_error,
7f939713 1473 .targetsize = IP6T_FUNCTION_MAXNAMELEN,
a45049c5 1474 .family = AF_INET6,
1da177e4
LT
1475};
1476
1477static struct nf_sockopt_ops ip6t_sockopts = {
1478 .pf = PF_INET6,
1479 .set_optmin = IP6T_BASE_CTL,
1480 .set_optmax = IP6T_SO_SET_MAX+1,
1481 .set = do_ip6t_set_ctl,
1482 .get_optmin = IP6T_BASE_CTL,
1483 .get_optmax = IP6T_SO_GET_MAX+1,
1484 .get = do_ip6t_get_ctl,
16fcec35 1485 .owner = THIS_MODULE,
1da177e4
LT
1486};
1487
9f15c530 1488static struct xt_match icmp6_matchstruct __read_mostly = {
1da177e4
LT
1489 .name = "icmp6",
1490 .match = &icmp6_match,
7f939713
PM
1491 .matchsize = sizeof(struct ip6t_icmp),
1492 .checkentry = icmp6_checkentry,
1493 .proto = IPPROTO_ICMPV6,
a45049c5 1494 .family = AF_INET6,
1da177e4
LT
1495};
1496
65b4b4e8 1497static int __init ip6_tables_init(void)
1da177e4
LT
1498{
1499 int ret;
1500
0eff66e6
PM
1501 ret = xt_proto_init(AF_INET6);
1502 if (ret < 0)
1503 goto err1;
2e4e6a17 1504
1da177e4 1505 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
1506 ret = xt_register_target(&ip6t_standard_target);
1507 if (ret < 0)
1508 goto err2;
1509 ret = xt_register_target(&ip6t_error_target);
1510 if (ret < 0)
1511 goto err3;
1512 ret = xt_register_match(&icmp6_matchstruct);
1513 if (ret < 0)
1514 goto err4;
1da177e4
LT
1515
1516 /* Register setsockopt */
1517 ret = nf_register_sockopt(&ip6t_sockopts);
0eff66e6
PM
1518 if (ret < 0)
1519 goto err5;
1da177e4 1520
a887c1c1 1521 printk(KERN_INFO "ip6_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 1522 return 0;
0eff66e6
PM
1523
1524err5:
1525 xt_unregister_match(&icmp6_matchstruct);
1526err4:
1527 xt_unregister_target(&ip6t_error_target);
1528err3:
1529 xt_unregister_target(&ip6t_standard_target);
1530err2:
1531 xt_proto_fini(AF_INET6);
1532err1:
1533 return ret;
1da177e4
LT
1534}
1535
65b4b4e8 1536static void __exit ip6_tables_fini(void)
1da177e4
LT
1537{
1538 nf_unregister_sockopt(&ip6t_sockopts);
a45049c5
PNA
1539 xt_unregister_match(&icmp6_matchstruct);
1540 xt_unregister_target(&ip6t_error_target);
1541 xt_unregister_target(&ip6t_standard_target);
2e4e6a17 1542 xt_proto_fini(AF_INET6);
1da177e4
LT
1543}
1544
e674d0f3 1545/*
b777e0ce
PM
1546 * find the offset to specified header or the protocol number of last header
1547 * if target < 0. "last header" is transport protocol header, ESP, or
1548 * "No next header".
1549 *
1550 * If target header is found, its offset is set in *offset and return protocol
1551 * number. Otherwise, return -1.
1552 *
6d381634
PM
1553 * If the first fragment doesn't contain the final protocol header or
1554 * NEXTHDR_NONE it is considered invalid.
1555 *
b777e0ce
PM
1556 * Note that non-1st fragment is special case that "the protocol number
1557 * of last header" is "next header" field in Fragment header. In this case,
1558 * *offset is meaningless and fragment offset is stored in *fragoff if fragoff
1559 * isn't NULL.
e674d0f3 1560 *
e674d0f3 1561 */
b777e0ce
PM
1562int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset,
1563 int target, unsigned short *fragoff)
e674d0f3 1564{
6b88dd96 1565 unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
0660e03f 1566 u8 nexthdr = ipv6_hdr(skb)->nexthdr;
e674d0f3
YK
1567 unsigned int len = skb->len - start;
1568
b777e0ce
PM
1569 if (fragoff)
1570 *fragoff = 0;
1571
e674d0f3
YK
1572 while (nexthdr != target) {
1573 struct ipv6_opt_hdr _hdr, *hp;
1574 unsigned int hdrlen;
1575
b777e0ce
PM
1576 if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) {
1577 if (target < 0)
1578 break;
6d381634 1579 return -ENOENT;
b777e0ce
PM
1580 }
1581
e674d0f3
YK
1582 hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
1583 if (hp == NULL)
6d381634 1584 return -EBADMSG;
e674d0f3 1585 if (nexthdr == NEXTHDR_FRAGMENT) {
e69a4adc
AV
1586 unsigned short _frag_off;
1587 __be16 *fp;
e674d0f3
YK
1588 fp = skb_header_pointer(skb,
1589 start+offsetof(struct frag_hdr,
1590 frag_off),
1591 sizeof(_frag_off),
1592 &_frag_off);
1593 if (fp == NULL)
6d381634 1594 return -EBADMSG;
e674d0f3 1595
b777e0ce
PM
1596 _frag_off = ntohs(*fp) & ~0x7;
1597 if (_frag_off) {
1598 if (target < 0 &&
1599 ((!ipv6_ext_hdr(hp->nexthdr)) ||
337dde79 1600 hp->nexthdr == NEXTHDR_NONE)) {
b777e0ce
PM
1601 if (fragoff)
1602 *fragoff = _frag_off;
1603 return hp->nexthdr;
1604 }
6d381634 1605 return -ENOENT;
b777e0ce 1606 }
e674d0f3
YK
1607 hdrlen = 8;
1608 } else if (nexthdr == NEXTHDR_AUTH)
1ab1457c 1609 hdrlen = (hp->hdrlen + 2) << 2;
e674d0f3 1610 else
1ab1457c 1611 hdrlen = ipv6_optlen(hp);
e674d0f3
YK
1612
1613 nexthdr = hp->nexthdr;
1614 len -= hdrlen;
1615 start += hdrlen;
1616 }
1617
1618 *offset = start;
b777e0ce 1619 return nexthdr;
e674d0f3
YK
1620}
1621
1da177e4
LT
1622EXPORT_SYMBOL(ip6t_register_table);
1623EXPORT_SYMBOL(ip6t_unregister_table);
1624EXPORT_SYMBOL(ip6t_do_table);
1da177e4 1625EXPORT_SYMBOL(ip6t_ext_hdr);
e674d0f3 1626EXPORT_SYMBOL(ipv6_find_hdr);
1da177e4 1627
65b4b4e8
AM
1628module_init(ip6_tables_init);
1629module_exit(ip6_tables_fini);
This page took 0.685734 seconds and 5 git commands to generate.