root/src/linux/ar531x/linux-2.6.23/net/ipv4/netfilter/ip_tables.c

Revision 12400, 57.5 kB (checked in by BrainSlayer, 5 months ago)

more flexible EOC5610 partition layout, small performance increase by module remapping, lzma decoder speed improvements

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