* New match ipt_geoip
@ 2004-11-04 3:08 Samuel Jean
2004-11-05 0:57 ` Samuel Jean
2004-11-05 10:42 ` Harald Welte
0 siblings, 2 replies; 7+ messages in thread
From: Samuel Jean @ 2004-11-04 3:08 UTC (permalink / raw)
To: netfilter-devel
[-- Attachment #1: Type: text/plain, Size: 2147 bytes --]
Hi people,
We've been working on a way to match a packet based on its country.
We know it's not a 100% reliable way to filter, but it all
depends on the database one use.
This could be usefull for packets classification though.
Let's explain the concept.
First, you need a database - as specified below - which contains
field of the form :
"begin_subnet","end_subnet","bin_start","bin_end","ISO Code","Country"
exemple:
"2.6.190.56","2.6.190.63","33996344","33996351","GB","United Kingdom"
This is the MaxMind's GeoIP CSV db format. But we obviously don't need
all those values, so we've written a tool called csv2bin that converts
this database type to a smaller binary format.
csv2bin is available at www.cookinglinux.org/geoip/
People can get free MaxMind's GeoIP database at www.maxmind.com
This tools create 2 files, geoipdb.bin (the database)
and geoipdb.idx (the index file). Unfortunatelly, we need to move both
files in /var/geoip/ by default - unless someone rewrite the static path
in the shared library.
That's about all for this fuzzy extra requierement.
The match options look like :
[!] --src-cc, --source-country <country code>
[!] --dst-cc, --destination-country <country code>
NOTE: The country is inputed by its ISO3166 code.
You can match up to 15 countries in a rule.
-A INPUT -m geoip --source-country ca,us,jp,de,a1,a2
The library loads subnets of specified countries into user-memory
and passes pointers to the module which copies it into kernelspace.
If a country is specified more than once, a reference count is increased
for that country. When there's no more ref count, that country is freed
from memory.
What would be great is a caching system. Going to implement it when
we'll have time.
It all works for both linux-2.4 and 2.6.
Well, that's enough for theory that you can all presume by reading the
source code, so here it is.
(I'll put source for linux-2.4 but 2.6 is also provided in the pom-ng
package)
We'd like to thank Martin Josefsson for the great help he gave us.
Comments are greatly welcome.
Nicolas Bouliane,
Samuel Jean
at cookinglinux.org
[-- Attachment #2: ipt_geoip.h --]
[-- Type: text/x-chdr, Size: 865 bytes --]
#ifndef _IPT_GEOIP_H
#define _IPT_GEOIP_H
#define IPT_GEOIP_SRC 0x01 /* Perform check on Source IP */
#define IPT_GEOIP_DST 0x02 /* Perform check on Destination IP */
#define IPT_GEOIP_INV 0x04 /* Negate the condition */
#define IPT_GEOIP_MAX 15 /* Maximum of countries */
struct geoip_subnet {
u_int32_t begin;
u_int32_t end;
};
struct geoip_info {
struct geoip_subnet *subnets;
u_int32_t count;
u_int32_t ref;
u_int16_t cc;
struct geoip_info *next;
struct geoip_info *prev;
};
struct ipt_geoip_info {
u_int8_t flags;
u_int8_t count;
u_int16_t cc[IPT_GEOIP_MAX];
/* Used internally by the kernel */
struct geoip_info *mem[IPT_GEOIP_MAX];
u_int8_t *refcount;
/* not implemented yet:
void *fini;
*/
};
#define COUNTRY(cc) (cc >> 8), (cc & 0x00FF)
#endif
[-- Attachment #3: ipt_geoip.c --]
[-- Type: text/x-csrc, Size: 7541 bytes --]
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/netfilter_ipv4/ipt_geoip.h>
#include <linux/netfilter_ipv4/ip_tables.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Samuel Jean, Nicolas Bouliane");
MODULE_DESCRIPTION("iptables/netfilter's geoip match");
struct geoip_info *head = NULL;
static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED;
static struct geoip_info *add_node(struct geoip_info *memcpy)
{
struct geoip_info *p =
(struct geoip_info *)kmalloc(sizeof(struct geoip_info), GFP_KERNEL);
struct geoip_subnet *s =
(struct geoip_subnet *)kmalloc(p->count * sizeof(struct geoip_subnet), GFP_KERNEL);
if ((p == NULL) || (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0))
return NULL;
if ((s == NULL) || (copy_from_user(s, p->subnets, p->count * sizeof(struct geoip_subnet)) != 0))
return NULL;
spin_lock_bh(&geoip_lock);
p->subnets = s;
p->ref = 1;
p->next = head;
p->prev = NULL;
if (p->next) p->next->prev = p;
head = p;
spin_unlock_bh(&geoip_lock);
return p;
}
static void remove_node(struct geoip_info *p)
{
spin_lock_bh(&geoip_lock);
if (p->next) { /* Am I following a node ? */
p->next->prev = p->prev;
if (p->prev) p->prev->next = p->next; /* Is there a node behind me ? */
else head = p->next; /* No? Then I was the head */
}
else
if (p->prev) /* Is there a node behind me ? */
p->prev->next = NULL;
else
head = NULL; /* No, we're alone */
/* So now am unlinked or the only one alive, right ?
* What are you waiting ? Free up some memory!
*/
kfree(p->subnets);
kfree(p);
spin_unlock_bh(&geoip_lock);
return;
}
static struct geoip_info *find_node(u_int16_t cc)
{
struct geoip_info *p = head;
spin_lock_bh(&geoip_lock);
while (p) {
if (p->cc == cc) {
spin_unlock_bh(&geoip_lock);
return p;
}
p = p->next;
}
spin_unlock_bh(&geoip_lock);
return NULL;
}
static int match(const struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
const void *matchinfo,
int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop)
{
const struct ipt_geoip_info *info = matchinfo;
const struct geoip_info *node; /* This keeps the code sexy */
const struct iphdr *iph = skb->nh.iph;
u_int32_t ip, j;
u_int8_t i;
if (info->flags & IPT_GEOIP_SRC)
ip = ntohl(iph->saddr);
else
ip = ntohl(iph->daddr);
spin_lock_bh(&geoip_lock);
for (i = 0; i < info->count; i++) {
if ((node = info->mem[i]) == NULL) {
printk(KERN_ERR "ipt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n",
COUNTRY(info->cc[i]));
continue;
}
for (j = 0; j < node->count; j++)
if ((ip > node->subnets[j].begin) && (ip < node->subnets[j].end)) {
spin_unlock_bh(&geoip_lock);
return (info->flags & IPT_GEOIP_INV) ? 0 : 1;
}
}
spin_unlock_bh(&geoip_lock);
return (info->flags & IPT_GEOIP_INV) ? 1 : 0;
}
static int geoip_checkentry(const char *tablename,
const struct ipt_ip *ip,
void *matchinfo,
unsigned int matchsize,
unsigned int hook_mask)
{
struct ipt_geoip_info *info = matchinfo;
struct geoip_info *node;
u_int8_t i;
/* FIXME: Call a function to free userspace allocated memory.
* As Martin J. said; this match might eat lot of memory
* if commited with iptables-restore --noflush
void (*gfree)(struct geoip_info *oldmem);
gfree = info->fini;
*/
if (matchsize != IPT_ALIGN(sizeof(struct ipt_geoip_info))) {
printk(KERN_ERR "ipt_geoip: matchsize differ, you may have forgotten to recompile me\n");
return 0;
}
/* If info->refcount isn't NULL, then
* it means that checkentry() already
* initialized this entry. Increase a
* refcount to prevent destroy() of
* this entry. */
if (info->refcount != NULL) {
atomic_inc((atomic_t *)info->refcount);
return 1;
}
for (i = 0; i < info->count; i++) {
if ((node = find_node(info->cc[i])) != NULL)
atomic_inc((atomic_t *)&node->ref); //increase the reference
else
if ((node = add_node(info->mem[i])) == NULL) {
printk(KERN_ERR
"ipt_geoip: unable to load '%c%c' into memory\n",
COUNTRY(info->cc[i]));
return 0;
}
/* Free userspace allocated memory for that country.
* FIXME: It's a bit odd to call this function everytime
* we process a country. Would be nice to call
* it once after all countries've been processed.
* - SJ
* *not implemented for now*
gfree(info->mem[i]);
*/
/* Overwrite the now-useless pointer info->mem[i] with
* a pointer to the node's kernelspace structure.
* This avoids searching for a node in the match() and
* destroy() functions.
*/
info->mem[i] = node;
}
/* We allocate some memory and give info->refcount a pointer
* to this memory. This prevents checkentry() from increasing a refcount
* different from the one used by destroy().
* For explanation, see http://www.mail-archive.com/netfilter-devel@lists.samba.org/msg00625.html
*/
info->refcount = kmalloc(sizeof(u_int8_t), GFP_KERNEL);
if (info->refcount == NULL) {
printk(KERN_ERR "ipt_geoip: failed to allocate `refcount' memory\n");
return 0;
}
*(info->refcount) = 1;
return 1;
}
static void geoip_destroy(void *matchinfo, unsigned int matchsize)
{
struct ipt_geoip_info *info = matchinfo;
struct geoip_info *node; /* this keeps the code sexy */
u_int8_t i;
/* Decrease the previously increased refcount in checkentry()
* If it's equal to 1, we know this entry is just moving
* but not removed. We simply return to avoid useless destroy()
* processing.
*/
atomic_dec((atomic_t *)info->refcount);
if (*info->refcount)
return;
/* This entry has been removed from the table so
* decrease the refcount of all countries it is
* using.
*/
for (i = 0; i < info->count; i++)
if ((node = info->mem[i]) != NULL) {
atomic_dec((atomic_t *)&node->ref);
/* Free up some memory if that node isn't used
* anymore. */
if (node->ref < 1)
remove_node(node);
}
else
/* Something strange happened. There's no memory allocated for this
* country. Please send this bug to the mailing list. */
printk(KERN_ERR
"ipt_geoip: What happened peejix ? What happened acidmen ?\n"
"ipt_geoip: please report this bug to the maintainers\n");
return;
}
static struct ipt_match geoip_match
= { { NULL, NULL }, "geoip", &match, &geoip_checkentry, &geoip_destroy, THIS_MODULE };
static int __init init(void)
{
return ipt_register_match(&geoip_match);
}
static void __exit fini(void)
{
ipt_unregister_match(&geoip_match);
return;
}
module_init(init);
module_exit(fini);
[-- Attachment #4: libipt_geoip.c --]
[-- Type: text/x-csrc, Size: 11400 bytes --]
/* Shared library add-on to iptables to add geoip match support.
*
* For comments, bugs or suggestions, please contact
* Samuel Jean <sjean at cookinglinux.org>
* Nicolas Bouliane <nib at cookinglinux.org>
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <ctype.h>
#include <stddef.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iptables.h>
#include <linux/netfilter_ipv4/ipt_geoip.h>
// We need it to verify inputed country code
// This shouldn't go in ipt_geoip.h because only this library needs it.
// Also, those country codes *MUST* stand in alphabetic order due to the
// algorithm used to seek through this list.
#define COUNTRYCOUNT 243 /* Always re-adjust this value when
adding/removing a country */
#define COUNTRYCODESZ 2 // This value shouldn't be changed.
static char *cc_list[COUNTRYCOUNT] = {
"A1","A2", // Anonymous Proxies and Satellite Providers
"AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR","AS","AT","AU","AW","AZ",
"BA","BB","BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO","BR","BS","BT","BV",
"BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR",
"CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ","EC","EE","EG","EH","ER",
"ES","ET","FI","FJ","FK","FM","FO","FR","FX","GA","GB","GD","GE","GF","GH","GI",
"GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT",
"HU","ID","IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO","JP","KE","KG","KH",
"KI","KM","KN","KP","KR","KW","KY","KZ","LA","LB","LC","LI","LK","LR","LS","LT",
"LU","LV","LY","MA","MC","MD","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR",
"MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO",
"NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS",
"PT","PW","PY","QA","RE","RO","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI",
"SJ","SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH",
"TJ","TK","TM","TN","TO","TP","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY",
"UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","YU","ZA","ZM","ZR",
"ZW" };
static void help(void)
{
printf (
"GeoIP v%s options:\n"
" [!] --src-cc, --source-country country[,country,country,...]\n"
" Match packet coming from (one of)\n"
" the specified country(ies)\n"
"\n"
" [!] --dst-cc, --destination-country country[,country,country,...]\n"
" Match packet going to (one of)\n"
" the specified country(ies)\n"
"\n"
" NOTE: The country is inputed by its ISO3166 code.\n"
"\n"
"\n", IPTABLES_VERSION
);
}
static struct option opts[] = {
{ "dst-cc", 1, 0, '2' }, /* Alias for --destination-country */
{ "destination-country", 1, 0, '2' },
{ "src-cc", 1, 0, '1' }, /* Alias for --source-country */
{ "source-country", 1, 0, '1' },
{ 0 }
};
static void
init(struct ipt_entry_match *m, unsigned int *nfcache)
{
}
/* NOT IMPLEMENTED YET
static void geoip_free(struct geoip_info *oldmem)
{
}
*/
static u_int8_t
binary_search(const char *key, u_int8_t low, u_int8_t hi)
{
u_int8_t mid = (hi-low)/2 + low;
if (low >= hi)
return strncmp(key, cc_list[mid], 2);
if (!strncmp(key, cc_list[mid], 2))
return 0;
if (strncmp(key, cc_list[mid], 2) > 0)
return binary_search(key, mid+1, hi);
else
return binary_search(key, low, mid);
}
struct geoip_index {
u_int16_t cc;
u_int32_t offset;
} __attribute__ ((packed));
struct geoip_subnet *
get_country_subnets(u_int16_t cc, u_int32_t *count)
{
FILE *ixfd, *dbfd;
struct geoip_subnet *subnets;
struct geoip_index *index;
struct stat buf;
size_t idxsz;
u_int16_t i;
u_int16_t db_cc = 0;
u_int16_t db_nsubnets = 0;
if ((ixfd = fopen("/var/geoip/geoipdb.idx", "r")) == NULL) {
perror("/var/geoip/geoipdb.idx");
exit_error(OTHER_PROBLEM,
"geoip match: cannot open geoip's database index file");
}
stat("/var/geoip/geoipdb.idx", &buf);
idxsz = buf.st_size/sizeof(struct geoip_index);
index = (struct geoip_index *)malloc(buf.st_size);
fread(index, buf.st_size, 1, ixfd);
for (i = 0; i < idxsz; i++)
if (cc == index[i].cc)
break;
if (cc != index[i].cc)
exit_error(OTHER_PROBLEM,
"geoip match: sorry, '%c%c' isn't in the database\n", COUNTRY(cc));
fclose(ixfd);
if ((dbfd = fopen("/var/geoip/geoipdb.bin", "r")) == NULL) {
perror("/var/geoip/geoipdb.bin");
exit_error(OTHER_PROBLEM,
"geoip match: cannot open geoip's database file");
}
fseek(dbfd, index[i].offset, SEEK_SET);
fread(&db_cc, sizeof(u_int16_t), 1, dbfd);
if (db_cc != cc)
exit_error(OTHER_PROBLEM,
"geoip match: this shouldn't happened, the database might be corrupted, or there's a bug.\n"
"you should contact maintainers");
fread(&db_nsubnets, sizeof(u_int16_t), 1, dbfd);
subnets = (struct geoip_subnet*)malloc(db_nsubnets * sizeof(struct geoip_subnet));
if (!subnets)
exit_error(OTHER_PROBLEM,
"geoip match: insufficient memory available");
fread(subnets, db_nsubnets * sizeof(struct geoip_subnet), 1, dbfd);
fclose(dbfd);
free(index);
*count = db_nsubnets;
return subnets;
}
static struct geoip_info *
load_geoip_cc(u_int16_t cc)
{
static struct geoip_info *ginfo;
ginfo = malloc(sizeof(struct geoip_info));
if (!ginfo)
return NULL;
ginfo->subnets = get_country_subnets(cc, &ginfo->count);
ginfo->cc = cc;
return ginfo;
}
static u_int16_t
check_geoip_cc(char *cc, u_int16_t cc_used[], u_int8_t count)
{
u_int8_t i;
u_int16_t cc_int16;
if (strlen(cc) != COUNTRYCODESZ) /* Country must be 2 chars long according
to the ISO3166 standard */
exit_error(PARAMETER_PROBLEM,
"geoip match: invalid country code '%s'", cc);
// Verification will fail if chars aren't uppercased.
// Make sure they are..
for (i = 0; i < COUNTRYCODESZ; i++)
cc[i] = toupper(cc[i]);
// Verify for a valid value against the country code list.
if (binary_search(cc, 0, COUNTRYCOUNT-1) != 0)
exit_error(PARAMETER_PROBLEM,
"geoip match: invalid country code '%s'", cc);
/* Convert chars into a single 16 bit integer.
* FIXME: This assumes that a country code is
* exactly 2 chars long. If this is
* going to change someday, this whole
* match will need to be rewritten, anyway.
* - SJ */
cc_int16 = (cc[0]<<8) + cc[1];
// Check for presence of value in cc_used
for (i = 0; i < count; i++)
if (cc_int16 == cc_used[i])
return 0; // Present, skip it!
return cc_int16;
}
/* Based on libipt_multiport.c parsing code. */
static u_int8_t
parse_geoip_cc(const char *ccstr, u_int16_t *cc, struct geoip_info **mem)
{
char *buffer, *cp, *next;
u_int8_t i, count = 0;
u_int16_t cctmp;
buffer = strdup(ccstr);
if (!buffer) exit_error(OTHER_PROBLEM,
"geoip match: insufficient memory available");
for (cp = buffer, i = 0; cp && i < IPT_GEOIP_MAX; cp = next, i++)
{
next = strchr(cp, ',');
if (next) *next++ = '\0';
if ((cctmp = check_geoip_cc(cp, cc, count)) != 0) {
if ((mem[count++] = load_geoip_cc(cctmp)) == NULL)
exit_error(OTHER_PROBLEM,
"geoip match: insufficient memory available");
cc[count-1] = cctmp;
}
}
if (cp) exit_error(PARAMETER_PROBLEM,
"geoip match: too many countries specified");
free(buffer);
if (count == 0) exit_error(PARAMETER_PROBLEM,
"geoip match: don't know what happened");
return count;
}
static int
parse(int c, char **argv, int invert, unsigned int *flags,
const struct ipt_entry *entry,
unsigned int *nfcache,
struct ipt_entry_match **match)
{
struct ipt_geoip_info *info
= (struct ipt_geoip_info *)(*match)->data;
switch(c) {
case '1':
// Ensure that IPT_GEOIP_SRC *OR* IPT_GEOIP_DST haven't been used yet.
if (*flags & (IPT_GEOIP_SRC | IPT_GEOIP_DST))
exit_error(PARAMETER_PROBLEM,
"geoip match: only use --source-country *OR* --destination-country once!");
*flags |= IPT_GEOIP_SRC;
*nfcache |= NFC_IP_SRC;
break;
case '2':
// Ensure that IPT_GEOIP_SRC *OR* IPT_GEOIP_DST haven't been used yet.
if (*flags & (IPT_GEOIP_SRC | IPT_GEOIP_DST))
exit_error(PARAMETER_PROBLEM,
"geoip match: only use --source-country *OR* --destination-country once!");
*flags |= IPT_GEOIP_DST;
*nfcache |= NFC_IP_DST;
break;
default:
return 0;
}
if (invert)
*flags |= IPT_GEOIP_INV;
info->count = parse_geoip_cc(argv[optind-1], info->cc, info->mem);
info->flags = *flags;
info->refcount = NULL;
//info->fini = &geoip_free;
return 1;
}
static void
final_check(unsigned int flags)
{
if (!flags)
exit_error(PARAMETER_PROBLEM,
"geoip match: missing arguments");
}
static void
print(const struct ipt_ip *ip,
const struct ipt_entry_match *match,
int numeric)
{
const struct ipt_geoip_info *info
= (const struct ipt_geoip_info *)match->data;
u_int8_t i;
if (info->flags & IPT_GEOIP_SRC)
printf("Source ");
else printf("Destination ");
if (info->count > 1)
printf("countries: ");
else printf("country: ");
if (info->flags & IPT_GEOIP_INV)
printf("! ");
for (i = 0; i < info->count; i++)
printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i]));
}
static void
save(const struct ipt_ip *ip,
const struct ipt_entry_match *match)
{
const struct ipt_geoip_info *info
= (const struct ipt_geoip_info *)match->data;
u_int8_t i;
if (info->flags & IPT_GEOIP_INV)
printf("! ");
if (info->flags & IPT_GEOIP_SRC)
printf("--source-country ");
else printf("--destination-country ");
for (i = 0; i < info->count; i++)
printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i]));
printf(" ");
}
static struct iptables_match geoip = {
.name = "geoip",
.version = IPTABLES_VERSION,
.size = IPT_ALIGN(sizeof(struct ipt_geoip_info)),
.userspacesize = offsetof(struct ipt_geoip_info, mem),
.help = &help,
.init = &init,
.parse = &parse,
.final_check = &final_check,
.print = &print,
.save = &save,
.extra_opts = opts
};
void _init(void)
{
register_match(&geoip);
}
[-- Attachment #5: geoip-pomng-20041103-1.tar.gz --]
[-- Type: application/x-gzip, Size: 8090 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: New match ipt_geoip
2004-11-04 3:08 New match ipt_geoip Samuel Jean
@ 2004-11-05 0:57 ` Samuel Jean
2004-11-05 10:42 ` Harald Welte
1 sibling, 0 replies; 7+ messages in thread
From: Samuel Jean @ 2004-11-05 0:57 UTC (permalink / raw)
To: netfilter-devel
[-- Attachment #1: Type: text/plain, Size: 267 bytes --]
*Doh!*
Am sorry guys, I've made an error. Just before posting, I've changed
something without testing again. But that change introduced a
segmentation fault.
Here's the fixed file, and I also attached the fixed pomng package.
Hope you won't beat me :-)
Samuel
[-- Attachment #2: ipt_geoip.c --]
[-- Type: text/x-csrc, Size: 7540 bytes --]
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <linux/netfilter_ipv4/ipt_geoip.h>
#include <linux/netfilter_ipv4/ip_tables.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Samuel Jean, Nicolas Bouliane");
MODULE_DESCRIPTION("iptables/netfilter's geoip match");
struct geoip_info *head = NULL;
static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED;
static struct geoip_info *add_node(struct geoip_info *memcpy)
{
struct geoip_info *p =
(struct geoip_info *)kmalloc(sizeof(struct geoip_info), GFP_KERNEL);
struct geoip_subnet *s;
if ((p == NULL) || (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0))
return NULL;
s = (struct geoip_subnet *)kmalloc(p->count * sizeof(struct geoip_subnet), GFP_KERNEL);
if ((s == NULL) || (copy_from_user(s, p->subnets, p->count * sizeof(struct geoip_subnet)) != 0))
return NULL;
spin_lock_bh(&geoip_lock);
p->subnets = s;
p->ref = 1;
p->next = head;
p->prev = NULL;
if (p->next) p->next->prev = p;
head = p;
spin_unlock_bh(&geoip_lock);
return p;
}
static void remove_node(struct geoip_info *p)
{
spin_lock_bh(&geoip_lock);
if (p->next) { /* Am I following a node ? */
p->next->prev = p->prev;
if (p->prev) p->prev->next = p->next; /* Is there a node behind me ? */
else head = p->next; /* No? Then I was the head */
}
else
if (p->prev) /* Is there a node behind me ? */
p->prev->next = NULL;
else
head = NULL; /* No, we're alone */
/* So now am unlinked or the only one alive, right ?
* What are you waiting ? Free up some memory!
*/
kfree(p->subnets);
kfree(p);
spin_unlock_bh(&geoip_lock);
return;
}
static struct geoip_info *find_node(u_int16_t cc)
{
struct geoip_info *p = head;
spin_lock_bh(&geoip_lock);
while (p) {
if (p->cc == cc) {
spin_unlock_bh(&geoip_lock);
return p;
}
p = p->next;
}
spin_unlock_bh(&geoip_lock);
return NULL;
}
static int match(const struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
const void *matchinfo,
int offset,
const void *hdr,
u_int16_t datalen,
int *hotdrop)
{
const struct ipt_geoip_info *info = matchinfo;
const struct geoip_info *node; /* This keeps the code sexy */
const struct iphdr *iph = skb->nh.iph;
u_int32_t ip, j;
u_int8_t i;
if (info->flags & IPT_GEOIP_SRC)
ip = ntohl(iph->saddr);
else
ip = ntohl(iph->daddr);
spin_lock_bh(&geoip_lock);
for (i = 0; i < info->count; i++) {
if ((node = info->mem[i]) == NULL) {
printk(KERN_ERR "ipt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n",
COUNTRY(info->cc[i]));
continue;
}
for (j = 0; j < node->count; j++)
if ((ip > node->subnets[j].begin) && (ip < node->subnets[j].end)) {
spin_unlock_bh(&geoip_lock);
return (info->flags & IPT_GEOIP_INV) ? 0 : 1;
}
}
spin_unlock_bh(&geoip_lock);
return (info->flags & IPT_GEOIP_INV) ? 1 : 0;
}
static int geoip_checkentry(const char *tablename,
const struct ipt_ip *ip,
void *matchinfo,
unsigned int matchsize,
unsigned int hook_mask)
{
struct ipt_geoip_info *info = matchinfo;
struct geoip_info *node;
u_int8_t i;
/* FIXME: Call a function to free userspace allocated memory.
* As Martin J. said; this match might eat lot of memory
* if commited with iptables-restore --noflush
void (*gfree)(struct geoip_info *oldmem);
gfree = info->fini;
*/
if (matchsize != IPT_ALIGN(sizeof(struct ipt_geoip_info))) {
printk(KERN_ERR "ipt_geoip: matchsize differ, you may have forgotten to recompile me\n");
return 0;
}
/* If info->refcount isn't NULL, then
* it means that checkentry() already
* initialized this entry. Increase a
* refcount to prevent destroy() of
* this entry. */
if (info->refcount != NULL) {
atomic_inc((atomic_t *)info->refcount);
return 1;
}
for (i = 0; i < info->count; i++) {
if ((node = find_node(info->cc[i])) != NULL)
atomic_inc((atomic_t *)&node->ref); //increase the reference
else
if ((node = add_node(info->mem[i])) == NULL) {
printk(KERN_ERR
"ipt_geoip: unable to load '%c%c' into memory\n",
COUNTRY(info->cc[i]));
return 0;
}
/* Free userspace allocated memory for that country.
* FIXME: It's a bit odd to call this function everytime
* we process a country. Would be nice to call
* it once after all countries've been processed.
* - SJ
* *not implemented for now*
gfree(info->mem[i]);
*/
/* Overwrite the now-useless pointer info->mem[i] with
* a pointer to the node's kernelspace structure.
* This avoids searching for a node in the match() and
* destroy() functions.
*/
info->mem[i] = node;
}
/* We allocate some memory and give info->refcount a pointer
* to this memory. This prevents checkentry() from increasing a refcount
* different from the one used by destroy().
* For explanation, see http://www.mail-archive.com/netfilter-devel@lists.samba.org/msg00625.html
*/
info->refcount = kmalloc(sizeof(u_int8_t), GFP_KERNEL);
if (info->refcount == NULL) {
printk(KERN_ERR "ipt_geoip: failed to allocate `refcount' memory\n");
return 0;
}
*(info->refcount) = 1;
return 1;
}
static void geoip_destroy(void *matchinfo, unsigned int matchsize)
{
struct ipt_geoip_info *info = matchinfo;
struct geoip_info *node; /* this keeps the code sexy */
u_int8_t i;
/* Decrease the previously increased refcount in checkentry()
* If it's equal to 1, we know this entry is just moving
* but not removed. We simply return to avoid useless destroy()
* processing.
*/
atomic_dec((atomic_t *)info->refcount);
if (*info->refcount)
return;
/* This entry has been removed from the table so
* decrease the refcount of all countries it is
* using.
*/
for (i = 0; i < info->count; i++)
if ((node = info->mem[i]) != NULL) {
atomic_dec((atomic_t *)&node->ref);
/* Free up some memory if that node isn't used
* anymore. */
if (node->ref < 1)
remove_node(node);
}
else
/* Something strange happened. There's no memory allocated for this
* country. Please send this bug to the mailing list. */
printk(KERN_ERR
"ipt_geoip: What happened peejix ? What happened acidmen ?\n"
"ipt_geoip: please report this bug to the maintainers\n");
return;
}
static struct ipt_match geoip_match
= { { NULL, NULL }, "geoip", &match, &geoip_checkentry, &geoip_destroy, THIS_MODULE };
static int __init init(void)
{
return ipt_register_match(&geoip_match);
}
static void __exit fini(void)
{
ipt_unregister_match(&geoip_match);
return;
}
module_init(init);
module_exit(fini);
[-- Attachment #3: geoip-pomng-20041104-1.tar.gz --]
[-- Type: application/x-gzip, Size: 8089 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: New match ipt_geoip
2004-11-04 3:08 New match ipt_geoip Samuel Jean
2004-11-05 0:57 ` Samuel Jean
@ 2004-11-05 10:42 ` Harald Welte
2004-11-05 17:50 ` Samuel Jean
1 sibling, 1 reply; 7+ messages in thread
From: Harald Welte @ 2004-11-05 10:42 UTC (permalink / raw)
To: Samuel Jean; +Cc: netfilter-devel
[-- Attachment #1: Type: text/plain, Size: 1279 bytes --]
On Wed, Nov 03, 2004 at 10:08:07PM -0500, Samuel Jean wrote:
> Hi people,
Hi Samuel!
> We've been working on a way to match a packet based on its country.
> We know it's not a 100% reliable way to filter, but it all
> depends on the database one use.
Thanks, I think this is useful for a number of people (although I
generally dislike the concept, since it never works, especially in the
case of tunnels in multinational corporations/organizations...)
It's also nice to see that you implemented the database-part in
userspace, rather than feeding it via /proc into the kernel (yes, there
are people who would do that).
On the other hand, I think there is significant overlap between your
ipt_geoip.c kernel part and the ippool/ipset match.
Thus, I argue, rather than inventing a new patch for the kernel, this
can be done in userspace by configuring a pool of address ranges.
--
- Harald Welte <laforge@netfilter.org> http://www.netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: New match ipt_geoip
2004-11-05 10:42 ` Harald Welte
@ 2004-11-05 17:50 ` Samuel Jean
2004-11-05 23:27 ` Jozsef Kadlecsik
0 siblings, 1 reply; 7+ messages in thread
From: Samuel Jean @ 2004-11-05 17:50 UTC (permalink / raw)
To: Harald Welte; +Cc: netfilter-devel
On Fri, November 5, 2004 5:42 am, Harald Welte said:
Dear Harald.
> (although I
> generally dislike the concept, since it never works, especially in the
> case of tunnels in multinational corporations/organizations...)
I agree.
> On the other hand, I think there is significant overlap between your
> ipt_geoip.c kernel part and the ippool/ipset match.
What kind of overlap ? Is there incompatibilitie issue ? or is it
functionnality's similarity issue ?
> Thus, I argue, rather than inventing a new patch for the kernel, this
can > be done in userspace by configuring a pool of address ranges.
Yes, that could make sense if ipt_geoip.c was incompatible (conflicting)
with other modules. Else, I think we might consider using it.
Let me show you how I see it.
Arguement #1:
When someone insert rules like :
-m geoip --src-cc CA
-m geoip --src-cc US
-m geoip --src-cc DE
-m geoip --src-cc JP
-m geoip --src-cc CH
there's sum of about 20 000 subnets for those countries.
If we use ipt_pool as the subnets matcher, everytime a packet
hit one of previous rules, ipt_pool must seek through its whole
pool. (Do I am wrong ?) If that user also use ip_pool for other
things than geolocation matching, the pool size increase.
Or maybe ipt_pool handles multiple pool ? (forgive my ignorance)
Let's continue.
If we use ipt_geoip as the subnets matcher, everytime a packet
hit one of the previous rules, ipt_geoip knows exactly where
the subnets' pool is.
Argument #2:
It is possible to match up to 15 countries per rule.
-m geoip --src-cc ca,us,jp,ch,de,ad,a1,a2,...
How shall ipt_pool handle that ?
Is it going to create 15 diff pools or just one with all those subnets.
And if someone insert another rule with same countries, is ipt_pool going
to increment a refcount for the pool ?
In the other way, ipt_geoip is allocating ressources on a per-rule basis.
If ressources already exist for a given country, a refcount is simply
increased. If there's no more rule using that ressource, it is simply
freed.
Argument #3:
Nicolas and me are working on a caching system in ipt_geoip. I'm sure it's
going to decrease latency a bit. So, would be nice to keep that module.
Please, let me know your thought.
Thanks a lot, chief Harald :-)
Samuel
CookingLinux TM
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: New match ipt_geoip
2004-11-05 17:50 ` Samuel Jean
@ 2004-11-05 23:27 ` Jozsef Kadlecsik
2004-11-06 9:35 ` Harald Welte
0 siblings, 1 reply; 7+ messages in thread
From: Jozsef Kadlecsik @ 2004-11-05 23:27 UTC (permalink / raw)
To: Samuel Jean; +Cc: Harald Welte, netfilter-devel
On Fri, 5 Nov 2004, Samuel Jean wrote:
> > Thus, I argue, rather than inventing a new patch for the kernel, this
> can > be done in userspace by configuring a pool of address ranges.
>
> Yes, that could make sense if ipt_geoip.c was incompatible (conflicting)
> with other modules. Else, I think we might consider using it.
>
> Arguement #1:
> When someone insert rules like :
>
> -m geoip --src-cc CA
> -m geoip --src-cc US
> -m geoip --src-cc DE
> -m geoip --src-cc JP
> -m geoip --src-cc CH
>
> there's sum of about 20 000 subnets for those countries.
>
> If we use ipt_pool as the subnets matcher, everytime a packet
> hit one of previous rules, ipt_pool must seek through its whole
> pool. (Do I am wrong ?) If that user also use ip_pool for other
> things than geolocation matching, the pool size increase.
> Or maybe ipt_pool handles multiple pool ? (forgive my ignorance)
[Let's speak about ipset which is based on ippool.]
In the case of a bitmap type of set (ipmap/portmap/macipmap), there is no
seeking in the set: just one bit comparison is required to find an entry.
In the case of a hash type of set (iphash and the planned nethash), a
limited number of hashing is required.
Of course you can define multiple pools/sets, like one for CA, US, etc.
> Argument #2:
>
> It is possible to match up to 15 countries per rule.
>
> -m geoip --src-cc ca,us,jp,ch,de,ad,a1,a2,...
No, that's not possible. But it's an interesting idea, to introduce
supersets...
> How shall ipt_pool handle that ?
In ipset you can define "multilevel" matching. Let's say we have subnets
scattered along in bigger networks:
/16 /24
1.1.0.0 1.1.1.0
1.1.8.0
....
2.2.0.0 2.2.1.0
2.2.4.0
...
.... ....
Then you can define sets for the naturally grouped heaps: one for the /16
blocks, say toplevel, one for the /24 subnets in 1.1.0.0/16 say set-1.1,
one for the /24 subnets in 2.2.0.0/16 say set-2.2, etc. Then one can
bind the set elements in toplevel to the corresponding subnet sets:
1.1.0.0 -> set-1.1
2.2.0.0 -> set-2.2
...
and then the match
-m set --set toplevel src,src
will match any source IP address, for which the IP masked by /16 can be
found in set toplevel *and* the IP masked by /24 can be found in the set
according to the binding belonging to the element found in set toplevel.
> Is it going to create 15 diff pools or just one with all those subnets.
> And if someone insert another rule with same countries, is ipt_pool going
> to increment a refcount for the pool ?
If the same pool/set used in multiple rules, a refcount is incremented.
> Nicolas and me are working on a caching system in ipt_geoip. I'm sure it's
> going to decrease latency a bit. So, would be nice to keep that module.
I just wanted to comment the capabilities of ipset/ippool.
I fully support the goal to unify related matches/targets in netfilter - I
share the feeling we have too many similar of them. Probably ipt_geoip
could be expressed and replaced by appropriate ippool/ipset definitions.
However, without peeking into the code of ipt_geoip, the idea seems to be
quite special. The feature is compact, which can be grasped by the users
easily, without the need to apply/tune special recipes/rules/settings.
So it'd worth to add it to pom-ng, I believe.
Best regards,
Jozsef
-
E-mail : kadlec@blackhole.kfki.hu, kadlec@sunserv.kfki.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : KFKI Research Institute for Particle and Nuclear Physics
H-1525 Budapest 114, POB. 49, Hungary
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: New match ipt_geoip
2004-11-05 23:27 ` Jozsef Kadlecsik
@ 2004-11-06 9:35 ` Harald Welte
2004-11-08 1:04 ` New match ipt_geoip (GPLed) Samuel Jean
0 siblings, 1 reply; 7+ messages in thread
From: Harald Welte @ 2004-11-06 9:35 UTC (permalink / raw)
To: Jozsef Kadlecsik; +Cc: netfilter-devel, Samuel Jean
[-- Attachment #1: Type: text/plain, Size: 1277 bytes --]
On Sat, Nov 06, 2004 at 12:27:06AM +0100, Jozsef Kadlecsik wrote:
> I fully support the goal to unify related matches/targets in netfilter - I
> share the feeling we have too many similar of them. Probably ipt_geoip
> could be expressed and replaced by appropriate ippool/ipset definitions.
oh, of course. But one could write a frontend for ipset that configures
the sets accordingly. I don't suggest to abandon ipt_geoip without
providing a user interface that is equally easy to use.
> However, without peeking into the code of ipt_geoip, the idea seems to be
> quite special. The feature is compact, which can be grasped by the users
> easily, without the need to apply/tune special recipes/rules/settings.
> So it'd worth to add it to pom-ng, I believe.
ok, I agree, we have other way less useful stuff in it. But it's not
going on 'kernel track'.
> Best regards,
> Jozsef
--
- Harald Welte <laforge@netfilter.org> http://www.netfilter.org/
============================================================================
"Fragmentation is like classful addressing -- an interesting early
architectural error that shows how much experimentation was going
on while IP was being designed." -- Paul Vixie
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 189 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: New match ipt_geoip (GPLed)
2004-11-06 9:35 ` Harald Welte
@ 2004-11-08 1:04 ` Samuel Jean
0 siblings, 0 replies; 7+ messages in thread
From: Samuel Jean @ 2004-11-08 1:04 UTC (permalink / raw)
To: Harald Welte; +Cc: netfilter-devel, nib
[-- Attachment #1: Type: text/plain, Size: 1229 bytes --]
Hi there,
I am sorry for that code-repost.
I just talked with Sylvain from gnu.org and here's a snipplet from the
conversation :
> >For example, to release your program properly under the GPL you must
> >include a copyright notice and permission-to-copy statements at the
> >beginning of every file of source code. This is explained in
> >http://www.gnu.org/licenses/gpl-howto.html. Our review would help
> >catch potential omissions such as these.
>
> I wasn't aware of it. I did for csv2bin.c but all netfilter's add-ons
> doesn't contain that notice.
>
> Do netfilter's add-ons still need that copyright?
>
> I will do, anyway. Thanks.
Yes. Basically, all files of more than 10 lines should carry a
copyright and a license notice.
We have some additional documentation for GNU maintainers about these
issues. You may want to check:
www.gnu.org/prep/maintain/html_node/Copyright-Notices.html
www.gnu.org/prep/maintain/html_node/License-Notices.html
Thus, I added it into concerned files. It might seems useless for
people, but as I am not a GNU Monks like Harald, I prefered to add it
now before it get into pom.
Hope you won't whip me for a complete pom-ng package re-post.
Thanks,
Samuel
CookingLinux TM
[-- Attachment #2: geoip-pomng-20041107-1.tar.gz --]
[-- Type: application/x-gzip, Size: 8360 bytes --]
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2004-11-08 1:04 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-11-04 3:08 New match ipt_geoip Samuel Jean
2004-11-05 0:57 ` Samuel Jean
2004-11-05 10:42 ` Harald Welte
2004-11-05 17:50 ` Samuel Jean
2004-11-05 23:27 ` Jozsef Kadlecsik
2004-11-06 9:35 ` Harald Welte
2004-11-08 1:04 ` New match ipt_geoip (GPLed) Samuel Jean
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.