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