]>
Commit | Line | Data |
---|---|---|
6890440f | 1 | diff -NurpP --minimal linux-2.6.21.a/include/linux/netfilter_ipv4/ipt_geoip.h linux-2.6.21.b/include/linux/netfilter_ipv4/ipt_geoip.h |
2 | --- linux-2.6.21.a/include/linux/netfilter_ipv4/ipt_geoip.h 1970-01-01 01:00:00.000000000 +0100 | |
3 | +++ linux-2.6.21.b/include/linux/netfilter_ipv4/ipt_geoip.h 2007-05-30 12:08:43.000000000 +0200 | |
1aedc22c | 4 | @@ -0,0 +1,50 @@ |
5 | +/* ipt_geoip.h header file for libipt_geoip.c and ipt_geoip.c | |
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 as published by | |
9 | + * the Free Software Foundation; either version 2 of the License, or | |
10 | + * (at your option) any later version. | |
11 | + * | |
12 | + * Copyright (c) 2004, 2005, 2006 Samuel Jean & Nicolas Bouliane | |
13 | + */ | |
14 | +#ifndef _IPT_GEOIP_H | |
15 | +#define _IPT_GEOIP_H | |
16 | + | |
17 | +#define IPT_GEOIP_SRC 0x01 /* Perform check on Source IP */ | |
18 | +#define IPT_GEOIP_DST 0x02 /* Perform check on Destination IP */ | |
19 | +#define IPT_GEOIP_INV 0x04 /* Negate the condition */ | |
20 | + | |
21 | +#define IPT_GEOIP_MAX 15 /* Maximum of countries */ | |
22 | + | |
23 | +struct geoip_subnet { | |
24 | + u_int32_t begin; | |
25 | + u_int32_t end; | |
26 | +}; | |
27 | + | |
28 | +struct geoip_info { | |
29 | + struct geoip_subnet *subnets; | |
30 | + u_int32_t count; | |
31 | + u_int32_t ref; | |
32 | + u_int16_t cc; | |
33 | + struct geoip_info *next; | |
34 | + struct geoip_info *prev; | |
35 | +}; | |
36 | + | |
37 | +struct ipt_geoip_info { | |
38 | + u_int8_t flags; | |
39 | + u_int8_t count; | |
40 | + u_int16_t cc[IPT_GEOIP_MAX]; | |
41 | + | |
42 | + /* Used internally by the kernel */ | |
43 | + struct geoip_info *mem[IPT_GEOIP_MAX]; | |
44 | + u_int8_t *refcount; | |
45 | + | |
46 | + /* not implemented yet: | |
47 | + void *fini; | |
48 | + */ | |
49 | +}; | |
50 | + | |
51 | +#define COUNTRY(cc) (cc >> 8), (cc & 0x00FF) | |
52 | + | |
53 | +#endif | |
54 | + | |
6890440f | 55 | diff -NurpP --minimal linux-2.6.21.a/net/ipv4/netfilter/Kconfig linux-2.6.21.b/net/ipv4/netfilter/Kconfig |
56 | --- linux-2.6.21.a/net/ipv4/netfilter/Kconfig 2007-05-30 12:07:14.000000000 +0200 | |
57 | +++ linux-2.6.21.b/net/ipv4/netfilter/Kconfig 2007-05-30 12:08:43.000000000 +0200 | |
06c0c671 | 58 | @@ -921,5 +921,21 @@ config IP_NF_MATCH_CONNLIMIT |
1aedc22c | 59 | If you want to compile it as a module, say M here and read |
60 | Documentation/modules.txt. If unsure, say `N'. | |
61 | ||
62 | +config IP_NF_MATCH_GEOIP | |
63 | + tristate 'geoip match support' | |
64 | + depends on IP_NF_IPTABLES | |
65 | + help | |
66 | + This option allows you to match a packet by its source or | |
67 | + destination country. Basically, you need a country's | |
68 | + database containing all subnets and associated countries. | |
69 | + | |
70 | + For the complete procedure and understanding, read : | |
71 | + http://people.netfilter.org/peejix/geoip/howto/geoip-HOWTO.html | |
72 | + | |
73 | + If you want to compile it as a module, say M here and read | |
74 | + <file:Documentation/modules.txt>. The module will be | |
75 | + called `ipt_geoip'. If unsure, say `N'. | |
76 | + | |
77 | + | |
78 | endmenu | |
79 | ||
6890440f | 80 | diff -NurpP --minimal linux-2.6.21.a/net/ipv4/netfilter/Makefile linux-2.6.21.b/net/ipv4/netfilter/Makefile |
81 | --- linux-2.6.21.a/net/ipv4/netfilter/Makefile 2007-05-30 12:07:14.000000000 +0200 | |
82 | +++ linux-2.6.21.b/net/ipv4/netfilter/Makefile 2007-05-30 12:08:43.000000000 +0200 | |
b1443212 | 83 | @@ -0,0 +0,1 @@ |
1aedc22c | 84 | +obj-$(CONFIG_IP_NF_MATCH_GEOIP) += ipt_geoip.o |
6890440f | 85 | diff -NurpP --minimal linux-2.6.21.a/net/ipv4/netfilter/ipt_geoip.c linux-2.6.21.b/net/ipv4/netfilter/ipt_geoip.c |
86 | --- linux-2.6.21.a/net/ipv4/netfilter/ipt_geoip.c 1970-01-01 01:00:00.000000000 +0100 | |
87 | +++ linux-2.6.21.b/net/ipv4/netfilter/ipt_geoip.c 2007-05-30 12:08:43.000000000 +0200 | |
6447fea8 | 88 | @@ -0,0 +1,302 @@ |
1aedc22c | 89 | +/* iptables kernel module for the geoip match |
90 | + * | |
91 | + * This program is free software; you can redistribute it and/or modify | |
92 | + * it under the terms of the GNU General Public License as published by | |
93 | + * the Free Software Foundation; either version 2 of the License, or | |
94 | + * (at your option) any later version. | |
95 | + * | |
96 | + * Copyright (c) 2004, 2005, 2006 Samuel Jean & Nicolas Bouliane | |
97 | + */ | |
98 | +#include <linux/module.h> | |
99 | +#include <linux/kernel.h> | |
100 | +#include <linux/version.h> | |
101 | +#include <linux/skbuff.h> | |
102 | +#include <linux/netdevice.h> | |
103 | +#include <asm/uaccess.h> | |
104 | +#include <asm/atomic.h> | |
6447fea8 | 105 | +#include <linux/netfilter/x_tables.h> |
1aedc22c | 106 | +#include <linux/netfilter_ipv4/ipt_geoip.h> |
107 | +#include <linux/netfilter_ipv4/ip_tables.h> | |
108 | + | |
109 | +MODULE_LICENSE("GPL"); | |
110 | +MODULE_AUTHOR("Samuel Jean, Nicolas Bouliane"); | |
111 | +MODULE_DESCRIPTION("iptables module for geoip match"); | |
112 | + | |
113 | +struct geoip_info *head = NULL; | |
114 | +static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED; | |
115 | + | |
116 | +static struct geoip_info *add_node(struct geoip_info *memcpy) | |
117 | +{ | |
118 | + struct geoip_info *p = | |
119 | + (struct geoip_info *)kmalloc(sizeof(struct geoip_info), GFP_KERNEL); | |
120 | + | |
121 | + struct geoip_subnet *s; | |
122 | + | |
123 | + if ((p == NULL) || (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0)) | |
124 | + return NULL; | |
125 | + | |
126 | + s = (struct geoip_subnet *)kmalloc(p->count * sizeof(struct geoip_subnet), GFP_KERNEL); | |
127 | + if ((s == NULL) || (copy_from_user(s, p->subnets, p->count * sizeof(struct geoip_subnet)) != 0)) | |
128 | + return NULL; | |
129 | + | |
130 | + spin_lock_bh(&geoip_lock); | |
131 | + | |
132 | + p->subnets = s; | |
133 | + p->ref = 1; | |
134 | + p->next = head; | |
135 | + p->prev = NULL; | |
136 | + if (p->next) p->next->prev = p; | |
137 | + head = p; | |
138 | + | |
139 | + spin_unlock_bh(&geoip_lock); | |
140 | + return p; | |
141 | +} | |
142 | + | |
143 | +static void remove_node(struct geoip_info *p) | |
144 | + { | |
145 | + spin_lock_bh(&geoip_lock); | |
146 | + | |
147 | + if (p->next) { /* Am I following a node ? */ | |
148 | + p->next->prev = p->prev; | |
149 | + if (p->prev) p->prev->next = p->next; /* Is there a node behind me ? */ | |
150 | + else head = p->next; /* No? Then I was the head */ | |
151 | + } | |
152 | + | |
153 | + else | |
154 | + if (p->prev) /* Is there a node behind me ? */ | |
155 | + p->prev->next = NULL; | |
156 | + else | |
157 | + head = NULL; /* No, we're alone */ | |
158 | + | |
159 | + /* So now am unlinked or the only one alive, right ? | |
160 | + * What are you waiting ? Free up some memory! | |
161 | + */ | |
162 | + | |
163 | + kfree(p->subnets); | |
164 | + kfree(p); | |
165 | + | |
166 | + spin_unlock_bh(&geoip_lock); | |
167 | + return; | |
168 | +} | |
169 | + | |
170 | +static struct geoip_info *find_node(u_int16_t cc) | |
171 | +{ | |
172 | + struct geoip_info *p = head; | |
173 | + spin_lock_bh(&geoip_lock); | |
174 | + | |
175 | + while (p) { | |
176 | + if (p->cc == cc) { | |
177 | + spin_unlock_bh(&geoip_lock); | |
178 | + return p; | |
179 | + } | |
180 | + p = p->next; | |
181 | + } | |
182 | + spin_unlock_bh(&geoip_lock); | |
183 | + return NULL; | |
184 | +} | |
185 | + | |
186 | +static int match(const struct sk_buff *skb, | |
187 | + const struct net_device *in, | |
188 | + const struct net_device *out, | |
189 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) | |
190 | + const struct xt_match *match, | |
191 | +#endif | |
192 | + const void *matchinfo, | |
193 | + int offset, | |
194 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) | |
195 | + unsigned int protoff, | |
196 | +#endif | |
197 | + int *hotdrop) | |
198 | +{ | |
199 | + const struct ipt_geoip_info *info = matchinfo; | |
200 | + const struct geoip_info *node; /* This keeps the code sexy */ | |
5ce52adc | 201 | + const struct iphdr *iph = ip_hdr(skb); |
1aedc22c | 202 | + u_int32_t ip, j; |
203 | + u_int8_t i; | |
204 | + | |
205 | + if (info->flags & IPT_GEOIP_SRC) | |
206 | + ip = ntohl(iph->saddr); | |
207 | + else | |
208 | + ip = ntohl(iph->daddr); | |
209 | + | |
210 | + spin_lock_bh(&geoip_lock); | |
211 | + for (i = 0; i < info->count; i++) { | |
212 | + if ((node = info->mem[i]) == NULL) { | |
213 | + printk(KERN_ERR "ipt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n", | |
214 | + COUNTRY(info->cc[i])); | |
215 | + | |
216 | + continue; | |
217 | + } | |
218 | + | |
219 | + for (j = 0; j < node->count; j++) | |
220 | + if ((ip > node->subnets[j].begin) && (ip < node->subnets[j].end)) { | |
221 | + spin_unlock_bh(&geoip_lock); | |
222 | + return (info->flags & IPT_GEOIP_INV) ? 0 : 1; | |
223 | + } | |
224 | + } | |
225 | + | |
226 | + spin_unlock_bh(&geoip_lock); | |
227 | + return (info->flags & IPT_GEOIP_INV) ? 1 : 0; | |
228 | +} | |
229 | + | |
230 | +static int geoip_checkentry(const char *tablename, | |
231 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) | |
232 | + const void *ip, | |
233 | +#else | |
234 | + const struct ipt_ip *ip, | |
235 | +#endif | |
236 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) | |
237 | + const struct xt_match *match, | |
238 | +#endif | |
239 | + void *matchinfo, | |
240 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) | |
241 | + unsigned int matchsize, | |
242 | +#endif | |
243 | + unsigned int hook_mask) | |
244 | +{ | |
245 | + struct ipt_geoip_info *info = matchinfo; | |
246 | + struct geoip_info *node; | |
247 | + u_int8_t i; | |
248 | + | |
249 | + /* FIXME: Call a function to free userspace allocated memory. | |
250 | + * As Martin J. said; this match might eat lot of memory | |
251 | + * if commited with iptables-restore --noflush | |
252 | + void (*gfree)(struct geoip_info *oldmem); | |
253 | + gfree = info->fini; | |
254 | + */ | |
255 | + | |
256 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,17) | |
257 | + if (matchsize != IPT_ALIGN(sizeof(struct ipt_geoip_info))) { | |
258 | + printk(KERN_ERR "ipt_geoip: matchsize differ, you may have forgotten to recompile me\n"); | |
259 | + return 0; | |
260 | + } | |
261 | +#endif | |
262 | + | |
263 | + /* If info->refcount isn't NULL, then | |
264 | + * it means that checkentry() already | |
265 | + * initialized this entry. Increase a | |
266 | + * refcount to prevent destroy() of | |
267 | + * this entry. */ | |
268 | + if (info->refcount != NULL) { | |
269 | + atomic_inc((atomic_t *)info->refcount); | |
270 | + return 1; | |
271 | + } | |
272 | + | |
273 | + | |
274 | + for (i = 0; i < info->count; i++) { | |
275 | + | |
276 | + if ((node = find_node(info->cc[i])) != NULL) | |
277 | + atomic_inc((atomic_t *)&node->ref); //increase the reference | |
278 | + else | |
279 | + if ((node = add_node(info->mem[i])) == NULL) { | |
280 | + printk(KERN_ERR | |
281 | + "ipt_geoip: unable to load '%c%c' into memory\n", | |
282 | + COUNTRY(info->cc[i])); | |
283 | + return 0; | |
284 | + } | |
285 | + | |
286 | + /* Free userspace allocated memory for that country. | |
287 | + * FIXME: It's a bit odd to call this function everytime | |
288 | + * we process a country. Would be nice to call | |
289 | + * it once after all countries've been processed. | |
290 | + * - SJ | |
291 | + * *not implemented for now* | |
292 | + gfree(info->mem[i]); | |
293 | + */ | |
294 | + | |
295 | + /* Overwrite the now-useless pointer info->mem[i] with | |
296 | + * a pointer to the node's kernelspace structure. | |
297 | + * This avoids searching for a node in the match() and | |
298 | + * destroy() functions. | |
299 | + */ | |
300 | + info->mem[i] = node; | |
301 | + } | |
302 | + | |
303 | + /* We allocate some memory and give info->refcount a pointer | |
304 | + * to this memory. This prevents checkentry() from increasing a refcount | |
305 | + * different from the one used by destroy(). | |
306 | + * For explanation, see http://www.mail-archive.com/netfilter-devel@lists.samba.org/msg00625.html | |
307 | + */ | |
308 | + info->refcount = kmalloc(sizeof(u_int8_t), GFP_KERNEL); | |
309 | + if (info->refcount == NULL) { | |
310 | + printk(KERN_ERR "ipt_geoip: failed to allocate `refcount' memory\n"); | |
311 | + return 0; | |
312 | + } | |
313 | + *(info->refcount) = 1; | |
314 | + | |
315 | + return 1; | |
316 | +} | |
317 | + | |
318 | +static void geoip_destroy( | |
319 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) | |
320 | + const struct xt_match *match, void *matchinfo) | |
321 | +#else | |
322 | + void *matchinfo, unsigned int matchsize) | |
323 | +#endif | |
324 | +{ | |
325 | + u_int8_t i; | |
326 | + struct geoip_info *node; /* this keeps the code sexy */ | |
327 | + | |
328 | + struct ipt_geoip_info *info = matchinfo; | |
329 | + /* Decrease the previously increased refcount in checkentry() | |
330 | + * If it's equal to 1, we know this entry is just moving | |
331 | + * but not removed. We simply return to avoid useless destroy() | |
332 | + * processing. | |
333 | + */ | |
334 | + atomic_dec((atomic_t *)info->refcount); | |
335 | + if (*info->refcount) | |
336 | + return; | |
337 | + | |
338 | + /* Don't leak my memory, you idiot. | |
339 | + * Bug found with nfsim.. the netfilter's best | |
340 | + * friend. --peejix */ | |
341 | + kfree(info->refcount); | |
342 | + | |
343 | + /* This entry has been removed from the table so | |
344 | + * decrease the refcount of all countries it is | |
345 | + * using. | |
346 | + */ | |
347 | + | |
348 | + for (i = 0; i < info->count; i++) | |
349 | + if ((node = info->mem[i]) != NULL) { | |
350 | + atomic_dec((atomic_t *)&node->ref); | |
351 | + | |
352 | + /* Free up some memory if that node isn't used | |
353 | + * anymore. */ | |
354 | + if (node->ref < 1) | |
355 | + remove_node(node); | |
356 | + } | |
357 | + else | |
358 | + /* Something strange happened. There's no memory allocated for this | |
359 | + * country. Please send this bug to the mailing list. */ | |
360 | + printk(KERN_ERR | |
361 | + "ipt_geoip: What happened peejix ? What happened acidmen ?\n" | |
362 | + "ipt_geoip: please report this bug to the maintainers\n"); | |
363 | + return; | |
364 | +} | |
365 | + | |
6447fea8 | 366 | +static struct xt_match geoip_match = { |
1aedc22c | 367 | + .name = "geoip", |
6447fea8 | 368 | + .family = AF_INET, |
1aedc22c | 369 | + .match = &match, |
370 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17) | |
371 | + .matchsize = sizeof (struct ipt_geoip_info), | |
372 | +#endif | |
373 | + .checkentry = &geoip_checkentry, | |
374 | + .destroy = &geoip_destroy, | |
375 | + .me = THIS_MODULE | |
376 | +}; | |
377 | + | |
378 | +static int __init init(void) | |
379 | +{ | |
6447fea8 | 380 | + return xt_register_match(&geoip_match); |
1aedc22c | 381 | +} |
382 | + | |
383 | +static void __exit fini(void) | |
384 | +{ | |
6447fea8 | 385 | + xt_unregister_match(&geoip_match); |
1aedc22c | 386 | + return; |
387 | +} | |
388 | + | |
389 | +module_init(init); | |
390 | +module_exit(fini); |