• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/net/netfilter/
1/* iptables kernel module for the geoip match
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * Copyright (c) 2004, 2005, 2006, 2007, 2008
9 * Samuel Jean & Nicolas Bouliane
10 */
11#include <linux/module.h>
12#include <linux/kernel.h>
13#include <linux/version.h>
14#include <linux/skbuff.h>
15#include <linux/ip.h>
16#include <linux/netdevice.h>
17#include <asm/uaccess.h>
18#include <asm/atomic.h>
19
20#include <linux/netfilter/x_tables.h>
21#include <linux/netfilter/xt_geoip.h>
22
23MODULE_LICENSE("GPL");
24MODULE_AUTHOR("Nicolas Bouliane");
25MODULE_AUTHOR("Samuel Jean");
26MODULE_DESCRIPTION("xtables module for geoip match");
27MODULE_ALIAS("ipt_geoip");
28
29struct geoip_info *head = NULL;
30static DEFINE_SPINLOCK(geoip_lock);
31
32static struct geoip_info *add_node(struct geoip_info *memcpy)
33{
34   struct geoip_info *p =
35      (struct geoip_info *)kmalloc(sizeof(struct geoip_info), GFP_KERNEL);
36
37   struct geoip_subnet *s;
38
39   if ((p == NULL) || (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0))
40      return NULL;
41
42   s = (struct geoip_subnet *)kmalloc(p->count * sizeof(struct geoip_subnet), GFP_KERNEL);
43   if ((s == NULL) || (copy_from_user(s, p->subnets, p->count * sizeof(struct geoip_subnet)) != 0))
44      return NULL;
45
46   spin_lock_bh(&geoip_lock);
47
48   p->subnets = s;
49   p->ref = 1;
50   p->next = head;
51   p->prev = NULL;
52   if (p->next) p->next->prev = p;
53   head = p;
54
55   spin_unlock_bh(&geoip_lock);
56   return p;
57}
58
59static void remove_node(struct geoip_info *p)
60 {
61   spin_lock_bh(&geoip_lock);
62
63   if (p->next) { /* Am I following a node ? */
64      p->next->prev = p->prev;
65      if (p->prev) p->prev->next = p->next; /* Is there a node behind me ? */
66      else head = p->next; /* No? Then I was the head */
67   }
68
69   else
70      if (p->prev) /* Is there a node behind me ? */
71         p->prev->next = NULL;
72      else
73         head = NULL; /* No, we're alone */
74
75   /* So now am unlinked or the only one alive, right ?
76    * What are you waiting ? Free up some memory!
77    */
78
79   kfree(p->subnets);
80   kfree(p);
81
82   spin_unlock_bh(&geoip_lock);
83   return;
84}
85
86static struct geoip_info *find_node(u_int16_t cc)
87{
88   struct geoip_info *p = head;
89   spin_lock_bh(&geoip_lock);
90
91   while (p) {
92      if (p->cc == cc) {
93         spin_unlock_bh(&geoip_lock);
94         return p;
95      }
96      p = p->next;
97   }
98   spin_unlock_bh(&geoip_lock);
99   return NULL;
100}
101
102static bool xt_geoip_mt(const struct sk_buff *skb, struct xt_action_param *par)
103{
104   const struct xt_geoip_match_info *info = par->matchinfo;
105   const struct geoip_info *node; /* This keeps the code sexy */
106   const struct iphdr *iph = ip_hdr(skb);
107   u_int32_t ip, i, j;
108
109   if (info->flags & XT_GEOIP_SRC)
110      ip = ntohl(iph->saddr);
111   else
112      ip = ntohl(iph->daddr);
113
114   spin_lock_bh(&geoip_lock);
115   for (i = 0; i < info->count; i++) {
116      if ((node = info->mem[i]) == NULL) {
117         printk(KERN_ERR "xt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n",
118               COUNTRY(info->cc[i]));
119
120         continue;
121      }
122
123      for (j = 0; j < node->count; j++)
124         if ((ip > node->subnets[j].begin) && (ip < node->subnets[j].end)) {
125            spin_unlock_bh(&geoip_lock);
126            return (info->flags & XT_GEOIP_INV) ? 0 : 1;
127         }
128   }
129
130   spin_unlock_bh(&geoip_lock);
131   return (info->flags & XT_GEOIP_INV) ? 1 : 0;
132}
133
134static int xt_geoip_mt_checkentry(const struct xt_mtchk_param *par)
135{
136
137   struct xt_geoip_match_info *info = par->matchinfo;
138   struct geoip_info *node;
139   u_int8_t i;
140
141   /* FIXME:   Call a function to free userspace allocated memory.
142    *          As Martin J. said; this match might eat lot of memory
143    *          if commited with iptables-restore --noflush
144   void (*gfree)(struct geoip_info *oldmem);
145   gfree = info->fini;
146   */
147
148   /* If info->refcount isn't NULL, then
149    * it means that checkentry() already
150    * initialized this entry. Increase a
151    * refcount to prevent destroy() of
152    * this entry. */
153   if (info->refcount != NULL) {
154      atomic_inc((atomic_t *)info->refcount);
155      return 0;
156   }
157
158
159   for (i = 0; i < info->count; i++) {
160
161      if ((node = find_node(info->cc[i])) != NULL)
162            atomic_inc((atomic_t *)&node->ref);   //increase the reference
163      else
164         if ((node = add_node(info->mem[i])) == NULL) {
165            printk(KERN_ERR
166                  "xt_geoip: unable to load '%c%c' into memory\n",
167                  COUNTRY(info->cc[i]));
168            return -ENOMEM;
169         }
170
171      /* Free userspace allocated memory for that country.
172       * FIXME:   It's a bit odd to call this function everytime
173       *          we process a country.  Would be nice to call
174       *          it once after all countries've been processed.
175       *          - SJ
176       * *not implemented for now*
177      gfree(info->mem[i]);
178      */
179
180      /* Overwrite the now-useless pointer info->mem[i] with
181       * a pointer to the node's kernelspace structure.
182       * This avoids searching for a node in the match() and
183       * destroy() functions.
184       */
185      info->mem[i] = node;
186   }
187
188   /* We allocate some memory and give info->refcount a pointer
189    * to this memory.  This prevents checkentry() from increasing a refcount
190    * different from the one used by destroy().
191    * For explanation, see http://www.mail-archive.com/netfilter-devel@lists.samba.org/msg00625.html
192    */
193   info->refcount = kmalloc(sizeof(u_int8_t), GFP_KERNEL);
194   if (info->refcount == NULL) {
195      printk(KERN_ERR "xt_geoip: failed to allocate `refcount' memory\n");
196      return -ENOMEM;
197   }
198   *(info->refcount) = 1;
199
200   return 0;
201}
202
203static void xt_geoip_mt_destroy(const struct xt_mtdtor_param *par)
204{
205   struct xt_geoip_match_info *info = par->matchinfo;
206   struct geoip_info *node; /* this keeps the code sexy */
207   u_int8_t i;
208
209   /* Decrease the previously increased refcount in checkentry()
210    * If it's equal to 1, we know this entry is just moving
211    * but not removed. We simply return to avoid useless destroy()
212    * proce	ssing.
213    */
214   atomic_dec((atomic_t *)info->refcount);
215   if (*info->refcount)
216      return;
217
218   /* Don't leak my memory, you idiot.
219    * Bug found with nfsim.. the netfilter's best
220    * friend. --peejix */
221   kfree(info->refcount);
222
223   /* This entry has been removed from the table so
224    * decrease the refcount of all countries it is
225    * using.
226    */
227
228   for (i = 0; i < info->count; i++)
229      if ((node = info->mem[i]) != NULL) {
230         atomic_dec((atomic_t *)&node->ref);
231
232         /* Free up some memory if that node isn't used
233          * anymore. */
234         if (node->ref < 1)
235            remove_node(node);
236      }
237      else
238         /* Something strange happened. There's no memory allocated for this
239          * country.  Please send this bug to the mailing list. */
240         printk(KERN_ERR
241               "xt_geoip: What happened peejix ? What happened acidfu ?\n"
242               "xt_geoip: please report this bug to the maintainers\n");
243   return;
244}
245
246static struct xt_match xt_geoip_match __read_mostly = {
247		.family		= NFPROTO_IPV4,
248		.name		= "geoip",
249		.match		= xt_geoip_mt,
250		.checkentry	= xt_geoip_mt_checkentry,
251		.destroy	= xt_geoip_mt_destroy,
252		.matchsize	= sizeof(struct xt_geoip_match_info),
253		.me		= THIS_MODULE,
254};
255
256static int __init xt_geoip_mt_init(void)
257{
258   return xt_register_match(&xt_geoip_match);
259}
260
261static void __exit xt_geoip_mt_fini(void)
262{
263   xt_unregister_match(&xt_geoip_match);
264}
265
266module_init(xt_geoip_mt_init);
267module_exit(xt_geoip_mt_fini);
268