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