K2.6 patches and update.
[tomato.git] / release / src-rt / linux / linux-2.6 / net / netfilter / xt_geoip.c
blob962e9976bb96bd8c03d5a1287ae2bc7b1cf0905d
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.
8 * Copyright (c) 2004, 2005, 2006, 2007, 2008
9 * Samuel Jean & Nicolas Bouliane
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>
20 #include <linux/netfilter/x_tables.h>
21 #include <linux/netfilter/xt_geoip.h>
23 MODULE_LICENSE("GPL");
24 MODULE_AUTHOR("Nicolas Bouliane");
25 MODULE_AUTHOR("Samuel Jean");
26 MODULE_DESCRIPTION("xtables module for geoip match");
27 MODULE_ALIAS("ipt_geoip");
29 struct geoip_info *head = NULL;
30 static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED;
32 static struct geoip_info *add_node(struct geoip_info *memcpy)
34 struct geoip_info *p =
35 (struct geoip_info *)kmalloc(sizeof(struct geoip_info), GFP_KERNEL);
37 struct geoip_subnet *s;
39 if ((p == NULL) || (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0))
40 return NULL;
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;
46 spin_lock_bh(&geoip_lock);
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;
55 spin_unlock_bh(&geoip_lock);
56 return p;
59 static void remove_node(struct geoip_info *p)
61 spin_lock_bh(&geoip_lock);
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 */
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 */
75 /* So now am unlinked or the only one alive, right ?
76 * What are you waiting ? Free up some memory!
79 kfree(p->subnets);
80 kfree(p);
82 spin_unlock_bh(&geoip_lock);
83 return;
86 static struct geoip_info *find_node(u_int16_t cc)
88 struct geoip_info *p = head;
89 spin_lock_bh(&geoip_lock);
91 while (p) {
92 if (p->cc == cc) {
93 spin_unlock_bh(&geoip_lock);
94 return p;
96 p = p->next;
98 spin_unlock_bh(&geoip_lock);
99 return NULL;
102 static int xt_geoip_mt(const struct sk_buff *skb,
103 const struct net_device *in,
104 const struct net_device *out,
105 const struct xt_match *match,
106 const void *matchinfo,
107 int offset,
108 unsigned int protoff,
109 int *hotdrop)
111 const struct xt_geoip_match_info *info = (void*)matchinfo;
112 const struct geoip_info *node; /* This keeps the code sexy */
113 const struct iphdr *iph = ip_hdr(skb);
114 u_int32_t ip, i, j;
116 if (info->flags & XT_GEOIP_SRC)
117 ip = ntohl(iph->saddr);
118 else
119 ip = ntohl(iph->daddr);
121 spin_lock_bh(&geoip_lock);
122 for (i = 0; i < info->count; i++) {
123 if ((node = info->mem[i]) == NULL) {
124 printk(KERN_ERR "xt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n",
125 COUNTRY(info->cc[i]));
127 continue;
130 for (j = 0; j < node->count; j++)
131 if ((ip > node->subnets[j].begin) && (ip < node->subnets[j].end)) {
132 spin_unlock_bh(&geoip_lock);
133 return (info->flags & XT_GEOIP_INV) ? 0 : 1;
137 spin_unlock_bh(&geoip_lock);
138 return (info->flags & XT_GEOIP_INV) ? 1 : 0;
141 static int xt_geoip_mt_checkentry(const char *tablename,
142 const void *ip,
143 const struct xt_match *match,
144 void *matchinfo,
145 unsigned int hook_mask)
148 struct xt_geoip_match_info *info = (void *)matchinfo;
149 struct geoip_info *node;
150 u_int8_t i;
152 /* FIXME: Call a function to free userspace allocated memory.
153 * As Martin J. said; this match might eat lot of memory
154 * if commited with iptables-restore --noflush
155 void (*gfree)(struct geoip_info *oldmem);
156 gfree = info->fini;
159 /* If info->refcount isn't NULL, then
160 * it means that checkentry() already
161 * initialized this entry. Increase a
162 * refcount to prevent destroy() of
163 * this entry. */
164 if (info->refcount != NULL) {
165 atomic_inc((atomic_t *)info->refcount);
166 return 1;
170 for (i = 0; i < info->count; i++) {
172 if ((node = find_node(info->cc[i])) != NULL)
173 atomic_inc((atomic_t *)&node->ref); //increase the reference
174 else
175 if ((node = add_node(info->mem[i])) == NULL) {
176 printk(KERN_ERR
177 "xt_geoip: unable to load '%c%c' into memory\n",
178 COUNTRY(info->cc[i]));
179 return 0;
182 /* Free userspace allocated memory for that country.
183 * FIXME: It's a bit odd to call this function everytime
184 * we process a country. Would be nice to call
185 * it once after all countries've been processed.
186 * - SJ
187 * *not implemented for now*
188 gfree(info->mem[i]);
191 /* Overwrite the now-useless pointer info->mem[i] with
192 * a pointer to the node's kernelspace structure.
193 * This avoids searching for a node in the match() and
194 * destroy() functions.
196 info->mem[i] = node;
199 /* We allocate some memory and give info->refcount a pointer
200 * to this memory. This prevents checkentry() from increasing a refcount
201 * different from the one used by destroy().
202 * For explanation, see http://www.mail-archive.com/netfilter-devel@lists.samba.org/msg00625.html
204 info->refcount = kmalloc(sizeof(u_int8_t), GFP_KERNEL);
205 if (info->refcount == NULL) {
206 printk(KERN_ERR "xt_geoip: failed to allocate `refcount' memory\n");
207 return 0;
209 *(info->refcount) = 1;
211 return 1;
214 static void xt_geoip_mt_destroy(const struct xt_match *matcn,
215 void *matchinfo)
217 struct xt_geoip_match_info *info = (void *)matchinfo;
218 struct geoip_info *node; /* this keeps the code sexy */
219 u_int8_t i;
221 /* Decrease the previously increased refcount in checkentry()
222 * If it's equal to 1, we know this entry is just moving
223 * but not removed. We simply return to avoid useless destroy()
224 * proce ssing.
226 atomic_dec((atomic_t *)info->refcount);
227 if (*info->refcount)
228 return;
230 /* Don't leak my memory, you idiot.
231 * Bug found with nfsim.. the netfilter's best
232 * friend. --peejix */
233 kfree(info->refcount);
235 /* This entry has been removed from the table so
236 * decrease the refcount of all countries it is
237 * using.
240 for (i = 0; i < info->count; i++)
241 if ((node = info->mem[i]) != NULL) {
242 atomic_dec((atomic_t *)&node->ref);
244 /* Free up some memory if that node isn't used
245 * anymore. */
246 if (node->ref < 1)
247 remove_node(node);
249 else
250 /* Something strange happened. There's no memory allocated for this
251 * country. Please send this bug to the mailing list. */
252 printk(KERN_ERR
253 "xt_geoip: What happened peejix ? What happened acidfu ?\n"
254 "xt_geoip: please report this bug to the maintainers\n");
255 return;
258 static struct xt_match xt_geoip_match __read_mostly = {
259 .family = AF_INET,
260 .name = "geoip",
261 .match = xt_geoip_mt,
262 .checkentry = xt_geoip_mt_checkentry,
263 .destroy = xt_geoip_mt_destroy,
264 .matchsize = sizeof(struct xt_geoip_match_info),
265 .me = THIS_MODULE,
268 static int __init xt_geoip_mt_init(void)
270 return xt_register_match(&xt_geoip_match);
273 static void __exit xt_geoip_mt_fini(void)
275 xt_unregister_match(&xt_geoip_match);
278 module_init(xt_geoip_mt_init);
279 module_exit(xt_geoip_mt_fini);