1 /* iptables kernel module for the geoip match
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>
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 DEFINE_SPINLOCK(geoip_lock
);
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))
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))
46 spin_lock_bh(&geoip_lock
);
52 if (p
->next
) p
->next
->prev
= p
;
55 spin_unlock_bh(&geoip_lock
);
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 */
70 if (p
->prev
) /* Is there a node behind me ? */
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!
82 spin_unlock_bh(&geoip_lock
);
86 static struct geoip_info
*find_node(u_int16_t cc
)
88 struct geoip_info
*p
= head
;
89 spin_lock_bh(&geoip_lock
);
93 spin_unlock_bh(&geoip_lock
);
98 spin_unlock_bh(&geoip_lock
);
102 static bool xt_geoip_mt(const struct sk_buff
*skb
, struct xt_action_param
*par
)
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
);
109 if (info
->flags
& XT_GEOIP_SRC
)
110 ip
= ntohl(iph
->saddr
);
112 ip
= ntohl(iph
->daddr
);
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
]));
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;
130 spin_unlock_bh(&geoip_lock
);
131 return (info
->flags
& XT_GEOIP_INV
) ? 1 : 0;
134 static int xt_geoip_mt_checkentry(const struct xt_mtchk_param
*par
)
137 struct xt_geoip_match_info
*info
= par
->matchinfo
;
138 struct geoip_info
*node
;
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);
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
153 if (info
->refcount
!= NULL
) {
154 atomic_inc((atomic_t
*)info
->refcount
);
159 for (i
= 0; i
< info
->count
; i
++) {
161 if ((node
= find_node(info
->cc
[i
])) != NULL
)
162 atomic_inc((atomic_t
*)&node
->ref
); //increase the reference
164 if ((node
= add_node(info
->mem
[i
])) == NULL
) {
166 "xt_geoip: unable to load '%c%c' into memory\n",
167 COUNTRY(info
->cc
[i
]));
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.
176 * *not implemented for now*
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.
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
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");
198 *(info
->refcount
) = 1;
203 static void xt_geoip_mt_destroy(const struct xt_mtdtor_param
*par
)
205 struct xt_geoip_match_info
*info
= par
->matchinfo
;
206 struct geoip_info
*node
; /* this keeps the code sexy */
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()
214 atomic_dec((atomic_t
*)info
->refcount
);
218 /* Don't leak my memory, you idiot.
219 * Bug found with nfsim.. the netfilter's best
220 * friend. --peejix */
221 kfree(info
->refcount
);
223 /* This entry has been removed from the table so
224 * decrease the refcount of all countries it is
228 for (i
= 0; i
< info
->count
; i
++)
229 if ((node
= info
->mem
[i
]) != NULL
) {
230 atomic_dec((atomic_t
*)&node
->ref
);
232 /* Free up some memory if that node isn't used
238 /* Something strange happened. There's no memory allocated for this
239 * country. Please send this bug to the mailing list. */
241 "xt_geoip: What happened peejix ? What happened acidfu ?\n"
242 "xt_geoip: please report this bug to the maintainers\n");
246 static struct xt_match xt_geoip_match __read_mostly
= {
247 .family
= NFPROTO_IPV4
,
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
),
256 static int __init
xt_geoip_mt_init(void)
258 return xt_register_match(&xt_geoip_match
);
261 static void __exit
xt_geoip_mt_fini(void)
263 xt_unregister_match(&xt_geoip_match
);
266 module_init(xt_geoip_mt_init
);
267 module_exit(xt_geoip_mt_fini
);