blob: d25ac8ba6ebaedb0e0e1cbe658b80c94e0814b39 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * Packet matching code.
3 *
4 * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
Harald Welte2e4e6a12006-01-12 13:30:04 -08005 * Copyright (C) 2000-2005 Netfilter Core Team <coreteam@netfilter.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 *
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
Harald Welte2e4e6a12006-01-12 13:30:04 -080014 * 08 Oct 2005 Harald Welte <lafore@netfilter.org>
15 * - Generalize into "x_tables" layer and "{ip,ip6,arp}_tables"
Linus Torvalds1da177e2005-04-16 15:20:36 -070016 */
17#include <linux/config.h>
18#include <linux/cache.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080019#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/skbuff.h>
21#include <linux/kmod.h>
22#include <linux/vmalloc.h>
23#include <linux/netdevice.h>
24#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <linux/icmp.h>
26#include <net/ip.h>
Dmitry Mishin27229712006-04-01 02:25:19 -080027#include <net/compat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <asm/uaccess.h>
Ingo Molnar57b47a52006-03-20 22:35:41 -080029#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/proc_fs.h>
31#include <linux/err.h>
David S. Millerc8923c62005-10-13 14:41:23 -070032#include <linux/cpumask.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Harald Welte2e4e6a12006-01-12 13:30:04 -080034#include <linux/netfilter/x_tables.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/netfilter_ipv4/ip_tables.h>
36
37MODULE_LICENSE("GPL");
38MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
39MODULE_DESCRIPTION("IPv4 packet filter");
40
41/*#define DEBUG_IP_FIREWALL*/
42/*#define DEBUG_ALLOW_ALL*/ /* Useful for remote debugging */
43/*#define DEBUG_IP_FIREWALL_USER*/
44
45#ifdef DEBUG_IP_FIREWALL
46#define dprintf(format, args...) printk(format , ## args)
47#else
48#define dprintf(format, args...)
49#endif
50
51#ifdef DEBUG_IP_FIREWALL_USER
52#define duprintf(format, args...) printk(format , ## args)
53#else
54#define duprintf(format, args...)
55#endif
56
57#ifdef CONFIG_NETFILTER_DEBUG
58#define IP_NF_ASSERT(x) \
59do { \
60 if (!(x)) \
61 printk("IP_NF_ASSERT: %s:%s:%u\n", \
62 __FUNCTION__, __FILE__, __LINE__); \
63} while(0)
64#else
65#define IP_NF_ASSERT(x)
66#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68#if 0
69/* All the better to debug you with... */
70#define static
71#define inline
72#endif
73
74/*
75 We keep a set of rules for each CPU, so we can avoid write-locking
76 them in the softirq when updating the counters and therefore
77 only need to read-lock in the softirq; doing a write_lock_bh() in user
78 context stops packets coming through and allows user context to read
79 the counters or update the rules.
80
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 Hence the start of any table is given by get_table() below. */
82
Linus Torvalds1da177e2005-04-16 15:20:36 -070083/* Returns whether matches rule or not. */
84static inline int
85ip_packet_match(const struct iphdr *ip,
86 const char *indev,
87 const char *outdev,
88 const struct ipt_ip *ipinfo,
89 int isfrag)
90{
91 size_t i;
92 unsigned long ret;
93
94#define FWINV(bool,invflg) ((bool) ^ !!(ipinfo->invflags & invflg))
95
96 if (FWINV((ip->saddr&ipinfo->smsk.s_addr) != ipinfo->src.s_addr,
97 IPT_INV_SRCIP)
98 || FWINV((ip->daddr&ipinfo->dmsk.s_addr) != ipinfo->dst.s_addr,
99 IPT_INV_DSTIP)) {
100 dprintf("Source or dest mismatch.\n");
101
102 dprintf("SRC: %u.%u.%u.%u. Mask: %u.%u.%u.%u. Target: %u.%u.%u.%u.%s\n",
103 NIPQUAD(ip->saddr),
104 NIPQUAD(ipinfo->smsk.s_addr),
105 NIPQUAD(ipinfo->src.s_addr),
106 ipinfo->invflags & IPT_INV_SRCIP ? " (INV)" : "");
107 dprintf("DST: %u.%u.%u.%u Mask: %u.%u.%u.%u Target: %u.%u.%u.%u.%s\n",
108 NIPQUAD(ip->daddr),
109 NIPQUAD(ipinfo->dmsk.s_addr),
110 NIPQUAD(ipinfo->dst.s_addr),
111 ipinfo->invflags & IPT_INV_DSTIP ? " (INV)" : "");
112 return 0;
113 }
114
115 /* Look for ifname matches; this should unroll nicely. */
116 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
117 ret |= (((const unsigned long *)indev)[i]
118 ^ ((const unsigned long *)ipinfo->iniface)[i])
119 & ((const unsigned long *)ipinfo->iniface_mask)[i];
120 }
121
122 if (FWINV(ret != 0, IPT_INV_VIA_IN)) {
123 dprintf("VIA in mismatch (%s vs %s).%s\n",
124 indev, ipinfo->iniface,
125 ipinfo->invflags&IPT_INV_VIA_IN ?" (INV)":"");
126 return 0;
127 }
128
129 for (i = 0, ret = 0; i < IFNAMSIZ/sizeof(unsigned long); i++) {
130 ret |= (((const unsigned long *)outdev)[i]
131 ^ ((const unsigned long *)ipinfo->outiface)[i])
132 & ((const unsigned long *)ipinfo->outiface_mask)[i];
133 }
134
135 if (FWINV(ret != 0, IPT_INV_VIA_OUT)) {
136 dprintf("VIA out mismatch (%s vs %s).%s\n",
137 outdev, ipinfo->outiface,
138 ipinfo->invflags&IPT_INV_VIA_OUT ?" (INV)":"");
139 return 0;
140 }
141
142 /* Check specific protocol */
143 if (ipinfo->proto
144 && FWINV(ip->protocol != ipinfo->proto, IPT_INV_PROTO)) {
145 dprintf("Packet protocol %hi does not match %hi.%s\n",
146 ip->protocol, ipinfo->proto,
147 ipinfo->invflags&IPT_INV_PROTO ? " (INV)":"");
148 return 0;
149 }
150
151 /* If we have a fragment rule but the packet is not a fragment
152 * then we return zero */
153 if (FWINV((ipinfo->flags&IPT_F_FRAG) && !isfrag, IPT_INV_FRAG)) {
154 dprintf("Fragment rule but not fragment.%s\n",
155 ipinfo->invflags & IPT_INV_FRAG ? " (INV)" : "");
156 return 0;
157 }
158
159 return 1;
160}
161
162static inline int
163ip_checkentry(const struct ipt_ip *ip)
164{
165 if (ip->flags & ~IPT_F_MASK) {
166 duprintf("Unknown flag bits set: %08X\n",
167 ip->flags & ~IPT_F_MASK);
168 return 0;
169 }
170 if (ip->invflags & ~IPT_INV_MASK) {
171 duprintf("Unknown invflag bits set: %08X\n",
172 ip->invflags & ~IPT_INV_MASK);
173 return 0;
174 }
175 return 1;
176}
177
178static unsigned int
179ipt_error(struct sk_buff **pskb,
180 const struct net_device *in,
181 const struct net_device *out,
182 unsigned int hooknum,
Patrick McHardyc4986732006-03-20 18:02:56 -0800183 const struct xt_target *target,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 const void *targinfo,
185 void *userinfo)
186{
187 if (net_ratelimit())
188 printk("ip_tables: error: `%s'\n", (char *)targinfo);
189
190 return NF_DROP;
191}
192
193static inline
194int do_match(struct ipt_entry_match *m,
195 const struct sk_buff *skb,
196 const struct net_device *in,
197 const struct net_device *out,
198 int offset,
199 int *hotdrop)
200{
201 /* Stop iteration if it doesn't match */
Patrick McHardy1c524832006-03-20 18:02:15 -0800202 if (!m->u.kernel.match->match(skb, in, out, m->u.kernel.match, m->data,
203 offset, skb->nh.iph->ihl*4, hotdrop))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 return 1;
205 else
206 return 0;
207}
208
209static inline struct ipt_entry *
210get_entry(void *base, unsigned int offset)
211{
212 return (struct ipt_entry *)(base + offset);
213}
214
215/* Returns one of the generic firewall policies, like NF_ACCEPT. */
216unsigned int
217ipt_do_table(struct sk_buff **pskb,
218 unsigned int hook,
219 const struct net_device *in,
220 const struct net_device *out,
221 struct ipt_table *table,
222 void *userdata)
223{
224 static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
225 u_int16_t offset;
226 struct iphdr *ip;
227 u_int16_t datalen;
228 int hotdrop = 0;
229 /* Initializing verdict to NF_DROP keeps gcc happy. */
230 unsigned int verdict = NF_DROP;
231 const char *indev, *outdev;
232 void *table_base;
233 struct ipt_entry *e, *back;
Harald Welte2e4e6a12006-01-12 13:30:04 -0800234 struct xt_table_info *private = table->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
236 /* Initialization */
237 ip = (*pskb)->nh.iph;
238 datalen = (*pskb)->len - ip->ihl * 4;
239 indev = in ? in->name : nulldevname;
240 outdev = out ? out->name : nulldevname;
241 /* We handle fragments by dealing with the first fragment as
242 * if it was a normal packet. All other fragments are treated
243 * normally, except that they will NEVER match rules that ask
244 * things we don't know, ie. tcp syn flag or ports). If the
245 * rule is also a fragment-specific rule, non-fragments won't
246 * match it. */
247 offset = ntohs(ip->frag_off) & IP_OFFSET;
248
249 read_lock_bh(&table->lock);
250 IP_NF_ASSERT(table->valid_hooks & (1 << hook));
Harald Welte2e4e6a12006-01-12 13:30:04 -0800251 table_base = (void *)private->entries[smp_processor_id()];
252 e = get_entry(table_base, private->hook_entry[hook]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
254 /* For return from builtin chain */
Harald Welte2e4e6a12006-01-12 13:30:04 -0800255 back = get_entry(table_base, private->underflow[hook]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
257 do {
258 IP_NF_ASSERT(e);
259 IP_NF_ASSERT(back);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 if (ip_packet_match(ip, indev, outdev, &e->ip, offset)) {
261 struct ipt_entry_target *t;
262
263 if (IPT_MATCH_ITERATE(e, do_match,
264 *pskb, in, out,
265 offset, &hotdrop) != 0)
266 goto no_match;
267
268 ADD_COUNTER(e->counters, ntohs(ip->tot_len), 1);
269
270 t = ipt_get_target(e);
271 IP_NF_ASSERT(t->u.kernel.target);
272 /* Standard target? */
273 if (!t->u.kernel.target->target) {
274 int v;
275
276 v = ((struct ipt_standard_target *)t)->verdict;
277 if (v < 0) {
278 /* Pop from stack? */
279 if (v != IPT_RETURN) {
280 verdict = (unsigned)(-v) - 1;
281 break;
282 }
283 e = back;
284 back = get_entry(table_base,
285 back->comefrom);
286 continue;
287 }
Patrick McHardy05465342005-08-21 23:31:43 -0700288 if (table_base + v != (void *)e + e->next_offset
289 && !(e->ip.flags & IPT_F_GOTO)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 /* Save old back ptr in next entry */
291 struct ipt_entry *next
292 = (void *)e + e->next_offset;
293 next->comefrom
294 = (void *)back - table_base;
295 /* set back pointer to next entry */
296 back = next;
297 }
298
299 e = get_entry(table_base, v);
300 } else {
301 /* Targets which reenter must return
302 abs. verdicts */
303#ifdef CONFIG_NETFILTER_DEBUG
304 ((struct ipt_entry *)table_base)->comefrom
305 = 0xeeeeeeec;
306#endif
307 verdict = t->u.kernel.target->target(pskb,
308 in, out,
309 hook,
Patrick McHardy1c524832006-03-20 18:02:15 -0800310 t->u.kernel.target,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 t->data,
312 userdata);
313
314#ifdef CONFIG_NETFILTER_DEBUG
315 if (((struct ipt_entry *)table_base)->comefrom
316 != 0xeeeeeeec
317 && verdict == IPT_CONTINUE) {
318 printk("Target %s reentered!\n",
319 t->u.kernel.target->name);
320 verdict = NF_DROP;
321 }
322 ((struct ipt_entry *)table_base)->comefrom
323 = 0x57acc001;
324#endif
325 /* Target might have changed stuff. */
326 ip = (*pskb)->nh.iph;
327 datalen = (*pskb)->len - ip->ihl * 4;
328
329 if (verdict == IPT_CONTINUE)
330 e = (void *)e + e->next_offset;
331 else
332 /* Verdict */
333 break;
334 }
335 } else {
336
337 no_match:
338 e = (void *)e + e->next_offset;
339 }
340 } while (!hotdrop);
341
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 read_unlock_bh(&table->lock);
343
344#ifdef DEBUG_ALLOW_ALL
345 return NF_ACCEPT;
346#else
347 if (hotdrop)
348 return NF_DROP;
349 else return verdict;
350#endif
351}
352
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353/* All zeroes == unconditional rule. */
354static inline int
355unconditional(const struct ipt_ip *ip)
356{
357 unsigned int i;
358
359 for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
360 if (((__u32 *)ip)[i])
361 return 0;
362
363 return 1;
364}
365
366/* Figures out from what hook each rule can be called: returns 0 if
367 there are loops. Puts hook bitmask in comefrom. */
368static int
Harald Welte2e4e6a12006-01-12 13:30:04 -0800369mark_source_chains(struct xt_table_info *newinfo,
Eric Dumazet31836062005-12-13 23:13:48 -0800370 unsigned int valid_hooks, void *entry0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371{
372 unsigned int hook;
373
374 /* No recursion; use packet counter to save back ptrs (reset
375 to 0 as we leave), and comefrom to save source hook bitmask */
376 for (hook = 0; hook < NF_IP_NUMHOOKS; hook++) {
377 unsigned int pos = newinfo->hook_entry[hook];
378 struct ipt_entry *e
Eric Dumazet31836062005-12-13 23:13:48 -0800379 = (struct ipt_entry *)(entry0 + pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
381 if (!(valid_hooks & (1 << hook)))
382 continue;
383
384 /* Set initial back pointer. */
385 e->counters.pcnt = pos;
386
387 for (;;) {
388 struct ipt_standard_target *t
389 = (void *)ipt_get_target(e);
390
391 if (e->comefrom & (1 << NF_IP_NUMHOOKS)) {
392 printk("iptables: loop hook %u pos %u %08X.\n",
393 hook, pos, e->comefrom);
394 return 0;
395 }
396 e->comefrom
397 |= ((1 << hook) | (1 << NF_IP_NUMHOOKS));
398
399 /* Unconditional return/END. */
400 if (e->target_offset == sizeof(struct ipt_entry)
401 && (strcmp(t->target.u.user.name,
402 IPT_STANDARD_TARGET) == 0)
403 && t->verdict < 0
404 && unconditional(&e->ip)) {
405 unsigned int oldpos, size;
406
407 /* Return: backtrack through the last
408 big jump. */
409 do {
410 e->comefrom ^= (1<<NF_IP_NUMHOOKS);
411#ifdef DEBUG_IP_FIREWALL_USER
412 if (e->comefrom
413 & (1 << NF_IP_NUMHOOKS)) {
414 duprintf("Back unset "
415 "on hook %u "
416 "rule %u\n",
417 hook, pos);
418 }
419#endif
420 oldpos = pos;
421 pos = e->counters.pcnt;
422 e->counters.pcnt = 0;
423
424 /* We're at the start. */
425 if (pos == oldpos)
426 goto next;
427
428 e = (struct ipt_entry *)
Eric Dumazet31836062005-12-13 23:13:48 -0800429 (entry0 + pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 } while (oldpos == pos + e->next_offset);
431
432 /* Move along one */
433 size = e->next_offset;
434 e = (struct ipt_entry *)
Eric Dumazet31836062005-12-13 23:13:48 -0800435 (entry0 + pos + size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 e->counters.pcnt = pos;
437 pos += size;
438 } else {
439 int newpos = t->verdict;
440
441 if (strcmp(t->target.u.user.name,
442 IPT_STANDARD_TARGET) == 0
443 && newpos >= 0) {
444 /* This a jump; chase it. */
445 duprintf("Jump rule %u -> %u\n",
446 pos, newpos);
447 } else {
448 /* ... this is a fallthru */
449 newpos = pos + e->next_offset;
450 }
451 e = (struct ipt_entry *)
Eric Dumazet31836062005-12-13 23:13:48 -0800452 (entry0 + newpos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 e->counters.pcnt = pos;
454 pos = newpos;
455 }
456 }
457 next:
458 duprintf("Finished chain %u\n", hook);
459 }
460 return 1;
461}
462
463static inline int
464cleanup_match(struct ipt_entry_match *m, unsigned int *i)
465{
466 if (i && (*i)-- == 0)
467 return 1;
468
469 if (m->u.kernel.match->destroy)
Patrick McHardy1c524832006-03-20 18:02:15 -0800470 m->u.kernel.match->destroy(m->u.kernel.match, m->data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 m->u.match_size - sizeof(*m));
472 module_put(m->u.kernel.match->me);
473 return 0;
474}
475
476static inline int
477standard_check(const struct ipt_entry_target *t,
478 unsigned int max_offset)
479{
480 struct ipt_standard_target *targ = (void *)t;
481
482 /* Check standard info. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 if (targ->verdict >= 0
484 && targ->verdict > max_offset - sizeof(struct ipt_entry)) {
485 duprintf("ipt_standard_check: bad verdict (%i)\n",
486 targ->verdict);
487 return 0;
488 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 if (targ->verdict < -NF_MAX_VERDICT - 1) {
490 duprintf("ipt_standard_check: bad negative verdict (%i)\n",
491 targ->verdict);
492 return 0;
493 }
494 return 1;
495}
496
497static inline int
498check_match(struct ipt_entry_match *m,
499 const char *name,
500 const struct ipt_ip *ip,
501 unsigned int hookmask,
502 unsigned int *i)
503{
504 struct ipt_match *match;
Patrick McHardy3cdc7c92006-03-20 18:00:36 -0800505 int ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506
Harald Welte2e4e6a12006-01-12 13:30:04 -0800507 match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 m->u.user.revision),
509 "ipt_%s", m->u.user.name);
510 if (IS_ERR(match) || !match) {
511 duprintf("check_match: `%s' not found\n", m->u.user.name);
512 return match ? PTR_ERR(match) : -ENOENT;
513 }
514 m->u.kernel.match = match;
515
Patrick McHardy3cdc7c92006-03-20 18:00:36 -0800516 ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m),
517 name, hookmask, ip->proto,
518 ip->invflags & IPT_INV_PROTO);
519 if (ret)
520 goto err;
521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 if (m->u.kernel.match->checkentry
Patrick McHardy1c524832006-03-20 18:02:15 -0800523 && !m->u.kernel.match->checkentry(name, ip, match, m->data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 m->u.match_size - sizeof(*m),
525 hookmask)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 duprintf("ip_tables: check failed for `%s'.\n",
527 m->u.kernel.match->name);
Patrick McHardy3cdc7c92006-03-20 18:00:36 -0800528 ret = -EINVAL;
529 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 }
531
532 (*i)++;
533 return 0;
Patrick McHardy3cdc7c92006-03-20 18:00:36 -0800534err:
535 module_put(m->u.kernel.match->me);
536 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537}
538
539static struct ipt_target ipt_standard_target;
540
541static inline int
542check_entry(struct ipt_entry *e, const char *name, unsigned int size,
543 unsigned int *i)
544{
545 struct ipt_entry_target *t;
546 struct ipt_target *target;
547 int ret;
548 unsigned int j;
549
550 if (!ip_checkentry(&e->ip)) {
551 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
552 return -EINVAL;
553 }
554
555 j = 0;
556 ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);
557 if (ret != 0)
558 goto cleanup_matches;
559
560 t = ipt_get_target(e);
Harald Welte2e4e6a12006-01-12 13:30:04 -0800561 target = try_then_request_module(xt_find_target(AF_INET,
562 t->u.user.name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 t->u.user.revision),
564 "ipt_%s", t->u.user.name);
565 if (IS_ERR(target) || !target) {
566 duprintf("check_entry: `%s' not found\n", t->u.user.name);
567 ret = target ? PTR_ERR(target) : -ENOENT;
568 goto cleanup_matches;
569 }
570 t->u.kernel.target = target;
571
Patrick McHardy3cdc7c92006-03-20 18:00:36 -0800572 ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t),
573 name, e->comefrom, e->ip.proto,
574 e->ip.invflags & IPT_INV_PROTO);
575 if (ret)
576 goto err;
577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 if (t->u.kernel.target == &ipt_standard_target) {
579 if (!standard_check(t, size)) {
580 ret = -EINVAL;
581 goto cleanup_matches;
582 }
583 } else if (t->u.kernel.target->checkentry
Patrick McHardy1c524832006-03-20 18:02:15 -0800584 && !t->u.kernel.target->checkentry(name, e, target, t->data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 t->u.target_size
586 - sizeof(*t),
587 e->comefrom)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 duprintf("ip_tables: check failed for `%s'.\n",
589 t->u.kernel.target->name);
590 ret = -EINVAL;
Patrick McHardy3cdc7c92006-03-20 18:00:36 -0800591 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 }
593
594 (*i)++;
595 return 0;
Patrick McHardy3cdc7c92006-03-20 18:00:36 -0800596 err:
597 module_put(t->u.kernel.target->me);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 cleanup_matches:
599 IPT_MATCH_ITERATE(e, cleanup_match, &j);
600 return ret;
601}
602
603static inline int
604check_entry_size_and_hooks(struct ipt_entry *e,
Harald Welte2e4e6a12006-01-12 13:30:04 -0800605 struct xt_table_info *newinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 unsigned char *base,
607 unsigned char *limit,
608 const unsigned int *hook_entries,
609 const unsigned int *underflows,
610 unsigned int *i)
611{
612 unsigned int h;
613
614 if ((unsigned long)e % __alignof__(struct ipt_entry) != 0
615 || (unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
616 duprintf("Bad offset %p\n", e);
617 return -EINVAL;
618 }
619
620 if (e->next_offset
621 < sizeof(struct ipt_entry) + sizeof(struct ipt_entry_target)) {
622 duprintf("checking: element %p size %u\n",
623 e, e->next_offset);
624 return -EINVAL;
625 }
626
627 /* Check hooks & underflows */
628 for (h = 0; h < NF_IP_NUMHOOKS; h++) {
629 if ((unsigned char *)e - base == hook_entries[h])
630 newinfo->hook_entry[h] = hook_entries[h];
631 if ((unsigned char *)e - base == underflows[h])
632 newinfo->underflow[h] = underflows[h];
633 }
634
635 /* FIXME: underflows must be unconditional, standard verdicts
636 < 0 (not IPT_RETURN). --RR */
637
638 /* Clear counters and comefrom */
Harald Welte2e4e6a12006-01-12 13:30:04 -0800639 e->counters = ((struct xt_counters) { 0, 0 });
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640 e->comefrom = 0;
641
642 (*i)++;
643 return 0;
644}
645
646static inline int
647cleanup_entry(struct ipt_entry *e, unsigned int *i)
648{
649 struct ipt_entry_target *t;
650
651 if (i && (*i)-- == 0)
652 return 1;
653
654 /* Cleanup all matches */
655 IPT_MATCH_ITERATE(e, cleanup_match, NULL);
656 t = ipt_get_target(e);
657 if (t->u.kernel.target->destroy)
Patrick McHardy1c524832006-03-20 18:02:15 -0800658 t->u.kernel.target->destroy(t->u.kernel.target, t->data,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 t->u.target_size - sizeof(*t));
660 module_put(t->u.kernel.target->me);
661 return 0;
662}
663
664/* Checks and translates the user-supplied table segment (held in
665 newinfo) */
666static int
667translate_table(const char *name,
668 unsigned int valid_hooks,
Harald Welte2e4e6a12006-01-12 13:30:04 -0800669 struct xt_table_info *newinfo,
Eric Dumazet31836062005-12-13 23:13:48 -0800670 void *entry0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 unsigned int size,
672 unsigned int number,
673 const unsigned int *hook_entries,
674 const unsigned int *underflows)
675{
676 unsigned int i;
677 int ret;
678
679 newinfo->size = size;
680 newinfo->number = number;
681
682 /* Init all hooks to impossible value. */
683 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
684 newinfo->hook_entry[i] = 0xFFFFFFFF;
685 newinfo->underflow[i] = 0xFFFFFFFF;
686 }
687
688 duprintf("translate_table: size %u\n", newinfo->size);
689 i = 0;
690 /* Walk through entries, checking offsets. */
Eric Dumazet31836062005-12-13 23:13:48 -0800691 ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 check_entry_size_and_hooks,
693 newinfo,
Eric Dumazet31836062005-12-13 23:13:48 -0800694 entry0,
695 entry0 + size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 hook_entries, underflows, &i);
697 if (ret != 0)
698 return ret;
699
700 if (i != number) {
701 duprintf("translate_table: %u not %u entries\n",
702 i, number);
703 return -EINVAL;
704 }
705
706 /* Check hooks all assigned */
707 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
708 /* Only hooks which are valid */
709 if (!(valid_hooks & (1 << i)))
710 continue;
711 if (newinfo->hook_entry[i] == 0xFFFFFFFF) {
712 duprintf("Invalid hook entry %u %u\n",
713 i, hook_entries[i]);
714 return -EINVAL;
715 }
716 if (newinfo->underflow[i] == 0xFFFFFFFF) {
717 duprintf("Invalid underflow %u %u\n",
718 i, underflows[i]);
719 return -EINVAL;
720 }
721 }
722
Eric Dumazet31836062005-12-13 23:13:48 -0800723 if (!mark_source_chains(newinfo, valid_hooks, entry0))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 return -ELOOP;
725
726 /* Finally, each sanity check must pass */
727 i = 0;
Eric Dumazet31836062005-12-13 23:13:48 -0800728 ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 check_entry, name, size, &i);
730
731 if (ret != 0) {
Eric Dumazet31836062005-12-13 23:13:48 -0800732 IPT_ENTRY_ITERATE(entry0, newinfo->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 cleanup_entry, &i);
734 return ret;
735 }
736
737 /* And one copy for every other CPU */
KAMEZAWA Hiroyuki6f912042006-04-10 22:52:50 -0700738 for_each_possible_cpu(i) {
Eric Dumazet31836062005-12-13 23:13:48 -0800739 if (newinfo->entries[i] && newinfo->entries[i] != entry0)
740 memcpy(newinfo->entries[i], entry0, newinfo->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 }
742
743 return ret;
744}
745
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746/* Gets counters. */
747static inline int
748add_entry_to_counter(const struct ipt_entry *e,
Harald Welte2e4e6a12006-01-12 13:30:04 -0800749 struct xt_counters total[],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 unsigned int *i)
751{
752 ADD_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
753
754 (*i)++;
755 return 0;
756}
757
Eric Dumazet31836062005-12-13 23:13:48 -0800758static inline int
759set_entry_to_counter(const struct ipt_entry *e,
760 struct ipt_counters total[],
761 unsigned int *i)
762{
763 SET_COUNTER(total[*i], e->counters.bcnt, e->counters.pcnt);
764
765 (*i)++;
766 return 0;
767}
768
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769static void
Harald Welte2e4e6a12006-01-12 13:30:04 -0800770get_counters(const struct xt_table_info *t,
771 struct xt_counters counters[])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772{
773 unsigned int cpu;
774 unsigned int i;
Eric Dumazet31836062005-12-13 23:13:48 -0800775 unsigned int curcpu;
776
777 /* Instead of clearing (by a previous call to memset())
778 * the counters and using adds, we set the counters
779 * with data used by 'current' CPU
780 * We dont care about preemption here.
781 */
782 curcpu = raw_smp_processor_id();
783
784 i = 0;
785 IPT_ENTRY_ITERATE(t->entries[curcpu],
786 t->size,
787 set_entry_to_counter,
788 counters,
789 &i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
KAMEZAWA Hiroyuki6f912042006-04-10 22:52:50 -0700791 for_each_possible_cpu(cpu) {
Eric Dumazet31836062005-12-13 23:13:48 -0800792 if (cpu == curcpu)
793 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 i = 0;
Eric Dumazet31836062005-12-13 23:13:48 -0800795 IPT_ENTRY_ITERATE(t->entries[cpu],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 t->size,
797 add_entry_to_counter,
798 counters,
799 &i);
800 }
801}
802
Dmitry Mishin27229712006-04-01 02:25:19 -0800803static inline struct xt_counters * alloc_counters(struct ipt_table *table)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804{
Dmitry Mishin27229712006-04-01 02:25:19 -0800805 unsigned int countersize;
Harald Welte2e4e6a12006-01-12 13:30:04 -0800806 struct xt_counters *counters;
807 struct xt_table_info *private = table->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
809 /* We need atomic snapshot of counters: rest doesn't change
810 (other than comefrom, which userspace doesn't care
811 about). */
Harald Welte2e4e6a12006-01-12 13:30:04 -0800812 countersize = sizeof(struct xt_counters) * private->number;
Eric Dumazet31836062005-12-13 23:13:48 -0800813 counters = vmalloc_node(countersize, numa_node_id());
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
815 if (counters == NULL)
Dmitry Mishin27229712006-04-01 02:25:19 -0800816 return ERR_PTR(-ENOMEM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
818 /* First, sum counters... */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 write_lock_bh(&table->lock);
Harald Welte2e4e6a12006-01-12 13:30:04 -0800820 get_counters(private, counters);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821 write_unlock_bh(&table->lock);
822
Dmitry Mishin27229712006-04-01 02:25:19 -0800823 return counters;
824}
825
826static int
827copy_entries_to_user(unsigned int total_size,
828 struct ipt_table *table,
829 void __user *userptr)
830{
831 unsigned int off, num;
832 struct ipt_entry *e;
833 struct xt_counters *counters;
834 struct xt_table_info *private = table->private;
835 int ret = 0;
836 void *loc_cpu_entry;
837
838 counters = alloc_counters(table);
839 if (IS_ERR(counters))
840 return PTR_ERR(counters);
841
Eric Dumazet31836062005-12-13 23:13:48 -0800842 /* choose the copy that is on our node/cpu, ...
843 * This choice is lazy (because current thread is
844 * allowed to migrate to another cpu)
845 */
Harald Welte2e4e6a12006-01-12 13:30:04 -0800846 loc_cpu_entry = private->entries[raw_smp_processor_id()];
Eric Dumazet31836062005-12-13 23:13:48 -0800847 /* ... then copy entire thing ... */
848 if (copy_to_user(userptr, loc_cpu_entry, total_size) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 ret = -EFAULT;
850 goto free_counters;
851 }
852
853 /* FIXME: use iterator macros --RR */
854 /* ... then go back and fix counters and names */
855 for (off = 0, num = 0; off < total_size; off += e->next_offset, num++){
856 unsigned int i;
857 struct ipt_entry_match *m;
858 struct ipt_entry_target *t;
859
Eric Dumazet31836062005-12-13 23:13:48 -0800860 e = (struct ipt_entry *)(loc_cpu_entry + off);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 if (copy_to_user(userptr + off
862 + offsetof(struct ipt_entry, counters),
863 &counters[num],
864 sizeof(counters[num])) != 0) {
865 ret = -EFAULT;
866 goto free_counters;
867 }
868
869 for (i = sizeof(struct ipt_entry);
870 i < e->target_offset;
871 i += m->u.match_size) {
872 m = (void *)e + i;
873
874 if (copy_to_user(userptr + off + i
875 + offsetof(struct ipt_entry_match,
876 u.user.name),
877 m->u.kernel.match->name,
878 strlen(m->u.kernel.match->name)+1)
879 != 0) {
880 ret = -EFAULT;
881 goto free_counters;
882 }
883 }
884
885 t = ipt_get_target(e);
886 if (copy_to_user(userptr + off + e->target_offset
887 + offsetof(struct ipt_entry_target,
888 u.user.name),
889 t->u.kernel.target->name,
890 strlen(t->u.kernel.target->name)+1) != 0) {
891 ret = -EFAULT;
892 goto free_counters;
893 }
894 }
895
896 free_counters:
897 vfree(counters);
898 return ret;
899}
900
Dmitry Mishin27229712006-04-01 02:25:19 -0800901#ifdef CONFIG_COMPAT
902struct compat_delta {
903 struct compat_delta *next;
904 u_int16_t offset;
905 short delta;
906};
907
908static struct compat_delta *compat_offsets = NULL;
909
910static int compat_add_offset(u_int16_t offset, short delta)
911{
912 struct compat_delta *tmp;
913
914 tmp = kmalloc(sizeof(struct compat_delta), GFP_KERNEL);
915 if (!tmp)
916 return -ENOMEM;
917 tmp->offset = offset;
918 tmp->delta = delta;
919 if (compat_offsets) {
920 tmp->next = compat_offsets->next;
921 compat_offsets->next = tmp;
922 } else {
923 compat_offsets = tmp;
924 tmp->next = NULL;
925 }
926 return 0;
927}
928
929static void compat_flush_offsets(void)
930{
931 struct compat_delta *tmp, *next;
932
933 if (compat_offsets) {
934 for(tmp = compat_offsets; tmp; tmp = next) {
935 next = tmp->next;
936 kfree(tmp);
937 }
938 compat_offsets = NULL;
939 }
940}
941
942static short compat_calc_jump(u_int16_t offset)
943{
944 struct compat_delta *tmp;
945 short delta;
946
947 for(tmp = compat_offsets, delta = 0; tmp; tmp = tmp->next)
948 if (tmp->offset < offset)
949 delta += tmp->delta;
950 return delta;
951}
952
953struct compat_ipt_standard_target
954{
955 struct compat_xt_entry_target target;
956 compat_int_t verdict;
957};
958
959#define IPT_ST_OFFSET (sizeof(struct ipt_standard_target) - \
960 sizeof(struct compat_ipt_standard_target))
961
962struct compat_ipt_standard
963{
964 struct compat_ipt_entry entry;
965 struct compat_ipt_standard_target target;
966};
967
968static int compat_ipt_standard_fn(void *target,
969 void **dstptr, int *size, int convert)
970{
971 struct compat_ipt_standard_target compat_st, *pcompat_st;
972 struct ipt_standard_target st, *pst;
973 int ret;
974
975 ret = 0;
976 switch (convert) {
977 case COMPAT_TO_USER:
978 pst = (struct ipt_standard_target *)target;
979 memcpy(&compat_st.target, &pst->target,
980 sizeof(struct ipt_entry_target));
981 compat_st.verdict = pst->verdict;
982 if (compat_st.verdict > 0)
983 compat_st.verdict -=
984 compat_calc_jump(compat_st.verdict);
985 compat_st.target.u.user.target_size =
986 sizeof(struct compat_ipt_standard_target);
987 if (__copy_to_user(*dstptr, &compat_st,
988 sizeof(struct compat_ipt_standard_target)))
989 ret = -EFAULT;
990 *size -= IPT_ST_OFFSET;
991 *dstptr += sizeof(struct compat_ipt_standard_target);
992 break;
993 case COMPAT_FROM_USER:
994 pcompat_st =
995 (struct compat_ipt_standard_target *)target;
996 memcpy(&st.target, &pcompat_st->target,
997 sizeof(struct ipt_entry_target));
998 st.verdict = pcompat_st->verdict;
999 if (st.verdict > 0)
1000 st.verdict += compat_calc_jump(st.verdict);
1001 st.target.u.user.target_size =
1002 sizeof(struct ipt_standard_target);
1003 memcpy(*dstptr, &st,
1004 sizeof(struct ipt_standard_target));
1005 *size += IPT_ST_OFFSET;
1006 *dstptr += sizeof(struct ipt_standard_target);
1007 break;
1008 case COMPAT_CALC_SIZE:
1009 *size += IPT_ST_OFFSET;
1010 break;
1011 default:
1012 ret = -ENOPROTOOPT;
1013 break;
1014 }
1015 return ret;
1016}
1017
1018static inline int
1019compat_calc_match(struct ipt_entry_match *m, int * size)
1020{
1021 if (m->u.kernel.match->compat)
1022 m->u.kernel.match->compat(m, NULL, size, COMPAT_CALC_SIZE);
1023 else
1024 xt_compat_match(m, NULL, size, COMPAT_CALC_SIZE);
1025 return 0;
1026}
1027
1028static int compat_calc_entry(struct ipt_entry *e, struct xt_table_info *info,
1029 void *base, struct xt_table_info *newinfo)
1030{
1031 struct ipt_entry_target *t;
1032 u_int16_t entry_offset;
1033 int off, i, ret;
1034
1035 off = 0;
1036 entry_offset = (void *)e - base;
1037 IPT_MATCH_ITERATE(e, compat_calc_match, &off);
1038 t = ipt_get_target(e);
1039 if (t->u.kernel.target->compat)
1040 t->u.kernel.target->compat(t, NULL, &off, COMPAT_CALC_SIZE);
1041 else
1042 xt_compat_target(t, NULL, &off, COMPAT_CALC_SIZE);
1043 newinfo->size -= off;
1044 ret = compat_add_offset(entry_offset, off);
1045 if (ret)
1046 return ret;
1047
1048 for (i = 0; i< NF_IP_NUMHOOKS; i++) {
1049 if (info->hook_entry[i] && (e < (struct ipt_entry *)
1050 (base + info->hook_entry[i])))
1051 newinfo->hook_entry[i] -= off;
1052 if (info->underflow[i] && (e < (struct ipt_entry *)
1053 (base + info->underflow[i])))
1054 newinfo->underflow[i] -= off;
1055 }
1056 return 0;
1057}
1058
1059static int compat_table_info(struct xt_table_info *info,
1060 struct xt_table_info *newinfo)
1061{
1062 void *loc_cpu_entry;
1063 int i;
1064
1065 if (!newinfo || !info)
1066 return -EINVAL;
1067
1068 memset(newinfo, 0, sizeof(struct xt_table_info));
1069 newinfo->size = info->size;
1070 newinfo->number = info->number;
1071 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
1072 newinfo->hook_entry[i] = info->hook_entry[i];
1073 newinfo->underflow[i] = info->underflow[i];
1074 }
1075 loc_cpu_entry = info->entries[raw_smp_processor_id()];
1076 return IPT_ENTRY_ITERATE(loc_cpu_entry, info->size,
1077 compat_calc_entry, info, loc_cpu_entry, newinfo);
1078}
1079#endif
1080
1081static int get_info(void __user *user, int *len, int compat)
1082{
1083 char name[IPT_TABLE_MAXNAMELEN];
1084 struct ipt_table *t;
1085 int ret;
1086
1087 if (*len != sizeof(struct ipt_getinfo)) {
1088 duprintf("length %u != %u\n", *len,
1089 (unsigned int)sizeof(struct ipt_getinfo));
1090 return -EINVAL;
1091 }
1092
1093 if (copy_from_user(name, user, sizeof(name)) != 0)
1094 return -EFAULT;
1095
1096 name[IPT_TABLE_MAXNAMELEN-1] = '\0';
1097#ifdef CONFIG_COMPAT
1098 if (compat)
1099 xt_compat_lock(AF_INET);
1100#endif
1101 t = try_then_request_module(xt_find_table_lock(AF_INET, name),
1102 "iptable_%s", name);
1103 if (t && !IS_ERR(t)) {
1104 struct ipt_getinfo info;
1105 struct xt_table_info *private = t->private;
1106
1107#ifdef CONFIG_COMPAT
1108 if (compat) {
1109 struct xt_table_info tmp;
1110 ret = compat_table_info(private, &tmp);
1111 compat_flush_offsets();
1112 private = &tmp;
1113 }
1114#endif
1115 info.valid_hooks = t->valid_hooks;
1116 memcpy(info.hook_entry, private->hook_entry,
1117 sizeof(info.hook_entry));
1118 memcpy(info.underflow, private->underflow,
1119 sizeof(info.underflow));
1120 info.num_entries = private->number;
1121 info.size = private->size;
1122 strcpy(info.name, name);
1123
1124 if (copy_to_user(user, &info, *len) != 0)
1125 ret = -EFAULT;
1126 else
1127 ret = 0;
1128
1129 xt_table_unlock(t);
1130 module_put(t->me);
1131 } else
1132 ret = t ? PTR_ERR(t) : -ENOENT;
1133#ifdef CONFIG_COMPAT
1134 if (compat)
1135 xt_compat_unlock(AF_INET);
1136#endif
1137 return ret;
1138}
1139
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140static int
Dmitry Mishin27229712006-04-01 02:25:19 -08001141get_entries(struct ipt_get_entries __user *uptr, int *len)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142{
1143 int ret;
Dmitry Mishin27229712006-04-01 02:25:19 -08001144 struct ipt_get_entries get;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 struct ipt_table *t;
1146
Dmitry Mishin27229712006-04-01 02:25:19 -08001147 if (*len < sizeof(get)) {
1148 duprintf("get_entries: %u < %d\n", *len,
1149 (unsigned int)sizeof(get));
1150 return -EINVAL;
1151 }
1152 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1153 return -EFAULT;
1154 if (*len != sizeof(struct ipt_get_entries) + get.size) {
1155 duprintf("get_entries: %u != %u\n", *len,
1156 (unsigned int)(sizeof(struct ipt_get_entries) +
1157 get.size));
1158 return -EINVAL;
1159 }
1160
1161 t = xt_find_table_lock(AF_INET, get.name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 if (t && !IS_ERR(t)) {
Harald Welte2e4e6a12006-01-12 13:30:04 -08001163 struct xt_table_info *private = t->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 duprintf("t->private->number = %u\n",
Harald Welte2e4e6a12006-01-12 13:30:04 -08001165 private->number);
Dmitry Mishin27229712006-04-01 02:25:19 -08001166 if (get.size == private->size)
Harald Welte2e4e6a12006-01-12 13:30:04 -08001167 ret = copy_entries_to_user(private->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 t, uptr->entrytable);
1169 else {
1170 duprintf("get_entries: I've got %u not %u!\n",
Harald Welte2e4e6a12006-01-12 13:30:04 -08001171 private->size,
Dmitry Mishin27229712006-04-01 02:25:19 -08001172 get.size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 ret = -EINVAL;
1174 }
1175 module_put(t->me);
Harald Welte2e4e6a12006-01-12 13:30:04 -08001176 xt_table_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 } else
1178 ret = t ? PTR_ERR(t) : -ENOENT;
1179
1180 return ret;
1181}
1182
1183static int
Dmitry Mishin27229712006-04-01 02:25:19 -08001184__do_replace(const char *name, unsigned int valid_hooks,
1185 struct xt_table_info *newinfo, unsigned int num_counters,
1186 void __user *counters_ptr)
1187{
1188 int ret;
1189 struct ipt_table *t;
1190 struct xt_table_info *oldinfo;
1191 struct xt_counters *counters;
1192 void *loc_cpu_old_entry;
1193
1194 ret = 0;
1195 counters = vmalloc(num_counters * sizeof(struct xt_counters));
1196 if (!counters) {
1197 ret = -ENOMEM;
1198 goto out;
1199 }
1200
1201 t = try_then_request_module(xt_find_table_lock(AF_INET, name),
1202 "iptable_%s", name);
1203 if (!t || IS_ERR(t)) {
1204 ret = t ? PTR_ERR(t) : -ENOENT;
1205 goto free_newinfo_counters_untrans;
1206 }
1207
1208 /* You lied! */
1209 if (valid_hooks != t->valid_hooks) {
1210 duprintf("Valid hook crap: %08X vs %08X\n",
1211 valid_hooks, t->valid_hooks);
1212 ret = -EINVAL;
1213 goto put_module;
1214 }
1215
1216 oldinfo = xt_replace_table(t, num_counters, newinfo, &ret);
1217 if (!oldinfo)
1218 goto put_module;
1219
1220 /* Update module usage count based on number of rules */
1221 duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
1222 oldinfo->number, oldinfo->initial_entries, newinfo->number);
1223 if ((oldinfo->number > oldinfo->initial_entries) ||
1224 (newinfo->number <= oldinfo->initial_entries))
1225 module_put(t->me);
1226 if ((oldinfo->number > oldinfo->initial_entries) &&
1227 (newinfo->number <= oldinfo->initial_entries))
1228 module_put(t->me);
1229
1230 /* Get the old counters. */
1231 get_counters(oldinfo, counters);
1232 /* Decrease module usage counts and free resource */
1233 loc_cpu_old_entry = oldinfo->entries[raw_smp_processor_id()];
1234 IPT_ENTRY_ITERATE(loc_cpu_old_entry, oldinfo->size, cleanup_entry,NULL);
1235 xt_free_table_info(oldinfo);
1236 if (copy_to_user(counters_ptr, counters,
1237 sizeof(struct xt_counters) * num_counters) != 0)
1238 ret = -EFAULT;
1239 vfree(counters);
1240 xt_table_unlock(t);
1241 return ret;
1242
1243 put_module:
1244 module_put(t->me);
1245 xt_table_unlock(t);
1246 free_newinfo_counters_untrans:
1247 vfree(counters);
1248 out:
1249 return ret;
1250}
1251
1252static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253do_replace(void __user *user, unsigned int len)
1254{
1255 int ret;
1256 struct ipt_replace tmp;
Dmitry Mishin27229712006-04-01 02:25:19 -08001257 struct xt_table_info *newinfo;
1258 void *loc_cpu_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259
1260 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1261 return -EFAULT;
1262
1263 /* Hack: Causes ipchains to give correct error msg --RR */
1264 if (len != sizeof(tmp) + tmp.size)
1265 return -ENOPROTOOPT;
1266
Kirill Korotaevee4bb812006-02-04 02:16:56 -08001267 /* overflow check */
1268 if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
1269 SMP_CACHE_BYTES)
1270 return -ENOMEM;
1271 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1272 return -ENOMEM;
1273
Harald Welte2e4e6a12006-01-12 13:30:04 -08001274 newinfo = xt_alloc_table_info(tmp.size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 if (!newinfo)
1276 return -ENOMEM;
1277
Eric Dumazet31836062005-12-13 23:13:48 -08001278 /* choose the copy that is our node/cpu */
1279 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1280 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281 tmp.size) != 0) {
1282 ret = -EFAULT;
1283 goto free_newinfo;
1284 }
1285
Linus Torvalds1da177e2005-04-16 15:20:36 -07001286 ret = translate_table(tmp.name, tmp.valid_hooks,
Eric Dumazet31836062005-12-13 23:13:48 -08001287 newinfo, loc_cpu_entry, tmp.size, tmp.num_entries,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 tmp.hook_entry, tmp.underflow);
1289 if (ret != 0)
Dmitry Mishin27229712006-04-01 02:25:19 -08001290 goto free_newinfo;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
1292 duprintf("ip_tables: Translated table\n");
1293
Dmitry Mishin27229712006-04-01 02:25:19 -08001294 ret = __do_replace(tmp.name, tmp.valid_hooks,
1295 newinfo, tmp.num_counters,
1296 tmp.counters);
1297 if (ret)
1298 goto free_newinfo_untrans;
1299 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300
Dmitry Mishin27229712006-04-01 02:25:19 -08001301 free_newinfo_untrans:
Eric Dumazet31836062005-12-13 23:13:48 -08001302 IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303 free_newinfo:
Harald Welte2e4e6a12006-01-12 13:30:04 -08001304 xt_free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 return ret;
1306}
1307
1308/* We're lazy, and add to the first CPU; overflow works its fey magic
1309 * and everything is OK. */
1310static inline int
1311add_counter_to_entry(struct ipt_entry *e,
Harald Welte2e4e6a12006-01-12 13:30:04 -08001312 const struct xt_counters addme[],
Linus Torvalds1da177e2005-04-16 15:20:36 -07001313 unsigned int *i)
1314{
1315#if 0
1316 duprintf("add_counter: Entry %u %lu/%lu + %lu/%lu\n",
1317 *i,
1318 (long unsigned int)e->counters.pcnt,
1319 (long unsigned int)e->counters.bcnt,
1320 (long unsigned int)addme[*i].pcnt,
1321 (long unsigned int)addme[*i].bcnt);
1322#endif
1323
1324 ADD_COUNTER(e->counters, addme[*i].bcnt, addme[*i].pcnt);
1325
1326 (*i)++;
1327 return 0;
1328}
1329
1330static int
Dmitry Mishin27229712006-04-01 02:25:19 -08001331do_add_counters(void __user *user, unsigned int len, int compat)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332{
1333 unsigned int i;
Dmitry Mishin27229712006-04-01 02:25:19 -08001334 struct xt_counters_info tmp;
1335 struct xt_counters *paddc;
1336 unsigned int num_counters;
1337 char *name;
1338 int size;
1339 void *ptmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 struct ipt_table *t;
Harald Welte2e4e6a12006-01-12 13:30:04 -08001341 struct xt_table_info *private;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342 int ret = 0;
Eric Dumazet31836062005-12-13 23:13:48 -08001343 void *loc_cpu_entry;
Dmitry Mishin27229712006-04-01 02:25:19 -08001344#ifdef CONFIG_COMPAT
1345 struct compat_xt_counters_info compat_tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
Dmitry Mishin27229712006-04-01 02:25:19 -08001347 if (compat) {
1348 ptmp = &compat_tmp;
1349 size = sizeof(struct compat_xt_counters_info);
1350 } else
1351#endif
1352 {
1353 ptmp = &tmp;
1354 size = sizeof(struct xt_counters_info);
1355 }
1356
1357 if (copy_from_user(ptmp, user, size) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358 return -EFAULT;
1359
Dmitry Mishin27229712006-04-01 02:25:19 -08001360#ifdef CONFIG_COMPAT
1361 if (compat) {
1362 num_counters = compat_tmp.num_counters;
1363 name = compat_tmp.name;
1364 } else
1365#endif
1366 {
1367 num_counters = tmp.num_counters;
1368 name = tmp.name;
1369 }
1370
1371 if (len != size + num_counters * sizeof(struct xt_counters))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372 return -EINVAL;
1373
Dmitry Mishin27229712006-04-01 02:25:19 -08001374 paddc = vmalloc_node(len - size, numa_node_id());
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375 if (!paddc)
1376 return -ENOMEM;
1377
Dmitry Mishin27229712006-04-01 02:25:19 -08001378 if (copy_from_user(paddc, user + size, len - size) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379 ret = -EFAULT;
1380 goto free;
1381 }
1382
Dmitry Mishin27229712006-04-01 02:25:19 -08001383 t = xt_find_table_lock(AF_INET, name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384 if (!t || IS_ERR(t)) {
1385 ret = t ? PTR_ERR(t) : -ENOENT;
1386 goto free;
1387 }
1388
1389 write_lock_bh(&t->lock);
Harald Welte2e4e6a12006-01-12 13:30:04 -08001390 private = t->private;
Dmitry Mishin27229712006-04-01 02:25:19 -08001391 if (private->number != num_counters) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 ret = -EINVAL;
1393 goto unlock_up_free;
1394 }
1395
1396 i = 0;
Eric Dumazet31836062005-12-13 23:13:48 -08001397 /* Choose the copy that is on our node */
Harald Welte2e4e6a12006-01-12 13:30:04 -08001398 loc_cpu_entry = private->entries[raw_smp_processor_id()];
Eric Dumazet31836062005-12-13 23:13:48 -08001399 IPT_ENTRY_ITERATE(loc_cpu_entry,
Harald Welte2e4e6a12006-01-12 13:30:04 -08001400 private->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401 add_counter_to_entry,
Dmitry Mishin27229712006-04-01 02:25:19 -08001402 paddc,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 &i);
1404 unlock_up_free:
1405 write_unlock_bh(&t->lock);
Harald Welte2e4e6a12006-01-12 13:30:04 -08001406 xt_table_unlock(t);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407 module_put(t->me);
1408 free:
1409 vfree(paddc);
1410
1411 return ret;
1412}
1413
Dmitry Mishin27229712006-04-01 02:25:19 -08001414#ifdef CONFIG_COMPAT
1415struct compat_ipt_replace {
1416 char name[IPT_TABLE_MAXNAMELEN];
1417 u32 valid_hooks;
1418 u32 num_entries;
1419 u32 size;
1420 u32 hook_entry[NF_IP_NUMHOOKS];
1421 u32 underflow[NF_IP_NUMHOOKS];
1422 u32 num_counters;
1423 compat_uptr_t counters; /* struct ipt_counters * */
1424 struct compat_ipt_entry entries[0];
1425};
1426
1427static inline int compat_copy_match_to_user(struct ipt_entry_match *m,
1428 void __user **dstptr, compat_uint_t *size)
1429{
1430 if (m->u.kernel.match->compat)
1431 return m->u.kernel.match->compat(m, dstptr, size,
1432 COMPAT_TO_USER);
1433 else
1434 return xt_compat_match(m, dstptr, size, COMPAT_TO_USER);
1435}
1436
1437static int compat_copy_entry_to_user(struct ipt_entry *e,
1438 void __user **dstptr, compat_uint_t *size)
1439{
1440 struct ipt_entry_target __user *t;
1441 struct compat_ipt_entry __user *ce;
1442 u_int16_t target_offset, next_offset;
1443 compat_uint_t origsize;
1444 int ret;
1445
1446 ret = -EFAULT;
1447 origsize = *size;
1448 ce = (struct compat_ipt_entry __user *)*dstptr;
1449 if (__copy_to_user(ce, e, sizeof(struct ipt_entry)))
1450 goto out;
1451
1452 *dstptr += sizeof(struct compat_ipt_entry);
1453 ret = IPT_MATCH_ITERATE(e, compat_copy_match_to_user, dstptr, size);
1454 target_offset = e->target_offset - (origsize - *size);
1455 if (ret)
1456 goto out;
1457 t = ipt_get_target(e);
1458 if (t->u.kernel.target->compat)
1459 ret = t->u.kernel.target->compat(t, dstptr, size,
1460 COMPAT_TO_USER);
1461 else
1462 ret = xt_compat_target(t, dstptr, size, COMPAT_TO_USER);
1463 if (ret)
1464 goto out;
1465 ret = -EFAULT;
1466 next_offset = e->next_offset - (origsize - *size);
1467 if (__put_user(target_offset, &ce->target_offset))
1468 goto out;
1469 if (__put_user(next_offset, &ce->next_offset))
1470 goto out;
1471 return 0;
1472out:
1473 return ret;
1474}
1475
1476static inline int
1477compat_check_calc_match(struct ipt_entry_match *m,
1478 const char *name,
1479 const struct ipt_ip *ip,
1480 unsigned int hookmask,
1481 int *size, int *i)
1482{
1483 struct ipt_match *match;
1484
1485 match = try_then_request_module(xt_find_match(AF_INET, m->u.user.name,
1486 m->u.user.revision),
1487 "ipt_%s", m->u.user.name);
1488 if (IS_ERR(match) || !match) {
1489 duprintf("compat_check_calc_match: `%s' not found\n",
1490 m->u.user.name);
1491 return match ? PTR_ERR(match) : -ENOENT;
1492 }
1493 m->u.kernel.match = match;
1494
1495 if (m->u.kernel.match->compat)
1496 m->u.kernel.match->compat(m, NULL, size, COMPAT_CALC_SIZE);
1497 else
1498 xt_compat_match(m, NULL, size, COMPAT_CALC_SIZE);
1499
1500 (*i)++;
1501 return 0;
1502}
1503
1504static inline int
1505check_compat_entry_size_and_hooks(struct ipt_entry *e,
1506 struct xt_table_info *newinfo,
1507 unsigned int *size,
1508 unsigned char *base,
1509 unsigned char *limit,
1510 unsigned int *hook_entries,
1511 unsigned int *underflows,
1512 unsigned int *i,
1513 const char *name)
1514{
1515 struct ipt_entry_target *t;
1516 struct ipt_target *target;
1517 u_int16_t entry_offset;
1518 int ret, off, h, j;
1519
1520 duprintf("check_compat_entry_size_and_hooks %p\n", e);
1521 if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0
1522 || (unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
1523 duprintf("Bad offset %p, limit = %p\n", e, limit);
1524 return -EINVAL;
1525 }
1526
1527 if (e->next_offset < sizeof(struct compat_ipt_entry) +
1528 sizeof(struct compat_xt_entry_target)) {
1529 duprintf("checking: element %p size %u\n",
1530 e, e->next_offset);
1531 return -EINVAL;
1532 }
1533
1534 if (!ip_checkentry(&e->ip)) {
1535 duprintf("ip_tables: ip check failed %p %s.\n", e, name);
1536 return -EINVAL;
1537 }
1538
1539 off = 0;
1540 entry_offset = (void *)e - (void *)base;
1541 j = 0;
1542 ret = IPT_MATCH_ITERATE(e, compat_check_calc_match, name, &e->ip,
1543 e->comefrom, &off, &j);
1544 if (ret != 0)
1545 goto out;
1546
1547 t = ipt_get_target(e);
1548 target = try_then_request_module(xt_find_target(AF_INET,
1549 t->u.user.name,
1550 t->u.user.revision),
1551 "ipt_%s", t->u.user.name);
1552 if (IS_ERR(target) || !target) {
1553 duprintf("check_entry: `%s' not found\n", t->u.user.name);
1554 ret = target ? PTR_ERR(target) : -ENOENT;
1555 goto out;
1556 }
1557 t->u.kernel.target = target;
1558
1559 if (t->u.kernel.target->compat)
1560 t->u.kernel.target->compat(t, NULL, &off, COMPAT_CALC_SIZE);
1561 else
1562 xt_compat_target(t, NULL, &off, COMPAT_CALC_SIZE);
1563 *size += off;
1564 ret = compat_add_offset(entry_offset, off);
1565 if (ret)
1566 goto out;
1567
1568 /* Check hooks & underflows */
1569 for (h = 0; h < NF_IP_NUMHOOKS; h++) {
1570 if ((unsigned char *)e - base == hook_entries[h])
1571 newinfo->hook_entry[h] = hook_entries[h];
1572 if ((unsigned char *)e - base == underflows[h])
1573 newinfo->underflow[h] = underflows[h];
1574 }
1575
1576 /* Clear counters and comefrom */
1577 e->counters = ((struct ipt_counters) { 0, 0 });
1578 e->comefrom = 0;
1579
1580 (*i)++;
1581 return 0;
1582out:
1583 IPT_MATCH_ITERATE(e, cleanup_match, &j);
1584 return ret;
1585}
1586
1587static inline int compat_copy_match_from_user(struct ipt_entry_match *m,
1588 void **dstptr, compat_uint_t *size, const char *name,
1589 const struct ipt_ip *ip, unsigned int hookmask)
1590{
1591 struct ipt_entry_match *dm;
1592 struct ipt_match *match;
1593 int ret;
1594
1595 dm = (struct ipt_entry_match *)*dstptr;
1596 match = m->u.kernel.match;
1597 if (match->compat)
1598 match->compat(m, dstptr, size, COMPAT_FROM_USER);
1599 else
1600 xt_compat_match(m, dstptr, size, COMPAT_FROM_USER);
1601
1602 ret = xt_check_match(match, AF_INET, dm->u.match_size - sizeof(*dm),
1603 name, hookmask, ip->proto,
1604 ip->invflags & IPT_INV_PROTO);
1605 if (ret)
1606 return ret;
1607
1608 if (m->u.kernel.match->checkentry
1609 && !m->u.kernel.match->checkentry(name, ip, match, dm->data,
1610 dm->u.match_size - sizeof(*dm),
1611 hookmask)) {
1612 duprintf("ip_tables: check failed for `%s'.\n",
1613 m->u.kernel.match->name);
1614 return -EINVAL;
1615 }
1616 return 0;
1617}
1618
1619static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr,
1620 unsigned int *size, const char *name,
1621 struct xt_table_info *newinfo, unsigned char *base)
1622{
1623 struct ipt_entry_target *t;
1624 struct ipt_target *target;
1625 struct ipt_entry *de;
1626 unsigned int origsize;
1627 int ret, h;
1628
1629 ret = 0;
1630 origsize = *size;
1631 de = (struct ipt_entry *)*dstptr;
1632 memcpy(de, e, sizeof(struct ipt_entry));
1633
1634 *dstptr += sizeof(struct compat_ipt_entry);
1635 ret = IPT_MATCH_ITERATE(e, compat_copy_match_from_user, dstptr, size,
1636 name, &de->ip, de->comefrom);
1637 if (ret)
1638 goto out;
1639 de->target_offset = e->target_offset - (origsize - *size);
1640 t = ipt_get_target(e);
1641 target = t->u.kernel.target;
1642 if (target->compat)
1643 target->compat(t, dstptr, size, COMPAT_FROM_USER);
1644 else
1645 xt_compat_target(t, dstptr, size, COMPAT_FROM_USER);
1646
1647 de->next_offset = e->next_offset - (origsize - *size);
1648 for (h = 0; h < NF_IP_NUMHOOKS; h++) {
1649 if ((unsigned char *)de - base < newinfo->hook_entry[h])
1650 newinfo->hook_entry[h] -= origsize - *size;
1651 if ((unsigned char *)de - base < newinfo->underflow[h])
1652 newinfo->underflow[h] -= origsize - *size;
1653 }
1654
1655 t = ipt_get_target(de);
1656 target = t->u.kernel.target;
1657 ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t),
1658 name, e->comefrom, e->ip.proto,
1659 e->ip.invflags & IPT_INV_PROTO);
1660 if (ret)
1661 goto out;
1662
1663 ret = -EINVAL;
1664 if (t->u.kernel.target == &ipt_standard_target) {
1665 if (!standard_check(t, *size))
1666 goto out;
1667 } else if (t->u.kernel.target->checkentry
1668 && !t->u.kernel.target->checkentry(name, de, target,
1669 t->data, t->u.target_size - sizeof(*t),
1670 de->comefrom)) {
1671 duprintf("ip_tables: compat: check failed for `%s'.\n",
1672 t->u.kernel.target->name);
1673 goto out;
1674 }
1675 ret = 0;
1676out:
1677 return ret;
1678}
1679
1680static int
1681translate_compat_table(const char *name,
1682 unsigned int valid_hooks,
1683 struct xt_table_info **pinfo,
1684 void **pentry0,
1685 unsigned int total_size,
1686 unsigned int number,
1687 unsigned int *hook_entries,
1688 unsigned int *underflows)
1689{
1690 unsigned int i;
1691 struct xt_table_info *newinfo, *info;
1692 void *pos, *entry0, *entry1;
1693 unsigned int size;
1694 int ret;
1695
1696 info = *pinfo;
1697 entry0 = *pentry0;
1698 size = total_size;
1699 info->number = number;
1700
1701 /* Init all hooks to impossible value. */
1702 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
1703 info->hook_entry[i] = 0xFFFFFFFF;
1704 info->underflow[i] = 0xFFFFFFFF;
1705 }
1706
1707 duprintf("translate_compat_table: size %u\n", info->size);
1708 i = 0;
1709 xt_compat_lock(AF_INET);
1710 /* Walk through entries, checking offsets. */
1711 ret = IPT_ENTRY_ITERATE(entry0, total_size,
1712 check_compat_entry_size_and_hooks,
1713 info, &size, entry0,
1714 entry0 + total_size,
1715 hook_entries, underflows, &i, name);
1716 if (ret != 0)
1717 goto out_unlock;
1718
1719 ret = -EINVAL;
1720 if (i != number) {
1721 duprintf("translate_compat_table: %u not %u entries\n",
1722 i, number);
1723 goto out_unlock;
1724 }
1725
1726 /* Check hooks all assigned */
1727 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
1728 /* Only hooks which are valid */
1729 if (!(valid_hooks & (1 << i)))
1730 continue;
1731 if (info->hook_entry[i] == 0xFFFFFFFF) {
1732 duprintf("Invalid hook entry %u %u\n",
1733 i, hook_entries[i]);
1734 goto out_unlock;
1735 }
1736 if (info->underflow[i] == 0xFFFFFFFF) {
1737 duprintf("Invalid underflow %u %u\n",
1738 i, underflows[i]);
1739 goto out_unlock;
1740 }
1741 }
1742
1743 ret = -ENOMEM;
1744 newinfo = xt_alloc_table_info(size);
1745 if (!newinfo)
1746 goto out_unlock;
1747
1748 newinfo->number = number;
1749 for (i = 0; i < NF_IP_NUMHOOKS; i++) {
1750 newinfo->hook_entry[i] = info->hook_entry[i];
1751 newinfo->underflow[i] = info->underflow[i];
1752 }
1753 entry1 = newinfo->entries[raw_smp_processor_id()];
1754 pos = entry1;
1755 size = total_size;
1756 ret = IPT_ENTRY_ITERATE(entry0, total_size,
1757 compat_copy_entry_from_user, &pos, &size,
1758 name, newinfo, entry1);
1759 compat_flush_offsets();
1760 xt_compat_unlock(AF_INET);
1761 if (ret)
1762 goto free_newinfo;
1763
1764 ret = -ELOOP;
1765 if (!mark_source_chains(newinfo, valid_hooks, entry1))
1766 goto free_newinfo;
1767
1768 /* And one copy for every other CPU */
1769 for_each_cpu(i)
1770 if (newinfo->entries[i] && newinfo->entries[i] != entry1)
1771 memcpy(newinfo->entries[i], entry1, newinfo->size);
1772
1773 *pinfo = newinfo;
1774 *pentry0 = entry1;
1775 xt_free_table_info(info);
1776 return 0;
1777
1778free_newinfo:
1779 xt_free_table_info(newinfo);
1780out:
1781 return ret;
1782out_unlock:
1783 xt_compat_unlock(AF_INET);
1784 goto out;
1785}
1786
1787static int
1788compat_do_replace(void __user *user, unsigned int len)
1789{
1790 int ret;
1791 struct compat_ipt_replace tmp;
1792 struct xt_table_info *newinfo;
1793 void *loc_cpu_entry;
1794
1795 if (copy_from_user(&tmp, user, sizeof(tmp)) != 0)
1796 return -EFAULT;
1797
1798 /* Hack: Causes ipchains to give correct error msg --RR */
1799 if (len != sizeof(tmp) + tmp.size)
1800 return -ENOPROTOOPT;
1801
1802 /* overflow check */
1803 if (tmp.size >= (INT_MAX - sizeof(struct xt_table_info)) / NR_CPUS -
1804 SMP_CACHE_BYTES)
1805 return -ENOMEM;
1806 if (tmp.num_counters >= INT_MAX / sizeof(struct xt_counters))
1807 return -ENOMEM;
1808
1809 newinfo = xt_alloc_table_info(tmp.size);
1810 if (!newinfo)
1811 return -ENOMEM;
1812
1813 /* choose the copy that is our node/cpu */
1814 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
1815 if (copy_from_user(loc_cpu_entry, user + sizeof(tmp),
1816 tmp.size) != 0) {
1817 ret = -EFAULT;
1818 goto free_newinfo;
1819 }
1820
1821 ret = translate_compat_table(tmp.name, tmp.valid_hooks,
1822 &newinfo, &loc_cpu_entry, tmp.size,
1823 tmp.num_entries, tmp.hook_entry, tmp.underflow);
1824 if (ret != 0)
1825 goto free_newinfo;
1826
1827 duprintf("compat_do_replace: Translated table\n");
1828
1829 ret = __do_replace(tmp.name, tmp.valid_hooks,
1830 newinfo, tmp.num_counters,
1831 compat_ptr(tmp.counters));
1832 if (ret)
1833 goto free_newinfo_untrans;
1834 return 0;
1835
1836 free_newinfo_untrans:
1837 IPT_ENTRY_ITERATE(loc_cpu_entry, newinfo->size, cleanup_entry,NULL);
1838 free_newinfo:
1839 xt_free_table_info(newinfo);
1840 return ret;
1841}
1842
1843static int
1844compat_do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user,
1845 unsigned int len)
1846{
1847 int ret;
1848
1849 if (!capable(CAP_NET_ADMIN))
1850 return -EPERM;
1851
1852 switch (cmd) {
1853 case IPT_SO_SET_REPLACE:
1854 ret = compat_do_replace(user, len);
1855 break;
1856
1857 case IPT_SO_SET_ADD_COUNTERS:
1858 ret = do_add_counters(user, len, 1);
1859 break;
1860
1861 default:
1862 duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
1863 ret = -EINVAL;
1864 }
1865
1866 return ret;
1867}
1868
1869struct compat_ipt_get_entries
1870{
1871 char name[IPT_TABLE_MAXNAMELEN];
1872 compat_uint_t size;
1873 struct compat_ipt_entry entrytable[0];
1874};
1875
1876static int compat_copy_entries_to_user(unsigned int total_size,
1877 struct ipt_table *table, void __user *userptr)
1878{
1879 unsigned int off, num;
1880 struct compat_ipt_entry e;
1881 struct xt_counters *counters;
1882 struct xt_table_info *private = table->private;
1883 void __user *pos;
1884 unsigned int size;
1885 int ret = 0;
1886 void *loc_cpu_entry;
1887
1888 counters = alloc_counters(table);
1889 if (IS_ERR(counters))
1890 return PTR_ERR(counters);
1891
1892 /* choose the copy that is on our node/cpu, ...
1893 * This choice is lazy (because current thread is
1894 * allowed to migrate to another cpu)
1895 */
1896 loc_cpu_entry = private->entries[raw_smp_processor_id()];
1897 pos = userptr;
1898 size = total_size;
1899 ret = IPT_ENTRY_ITERATE(loc_cpu_entry, total_size,
1900 compat_copy_entry_to_user, &pos, &size);
1901 if (ret)
1902 goto free_counters;
1903
1904 /* ... then go back and fix counters and names */
1905 for (off = 0, num = 0; off < size; off += e.next_offset, num++) {
1906 unsigned int i;
1907 struct ipt_entry_match m;
1908 struct ipt_entry_target t;
1909
1910 ret = -EFAULT;
1911 if (copy_from_user(&e, userptr + off,
1912 sizeof(struct compat_ipt_entry)))
1913 goto free_counters;
1914 if (copy_to_user(userptr + off +
1915 offsetof(struct compat_ipt_entry, counters),
1916 &counters[num], sizeof(counters[num])))
1917 goto free_counters;
1918
1919 for (i = sizeof(struct compat_ipt_entry);
1920 i < e.target_offset; i += m.u.match_size) {
1921 if (copy_from_user(&m, userptr + off + i,
1922 sizeof(struct ipt_entry_match)))
1923 goto free_counters;
1924 if (copy_to_user(userptr + off + i +
1925 offsetof(struct ipt_entry_match, u.user.name),
1926 m.u.kernel.match->name,
1927 strlen(m.u.kernel.match->name) + 1))
1928 goto free_counters;
1929 }
1930
1931 if (copy_from_user(&t, userptr + off + e.target_offset,
1932 sizeof(struct ipt_entry_target)))
1933 goto free_counters;
1934 if (copy_to_user(userptr + off + e.target_offset +
1935 offsetof(struct ipt_entry_target, u.user.name),
1936 t.u.kernel.target->name,
1937 strlen(t.u.kernel.target->name) + 1))
1938 goto free_counters;
1939 }
1940 ret = 0;
1941free_counters:
1942 vfree(counters);
1943 return ret;
1944}
1945
1946static int
1947compat_get_entries(struct compat_ipt_get_entries __user *uptr, int *len)
1948{
1949 int ret;
1950 struct compat_ipt_get_entries get;
1951 struct ipt_table *t;
1952
1953
1954 if (*len < sizeof(get)) {
1955 duprintf("compat_get_entries: %u < %u\n",
1956 *len, (unsigned int)sizeof(get));
1957 return -EINVAL;
1958 }
1959
1960 if (copy_from_user(&get, uptr, sizeof(get)) != 0)
1961 return -EFAULT;
1962
1963 if (*len != sizeof(struct compat_ipt_get_entries) + get.size) {
1964 duprintf("compat_get_entries: %u != %u\n", *len,
1965 (unsigned int)(sizeof(struct compat_ipt_get_entries) +
1966 get.size));
1967 return -EINVAL;
1968 }
1969
1970 xt_compat_lock(AF_INET);
1971 t = xt_find_table_lock(AF_INET, get.name);
1972 if (t && !IS_ERR(t)) {
1973 struct xt_table_info *private = t->private;
1974 struct xt_table_info info;
1975 duprintf("t->private->number = %u\n",
1976 private->number);
1977 ret = compat_table_info(private, &info);
1978 if (!ret && get.size == info.size) {
1979 ret = compat_copy_entries_to_user(private->size,
1980 t, uptr->entrytable);
1981 } else if (!ret) {
1982 duprintf("compat_get_entries: I've got %u not %u!\n",
1983 private->size,
1984 get.size);
1985 ret = -EINVAL;
1986 }
1987 compat_flush_offsets();
1988 module_put(t->me);
1989 xt_table_unlock(t);
1990 } else
1991 ret = t ? PTR_ERR(t) : -ENOENT;
1992
1993 xt_compat_unlock(AF_INET);
1994 return ret;
1995}
1996
1997static int
1998compat_do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
1999{
2000 int ret;
2001
2002 switch (cmd) {
2003 case IPT_SO_GET_INFO:
2004 ret = get_info(user, len, 1);
2005 break;
2006 case IPT_SO_GET_ENTRIES:
2007 ret = compat_get_entries(user, len);
2008 break;
2009 default:
2010 duprintf("compat_do_ipt_get_ctl: unknown request %i\n", cmd);
2011 ret = -EINVAL;
2012 }
2013 return ret;
2014}
2015#endif
2016
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017static int
2018do_ipt_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
2019{
2020 int ret;
2021
2022 if (!capable(CAP_NET_ADMIN))
2023 return -EPERM;
2024
2025 switch (cmd) {
2026 case IPT_SO_SET_REPLACE:
2027 ret = do_replace(user, len);
2028 break;
2029
2030 case IPT_SO_SET_ADD_COUNTERS:
Dmitry Mishin27229712006-04-01 02:25:19 -08002031 ret = do_add_counters(user, len, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 break;
2033
2034 default:
2035 duprintf("do_ipt_set_ctl: unknown request %i\n", cmd);
2036 ret = -EINVAL;
2037 }
2038
2039 return ret;
2040}
2041
2042static int
2043do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
2044{
2045 int ret;
2046
2047 if (!capable(CAP_NET_ADMIN))
2048 return -EPERM;
2049
2050 switch (cmd) {
Dmitry Mishin27229712006-04-01 02:25:19 -08002051 case IPT_SO_GET_INFO:
2052 ret = get_info(user, len, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 break;
Dmitry Mishin27229712006-04-01 02:25:19 -08002054
2055 case IPT_SO_GET_ENTRIES:
2056 ret = get_entries(user, len);
2057 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058
2059 case IPT_SO_GET_REVISION_MATCH:
2060 case IPT_SO_GET_REVISION_TARGET: {
2061 struct ipt_get_revision rev;
Harald Welte2e4e6a12006-01-12 13:30:04 -08002062 int target;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063
2064 if (*len != sizeof(rev)) {
2065 ret = -EINVAL;
2066 break;
2067 }
2068 if (copy_from_user(&rev, user, sizeof(rev)) != 0) {
2069 ret = -EFAULT;
2070 break;
2071 }
2072
2073 if (cmd == IPT_SO_GET_REVISION_TARGET)
Harald Welte2e4e6a12006-01-12 13:30:04 -08002074 target = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 else
Harald Welte2e4e6a12006-01-12 13:30:04 -08002076 target = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077
Harald Welte2e4e6a12006-01-12 13:30:04 -08002078 try_then_request_module(xt_find_revision(AF_INET, rev.name,
2079 rev.revision,
2080 target, &ret),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 "ipt_%s", rev.name);
2082 break;
2083 }
2084
2085 default:
2086 duprintf("do_ipt_get_ctl: unknown request %i\n", cmd);
2087 ret = -EINVAL;
2088 }
2089
2090 return ret;
2091}
2092
Harald Welte2e4e6a12006-01-12 13:30:04 -08002093int ipt_register_table(struct xt_table *table, const struct ipt_replace *repl)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094{
2095 int ret;
Harald Welte2e4e6a12006-01-12 13:30:04 -08002096 struct xt_table_info *newinfo;
2097 static struct xt_table_info bootstrap
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 = { 0, 0, 0, { 0 }, { 0 }, { } };
Eric Dumazet31836062005-12-13 23:13:48 -08002099 void *loc_cpu_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100
Harald Welte2e4e6a12006-01-12 13:30:04 -08002101 newinfo = xt_alloc_table_info(repl->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102 if (!newinfo)
2103 return -ENOMEM;
2104
Eric Dumazet31836062005-12-13 23:13:48 -08002105 /* choose the copy on our node/cpu
2106 * but dont care of preemption
2107 */
2108 loc_cpu_entry = newinfo->entries[raw_smp_processor_id()];
2109 memcpy(loc_cpu_entry, repl->entries, repl->size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110
2111 ret = translate_table(table->name, table->valid_hooks,
Eric Dumazet31836062005-12-13 23:13:48 -08002112 newinfo, loc_cpu_entry, repl->size,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 repl->num_entries,
2114 repl->hook_entry,
2115 repl->underflow);
2116 if (ret != 0) {
Harald Welte2e4e6a12006-01-12 13:30:04 -08002117 xt_free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118 return ret;
2119 }
2120
Harald Welte2e4e6a12006-01-12 13:30:04 -08002121 if (xt_register_table(table, &bootstrap, newinfo) != 0) {
2122 xt_free_table_info(newinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123 return ret;
2124 }
2125
Harald Welte2e4e6a12006-01-12 13:30:04 -08002126 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127}
2128
2129void ipt_unregister_table(struct ipt_table *table)
2130{
Harald Welte2e4e6a12006-01-12 13:30:04 -08002131 struct xt_table_info *private;
Eric Dumazet31836062005-12-13 23:13:48 -08002132 void *loc_cpu_entry;
2133
Harald Welte2e4e6a12006-01-12 13:30:04 -08002134 private = xt_unregister_table(table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135
2136 /* Decrease module usage counts and free resources */
Harald Welte2e4e6a12006-01-12 13:30:04 -08002137 loc_cpu_entry = private->entries[raw_smp_processor_id()];
2138 IPT_ENTRY_ITERATE(loc_cpu_entry, private->size, cleanup_entry, NULL);
2139 xt_free_table_info(private);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140}
2141
2142/* Returns 1 if the type and code is matched by the range, 0 otherwise */
2143static inline int
2144icmp_type_code_match(u_int8_t test_type, u_int8_t min_code, u_int8_t max_code,
2145 u_int8_t type, u_int8_t code,
2146 int invert)
2147{
2148 return ((test_type == 0xFF) || (type == test_type && code >= min_code && code <= max_code))
2149 ^ invert;
2150}
2151
2152static int
2153icmp_match(const struct sk_buff *skb,
2154 const struct net_device *in,
2155 const struct net_device *out,
Patrick McHardyc4986732006-03-20 18:02:56 -08002156 const struct xt_match *match,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157 const void *matchinfo,
2158 int offset,
Harald Welte2e4e6a12006-01-12 13:30:04 -08002159 unsigned int protoff,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160 int *hotdrop)
2161{
2162 struct icmphdr _icmph, *ic;
2163 const struct ipt_icmp *icmpinfo = matchinfo;
2164
2165 /* Must not be a fragment. */
2166 if (offset)
2167 return 0;
2168
Harald Welte2e4e6a12006-01-12 13:30:04 -08002169 ic = skb_header_pointer(skb, protoff, sizeof(_icmph), &_icmph);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002170 if (ic == NULL) {
2171 /* We've been asked to examine this packet, and we
2172 * can't. Hence, no choice but to drop.
2173 */
2174 duprintf("Dropping evil ICMP tinygram.\n");
2175 *hotdrop = 1;
2176 return 0;
2177 }
2178
2179 return icmp_type_code_match(icmpinfo->type,
2180 icmpinfo->code[0],
2181 icmpinfo->code[1],
2182 ic->type, ic->code,
2183 !!(icmpinfo->invflags&IPT_ICMP_INV));
2184}
2185
2186/* Called when user tries to insert an entry of this type. */
2187static int
2188icmp_checkentry(const char *tablename,
Harald Welte2e4e6a12006-01-12 13:30:04 -08002189 const void *info,
Patrick McHardyc4986732006-03-20 18:02:56 -08002190 const struct xt_match *match,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 void *matchinfo,
2192 unsigned int matchsize,
2193 unsigned int hook_mask)
2194{
2195 const struct ipt_icmp *icmpinfo = matchinfo;
2196
Patrick McHardy1d5cd902006-03-20 18:01:14 -08002197 /* Must specify no unknown invflags */
2198 return !(icmpinfo->invflags & ~IPT_ICMP_INV);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002199}
2200
2201/* The built-in targets: standard (NULL) and error. */
2202static struct ipt_target ipt_standard_target = {
2203 .name = IPT_STANDARD_TARGET,
Patrick McHardy1d5cd902006-03-20 18:01:14 -08002204 .targetsize = sizeof(int),
Pablo Neira Ayusoa45049c2006-03-22 13:55:40 -08002205 .family = AF_INET,
Dmitry Mishin27229712006-04-01 02:25:19 -08002206#ifdef CONFIG_COMPAT
2207 .compat = &compat_ipt_standard_fn,
2208#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209};
2210
2211static struct ipt_target ipt_error_target = {
2212 .name = IPT_ERROR_TARGET,
2213 .target = ipt_error,
Patrick McHardy1d5cd902006-03-20 18:01:14 -08002214 .targetsize = IPT_FUNCTION_MAXNAMELEN,
Pablo Neira Ayusoa45049c2006-03-22 13:55:40 -08002215 .family = AF_INET,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216};
2217
2218static struct nf_sockopt_ops ipt_sockopts = {
2219 .pf = PF_INET,
2220 .set_optmin = IPT_BASE_CTL,
2221 .set_optmax = IPT_SO_SET_MAX+1,
2222 .set = do_ipt_set_ctl,
Dmitry Mishin27229712006-04-01 02:25:19 -08002223#ifdef CONFIG_COMPAT
2224 .compat_set = compat_do_ipt_set_ctl,
2225#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226 .get_optmin = IPT_BASE_CTL,
2227 .get_optmax = IPT_SO_GET_MAX+1,
2228 .get = do_ipt_get_ctl,
Dmitry Mishin27229712006-04-01 02:25:19 -08002229#ifdef CONFIG_COMPAT
2230 .compat_get = compat_do_ipt_get_ctl,
2231#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232};
2233
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234static struct ipt_match icmp_matchstruct = {
2235 .name = "icmp",
Patrick McHardy1d5cd902006-03-20 18:01:14 -08002236 .match = icmp_match,
2237 .matchsize = sizeof(struct ipt_icmp),
2238 .proto = IPPROTO_ICMP,
Pablo Neira Ayusoa45049c2006-03-22 13:55:40 -08002239 .family = AF_INET,
Patrick McHardy1d5cd902006-03-20 18:01:14 -08002240 .checkentry = icmp_checkentry,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241};
2242
Andrew Morton65b4b4e2006-03-28 16:37:06 -08002243static int __init ip_tables_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244{
2245 int ret;
2246
Harald Welte2e4e6a12006-01-12 13:30:04 -08002247 xt_proto_init(AF_INET);
2248
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 /* Noone else will be downing sem now, so we won't sleep */
Pablo Neira Ayusoa45049c2006-03-22 13:55:40 -08002250 xt_register_target(&ipt_standard_target);
2251 xt_register_target(&ipt_error_target);
2252 xt_register_match(&icmp_matchstruct);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253
2254 /* Register setsockopt */
2255 ret = nf_register_sockopt(&ipt_sockopts);
2256 if (ret < 0) {
2257 duprintf("Unable to register sockopts.\n");
2258 return ret;
2259 }
2260
Harald Welte2e4e6a12006-01-12 13:30:04 -08002261 printk("ip_tables: (C) 2000-2006 Netfilter Core Team\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 return 0;
2263}
2264
Andrew Morton65b4b4e2006-03-28 16:37:06 -08002265static void __exit ip_tables_fini(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266{
2267 nf_unregister_sockopt(&ipt_sockopts);
Harald Welte2e4e6a12006-01-12 13:30:04 -08002268
Pablo Neira Ayusoa45049c2006-03-22 13:55:40 -08002269 xt_unregister_match(&icmp_matchstruct);
2270 xt_unregister_target(&ipt_error_target);
2271 xt_unregister_target(&ipt_standard_target);
Harald Welte2e4e6a12006-01-12 13:30:04 -08002272
2273 xt_proto_fini(AF_INET);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274}
2275
2276EXPORT_SYMBOL(ipt_register_table);
2277EXPORT_SYMBOL(ipt_unregister_table);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002278EXPORT_SYMBOL(ipt_do_table);
Andrew Morton65b4b4e2006-03-28 16:37:06 -08002279module_init(ip_tables_init);
2280module_exit(ip_tables_fini);