]> bbs.cooldavid.org Git - net-next-2.6.git/blame - net/ipv4/netfilter/ip_tables.c
[SK_BUFF]: Introduce ip_hdr(), remove skb->nh.iph
[net-next-2.6.git] / net / ipv4 / netfilter / ip_tables.c
CommitLineData
1da177e4
LT
1/*
2 * Packet matching code.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
2e4e6a17 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.
10 *
11 * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
12 * - increase module usage count as soon as we have rules inside
13 * a table
2e4e6a17
HW
14 * 08 Oct 2005 Harald Welte <lafore@netfilter.org>
15 * - Generalize into "x_tables" layer and "{ip,ip6,arp}_tables"
1da177e4 16 */
1da177e4 17#include <linux/cache.h>
4fc268d2 18#include <linux/capability.h>
1da177e4
LT
19#include <linux/skbuff.h>
20#include <linux/kmod.h>
21#include <linux/vmalloc.h>
22#include <linux/netdevice.h>
23#include <linux/module.h>
1da177e4
LT
24#include <linux/icmp.h>
25#include <net/ip.h>
2722971c 26#include <net/compat.h>
1da177e4 27#include <asm/uaccess.h>
57b47a53 28#include <linux/mutex.h>
1da177e4
LT
29#include <linux/proc_fs.h>
30#include <linux/err.h>
c8923c6b 31#include <linux/cpumask.h>
1da177e4 32
2e4e6a17 33#include <linux/netfilter/x_tables.h>
1da177e4
LT
34#include <linux/netfilter_ipv4/ip_tables.h>
35
36MODULE_LICENSE("GPL");
37MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
38MODULE_DESCRIPTION("IPv4 packet filter");
39
40/*#define DEBUG_IP_FIREWALL*/
41/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
42/*#define DEBUG_IP_FIREWALL_USER*/
43
44#ifdef DEBUG_IP_FIREWALL
45#define dprintf(format, args...) printk(format , ## args)
46#else
47#define dprintf(format, args...)
48#endif
49
50#ifdef DEBUG_IP_FIREWALL_USER
51#define duprintf(format, args...) printk(format , ## args)
52#else
53#define duprintf(format, args...)
54#endif
55
56#ifdef CONFIG_NETFILTER_DEBUG
57#define IP_NF_ASSERT(x) \
58do { \
59 if (!(x)) \
60 printk("IP_NF_ASSERT: %s:%s:%u\n", \
61 __FUNCTION__, __FILE__, __LINE__); \
62} while(0)
63#else
64#define IP_NF_ASSERT(x)
65#endif
1da177e4
LT
66
67#if 0
68/* All the better to debug you with... */
69#define static
70#define inline
71#endif
72
73/*
74 We keep a set of rules for each CPU, so we can avoid write-locking
75 them in the softirq when updating the counters and therefore
76 only need to read-lock in the softirq; doing a write_lock_bh() in user
77 context stops packets coming through and allows user context to read
78 the counters or update the rules.
79
1da177e4
LT
80 Hence the start of any table is given by get_table() below. */
81
1da177e4
LT
82/* Returns whether matches rule or not. */
83static inline int
84ip_packet_match(const struct iphdr *ip,
85 const char *indev,
86 const char *outdev,
87 const struct ipt_ip *ipinfo,
88 int isfrag)
89{
90 size_t i;
91 unsigned long ret;
92
93#define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
94
95 if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,
96 IPT_INV_SRCIP)
97 || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
98 IPT_INV_DSTIP)) {
99 dprintf("Source or dest mismatch.\n");
100
101 dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",
102 NIPQUAD(ip->saddr),
103 NIPQUAD(ipinfo->smsk.s_addr),
104 NIPQUAD(ipinfo->src.s_addr),
105 ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");
106 dprintf("DST: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",
107 NIPQUAD(ip->daddr),
108 NIPQUAD(ipinfo->dmsk.s_addr),
109 NIPQUAD(ipinfo->dst.s_addr),
110 ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");
111 return 0;
112 }
113
114 /* Look for ifname matches; this should unroll nicely. */
115 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
116 ret |= (((const unsigned long *)indev)[i]
117 ^ ((const unsigned long *)ipinfo->iniface)[i])
118 & ((const unsigned long *)ipinfo->iniface_mask)[i];
119 }
120
121 if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
122 dprintf("VIA in mismatch (%s vs %s).%s\n",
123 indev, ipinfo->iniface,
124 ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
125 return 0;
126 }
127
128 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
129 ret |= (((const unsigned long *)outdev)[i]
130 ^ ((const unsigned long *)ipinfo->outiface)[i])
131 & ((const unsigned long *)ipinfo->outiface_mask)[i];
132 }
133
134 if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
135 dprintf("VIA out mismatch (%s vs %s).%s\n",
136 outdev, ipinfo->outiface,
137 ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
138 return 0;
139 }
140
141 /* Check specific protocol */
142 if (ipinfo->proto
143 && FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
144 dprintf("Packet protocol %hi does not match %hi.%s\n",
145 ip->protocol, ipinfo->proto,
146 ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");
147 return 0;
148 }
149
150 /* If we have a fragment rule but the packet is not a fragment
151 * then we return zero */
152 if (FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG)) {
153 dprintf("Fragment rule but not fragment.%s\n",
154 ipinfo->invflags & IPT_INV_FRAG ? " (INV)" : "");
155 return 0;
156 }
157
158 return 1;
159}
160
161static inline int
162ip_checkentry(const struct ipt_ip *ip)
163{
164 if (ip->flags & ~IPT_F_MASK) {
165 duprintf("Unknown flag bits set: %08X\n",
166 ip->flags & ~IPT_F_MASK);
167 return 0;
168 }
169 if (ip->invflags & ~IPT_INV_MASK) {
170 duprintf("Unknown invflag bits set: %08X\n",
171 ip->invflags & ~IPT_INV_MASK);
172 return 0;
173 }
174 return 1;
175}
176
177static unsigned int
178ipt_error(struct sk_buff **pskb,
179 const struct net_device *in,
180 const struct net_device *out,
181 unsigned int hooknum,
c4986734 182 const struct xt_target *target,
fe1cb108 183 const void *targinfo)
1da177e4
LT
184{
185 if (net_ratelimit())
186 printk("ip_tables: error: `%s'\n", (char *)targinfo);
187
188 return NF_DROP;
189}
190
191static inline
192int do_match(struct ipt_entry_match *m,
193 const struct sk_buff *skb,
194 const struct net_device *in,
195 const struct net_device *out,
196 int offset,
197 int *hotdrop)
198{
199 /* Stop iteration if it doesn't match */
1c524830 200 if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
c9bdd4b5 201 offset, ip_hdrlen(skb), hotdrop))
1da177e4
LT
202 return 1;
203 else
204 return 0;
205}
206
207static inline struct ipt_entry *
208get_entry(void *base, unsigned int offset)
209{
210 return (struct ipt_entry *)(base + offset);
211}
212
213/* Returns one of the generic firewall policies, like NF_ACCEPT. */
214unsigned int
215ipt_do_table(struct sk_buff **pskb,
216 unsigned int hook,
217 const struct net_device *in,
218 const struct net_device *out,
e60a13e0 219 struct xt_table *table)
1da177e4
LT
220{
221 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
222 u_int16_t offset;
223 struct iphdr *ip;
224 u_int16_t datalen;
225 int hotdrop = 0;
226 /* Initializing verdict to NF_DROP keeps gcc happy. */
227 unsigned int verdict = NF_DROP;
228 const char *indev, *outdev;
229 void *table_base;
230 struct ipt_entry *e, *back;
8311731a 231 struct xt_table_info *private;
1da177e4
LT
232
233 /* Initialization */
eddc9ec5 234 ip = ip_hdr(*pskb);
1da177e4
LT
235 datalen = (*pskb)->len - ip->ihl * 4;
236 indev = in ? in->name : nulldevname;
237 outdev = out ? out->name : nulldevname;
238 /* We handle fragments by dealing with the first fragment as
239 * if it was a normal packet. All other fragments are treated
240 * normally, except that they will NEVER match rules that ask
241 * things we don't know, ie. tcp syn flag or ports). If the
242 * rule is also a fragment-specific rule, non-fragments won't
243 * match it. */
244 offset = ntohs(ip->frag_off) & IP_OFFSET;
245
246 read_lock_bh(&table->lock);
247 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
8311731a 248 private = table->private;
2e4e6a17
HW
249 table_base = (void *)private->entries[smp_processor_id()];
250 e = get_entry(table_base, private->hook_entry[hook]);
1da177e4
LT
251
252 /* For return from builtin chain */
2e4e6a17 253 back = get_entry(table_base, private->underflow[hook]);
1da177e4
LT
254
255 do {
256 IP_NF_ASSERT(e);
257 IP_NF_ASSERT(back);
1da177e4
LT
258 if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
259 struct ipt_entry_target *t;
260
261 if (IPT_MATCH_ITERATE(e, do_match,
262 *pskb, in, out,
263 offset, &hotdrop) != 0)
264 goto no_match;
265
266 ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
267
268 t = ipt_get_target(e);
269 IP_NF_ASSERT(t->u.kernel.target);
270 /* Standard target? */
271 if (!t->u.kernel.target->target) {
272 int v;
273
274 v = ((struct ipt_standard_target *)t)->verdict;
275 if (v < 0) {
276 /* Pop from stack? */
277 if (v != IPT_RETURN) {
278 verdict = (unsigned)(-v) - 1;
279 break;
280 }
281 e = back;
282 back = get_entry(table_base,
283 back->comefrom);
284 continue;
285 }
05465343
PM
286 if (table_base + v != (void *)e + e->next_offset
287 && !(e->ip.flags & IPT_F_GOTO)) {
1da177e4
LT
288 /* Save old back ptr in next entry */
289 struct ipt_entry *next
290 = (void *)e + e->next_offset;
291 next->comefrom
292 = (void *)back - table_base;
293 /* set back pointer to next entry */
294 back = next;
295 }
296
297 e = get_entry(table_base, v);
298 } else {
299 /* Targets which reenter must return
e905a9ed 300 abs. verdicts */
1da177e4
LT
301#ifdef CONFIG_NETFILTER_DEBUG
302 ((struct ipt_entry *)table_base)->comefrom
303 = 0xeeeeeeec;
304#endif
305 verdict = t->u.kernel.target->target(pskb,
306 in, out,
307 hook,
1c524830 308 t->u.kernel.target,
fe1cb108 309 t->data);
1da177e4
LT
310
311#ifdef CONFIG_NETFILTER_DEBUG
312 if (((struct ipt_entry *)table_base)->comefrom
313 != 0xeeeeeeec
314 && verdict == IPT_CONTINUE) {
315 printk("Target %s reentered!\n",
316 t->u.kernel.target->name);
317 verdict = NF_DROP;
318 }
319 ((struct ipt_entry *)table_base)->comefrom
320 = 0x57acc001;
321#endif
322 /* Target might have changed stuff. */
eddc9ec5 323 ip = ip_hdr(*pskb);
1da177e4
LT
324 datalen = (*pskb)->len - ip->ihl * 4;
325
326 if (verdict == IPT_CONTINUE)
327 e = (void *)e + e->next_offset;
328 else
329 /* Verdict */
330 break;
331 }
332 } else {
333
334 no_match:
335 e = (void *)e + e->next_offset;
336 }
337 } while (!hotdrop);
338
1da177e4
LT
339 read_unlock_bh(&table->lock);
340
341#ifdef DEBUG_ALLOW_ALL
342 return NF_ACCEPT;
343#else
344 if (hotdrop)
345 return NF_DROP;
346 else return verdict;
347#endif
348}
349
1da177e4
LT
350/* All zeroes == unconditional rule. */
351static inline int
352unconditional(const struct ipt_ip *ip)
353{
354 unsigned int i;
355
356 for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
357 if (((__u32 *)ip)[i])
358 return 0;
359
360 return 1;
361}
362
363/* Figures out from what hook each rule can be called: returns 0 if
364 there are loops. Puts hook bitmask in comefrom. */
365static int
2e4e6a17 366mark_source_chains(struct xt_table_info *newinfo,
31836064 367 unsigned int valid_hooks, void *entry0)
1da177e4
LT
368{
369 unsigned int hook;
370
371 /* No recursion; use packet counter to save back ptrs (reset
372 to 0 as we leave), and comefrom to save source hook bitmask */
373 for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
374 unsigned int pos = newinfo->hook_entry[hook];
375 struct ipt_entry *e
31836064 376 = (struct ipt_entry *)(entry0 + pos);
1da177e4
LT
377
378 if (!(valid_hooks & (1 << hook)))
379 continue;
380
381 /* Set initial back pointer. */
382 e->counters.pcnt = pos;
383
384 for (;;) {
385 struct ipt_standard_target *t
386 = (void *)ipt_get_target(e);
e1b4b9f3 387 int visited = e->comefrom & (1 << hook);
1da177e4
LT
388
389 if (e->comefrom & (1 << NF_IP_NUMHOOKS)) {
390 printk("iptables: loop hook %u pos %u %08X.\n",
391 hook, pos, e->comefrom);
392 return 0;
393 }
394 e->comefrom
395 |= ((1 << hook) | (1 << NF_IP_NUMHOOKS));
396
397 /* Unconditional return/END. */
e1b4b9f3 398 if ((e->target_offset == sizeof(struct ipt_entry)
1da177e4
LT
399 && (strcmp(t->target.u.user.name,
400 IPT_STANDARD_TARGET) == 0)
401 && t->verdict < 0
e1b4b9f3 402 && unconditional(&e->ip)) || visited) {
1da177e4
LT
403 unsigned int oldpos, size;
404
74c9c0c1
DM
405 if (t->verdict < -NF_MAX_VERDICT - 1) {
406 duprintf("mark_source_chains: bad "
407 "negative verdict (%i)\n",
408 t->verdict);
409 return 0;
410 }
411
1da177e4
LT
412 /* Return: backtrack through the last
413 big jump. */
414 do {
415 e->comefrom ^= (1<<NF_IP_NUMHOOKS);
416#ifdef DEBUG_IP_FIREWALL_USER
417 if (e->comefrom
418 & (1 << NF_IP_NUMHOOKS)) {
419 duprintf("Back unset "
420 "on hook %u "
421 "rule %u\n",
422 hook, pos);
423 }
424#endif
425 oldpos = pos;
426 pos = e->counters.pcnt;
427 e->counters.pcnt = 0;
428
429 /* We're at the start. */
430 if (pos == oldpos)
431 goto next;
432
433 e = (struct ipt_entry *)
31836064 434 (entry0 + pos);
1da177e4
LT
435 } while (oldpos == pos + e->next_offset);
436
437 /* Move along one */
438 size = e->next_offset;
439 e = (struct ipt_entry *)
31836064 440 (entry0 + pos + size);
1da177e4
LT
441 e->counters.pcnt = pos;
442 pos += size;
443 } else {
444 int newpos = t->verdict;
445
446 if (strcmp(t->target.u.user.name,
447 IPT_STANDARD_TARGET) == 0
448 && newpos >= 0) {
74c9c0c1
DM
449 if (newpos > newinfo->size -
450 sizeof(struct ipt_entry)) {
451 duprintf("mark_source_chains: "
452 "bad verdict (%i)\n",
453 newpos);
454 return 0;
455 }
1da177e4
LT
456 /* This a jump; chase it. */
457 duprintf("Jump rule %u -> %u\n",
458 pos, newpos);
459 } else {
460 /* ... this is a fallthru */
461 newpos = pos + e->next_offset;
462 }
463 e = (struct ipt_entry *)
31836064 464 (entry0 + newpos);
1da177e4
LT
465 e->counters.pcnt = pos;
466 pos = newpos;
467 }
468 }
469 next:
470 duprintf("Finished chain %u\n", hook);
471 }
472 return 1;
473}
474
475static inline int
476cleanup_match(struct ipt_entry_match *m, unsigned int *i)
477{
478 if (i && (*i)-- == 0)
479 return 1;
480
481 if (m->u.kernel.match->destroy)
efa74165 482 m->u.kernel.match->destroy(m->u.kernel.match, m->data);
1da177e4
LT
483 module_put(m->u.kernel.match->me);
484 return 0;
485}
486
1da177e4 487static inline int
a96be246
DM
488check_entry(struct ipt_entry *e, const char *name)
489{
490 struct ipt_entry_target *t;
491
492 if (!ip_checkentry(&e->ip)) {
493 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
494 return -EINVAL;
495 }
496
497 if (e->target_offset + sizeof(struct ipt_entry_target) > e->next_offset)
498 return -EINVAL;
499
500 t = ipt_get_target(e);
501 if (e->target_offset + t->u.target_size > e->next_offset)
502 return -EINVAL;
503
504 return 0;
505}
506
507static inline int check_match(struct ipt_entry_match *m, const char *name,
508 const struct ipt_ip *ip, unsigned int hookmask)
509{
6709dbbb 510 struct xt_match *match;
a96be246
DM
511 int ret;
512
513 match = m->u.kernel.match;
514 ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m),
515 name, hookmask, ip->proto,
516 ip->invflags & IPT_INV_PROTO);
517 if (!ret && m->u.kernel.match->checkentry
518 && !m->u.kernel.match->checkentry(name, ip, match, m->data,
519 hookmask)) {
520 duprintf("ip_tables: check failed for `%s'.\n",
521 m->u.kernel.match->name);
522 ret = -EINVAL;
523 }
524 return ret;
525}
526
527static inline int
528find_check_match(struct ipt_entry_match *m,
1da177e4
LT
529 const char *name,
530 const struct ipt_ip *ip,
531 unsigned int hookmask,
532 unsigned int *i)
533{
6709dbbb 534 struct xt_match *match;
3cdc7c95 535 int ret;
1da177e4 536
2e4e6a17 537 match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
1da177e4
LT
538 m->u.user.revision),
539 "ipt_%s", m->u.user.name);
540 if (IS_ERR(match) || !match) {
a96be246 541 duprintf("find_check_match: `%s' not found\n", m->u.user.name);
1da177e4
LT
542 return match ? PTR_ERR(match) : -ENOENT;
543 }
544 m->u.kernel.match = match;
545
a96be246 546 ret = check_match(m, name, ip, hookmask);
3cdc7c95
PM
547 if (ret)
548 goto err;
549
1da177e4
LT
550 (*i)++;
551 return 0;
3cdc7c95
PM
552err:
553 module_put(m->u.kernel.match->me);
554 return ret;
1da177e4
LT
555}
556
a96be246
DM
557static inline int check_target(struct ipt_entry *e, const char *name)
558{
e905a9ed 559 struct ipt_entry_target *t;
6709dbbb 560 struct xt_target *target;
e905a9ed 561 int ret;
a96be246
DM
562
563 t = ipt_get_target(e);
564 target = t->u.kernel.target;
565 ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t),
566 name, e->comefrom, e->ip.proto,
567 e->ip.invflags & IPT_INV_PROTO);
568 if (!ret && t->u.kernel.target->checkentry
569 && !t->u.kernel.target->checkentry(name, e, target,
570 t->data, e->comefrom)) {
571 duprintf("ip_tables: check failed for `%s'.\n",
572 t->u.kernel.target->name);
573 ret = -EINVAL;
574 }
575 return ret;
576}
1da177e4
LT
577
578static inline int
a96be246 579find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
1da177e4
LT
580 unsigned int *i)
581{
582 struct ipt_entry_target *t;
6709dbbb 583 struct xt_target *target;
1da177e4
LT
584 int ret;
585 unsigned int j;
586
a96be246
DM
587 ret = check_entry(e, name);
588 if (ret)
589 return ret;
590bdf7f 590
1da177e4 591 j = 0;
a96be246
DM
592 ret = IPT_MATCH_ITERATE(e, find_check_match, name, &e->ip,
593 e->comefrom, &j);
1da177e4
LT
594 if (ret != 0)
595 goto cleanup_matches;
596
597 t = ipt_get_target(e);
2e4e6a17
HW
598 target = try_then_request_module(xt_find_target(AF_INET,
599 t->u.user.name,
1da177e4
LT
600 t->u.user.revision),
601 "ipt_%s", t->u.user.name);
602 if (IS_ERR(target) || !target) {
a96be246 603 duprintf("find_check_entry: `%s' not found\n", t->u.user.name);
1da177e4
LT
604 ret = target ? PTR_ERR(target) : -ENOENT;
605 goto cleanup_matches;
606 }
607 t->u.kernel.target = target;
608
a96be246 609 ret = check_target(e, name);
3cdc7c95
PM
610 if (ret)
611 goto err;
612
1da177e4
LT
613 (*i)++;
614 return 0;
3cdc7c95
PM
615 err:
616 module_put(t->u.kernel.target->me);
1da177e4
LT
617 cleanup_matches:
618 IPT_MATCH_ITERATE(e, cleanup_match, &j);
619 return ret;
620}
621
622static inline int
623check_entry_size_and_hooks(struct ipt_entry *e,
2e4e6a17 624 struct xt_table_info *newinfo,
1da177e4
LT
625 unsigned char *base,
626 unsigned char *limit,
627 const unsigned int *hook_entries,
628 const unsigned int *underflows,
629 unsigned int *i)
630{
631 unsigned int h;
632
633 if ((unsigned long)e % __alignof__(struct ipt_entry) != 0
634 || (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
635 duprintf("Bad offset %p\n", e);
636 return -EINVAL;
637 }
638
639 if (e->next_offset
640 < sizeof(struct ipt_entry) + sizeof(struct ipt_entry_target)) {
641 duprintf("checking: element %p size %u\n",
642 e, e->next_offset);
643 return -EINVAL;
644 }
645
646 /* Check hooks & underflows */
647 for (h = 0; h < NF_IP_NUMHOOKS; h++) {
648 if ((unsigned char *)e - base == hook_entries[h])
649 newinfo->hook_entry[h] = hook_entries[h];
650 if ((unsigned char *)e - base == underflows[h])
651 newinfo->underflow[h] = underflows[h];
652 }
653
654 /* FIXME: underflows must be unconditional, standard verdicts
e905a9ed 655 < 0 (not IPT_RETURN). --RR */
1da177e4
LT
656
657 /* Clear counters and comefrom */
2e4e6a17 658 e->counters = ((struct xt_counters) { 0, 0 });
1da177e4
LT
659 e->comefrom = 0;
660
661 (*i)++;
662 return 0;
663}
664
665static inline int
666cleanup_entry(struct ipt_entry *e, unsigned int *i)
667{
668 struct ipt_entry_target *t;
669
670 if (i && (*i)-- == 0)
671 return 1;
672
673 /* Cleanup all matches */
674 IPT_MATCH_ITERATE(e, cleanup_match, NULL);
675 t = ipt_get_target(e);
676 if (t->u.kernel.target->destroy)
efa74165 677 t->u.kernel.target->destroy(t->u.kernel.target, t->data);
1da177e4
LT
678 module_put(t->u.kernel.target->me);
679 return 0;
680}
681
682/* Checks and translates the user-supplied table segment (held in
683 newinfo) */
684static int
685translate_table(const char *name,
686 unsigned int valid_hooks,
2e4e6a17 687 struct xt_table_info *newinfo,
31836064 688 void *entry0,
1da177e4
LT
689 unsigned int size,
690 unsigned int number,
691 const unsigned int *hook_entries,
692 const unsigned int *underflows)
693{
694 unsigned int i;
695 int ret;
696
697 newinfo->size = size;
698 newinfo->number = number;
699
700 /* Init all hooks to impossible value. */
701 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
702 newinfo->hook_entry[i] = 0xFFFFFFFF;
703 newinfo->underflow[i] = 0xFFFFFFFF;
704 }
705
706 duprintf("translate_table: size %u\n", newinfo->size);
707 i = 0;
708 /* Walk through entries, checking offsets. */
31836064 709 ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
1da177e4
LT
710 check_entry_size_and_hooks,
711 newinfo,
31836064
ED
712 entry0,
713 entry0 + size,
1da177e4
LT
714 hook_entries, underflows, &i);
715 if (ret != 0)
716 return ret;
717
718 if (i != number) {
719 duprintf("translate_table: %u not %u entries\n",
720 i, number);
721 return -EINVAL;
722 }
723
724 /* Check hooks all assigned */
725 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
726 /* Only hooks which are valid */
727 if (!(valid_hooks & (1 << i)))
728 continue;
729 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
730 duprintf("Invalid hook entry %u %u\n",
731 i, hook_entries[i]);
732 return -EINVAL;
733 }
734 if (newinfo->underflow[i] == 0xFFFFFFFF) {
735 duprintf("Invalid underflow %u %u\n",
736 i, underflows[i]);
737 return -EINVAL;
738 }
739 }
740
74c9c0c1
DM
741 if (!mark_source_chains(newinfo, valid_hooks, entry0))
742 return -ELOOP;
743
1da177e4
LT
744 /* Finally, each sanity check must pass */
745 i = 0;
31836064 746 ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
a96be246 747 find_check_entry, name, size, &i);
1da177e4 748
74c9c0c1
DM
749 if (ret != 0) {
750 IPT_ENTRY_ITERATE(entry0, newinfo->size,
751 cleanup_entry, &i);
752 return ret;
753 }
1da177e4
LT
754
755 /* And one copy for every other CPU */
6f912042 756 for_each_possible_cpu(i) {
31836064
ED
757 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
758 memcpy(newinfo->entries[i], entry0, newinfo->size);
1da177e4
LT
759 }
760
761 return ret;
762}
763
1da177e4
LT
764/* Gets counters. */
765static inline int
766add_entry_to_counter(const struct ipt_entry *e,
2e4e6a17 767 struct xt_counters total[],
1da177e4
LT
768 unsigned int *i)
769{
770 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
771
772 (*i)++;
773 return 0;
774}
775
31836064
ED
776static inline int
777set_entry_to_counter(const struct ipt_entry *e,
778 struct ipt_counters total[],
779 unsigned int *i)
780{
781 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
782
783 (*i)++;
784 return 0;
785}
786
1da177e4 787static void
2e4e6a17
HW
788get_counters(const struct xt_table_info *t,
789 struct xt_counters counters[])
1da177e4
LT
790{
791 unsigned int cpu;
792 unsigned int i;
31836064
ED
793 unsigned int curcpu;
794
795 /* Instead of clearing (by a previous call to memset())
796 * the counters and using adds, we set the counters
797 * with data used by 'current' CPU
798 * We dont care about preemption here.
799 */
800 curcpu = raw_smp_processor_id();
801
802 i = 0;
803 IPT_ENTRY_ITERATE(t->entries[curcpu],
804 t->size,
805 set_entry_to_counter,
806 counters,
807 &i);
1da177e4 808
6f912042 809 for_each_possible_cpu(cpu) {
31836064
ED
810 if (cpu == curcpu)
811 continue;
1da177e4 812 i = 0;
31836064 813 IPT_ENTRY_ITERATE(t->entries[cpu],
1da177e4
LT
814 t->size,
815 add_entry_to_counter,
816 counters,
817 &i);
818 }
819}
820
e60a13e0 821static inline struct xt_counters * alloc_counters(struct xt_table *table)
1da177e4 822{
2722971c 823 unsigned int countersize;
2e4e6a17
HW
824 struct xt_counters *counters;
825 struct xt_table_info *private = table->private;
1da177e4
LT
826
827 /* We need atomic snapshot of counters: rest doesn't change
828 (other than comefrom, which userspace doesn't care
829 about). */
2e4e6a17 830 countersize = sizeof(struct xt_counters) * private->number;
31836064 831 counters = vmalloc_node(countersize, numa_node_id());
1da177e4
LT
832
833 if (counters == NULL)
2722971c 834 return ERR_PTR(-ENOMEM);
1da177e4
LT
835
836 /* First, sum counters... */
1da177e4 837 write_lock_bh(&table->lock);
2e4e6a17 838 get_counters(private, counters);
1da177e4
LT
839 write_unlock_bh(&table->lock);
840
2722971c
DM
841 return counters;
842}
843
844static int
845copy_entries_to_user(unsigned int total_size,
e60a13e0 846 struct xt_table *table,
2722971c
DM
847 void __user *userptr)
848{
849 unsigned int off, num;
850 struct ipt_entry *e;
851 struct xt_counters *counters;
852 struct xt_table_info *private = table->private;
853 int ret = 0;
854 void *loc_cpu_entry;
855
856 counters = alloc_counters(table);
857 if (IS_ERR(counters))
858 return PTR_ERR(counters);
859
31836064
ED
860 /* choose the copy that is on our node/cpu, ...
861 * This choice is lazy (because current thread is
862 * allowed to migrate to another cpu)
863 */
2e4e6a17 864 loc_cpu_entry = private->entries[raw_smp_processor_id()];
31836064
ED
865 /* ... then copy entire thing ... */
866 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
1da177e4
LT
867 ret = -EFAULT;
868 goto free_counters;
869 }
870
871 /* FIXME: use iterator macros --RR */
872 /* ... then go back and fix counters and names */
873 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
874 unsigned int i;
875 struct ipt_entry_match *m;
876 struct ipt_entry_target *t;
877
31836064 878 e = (struct ipt_entry *)(loc_cpu_entry + off);
1da177e4
LT
879 if (copy_to_user(userptr + off
880 + offsetof(struct ipt_entry, counters),
881 &counters[num],
882 sizeof(counters[num])) != 0) {
883 ret = -EFAULT;
884 goto free_counters;
885 }
886
887 for (i = sizeof(struct ipt_entry);
888 i < e->target_offset;
889 i += m->u.match_size) {
890 m = (void *)e + i;
891
892 if (copy_to_user(userptr + off + i
893 + offsetof(struct ipt_entry_match,
894 u.user.name),
895 m->u.kernel.match->name,
896 strlen(m->u.kernel.match->name)+1)
897 != 0) {
898 ret = -EFAULT;
899 goto free_counters;
900 }
901 }
902
903 t = ipt_get_target(e);
904 if (copy_to_user(userptr + off + e->target_offset
905 + offsetof(struct ipt_entry_target,
906 u.user.name),
907 t->u.kernel.target->name,
908 strlen(t->u.kernel.target->name)+1) != 0) {
909 ret = -EFAULT;
910 goto free_counters;
911 }
912 }
913
914 free_counters:
915 vfree(counters);
916 return ret;
917}
918
2722971c
DM
919#ifdef CONFIG_COMPAT
920struct compat_delta {
921 struct compat_delta *next;
e5b5ef7d 922 unsigned int offset;
2722971c
DM
923 short delta;
924};
925
926static struct compat_delta *compat_offsets = NULL;
927
e5b5ef7d 928static int compat_add_offset(unsigned int offset, short delta)
2722971c
DM
929{
930 struct compat_delta *tmp;
931
932 tmp = kmalloc(sizeof(struct compat_delta), GFP_KERNEL);
933 if (!tmp)
934 return -ENOMEM;
935 tmp->offset = offset;
936 tmp->delta = delta;
937 if (compat_offsets) {
938 tmp->next = compat_offsets->next;
939 compat_offsets->next = tmp;
940 } else {
941 compat_offsets = tmp;
942 tmp->next = NULL;
943 }
944 return 0;
945}
946
947static void compat_flush_offsets(void)
948{
949 struct compat_delta *tmp, *next;
950
951 if (compat_offsets) {
952 for(tmp = compat_offsets; tmp; tmp = next) {
953 next = tmp->next;
954 kfree(tmp);
955 }
956 compat_offsets = NULL;
957 }
958}
959
e5b5ef7d 960static short compat_calc_jump(unsigned int offset)
2722971c
DM
961{
962 struct compat_delta *tmp;
963 short delta;
964
965 for(tmp = compat_offsets, delta = 0; tmp; tmp = tmp->next)
966 if (tmp->offset < offset)
967 delta += tmp->delta;
968 return delta;
969}
970
9fa492cd 971static void compat_standard_from_user(void *dst, void *src)
2722971c 972{
9fa492cd 973 int v = *(compat_int_t *)src;
2722971c 974
9fa492cd
PM
975 if (v > 0)
976 v += compat_calc_jump(v);
977 memcpy(dst, &v, sizeof(v));
978}
46c5ea3c 979
9fa492cd 980static int compat_standard_to_user(void __user *dst, void *src)
2722971c 981{
9fa492cd 982 compat_int_t cv = *(int *)src;
2722971c 983
9fa492cd
PM
984 if (cv > 0)
985 cv -= compat_calc_jump(cv);
986 return copy_to_user(dst, &cv, sizeof(cv)) ? -EFAULT : 0;
2722971c
DM
987}
988
989static inline int
990compat_calc_match(struct ipt_entry_match *m, int * size)
991{
9fa492cd 992 *size += xt_compat_match_offset(m->u.kernel.match);
2722971c
DM
993 return 0;
994}
995
996static int compat_calc_entry(struct ipt_entry *e, struct xt_table_info *info,
997 void *base, struct xt_table_info *newinfo)
998{
999 struct ipt_entry_target *t;
e5b5ef7d 1000 unsigned int entry_offset;
2722971c
DM
1001 int off, i, ret;
1002
1003 off = 0;
1004 entry_offset = (void *)e - base;
1005 IPT_MATCH_ITERATE(e, compat_calc_match, &off);
1006 t = ipt_get_target(e);
9fa492cd 1007 off += xt_compat_target_offset(t->u.kernel.target);
2722971c
DM
1008 newinfo->size -= off;
1009 ret = compat_add_offset(entry_offset, off);
1010 if (ret)
1011 return ret;
1012
1013 for (i = 0; i< NF_IP_NUMHOOKS; i++) {
1014 if (info->hook_entry[i] && (e < (struct ipt_entry *)
1015 (base + info->hook_entry[i])))
1016 newinfo->hook_entry[i] -= off;
1017 if (info->underflow[i] && (e < (struct ipt_entry *)
1018 (base + info->underflow[i])))
1019 newinfo->underflow[i] -= off;
1020 }
1021 return 0;
1022}
1023
1024static int compat_table_info(struct xt_table_info *info,
1025 struct xt_table_info *newinfo)
1026{
1027 void *loc_cpu_entry;
1028 int i;
1029
1030 if (!newinfo || !info)
1031 return -EINVAL;
1032
1033 memset(newinfo, 0, sizeof(struct xt_table_info));
1034 newinfo->size = info->size;
1035 newinfo->number = info->number;
1036 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
1037 newinfo->hook_entry[i] = info->hook_entry[i];
1038 newinfo->underflow[i] = info->underflow[i];
1039 }
1040 loc_cpu_entry = info->entries[raw_smp_processor_id()];
1041 return IPT_ENTRY_ITERATE(loc_cpu_entry, info->size,
1042 compat_calc_entry, info, loc_cpu_entry, newinfo);
1043}
1044#endif
1045
1046static int get_info(void __user *user, int *len, int compat)
1047{
1048 char name[IPT_TABLE_MAXNAMELEN];
e60a13e0 1049 struct xt_table *t;
2722971c
DM
1050 int ret;
1051
1052 if (*len != sizeof(struct ipt_getinfo)) {
1053 duprintf("length %u != %u\n", *len,
1054 (unsigned int)sizeof(struct ipt_getinfo));
1055 return -EINVAL;
1056 }
1057
1058 if (copy_from_user(name, user, sizeof(name)) != 0)
1059 return -EFAULT;
1060
1061 name[IPT_TABLE_MAXNAMELEN-1] = '\0';
1062#ifdef CONFIG_COMPAT
1063 if (compat)
1064 xt_compat_lock(AF_INET);
1065#endif
1066 t = try_then_request_module(xt_find_table_lock(AF_INET, name),
1067 "iptable_%s", name);
1068 if (t && !IS_ERR(t)) {
1069 struct ipt_getinfo info;
1070 struct xt_table_info *private = t->private;
1071
1072#ifdef CONFIG_COMPAT
1073 if (compat) {
1074 struct xt_table_info tmp;
1075 ret = compat_table_info(private, &tmp);
1076 compat_flush_offsets();
1077 private = &tmp;
1078 }
1079#endif
1080 info.valid_hooks = t->valid_hooks;
1081 memcpy(info.hook_entry, private->hook_entry,
1082 sizeof(info.hook_entry));
1083 memcpy(info.underflow, private->underflow,
1084 sizeof(info.underflow));
1085 info.num_entries = private->number;
1086 info.size = private->size;
1087 strcpy(info.name, name);
1088
1089 if (copy_to_user(user, &info, *len) != 0)
1090 ret = -EFAULT;
1091 else
1092 ret = 0;
1093
1094 xt_table_unlock(t);
1095 module_put(t->me);
1096 } else
1097 ret = t ? PTR_ERR(t) : -ENOENT;
1098#ifdef CONFIG_COMPAT
1099 if (compat)
1100 xt_compat_unlock(AF_INET);
1101#endif
1102 return ret;
1103}
1104
1105static int
1106get_entries(struct ipt_get_entries __user *uptr, int *len)
1107{
1108 int ret;
1109 struct ipt_get_entries get;
e60a13e0 1110 struct xt_table *t;
2722971c
DM
1111
1112 if (*len < sizeof(get)) {
1113 duprintf("get_entries: %u < %d\n", *len,
1114 (unsigned int)sizeof(get));
1115 return -EINVAL;
1116 }
1117 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1118 return -EFAULT;
1119 if (*len != sizeof(struct ipt_get_entries) + get.size) {
1120 duprintf("get_entries: %u != %u\n", *len,
1121 (unsigned int)(sizeof(struct ipt_get_entries) +
1122 get.size));
1123 return -EINVAL;
1124 }
1125
1126 t = xt_find_table_lock(AF_INET, get.name);
1127 if (t && !IS_ERR(t)) {
1128 struct xt_table_info *private = t->private;
1129 duprintf("t->private->number = %u\n",
1130 private->number);
1131 if (get.size == private->size)
1132 ret = copy_entries_to_user(private->size,
1133 t, uptr->entrytable);
1134 else {
1135 duprintf("get_entries: I've got %u not %u!\n",
1136 private->size,
1137 get.size);
1138 ret = -EINVAL;
1139 }
1140 module_put(t->me);
1141 xt_table_unlock(t);
1142 } else
1143 ret = t ? PTR_ERR(t) : -ENOENT;
1144
1145 return ret;
1146}
1147
1148static int
1149__do_replace(const char *name, unsigned int valid_hooks,
1150 struct xt_table_info *newinfo, unsigned int num_counters,
1151 void __user *counters_ptr)
1152{
1153 int ret;
e60a13e0 1154 struct xt_table *t;
2722971c
DM
1155 struct xt_table_info *oldinfo;
1156 struct xt_counters *counters;
1157 void *loc_cpu_old_entry;
1158
1159 ret = 0;
1160 counters = vmalloc(num_counters * sizeof(struct xt_counters));
1161 if (!counters) {
1162 ret = -ENOMEM;
1163 goto out;
1164 }
1165
1166 t = try_then_request_module(xt_find_table_lock(AF_INET, name),
1167 "iptable_%s", name);
1168 if (!t || IS_ERR(t)) {
1169 ret = t ? PTR_ERR(t) : -ENOENT;
1170 goto free_newinfo_counters_untrans;
1171 }
1172
1173 /* You lied! */
1174 if (valid_hooks != t->valid_hooks) {
1175 duprintf("Valid hook crap: %08X vs %08X\n",
1176 valid_hooks, t->valid_hooks);
1177 ret = -EINVAL;
1178 goto put_module;
1179 }
1180
1181 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1182 if (!oldinfo)
1183 goto put_module;
1184
1185 /* Update module usage count based on number of rules */
1186 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1187 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1188 if ((oldinfo->number > oldinfo->initial_entries) ||
1189 (newinfo->number <= oldinfo->initial_entries))
1190 module_put(t->me);
1191 if ((oldinfo->number > oldinfo->initial_entries) &&
1192 (newinfo->number <= oldinfo->initial_entries))
1193 module_put(t->me);
1194
1195 /* Get the old counters. */
1196 get_counters(oldinfo, counters);
1197 /* Decrease module usage counts and free resource */
1198 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1199 IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1200 xt_free_table_info(oldinfo);
1201 if (copy_to_user(counters_ptr, counters,
1202 sizeof(struct xt_counters) * num_counters) != 0)
1203 ret = -EFAULT;
1204 vfree(counters);
1205 xt_table_unlock(t);
1206 return ret;
1207
1208 put_module:
1209 module_put(t->me);
1210 xt_table_unlock(t);
1211 free_newinfo_counters_untrans:
1212 vfree(counters);
1213 out:
1214 return ret;
1215}
1216
1217static int
1218do_replace(void __user *user, unsigned int len)
1219{
1220 int ret;
1221 struct ipt_replace tmp;
1222 struct xt_table_info *newinfo;
1223 void *loc_cpu_entry;
1224
1225 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1226 return -EFAULT;
1227
1228 /* Hack: Causes ipchains to give correct error msg --RR */
1229 if (len != sizeof(tmp) + tmp.size)
1230 return -ENOPROTOOPT;
1231
1232 /* overflow check */
1233 if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
1234 SMP_CACHE_BYTES)
1235 return -ENOMEM;
1236 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1237 return -ENOMEM;
1238
1239 newinfo = xt_alloc_table_info(tmp.size);
1240 if (!newinfo)
1241 return -ENOMEM;
1242
1243 /* choose the copy that is our node/cpu */
1244 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1245 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1246 tmp.size) != 0) {
1247 ret = -EFAULT;
1248 goto free_newinfo;
1249 }
1250
1251 ret = translate_table(tmp.name, tmp.valid_hooks,
1252 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
1253 tmp.hook_entry, tmp.underflow);
1254 if (ret != 0)
1255 goto free_newinfo;
1256
1257 duprintf("ip_tables: Translated table\n");
1258
1259 ret = __do_replace(tmp.name, tmp.valid_hooks,
1260 newinfo, tmp.num_counters,
1261 tmp.counters);
1262 if (ret)
1263 goto free_newinfo_untrans;
1264 return 0;
1265
1266 free_newinfo_untrans:
1267 IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1268 free_newinfo:
1269 xt_free_table_info(newinfo);
1270 return ret;
1271}
1272
1273/* We're lazy, and add to the first CPU; overflow works its fey magic
1274 * and everything is OK. */
1275static inline int
1276add_counter_to_entry(struct ipt_entry *e,
1277 const struct xt_counters addme[],
1278 unsigned int *i)
1279{
1280#if 0
1281 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1282 *i,
1283 (long unsigned int)e->counters.pcnt,
1284 (long unsigned int)e->counters.bcnt,
1285 (long unsigned int)addme[*i].pcnt,
1286 (long unsigned int)addme[*i].bcnt);
1287#endif
1288
1289 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1290
1291 (*i)++;
1292 return 0;
1293}
1294
1295static int
1296do_add_counters(void __user *user, unsigned int len, int compat)
1297{
1298 unsigned int i;
1299 struct xt_counters_info tmp;
1300 struct xt_counters *paddc;
1301 unsigned int num_counters;
1302 char *name;
1303 int size;
1304 void *ptmp;
e60a13e0 1305 struct xt_table *t;
2722971c
DM
1306 struct xt_table_info *private;
1307 int ret = 0;
1308 void *loc_cpu_entry;
1309#ifdef CONFIG_COMPAT
1310 struct compat_xt_counters_info compat_tmp;
1311
1312 if (compat) {
1313 ptmp = &compat_tmp;
1314 size = sizeof(struct compat_xt_counters_info);
1315 } else
1316#endif
1317 {
1318 ptmp = &tmp;
1319 size = sizeof(struct xt_counters_info);
1320 }
1321
1322 if (copy_from_user(ptmp, user, size) != 0)
1323 return -EFAULT;
1324
1325#ifdef CONFIG_COMPAT
1326 if (compat) {
1327 num_counters = compat_tmp.num_counters;
1328 name = compat_tmp.name;
1329 } else
1330#endif
1331 {
1332 num_counters = tmp.num_counters;
1333 name = tmp.name;
1334 }
1335
1336 if (len != size + num_counters * sizeof(struct xt_counters))
1337 return -EINVAL;
1338
1339 paddc = vmalloc_node(len - size, numa_node_id());
1340 if (!paddc)
1341 return -ENOMEM;
1342
1343 if (copy_from_user(paddc, user + size, len - size) != 0) {
1344 ret = -EFAULT;
1345 goto free;
1346 }
1347
1348 t = xt_find_table_lock(AF_INET, name);
1349 if (!t || IS_ERR(t)) {
1350 ret = t ? PTR_ERR(t) : -ENOENT;
1351 goto free;
1352 }
1353
1354 write_lock_bh(&t->lock);
1355 private = t->private;
1356 if (private->number != num_counters) {
1357 ret = -EINVAL;
1358 goto unlock_up_free;
1359 }
1360
1361 i = 0;
1362 /* Choose the copy that is on our node */
1363 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1364 IPT_ENTRY_ITERATE(loc_cpu_entry,
1365 private->size,
1366 add_counter_to_entry,
1367 paddc,
1368 &i);
1369 unlock_up_free:
1370 write_unlock_bh(&t->lock);
1371 xt_table_unlock(t);
1372 module_put(t->me);
1373 free:
1374 vfree(paddc);
1375
1376 return ret;
1377}
1378
1379#ifdef CONFIG_COMPAT
1380struct compat_ipt_replace {
1381 char name[IPT_TABLE_MAXNAMELEN];
1382 u32 valid_hooks;
1383 u32 num_entries;
1384 u32 size;
1385 u32 hook_entry[NF_IP_NUMHOOKS];
1386 u32 underflow[NF_IP_NUMHOOKS];
1387 u32 num_counters;
1388 compat_uptr_t counters; /* struct ipt_counters * */
1389 struct compat_ipt_entry entries[0];
1390};
1391
1392static inline int compat_copy_match_to_user(struct ipt_entry_match *m,
3e597c60 1393 void __user **dstptr, compat_uint_t *size)
2722971c 1394{
9fa492cd 1395 return xt_compat_match_to_user(m, dstptr, size);
2722971c
DM
1396}
1397
1398static int compat_copy_entry_to_user(struct ipt_entry *e,
3e597c60 1399 void __user **dstptr, compat_uint_t *size)
2722971c 1400{
3e597c60 1401 struct ipt_entry_target *t;
2722971c
DM
1402 struct compat_ipt_entry __user *ce;
1403 u_int16_t target_offset, next_offset;
1404 compat_uint_t origsize;
1405 int ret;
1406
1407 ret = -EFAULT;
1408 origsize = *size;
1409 ce = (struct compat_ipt_entry __user *)*dstptr;
7800007c 1410 if (copy_to_user(ce, e, sizeof(struct ipt_entry)))
2722971c
DM
1411 goto out;
1412
1413 *dstptr += sizeof(struct compat_ipt_entry);
1414 ret = IPT_MATCH_ITERATE(e, compat_copy_match_to_user, dstptr, size);
1415 target_offset = e->target_offset - (origsize - *size);
1416 if (ret)
1417 goto out;
1418 t = ipt_get_target(e);
9fa492cd 1419 ret = xt_compat_target_to_user(t, dstptr, size);
2722971c
DM
1420 if (ret)
1421 goto out;
1422 ret = -EFAULT;
1423 next_offset = e->next_offset - (origsize - *size);
7800007c 1424 if (put_user(target_offset, &ce->target_offset))
2722971c 1425 goto out;
7800007c 1426 if (put_user(next_offset, &ce->next_offset))
2722971c
DM
1427 goto out;
1428 return 0;
1429out:
1430 return ret;
1431}
1432
1433static inline int
1434compat_check_calc_match(struct ipt_entry_match *m,
1435 const char *name,
1436 const struct ipt_ip *ip,
1437 unsigned int hookmask,
1438 int *size, int *i)
1439{
6709dbbb 1440 struct xt_match *match;
2722971c
DM
1441
1442 match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
1443 m->u.user.revision),
1444 "ipt_%s", m->u.user.name);
1445 if (IS_ERR(match) || !match) {
1446 duprintf("compat_check_calc_match: `%s' not found\n",
1447 m->u.user.name);
1448 return match ? PTR_ERR(match) : -ENOENT;
1449 }
1450 m->u.kernel.match = match;
9fa492cd 1451 *size += xt_compat_match_offset(match);
2722971c
DM
1452
1453 (*i)++;
1454 return 0;
1455}
1456
1457static inline int
1458check_compat_entry_size_and_hooks(struct ipt_entry *e,
1459 struct xt_table_info *newinfo,
1460 unsigned int *size,
1461 unsigned char *base,
1462 unsigned char *limit,
1463 unsigned int *hook_entries,
1464 unsigned int *underflows,
1465 unsigned int *i,
1466 const char *name)
1467{
1468 struct ipt_entry_target *t;
6709dbbb 1469 struct xt_target *target;
e5b5ef7d 1470 unsigned int entry_offset;
2722971c
DM
1471 int ret, off, h, j;
1472
1473 duprintf("check_compat_entry_size_and_hooks %p\n", e);
1474 if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0
1475 || (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
1476 duprintf("Bad offset %p, limit = %p\n", e, limit);
1477 return -EINVAL;
1478 }
1479
1480 if (e->next_offset < sizeof(struct compat_ipt_entry) +
1481 sizeof(struct compat_xt_entry_target)) {
1482 duprintf("checking: element %p size %u\n",
1483 e, e->next_offset);
1484 return -EINVAL;
1485 }
1486
a96be246
DM
1487 ret = check_entry(e, name);
1488 if (ret)
1489 return ret;
590bdf7f 1490
2722971c
DM
1491 off = 0;
1492 entry_offset = (void *)e - (void *)base;
1493 j = 0;
1494 ret = IPT_MATCH_ITERATE(e, compat_check_calc_match, name, &e->ip,
1495 e->comefrom, &off, &j);
1496 if (ret != 0)
bec71b16 1497 goto cleanup_matches;
2722971c
DM
1498
1499 t = ipt_get_target(e);
1500 target = try_then_request_module(xt_find_target(AF_INET,
1501 t->u.user.name,
1502 t->u.user.revision),
1503 "ipt_%s", t->u.user.name);
1504 if (IS_ERR(target) || !target) {
a96be246
DM
1505 duprintf("check_compat_entry_size_and_hooks: `%s' not found\n",
1506 t->u.user.name);
2722971c 1507 ret = target ? PTR_ERR(target) : -ENOENT;
bec71b16 1508 goto cleanup_matches;
2722971c
DM
1509 }
1510 t->u.kernel.target = target;
1511
9fa492cd 1512 off += xt_compat_target_offset(target);
2722971c
DM
1513 *size += off;
1514 ret = compat_add_offset(entry_offset, off);
1515 if (ret)
1516 goto out;
1517
1518 /* Check hooks & underflows */
1519 for (h = 0; h < NF_IP_NUMHOOKS; h++) {
1520 if ((unsigned char *)e - base == hook_entries[h])
1521 newinfo->hook_entry[h] = hook_entries[h];
1522 if ((unsigned char *)e - base == underflows[h])
1523 newinfo->underflow[h] = underflows[h];
1524 }
1525
1526 /* Clear counters and comefrom */
1527 e->counters = ((struct ipt_counters) { 0, 0 });
1528 e->comefrom = 0;
1529
1530 (*i)++;
1531 return 0;
bec71b16 1532
2722971c 1533out:
bec71b16
PM
1534 module_put(t->u.kernel.target->me);
1535cleanup_matches:
2722971c
DM
1536 IPT_MATCH_ITERATE(e, cleanup_match, &j);
1537 return ret;
1538}
1539
1540static inline int compat_copy_match_from_user(struct ipt_entry_match *m,
1541 void **dstptr, compat_uint_t *size, const char *name,
920b868a 1542 const struct ipt_ip *ip, unsigned int hookmask)
2722971c 1543{
9fa492cd 1544 xt_compat_match_from_user(m, dstptr, size);
f6677f43 1545 return 0;
2722971c
DM
1546}
1547
1548static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
1549 unsigned int *size, const char *name,
1550 struct xt_table_info *newinfo, unsigned char *base)
1551{
1552 struct ipt_entry_target *t;
6709dbbb 1553 struct xt_target *target;
2722971c
DM
1554 struct ipt_entry *de;
1555 unsigned int origsize;
920b868a 1556 int ret, h;
2722971c
DM
1557
1558 ret = 0;
1559 origsize = *size;
1560 de = (struct ipt_entry *)*dstptr;
1561 memcpy(de, e, sizeof(struct ipt_entry));
1562
1563 *dstptr += sizeof(struct compat_ipt_entry);
1564 ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size,
920b868a 1565 name, &de->ip, de->comefrom);
2722971c 1566 if (ret)
f6677f43 1567 return ret;
2722971c
DM
1568 de->target_offset = e->target_offset - (origsize - *size);
1569 t = ipt_get_target(e);
1570 target = t->u.kernel.target;
9fa492cd 1571 xt_compat_target_from_user(t, dstptr, size);
2722971c
DM
1572
1573 de->next_offset = e->next_offset - (origsize - *size);
1574 for (h = 0; h < NF_IP_NUMHOOKS; h++) {
1575 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1576 newinfo->hook_entry[h] -= origsize - *size;
1577 if ((unsigned char *)de - base < newinfo->underflow[h])
1578 newinfo->underflow[h] -= origsize - *size;
1579 }
f6677f43
DM
1580 return ret;
1581}
1582
f6677f43
DM
1583static inline int compat_check_entry(struct ipt_entry *e, const char *name)
1584{
1585 int ret;
1586
a96be246 1587 ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom);
f6677f43
DM
1588 if (ret)
1589 return ret;
1590
a96be246 1591 return check_target(e, name);
f6677f43
DM
1592}
1593
1da177e4 1594static int
2722971c
DM
1595translate_compat_table(const char *name,
1596 unsigned int valid_hooks,
1597 struct xt_table_info **pinfo,
1598 void **pentry0,
1599 unsigned int total_size,
1600 unsigned int number,
1601 unsigned int *hook_entries,
1602 unsigned int *underflows)
1da177e4 1603{
920b868a 1604 unsigned int i, j;
2722971c
DM
1605 struct xt_table_info *newinfo, *info;
1606 void *pos, *entry0, *entry1;
1607 unsigned int size;
1da177e4 1608 int ret;
1da177e4 1609
2722971c
DM
1610 info = *pinfo;
1611 entry0 = *pentry0;
1612 size = total_size;
1613 info->number = number;
1614
1615 /* Init all hooks to impossible value. */
1616 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
1617 info->hook_entry[i] = 0xFFFFFFFF;
1618 info->underflow[i] = 0xFFFFFFFF;
1619 }
1620
1621 duprintf("translate_compat_table: size %u\n", info->size);
920b868a 1622 j = 0;
2722971c
DM
1623 xt_compat_lock(AF_INET);
1624 /* Walk through entries, checking offsets. */
1625 ret = IPT_ENTRY_ITERATE(entry0, total_size,
1626 check_compat_entry_size_and_hooks,
1627 info, &size, entry0,
1628 entry0 + total_size,
920b868a 1629 hook_entries, underflows, &j, name);
2722971c
DM
1630 if (ret != 0)
1631 goto out_unlock;
1632
1633 ret = -EINVAL;
920b868a 1634 if (j != number) {
2722971c 1635 duprintf("translate_compat_table: %u not %u entries\n",
920b868a 1636 j, number);
2722971c
DM
1637 goto out_unlock;
1638 }
1639
1640 /* Check hooks all assigned */
1641 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
1642 /* Only hooks which are valid */
1643 if (!(valid_hooks & (1 << i)))
1644 continue;
1645 if (info->hook_entry[i] == 0xFFFFFFFF) {
1646 duprintf("Invalid hook entry %u %u\n",
1647 i, hook_entries[i]);
1648 goto out_unlock;
1da177e4 1649 }
2722971c
DM
1650 if (info->underflow[i] == 0xFFFFFFFF) {
1651 duprintf("Invalid underflow %u %u\n",
1652 i, underflows[i]);
1653 goto out_unlock;
1654 }
1655 }
1656
1657 ret = -ENOMEM;
1658 newinfo = xt_alloc_table_info(size);
1659 if (!newinfo)
1660 goto out_unlock;
1661
1662 newinfo->number = number;
1663 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
1664 newinfo->hook_entry[i] = info->hook_entry[i];
1665 newinfo->underflow[i] = info->underflow[i];
1666 }
1667 entry1 = newinfo->entries[raw_smp_processor_id()];
1668 pos = entry1;
1669 size = total_size;
1670 ret = IPT_ENTRY_ITERATE(entry0, total_size,
1671 compat_copy_entry_from_user, &pos, &size,
1672 name, newinfo, entry1);
1673 compat_flush_offsets();
1674 xt_compat_unlock(AF_INET);
1675 if (ret)
1676 goto free_newinfo;
1677
1678 ret = -ELOOP;
1679 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1680 goto free_newinfo;
1681
f6677f43
DM
1682 ret = IPT_ENTRY_ITERATE(entry1, newinfo->size, compat_check_entry,
1683 name);
1684 if (ret)
1685 goto free_newinfo;
1686
2722971c 1687 /* And one copy for every other CPU */
fb1bb34d 1688 for_each_possible_cpu(i)
2722971c
DM
1689 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1690 memcpy(newinfo->entries[i], entry1, newinfo->size);
1691
1692 *pinfo = newinfo;
1693 *pentry0 = entry1;
1694 xt_free_table_info(info);
1695 return 0;
1da177e4 1696
2722971c
DM
1697free_newinfo:
1698 xt_free_table_info(newinfo);
1699out:
920b868a 1700 IPT_ENTRY_ITERATE(entry0, total_size, cleanup_entry, &j);
1da177e4 1701 return ret;
2722971c 1702out_unlock:
ef4512e7 1703 compat_flush_offsets();
2722971c
DM
1704 xt_compat_unlock(AF_INET);
1705 goto out;
1da177e4
LT
1706}
1707
1708static int
2722971c 1709compat_do_replace(void __user *user, unsigned int len)
1da177e4
LT
1710{
1711 int ret;
2722971c
DM
1712 struct compat_ipt_replace tmp;
1713 struct xt_table_info *newinfo;
1714 void *loc_cpu_entry;
1da177e4
LT
1715
1716 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1717 return -EFAULT;
1718
1719 /* Hack: Causes ipchains to give correct error msg --RR */
1720 if (len != sizeof(tmp) + tmp.size)
1721 return -ENOPROTOOPT;
1722
ee4bb818
KK
1723 /* overflow check */
1724 if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
1725 SMP_CACHE_BYTES)
1726 return -ENOMEM;
1727 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1728 return -ENOMEM;
1729
2e4e6a17 1730 newinfo = xt_alloc_table_info(tmp.size);
1da177e4
LT
1731 if (!newinfo)
1732 return -ENOMEM;
1733
31836064
ED
1734 /* choose the copy that is our node/cpu */
1735 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1736 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1da177e4
LT
1737 tmp.size) != 0) {
1738 ret = -EFAULT;
1739 goto free_newinfo;
1740 }
1741
2722971c
DM
1742 ret = translate_compat_table(tmp.name, tmp.valid_hooks,
1743 &newinfo, &loc_cpu_entry, tmp.size,
1744 tmp.num_entries, tmp.hook_entry, tmp.underflow);
1745 if (ret != 0)
1da177e4 1746 goto free_newinfo;
1da177e4 1747
2722971c 1748 duprintf("compat_do_replace: Translated table\n");
1da177e4 1749
2722971c
DM
1750 ret = __do_replace(tmp.name, tmp.valid_hooks,
1751 newinfo, tmp.num_counters,
1752 compat_ptr(tmp.counters));
1753 if (ret)
1754 goto free_newinfo_untrans;
1755 return 0;
1da177e4 1756
2722971c
DM
1757 free_newinfo_untrans:
1758 IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1759 free_newinfo:
1760 xt_free_table_info(newinfo);
1761 return ret;
1762}
1da177e4 1763
2722971c
DM
1764static int
1765compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
1766 unsigned int len)
1767{
1768 int ret;
1da177e4 1769
2722971c
DM
1770 if (!capable(CAP_NET_ADMIN))
1771 return -EPERM;
1da177e4 1772
2722971c
DM
1773 switch (cmd) {
1774 case IPT_SO_SET_REPLACE:
1775 ret = compat_do_replace(user, len);
1776 break;
1da177e4 1777
2722971c
DM
1778 case IPT_SO_SET_ADD_COUNTERS:
1779 ret = do_add_counters(user, len, 1);
1780 break;
1781
1782 default:
1783 duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
1784 ret = -EINVAL;
1785 }
1da177e4 1786
1da177e4
LT
1787 return ret;
1788}
1789
2722971c 1790struct compat_ipt_get_entries
1da177e4 1791{
2722971c
DM
1792 char name[IPT_TABLE_MAXNAMELEN];
1793 compat_uint_t size;
1794 struct compat_ipt_entry entrytable[0];
1795};
1da177e4 1796
2722971c 1797static int compat_copy_entries_to_user(unsigned int total_size,
e60a13e0 1798 struct xt_table *table, void __user *userptr)
2722971c
DM
1799{
1800 unsigned int off, num;
1801 struct compat_ipt_entry e;
1802 struct xt_counters *counters;
1803 struct xt_table_info *private = table->private;
1804 void __user *pos;
1805 unsigned int size;
1806 int ret = 0;
1807 void *loc_cpu_entry;
1da177e4 1808
2722971c
DM
1809 counters = alloc_counters(table);
1810 if (IS_ERR(counters))
1811 return PTR_ERR(counters);
1812
1813 /* choose the copy that is on our node/cpu, ...
1814 * This choice is lazy (because current thread is
1815 * allowed to migrate to another cpu)
1816 */
1817 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1818 pos = userptr;
1819 size = total_size;
1820 ret = IPT_ENTRY_ITERATE(loc_cpu_entry, total_size,
1821 compat_copy_entry_to_user, &pos, &size);
1822 if (ret)
1823 goto free_counters;
1824
1825 /* ... then go back and fix counters and names */
1826 for (off = 0, num = 0; off < size; off += e.next_offset, num++) {
1827 unsigned int i;
1828 struct ipt_entry_match m;
1829 struct ipt_entry_target t;
1830
1831 ret = -EFAULT;
1832 if (copy_from_user(&e, userptr + off,
1833 sizeof(struct compat_ipt_entry)))
1834 goto free_counters;
1835 if (copy_to_user(userptr + off +
1836 offsetof(struct compat_ipt_entry, counters),
1837 &counters[num], sizeof(counters[num])))
1838 goto free_counters;
1839
1840 for (i = sizeof(struct compat_ipt_entry);
1841 i < e.target_offset; i += m.u.match_size) {
1842 if (copy_from_user(&m, userptr + off + i,
1843 sizeof(struct ipt_entry_match)))
1844 goto free_counters;
1845 if (copy_to_user(userptr + off + i +
1846 offsetof(struct ipt_entry_match, u.user.name),
1847 m.u.kernel.match->name,
1848 strlen(m.u.kernel.match->name) + 1))
1849 goto free_counters;
1850 }
1851
1852 if (copy_from_user(&t, userptr + off + e.target_offset,
1853 sizeof(struct ipt_entry_target)))
1854 goto free_counters;
1855 if (copy_to_user(userptr + off + e.target_offset +
1856 offsetof(struct ipt_entry_target, u.user.name),
1857 t.u.kernel.target->name,
1858 strlen(t.u.kernel.target->name) + 1))
1859 goto free_counters;
1860 }
1861 ret = 0;
1862free_counters:
1863 vfree(counters);
1864 return ret;
1da177e4
LT
1865}
1866
1867static int
2722971c 1868compat_get_entries(struct compat_ipt_get_entries __user *uptr, int *len)
1da177e4 1869{
2722971c
DM
1870 int ret;
1871 struct compat_ipt_get_entries get;
e60a13e0 1872 struct xt_table *t;
1da177e4 1873
1da177e4 1874
2722971c
DM
1875 if (*len < sizeof(get)) {
1876 duprintf("compat_get_entries: %u < %u\n",
1877 *len, (unsigned int)sizeof(get));
1da177e4 1878 return -EINVAL;
2722971c 1879 }
1da177e4 1880
2722971c
DM
1881 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1882 return -EFAULT;
1da177e4 1883
2722971c
DM
1884 if (*len != sizeof(struct compat_ipt_get_entries) + get.size) {
1885 duprintf("compat_get_entries: %u != %u\n", *len,
1886 (unsigned int)(sizeof(struct compat_ipt_get_entries) +
1887 get.size));
1888 return -EINVAL;
1da177e4
LT
1889 }
1890
2722971c
DM
1891 xt_compat_lock(AF_INET);
1892 t = xt_find_table_lock(AF_INET, get.name);
1893 if (t && !IS_ERR(t)) {
1894 struct xt_table_info *private = t->private;
1895 struct xt_table_info info;
1896 duprintf("t->private->number = %u\n",
1897 private->number);
1898 ret = compat_table_info(private, &info);
1899 if (!ret && get.size == info.size) {
1900 ret = compat_copy_entries_to_user(private->size,
1901 t, uptr->entrytable);
1902 } else if (!ret) {
1903 duprintf("compat_get_entries: I've got %u not %u!\n",
1904 private->size,
1905 get.size);
1906 ret = -EINVAL;
1907 }
1908 compat_flush_offsets();
1909 module_put(t->me);
1910 xt_table_unlock(t);
1911 } else
1da177e4 1912 ret = t ? PTR_ERR(t) : -ENOENT;
1da177e4 1913
2722971c
DM
1914 xt_compat_unlock(AF_INET);
1915 return ret;
1916}
1da177e4 1917
79030ed0
PM
1918static int do_ipt_get_ctl(struct sock *, int, void __user *, int *);
1919
2722971c
DM
1920static int
1921compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1922{
1923 int ret;
1da177e4 1924
82fac054
BS
1925 if (!capable(CAP_NET_ADMIN))
1926 return -EPERM;
1927
2722971c
DM
1928 switch (cmd) {
1929 case IPT_SO_GET_INFO:
1930 ret = get_info(user, len, 1);
1931 break;
1932 case IPT_SO_GET_ENTRIES:
1933 ret = compat_get_entries(user, len);
1934 break;
1935 default:
79030ed0 1936 ret = do_ipt_get_ctl(sk, cmd, user, len);
2722971c 1937 }
1da177e4
LT
1938 return ret;
1939}
2722971c 1940#endif
1da177e4
LT
1941
1942static int
1943do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
1944{
1945 int ret;
1946
1947 if (!capable(CAP_NET_ADMIN))
1948 return -EPERM;
1949
1950 switch (cmd) {
1951 case IPT_SO_SET_REPLACE:
1952 ret = do_replace(user, len);
1953 break;
1954
1955 case IPT_SO_SET_ADD_COUNTERS:
2722971c 1956 ret = do_add_counters(user, len, 0);
1da177e4
LT
1957 break;
1958
1959 default:
1960 duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
1961 ret = -EINVAL;
1962 }
1963
1964 return ret;
1965}
1966
1967static int
1968do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1969{
1970 int ret;
1971
1972 if (!capable(CAP_NET_ADMIN))
1973 return -EPERM;
1974
1975 switch (cmd) {
2722971c
DM
1976 case IPT_SO_GET_INFO:
1977 ret = get_info(user, len, 0);
1978 break;
1da177e4 1979
2722971c
DM
1980 case IPT_SO_GET_ENTRIES:
1981 ret = get_entries(user, len);
1da177e4 1982 break;
1da177e4
LT
1983
1984 case IPT_SO_GET_REVISION_MATCH:
1985 case IPT_SO_GET_REVISION_TARGET: {
1986 struct ipt_get_revision rev;
2e4e6a17 1987 int target;
1da177e4
LT
1988
1989 if (*len != sizeof(rev)) {
1990 ret = -EINVAL;
1991 break;
1992 }
1993 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
1994 ret = -EFAULT;
1995 break;
1996 }
1997
1998 if (cmd == IPT_SO_GET_REVISION_TARGET)
2e4e6a17 1999 target = 1;
1da177e4 2000 else
2e4e6a17 2001 target = 0;
1da177e4 2002
2e4e6a17
HW
2003 try_then_request_module(xt_find_revision(AF_INET, rev.name,
2004 rev.revision,
2005 target, &ret),
1da177e4
LT
2006 "ipt_%s", rev.name);
2007 break;
2008 }
2009
2010 default:
2011 duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
2012 ret = -EINVAL;
2013 }
2014
2015 return ret;
2016}
2017
2e4e6a17 2018int ipt_register_table(struct xt_table *table, const struct ipt_replace *repl)
1da177e4
LT
2019{
2020 int ret;
2e4e6a17
HW
2021 struct xt_table_info *newinfo;
2022 static struct xt_table_info bootstrap
1da177e4 2023 = { 0, 0, 0, { 0 }, { 0 }, { } };
31836064 2024 void *loc_cpu_entry;
1da177e4 2025
2e4e6a17 2026 newinfo = xt_alloc_table_info(repl->size);
1da177e4
LT
2027 if (!newinfo)
2028 return -ENOMEM;
2029
31836064
ED
2030 /* choose the copy on our node/cpu
2031 * but dont care of preemption
2032 */
2033 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2034 memcpy(loc_cpu_entry, repl->entries, repl->size);
1da177e4
LT
2035
2036 ret = translate_table(table->name, table->valid_hooks,
31836064 2037 newinfo, loc_cpu_entry, repl->size,
1da177e4
LT
2038 repl->num_entries,
2039 repl->hook_entry,
2040 repl->underflow);
2041 if (ret != 0) {
2e4e6a17 2042 xt_free_table_info(newinfo);
1da177e4
LT
2043 return ret;
2044 }
2045
da298d3a
PM
2046 ret = xt_register_table(table, &bootstrap, newinfo);
2047 if (ret != 0) {
2e4e6a17 2048 xt_free_table_info(newinfo);
1da177e4
LT
2049 return ret;
2050 }
2051
2e4e6a17 2052 return 0;
1da177e4
LT
2053}
2054
e60a13e0 2055void ipt_unregister_table(struct xt_table *table)
1da177e4 2056{
2e4e6a17 2057 struct xt_table_info *private;
31836064
ED
2058 void *loc_cpu_entry;
2059
e905a9ed 2060 private = xt_unregister_table(table);
1da177e4
LT
2061
2062 /* Decrease module usage counts and free resources */
2e4e6a17
HW
2063 loc_cpu_entry = private->entries[raw_smp_processor_id()];
2064 IPT_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
2065 xt_free_table_info(private);
1da177e4
LT
2066}
2067
2068/* Returns 1 if the type and code is matched by the range, 0 otherwise */
2069static inline int
2070icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2071 u_int8_t type, u_int8_t code,
2072 int invert)
2073{
2074 return ((test_type == 0xFF) || (type == test_type && code >= min_code && code <= max_code))
2075 ^ invert;
2076}
2077
2078static int
2079icmp_match(const struct sk_buff *skb,
2080 const struct net_device *in,
2081 const struct net_device *out,
c4986734 2082 const struct xt_match *match,
1da177e4
LT
2083 const void *matchinfo,
2084 int offset,
2e4e6a17 2085 unsigned int protoff,
1da177e4
LT
2086 int *hotdrop)
2087{
2088 struct icmphdr _icmph, *ic;
2089 const struct ipt_icmp *icmpinfo = matchinfo;
2090
2091 /* Must not be a fragment. */
2092 if (offset)
2093 return 0;
2094
2e4e6a17 2095 ic = skb_header_pointer(skb, protoff, sizeof(_icmph), &_icmph);
1da177e4
LT
2096 if (ic == NULL) {
2097 /* We've been asked to examine this packet, and we
2098 * can't. Hence, no choice but to drop.
2099 */
2100 duprintf("Dropping evil ICMP tinygram.\n");
2101 *hotdrop = 1;
2102 return 0;
2103 }
2104
2105 return icmp_type_code_match(icmpinfo->type,
2106 icmpinfo->code[0],
2107 icmpinfo->code[1],
2108 ic->type, ic->code,
2109 !!(icmpinfo->invflags&IPT_ICMP_INV));
2110}
2111
2112/* Called when user tries to insert an entry of this type. */
2113static int
2114icmp_checkentry(const char *tablename,
2e4e6a17 2115 const void *info,
c4986734 2116 const struct xt_match *match,
1da177e4 2117 void *matchinfo,
1da177e4
LT
2118 unsigned int hook_mask)
2119{
2120 const struct ipt_icmp *icmpinfo = matchinfo;
2121
1d5cd909
PM
2122 /* Must specify no unknown invflags */
2123 return !(icmpinfo->invflags & ~IPT_ICMP_INV);
1da177e4
LT
2124}
2125
2126/* The built-in targets: standard (NULL) and error. */
6709dbbb 2127static struct xt_target ipt_standard_target = {
1da177e4 2128 .name = IPT_STANDARD_TARGET,
1d5cd909 2129 .targetsize = sizeof(int),
a45049c5 2130 .family = AF_INET,
2722971c 2131#ifdef CONFIG_COMPAT
9fa492cd
PM
2132 .compatsize = sizeof(compat_int_t),
2133 .compat_from_user = compat_standard_from_user,
2134 .compat_to_user = compat_standard_to_user,
2722971c 2135#endif
1da177e4
LT
2136};
2137
6709dbbb 2138static struct xt_target ipt_error_target = {
1da177e4
LT
2139 .name = IPT_ERROR_TARGET,
2140 .target = ipt_error,
1d5cd909 2141 .targetsize = IPT_FUNCTION_MAXNAMELEN,
a45049c5 2142 .family = AF_INET,
1da177e4
LT
2143};
2144
2145static struct nf_sockopt_ops ipt_sockopts = {
2146 .pf = PF_INET,
2147 .set_optmin = IPT_BASE_CTL,
2148 .set_optmax = IPT_SO_SET_MAX+1,
2149 .set = do_ipt_set_ctl,
2722971c
DM
2150#ifdef CONFIG_COMPAT
2151 .compat_set = compat_do_ipt_set_ctl,
2152#endif
1da177e4
LT
2153 .get_optmin = IPT_BASE_CTL,
2154 .get_optmax = IPT_SO_GET_MAX+1,
2155 .get = do_ipt_get_ctl,
2722971c
DM
2156#ifdef CONFIG_COMPAT
2157 .compat_get = compat_do_ipt_get_ctl,
2158#endif
1da177e4
LT
2159};
2160
6709dbbb 2161static struct xt_match icmp_matchstruct = {
1da177e4 2162 .name = "icmp",
1d5cd909
PM
2163 .match = icmp_match,
2164 .matchsize = sizeof(struct ipt_icmp),
2165 .proto = IPPROTO_ICMP,
a45049c5 2166 .family = AF_INET,
1d5cd909 2167 .checkentry = icmp_checkentry,
1da177e4
LT
2168};
2169
65b4b4e8 2170static int __init ip_tables_init(void)
1da177e4
LT
2171{
2172 int ret;
2173
0eff66e6
PM
2174 ret = xt_proto_init(AF_INET);
2175 if (ret < 0)
2176 goto err1;
2e4e6a17 2177
1da177e4 2178 /* Noone else will be downing sem now, so we won't sleep */
0eff66e6
PM
2179 ret = xt_register_target(&ipt_standard_target);
2180 if (ret < 0)
2181 goto err2;
2182 ret = xt_register_target(&ipt_error_target);
2183 if (ret < 0)
2184 goto err3;
2185 ret = xt_register_match(&icmp_matchstruct);
2186 if (ret < 0)
2187 goto err4;
1da177e4
LT
2188
2189 /* Register setsockopt */
2190 ret = nf_register_sockopt(&ipt_sockopts);
0eff66e6
PM
2191 if (ret < 0)
2192 goto err5;
1da177e4 2193
2e4e6a17 2194 printk("ip_tables: (C) 2000-2006 Netfilter Core Team\n");
1da177e4 2195 return 0;
0eff66e6
PM
2196
2197err5:
2198 xt_unregister_match(&icmp_matchstruct);
2199err4:
2200 xt_unregister_target(&ipt_error_target);
2201err3:
2202 xt_unregister_target(&ipt_standard_target);
2203err2:
2204 xt_proto_fini(AF_INET);
2205err1:
2206 return ret;
1da177e4
LT
2207}
2208
65b4b4e8 2209static void __exit ip_tables_fini(void)
1da177e4
LT
2210{
2211 nf_unregister_sockopt(&ipt_sockopts);
2e4e6a17 2212
a45049c5
PNA
2213 xt_unregister_match(&icmp_matchstruct);
2214 xt_unregister_target(&ipt_error_target);
2215 xt_unregister_target(&ipt_standard_target);
2e4e6a17
HW
2216
2217 xt_proto_fini(AF_INET);
1da177e4
LT
2218}
2219
2220EXPORT_SYMBOL(ipt_register_table);
2221EXPORT_SYMBOL(ipt_unregister_table);
1da177e4 2222EXPORT_SYMBOL(ipt_do_table);
65b4b4e8
AM
2223module_init(ip_tables_init);
2224module_exit(ip_tables_fini);