From 3554e348bcd9dcfe13238399324da1c170ecfe1d Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 12:46:15 +0100 Subject: [PATCH 01/18] geoip: import 20080214 code base --- extensions/Kbuild | 1 + extensions/libxt_geoip.c | 325 +++++++++++++++++++++++++++++++++++++ extensions/libxt_geoip.man | 15 ++ extensions/xt_geoip.c | 280 ++++++++++++++++++++++++++++++++ extensions/xt_geoip.h | 53 ++++++ mconfig | 1 + 6 files changed, 675 insertions(+) create mode 100644 extensions/libxt_geoip.c create mode 100644 extensions/libxt_geoip.man create mode 100644 extensions/xt_geoip.c create mode 100644 extensions/xt_geoip.h diff --git a/extensions/Kbuild b/extensions/Kbuild index 7155e3e..5fa2bab 100644 --- a/extensions/Kbuild +++ b/extensions/Kbuild @@ -10,6 +10,7 @@ obj-${build_DELUDE} += xt_DELUDE.o obj-${build_LOGMARK} += xt_LOGMARK.o obj-${build_TARPIT} += xt_TARPIT.o obj-${build_TEE} += xt_TEE.o +obj-${build_geoip} += xt_geoip.o obj-${build_portscan} += xt_portscan.o -include ${M}/*.Kbuild diff --git a/extensions/libxt_geoip.c b/extensions/libxt_geoip.c new file mode 100644 index 0000000..8ac73be --- /dev/null +++ b/extensions/libxt_geoip.c @@ -0,0 +1,325 @@ +/* Shared library add-on to iptables to add geoip match support. + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Copyright (c) 2004, 2005, 2006, 2007, 2008 + * Samuel Jean & Nicolas Bouliane + * + * For comments, bugs or suggestions, please contact + * Samuel Jean + * Nicolas Bouliane + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "xt_geoip.h" + +static void geoip_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", XTABLES_VERSION + ); +} + +static struct option geoip_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 }, +}; + +/* NOT IMPLEMENTED YET +static void geoip_free(struct geoip_info *oldmem) +{ +} +*/ + +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: 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: 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: 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: 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) != 2) /* Country must be 2 chars long according + to the ISO3166 standard */ + exit_error(PARAMETER_PROBLEM, + "geoip: invalid country code '%s'", cc); + + // Verification will fail if chars aren't uppercased. + // Make sure they are.. + for (i = 0; i < 2; i++) + if (isalnum(cc[i]) != 0) + cc[i] = toupper(cc[i]); + else + exit_error(PARAMETER_PROBLEM, + "geoip: 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: insufficient memory available"); + + for (cp = buffer, i = 0; cp && i < XT_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: insufficient memory available"); + cc[count-1] = cctmp; + } + } + + if (cp) exit_error(PARAMETER_PROBLEM, + "geoip: too many countries specified"); + free(buffer); + + if (count == 0) exit_error(PARAMETER_PROBLEM, + "geoip: don't know what happened"); + + return count; +} + +static int +geoip_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_geoip_match_info *info = (void *)(*match)->data; + + switch(c) { + case '1': + // Ensure that XT_GEOIP_SRC *OR* XT_GEOIP_DST haven't been used yet. + if (*flags & (XT_GEOIP_SRC | XT_GEOIP_DST)) + exit_error(PARAMETER_PROBLEM, + "geoip: only use --source-country *OR* --destination-country once!"); + + *flags |= XT_GEOIP_SRC; + break; + + case '2': + // Ensure that XT_GEOIP_SRC *OR* XT_GEOIP_DST haven't been used yet. + if (*flags & (XT_GEOIP_SRC | XT_GEOIP_DST)) + exit_error(PARAMETER_PROBLEM, + "geoip: only use --source-country *OR* --destination-country once!"); + + *flags |= XT_GEOIP_DST; + break; + + default: + return 0; + } + + if (invert) + *flags |= XT_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 +geoip_final_check(unsigned int flags) +{ + if (!flags) + exit_error(PARAMETER_PROBLEM, + "geoip: missing arguments"); +} + +static void +geoip_print(const void *ip, const struct xt_entry_match *match, int numeric) +{ + const struct xt_geoip_match_info *info = (void*)match->data; + + u_int8_t i; + + if (info->flags & XT_GEOIP_SRC) + printf("Source "); + else printf("Destination "); + + if (info->count > 1) + printf("countries: "); + else printf("country: "); + + if (info->flags & XT_GEOIP_INV) + printf("! "); + + for (i = 0; i < info->count; i++) + printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i])); + printf(" "); +} + +static void +geoip_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_geoip_match_info *info = (void *)match->data; + u_int8_t i; + + if (info->flags & XT_GEOIP_INV) + printf("! "); + + if (info->flags & XT_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 xtables_match geoip_match = { + .family = AF_INET, + .name = "geoip", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_geoip_match_info)), + .userspacesize = XT_ALIGN(offsetof(struct xt_geoip_match_info, mem)), + .help = geoip_help, + .parse = geoip_parse, + .final_check = geoip_final_check, + .print = geoip_print, + .save = geoip_save, + .extra_opts = geoip_opts +}; + +void _init(void) +{ + xtables_register_match(&geoip_match); +} diff --git a/extensions/libxt_geoip.man b/extensions/libxt_geoip.man new file mode 100644 index 0000000..bc3b517 --- /dev/null +++ b/extensions/libxt_geoip.man @@ -0,0 +1,15 @@ +Match a packet by its source or destination country. +.TP +[\fB!\fR] \fB--src-cc\fR, \fB--source-country \fIcountry\fR[\fB,\fIcountry\fB,\fIcountry\fB,\fI...\fR] +Match packet coming from (one of) the specified country(ies) +.TP +[\fB!\fR] \fB--dst-cc\fR, \fB--destination-country \fIcountry\fR[\fB,\fIcountry\fB,\fIcountry\fB,\fI...\fR] +Match packet going to (one of) the specified country(ies) +.TP +NOTE: +The country is inputed by its ISO3166 code. +.P +The only extra files you need is a binary db (geoipdb.bin) & its index file (geoipdb.idx). +Both files are generated from a countries & subnets database with the csv2bin tool, +available at http://people.netfilter.org/peejix/geoip/. Both files MUST also be moved in /var/geoip/ +as the shared library is statically looking for that pathname (ex.: /var/geoip/geoipdb.bin). diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c new file mode 100644 index 0000000..22106bc --- /dev/null +++ b/extensions/xt_geoip.c @@ -0,0 +1,280 @@ +/* iptables kernel module for the geoip match + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Copyright (c) 2004, 2005, 2006, 2007, 2008 + * Samuel Jean & Nicolas Bouliane + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "xt_geoip.h" +#include "compat_xtables.h" + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nicolas Bouliane"); +MODULE_AUTHOR("Samuel Jean"); +MODULE_DESCRIPTION("xtables module for geoip match"); +MODULE_ALIAS("ipt_geoip"); + +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 bool xt_geoip_mt(const struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + const struct xt_match *match, + const void *matchinfo, + int offset, + unsigned int protoff, + bool *hotdrop) +{ + const struct xt_geoip_match_info *info = (void*)matchinfo; + const struct geoip_info *node; /* This keeps the code sexy */ + const struct iphdr *iph = ip_hdr(skb); + u_int32_t ip, i, j; + + if (info->flags & XT_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 "xt_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 & XT_GEOIP_INV) ? 0 : 1; + } + } + + spin_unlock_bh(&geoip_lock); + return (info->flags & XT_GEOIP_INV) ? 1 : 0; +} + +static bool xt_geoip_mt_checkentry(const char *tablename, + const void *ip, + const struct xt_match *match, + void *matchinfo, + unsigned int hook_mas) +{ + + struct xt_geoip_match_info *info = (void *)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 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 + "xt_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 "xt_geoip: failed to allocate `refcount' memory\n"); + return 0; + } + *(info->refcount) = 1; + + return 1; +} + +static void xt_geoip_mt_destroy(const struct xt_match *matcn, + void *matchinfo) +{ + struct xt_geoip_match_info *info = (void *)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() + * proce ssing. + */ + atomic_dec((atomic_t *)info->refcount); + if (*info->refcount) + return; + + /* Don't leak my memory, you idiot. + * Bug found with nfsim.. the netfilter's best + * friend. --peejix */ + kfree(info->refcount); + + /* 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 + "xt_geoip: What happened peejix ? What happened acidfu ?\n" + "xt_geoip: please report this bug to the maintainers\n"); + return; +} + +static struct xt_match xt_geoip_match __read_mostly = { + .family = AF_INET, + .name = "geoip", + .match = &xt_geoip_mt, + .checkentry = &xt_geoip_mt_checkentry, + .destroy = &xt_geoip_mt_destroy, + .matchsize = sizeof(struct xt_geoip_match_info), + .me = THIS_MODULE, +}; + +static int __init xt_geoip_mt_init(void) +{ + return xt_register_match(&xt_geoip_match); +} + +static void __exit xt_geoip_mt_fini(void) +{ + xt_unregister_match(&xt_geoip_match); +} + +module_init(xt_geoip_mt_init); +module_exit(xt_geoip_mt_fini); diff --git a/extensions/xt_geoip.h b/extensions/xt_geoip.h new file mode 100644 index 0000000..52adc83 --- /dev/null +++ b/extensions/xt_geoip.h @@ -0,0 +1,53 @@ +/* ipt_geoip.h header file for libipt_geoip.c and ipt_geoip.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Copyright (c) 2004, 2005, 2006, 2007, 2008 + * + * Samuel Jean + * Nicolas Bouliane + */ +#ifndef _XT_GEOIP_H +#define _XT_GEOIP_H + +#define XT_GEOIP_SRC 0x01 /* Perform check on Source IP */ +#define XT_GEOIP_DST 0x02 /* Perform check on Destination IP */ +#define XT_GEOIP_INV 0x04 /* Negate the condition */ + +#define XT_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 xt_geoip_match_info { + u_int8_t flags; + u_int8_t count; + u_int16_t cc[XT_GEOIP_MAX]; + + /* Used internally by the kernel */ + struct geoip_info *mem[XT_GEOIP_MAX]; + u_int8_t *refcount; + + /* not implemented yet: + void *fini; + */ +}; + +#define COUNTRY(cc) (cc >> 8), (cc & 0x00FF) + +#endif + diff --git a/mconfig b/mconfig index afbe701..f1b6aff 100644 --- a/mconfig +++ b/mconfig @@ -8,4 +8,5 @@ build_DELUDE=m build_LOGMARK=m build_TARPIT=m build_TEE=m +build_geoip=m build_portscan=m From f1615a03f3d4e45019d4e9c3f540063a488d55a1 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 12:49:28 +0100 Subject: [PATCH 02/18] geoip: remove unused code and unneeded per-info refcount - freeing userspace memory is not the kernel's job, really. - checkentry is called exactly once, as is destroy. --- extensions/libxt_geoip.c | 9 ------- extensions/xt_geoip.c | 53 ---------------------------------------- extensions/xt_geoip.h | 5 ---- 3 files changed, 67 deletions(-) diff --git a/extensions/libxt_geoip.c b/extensions/libxt_geoip.c index 8ac73be..82e3375 100644 --- a/extensions/libxt_geoip.c +++ b/extensions/libxt_geoip.c @@ -53,12 +53,6 @@ static struct option geoip_opts[] = { { 0 }, }; -/* NOT IMPLEMENTED YET -static void geoip_free(struct geoip_info *oldmem) -{ -} -*/ - struct geoip_index { u_int16_t cc; u_int32_t offset; @@ -250,9 +244,6 @@ geoip_parse(int c, char **argv, int invert, unsigned int *flags, info->count = parse_geoip_cc(argv[optind-1], info->cc, info->mem); info->flags = *flags; - info->refcount = NULL; - //info->fini = &geoip_free; - return 1; } diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 22106bc..61084db 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -150,24 +150,6 @@ static bool xt_geoip_mt_checkentry(const char *tablename, 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 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) @@ -180,15 +162,6 @@ static bool xt_geoip_mt_checkentry(const char *tablename, 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 @@ -197,18 +170,6 @@ static bool xt_geoip_mt_checkentry(const char *tablename, 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 "xt_geoip: failed to allocate `refcount' memory\n"); - return 0; - } - *(info->refcount) = 1; - return 1; } @@ -219,20 +180,6 @@ static void xt_geoip_mt_destroy(const struct xt_match *matcn, 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() - * proce ssing. - */ - atomic_dec((atomic_t *)info->refcount); - if (*info->refcount) - return; - - /* Don't leak my memory, you idiot. - * Bug found with nfsim.. the netfilter's best - * friend. --peejix */ - kfree(info->refcount); - /* This entry has been removed from the table so * decrease the refcount of all countries it is * using. diff --git a/extensions/xt_geoip.h b/extensions/xt_geoip.h index 52adc83..8169f6d 100644 --- a/extensions/xt_geoip.h +++ b/extensions/xt_geoip.h @@ -40,11 +40,6 @@ struct xt_geoip_match_info { /* Used internally by the kernel */ struct geoip_info *mem[XT_GEOIP_MAX]; - u_int8_t *refcount; - - /* not implemented yet: - void *fini; - */ }; #define COUNTRY(cc) (cc >> 8), (cc & 0x00FF) From 9f45aa737a10ee399f9a0576a30fa89bc5b81348 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 13:26:11 +0100 Subject: [PATCH 03/18] geoip: remove redundant casts --- extensions/libxt_geoip.c | 4 ++-- extensions/xt_geoip.c | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/extensions/libxt_geoip.c b/extensions/libxt_geoip.c index 82e3375..1d3a1bb 100644 --- a/extensions/libxt_geoip.c +++ b/extensions/libxt_geoip.c @@ -80,7 +80,7 @@ get_country_subnets(u_int16_t cc, u_int32_t *count) stat("/var/geoip/geoipdb.idx", &buf); idxsz = buf.st_size/sizeof(struct geoip_index); - index = (struct geoip_index *)malloc(buf.st_size); + index = malloc(buf.st_size); fread(index, buf.st_size, 1, ixfd); @@ -110,7 +110,7 @@ get_country_subnets(u_int16_t cc, u_int32_t *count) fread(&db_nsubnets, sizeof(u_int16_t), 1, dbfd); - subnets = (struct geoip_subnet*)malloc(db_nsubnets * sizeof(struct geoip_subnet)); + subnets = malloc(db_nsubnets * sizeof(struct geoip_subnet)); if (!subnets) exit_error(OTHER_PROBLEM, diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 61084db..953dede 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -32,15 +32,14 @@ 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_info *p = 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); + s = 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; @@ -109,7 +108,7 @@ static bool xt_geoip_mt(const struct sk_buff *skb, unsigned int protoff, bool *hotdrop) { - const struct xt_geoip_match_info *info = (void*)matchinfo; + const struct xt_geoip_match_info *info = matchinfo; const struct geoip_info *node; /* This keeps the code sexy */ const struct iphdr *iph = ip_hdr(skb); u_int32_t ip, i, j; @@ -146,7 +145,7 @@ static bool xt_geoip_mt_checkentry(const char *tablename, unsigned int hook_mas) { - struct xt_geoip_match_info *info = (void *)matchinfo; + struct xt_geoip_match_info *info = matchinfo; struct geoip_info *node; u_int8_t i; @@ -176,7 +175,7 @@ static bool xt_geoip_mt_checkentry(const char *tablename, static void xt_geoip_mt_destroy(const struct xt_match *matcn, void *matchinfo) { - struct xt_geoip_match_info *info = (void *)matchinfo; + struct xt_geoip_match_info *info = matchinfo; struct geoip_info *node; /* this keeps the code sexy */ u_int8_t i; From 205a006ac986ece421dd44e698953a2bf2b0f15f Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 13:35:17 +0100 Subject: [PATCH 04/18] geoip: use tabs not spaces and indent --- extensions/libxt_geoip.c | 445 ++++++++++++++++++++------------------- extensions/xt_geoip.c | 289 ++++++++++++------------- extensions/xt_geoip.h | 35 ++- 3 files changed, 382 insertions(+), 387 deletions(-) diff --git a/extensions/libxt_geoip.c b/extensions/libxt_geoip.c index 1d3a1bb..38e7660 100644 --- a/extensions/libxt_geoip.c +++ b/extensions/libxt_geoip.c @@ -1,5 +1,5 @@ /* Shared library add-on to iptables to add geoip match support. - + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -29,288 +29,293 @@ static void geoip_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", XTABLES_VERSION - ); + 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", XTABLES_VERSION + ); } static struct option geoip_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 }, + { "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 }, }; struct geoip_index { - u_int16_t cc; - u_int32_t offset; + 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; + FILE *ixfd, *dbfd; + struct geoip_subnet *subnets; + struct geoip_index *index; + struct stat buf; - if ((ixfd = fopen("/var/geoip/geoipdb.idx", "r")) == NULL) { - perror("/var/geoip/geoipdb.idx"); - exit_error(OTHER_PROBLEM, - "geoip: cannot open geoip's database index file"); - } - - stat("/var/geoip/geoipdb.idx", &buf); - idxsz = buf.st_size/sizeof(struct geoip_index); - index = malloc(buf.st_size); + size_t idxsz; + u_int16_t i; - fread(index, buf.st_size, 1, ixfd); + u_int16_t db_cc = 0; + u_int16_t db_nsubnets = 0; - 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)); + if ((ixfd = fopen("/var/geoip/geoipdb.idx", "r")) == NULL) { + perror("/var/geoip/geoipdb.idx"); + exit_error(OTHER_PROBLEM, + "geoip: cannot open geoip's database index file"); + } - fclose(ixfd); + stat("/var/geoip/geoipdb.idx", &buf); + idxsz = buf.st_size/sizeof(struct geoip_index); + index = malloc(buf.st_size); - if ((dbfd = fopen("/var/geoip/geoipdb.bin", "r")) == NULL) { - perror("/var/geoip/geoipdb.bin"); - exit_error(OTHER_PROBLEM, - "geoip: cannot open geoip's database file"); - } + fread(index, buf.st_size, 1, ixfd); - fseek(dbfd, index[i].offset, SEEK_SET); - fread(&db_cc, sizeof(u_int16_t), 1, dbfd); + for (i = 0; i < idxsz; i++) + if (cc == index[i].cc) + break; - if (db_cc != cc) - exit_error(OTHER_PROBLEM, - "geoip: 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); + if (cc != index[i].cc) + exit_error(OTHER_PROBLEM, + "geoip match: sorry, '%c%c' isn't in the database\n", COUNTRY(cc)); - subnets = malloc(db_nsubnets * sizeof(struct geoip_subnet)); + fclose(ixfd); - if (!subnets) - exit_error(OTHER_PROBLEM, - "geoip: insufficient memory available"); - - fread(subnets, db_nsubnets * sizeof(struct geoip_subnet), 1, dbfd); - - fclose(dbfd); - free(index); - *count = db_nsubnets; - return subnets; + if ((dbfd = fopen("/var/geoip/geoipdb.bin", "r")) == NULL) { + perror("/var/geoip/geoipdb.bin"); + exit_error(OTHER_PROBLEM, + "geoip: 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: 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 = malloc(db_nsubnets * sizeof(struct geoip_subnet)); + + if (!subnets) + exit_error(OTHER_PROBLEM, + "geoip: 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)); + 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; + 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; + u_int8_t i; + u_int16_t cc_int16; - if (strlen(cc) != 2) /* Country must be 2 chars long according - to the ISO3166 standard */ - exit_error(PARAMETER_PROBLEM, - "geoip: invalid country code '%s'", cc); + if (strlen(cc) != 2) /* Country must be 2 chars long according + to the ISO3166 standard */ + exit_error(PARAMETER_PROBLEM, + "geoip: invalid country code '%s'", cc); - // Verification will fail if chars aren't uppercased. - // Make sure they are.. - for (i = 0; i < 2; i++) - if (isalnum(cc[i]) != 0) - cc[i] = toupper(cc[i]); - else - exit_error(PARAMETER_PROBLEM, - "geoip: invalid country code '%s'", cc); + // Verification will fail if chars aren't uppercased. + // Make sure they are.. + for (i = 0; i < 2; i++) + if (isalnum(cc[i]) != 0) + cc[i] = toupper(cc[i]); + else + exit_error(PARAMETER_PROBLEM, + "geoip: 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]; + /* 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; + // 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. */ +/* 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; + char *buffer, *cp, *next; + u_int8_t i, count = 0; + u_int16_t cctmp; - buffer = strdup(ccstr); - if (!buffer) exit_error(OTHER_PROBLEM, - "geoip: insufficient memory available"); + buffer = strdup(ccstr); + if (!buffer) + exit_error(OTHER_PROBLEM, + "geoip: insufficient memory available"); - for (cp = buffer, i = 0; cp && i < XT_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: insufficient memory available"); - cc[count-1] = cctmp; - } - } - - if (cp) exit_error(PARAMETER_PROBLEM, - "geoip: too many countries specified"); - free(buffer); + for (cp = buffer, i = 0; cp && i < XT_GEOIP_MAX; cp = next, i++) + { + next = strchr(cp, ','); + if (next) *next++ = '\0'; - if (count == 0) exit_error(PARAMETER_PROBLEM, - "geoip: don't know what happened"); - - return count; + if ((cctmp = check_geoip_cc(cp, cc, count)) != 0) { + if ((mem[count++] = load_geoip_cc(cctmp)) == NULL) + exit_error(OTHER_PROBLEM, + "geoip: insufficient memory available"); + cc[count-1] = cctmp; + } + } + + if (cp) + exit_error(PARAMETER_PROBLEM, + "geoip: too many countries specified"); + free(buffer); + + if (count == 0) + exit_error(PARAMETER_PROBLEM, + "geoip: don't know what happened"); + + return count; } -static int -geoip_parse(int c, char **argv, int invert, unsigned int *flags, - const void *entry, struct xt_entry_match **match) +static int geoip_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) { - struct xt_geoip_match_info *info = (void *)(*match)->data; - - switch(c) { - case '1': - // Ensure that XT_GEOIP_SRC *OR* XT_GEOIP_DST haven't been used yet. - if (*flags & (XT_GEOIP_SRC | XT_GEOIP_DST)) - exit_error(PARAMETER_PROBLEM, - "geoip: only use --source-country *OR* --destination-country once!"); - - *flags |= XT_GEOIP_SRC; - break; - - case '2': - // Ensure that XT_GEOIP_SRC *OR* XT_GEOIP_DST haven't been used yet. - if (*flags & (XT_GEOIP_SRC | XT_GEOIP_DST)) - exit_error(PARAMETER_PROBLEM, - "geoip: only use --source-country *OR* --destination-country once!"); - - *flags |= XT_GEOIP_DST; - break; - - default: - return 0; - } - - if (invert) - *flags |= XT_GEOIP_INV; - - info->count = parse_geoip_cc(argv[optind-1], info->cc, info->mem); - info->flags = *flags; - return 1; + struct xt_geoip_match_info *info = (void *)(*match)->data; + + switch(c) { + case '1': + // Ensure that XT_GEOIP_SRC *OR* XT_GEOIP_DST haven't been used yet. + if (*flags & (XT_GEOIP_SRC | XT_GEOIP_DST)) + exit_error(PARAMETER_PROBLEM, + "geoip: only use --source-country *OR* --destination-country once!"); + + *flags |= XT_GEOIP_SRC; + break; + + case '2': + // Ensure that XT_GEOIP_SRC *OR* XT_GEOIP_DST haven't been used yet. + if (*flags & (XT_GEOIP_SRC | XT_GEOIP_DST)) + exit_error(PARAMETER_PROBLEM, + "geoip: only use --source-country *OR* --destination-country once!"); + + *flags |= XT_GEOIP_DST; + break; + + default: + return 0; + } + + if (invert) + *flags |= XT_GEOIP_INV; + + info->count = parse_geoip_cc(argv[optind-1], info->cc, info->mem); + info->flags = *flags; + return 1; } -static void +static void geoip_final_check(unsigned int flags) { - if (!flags) - exit_error(PARAMETER_PROBLEM, - "geoip: missing arguments"); + if (!flags) + exit_error(PARAMETER_PROBLEM, + "geoip: missing arguments"); } -static void +static void geoip_print(const void *ip, const struct xt_entry_match *match, int numeric) { - const struct xt_geoip_match_info *info = (void*)match->data; - - u_int8_t i; - - if (info->flags & XT_GEOIP_SRC) - printf("Source "); - else printf("Destination "); - - if (info->count > 1) - printf("countries: "); - else printf("country: "); - - if (info->flags & XT_GEOIP_INV) - printf("! "); - - for (i = 0; i < info->count; i++) - printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i])); - printf(" "); + const struct xt_geoip_match_info *info = (void*)match->data; + + u_int8_t i; + + if (info->flags & XT_GEOIP_SRC) + printf("Source "); + else + printf("Destination "); + + if (info->count > 1) + printf("countries: "); + else + printf("country: "); + + if (info->flags & XT_GEOIP_INV) + printf("! "); + + for (i = 0; i < info->count; i++) + printf("%s%c%c", i ? "," : "", COUNTRY(info->cc[i])); + printf(" "); } -static void +static void geoip_save(const void *ip, const struct xt_entry_match *match) { - const struct xt_geoip_match_info *info = (void *)match->data; - u_int8_t i; + const struct xt_geoip_match_info *info = (void *)match->data; + u_int8_t i; - if (info->flags & XT_GEOIP_INV) - printf("! "); - - if (info->flags & XT_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(" "); + if (info->flags & XT_GEOIP_INV) + printf("! "); + + if (info->flags & XT_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 xtables_match geoip_match = { - .family = AF_INET, - .name = "geoip", - .version = XTABLES_VERSION, - .size = XT_ALIGN(sizeof(struct xt_geoip_match_info)), - .userspacesize = XT_ALIGN(offsetof(struct xt_geoip_match_info, mem)), - .help = geoip_help, - .parse = geoip_parse, - .final_check = geoip_final_check, - .print = geoip_print, - .save = geoip_save, - .extra_opts = geoip_opts + .family = AF_INET, + .name = "geoip", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_geoip_match_info)), + .userspacesize = XT_ALIGN(offsetof(struct xt_geoip_match_info, mem)), + .help = geoip_help, + .parse = geoip_parse, + .final_check = geoip_final_check, + .print = geoip_print, + .save = geoip_save, + .extra_opts = geoip_opts, }; -void _init(void) +static void _init(void) { xtables_register_match(&geoip_match); } diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 953dede..ecb38f7 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -1,5 +1,5 @@ /* iptables kernel module for the geoip match - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -32,194 +32,185 @@ static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED; static struct geoip_info *add_node(struct geoip_info *memcpy) { - struct geoip_info *p = kmalloc(sizeof(struct geoip_info), GFP_KERNEL); + struct geoip_info *p = 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; + struct geoip_subnet *s; - s = 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); + if ((p == NULL) || (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0)) + return NULL; - p->subnets = s; - p->ref = 1; - p->next = head; - p->prev = NULL; - if (p->next) p->next->prev = p; - head = p; + s = 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_unlock_bh(&geoip_lock); - return p; + 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 */ +{ + spin_lock_bh(&geoip_lock); - /* So now am unlinked or the only one alive, right ? - * What are you waiting ? Free up some memory! - */ + 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 */ + } - kfree(p->subnets); - kfree(p); - - spin_unlock_bh(&geoip_lock); - return; + 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; + 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 bool xt_geoip_mt(const struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - const struct xt_match *match, - const void *matchinfo, - int offset, - unsigned int protoff, - bool *hotdrop) +static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) { - const struct xt_geoip_match_info *info = matchinfo; - const struct geoip_info *node; /* This keeps the code sexy */ - const struct iphdr *iph = ip_hdr(skb); - u_int32_t ip, i, j; + const struct xt_geoip_match_info *info = matchinfo; + const struct geoip_info *node; /* This keeps the code sexy */ + const struct iphdr *iph = ip_hdr(skb); + u_int32_t ip, i, j; - if (info->flags & XT_GEOIP_SRC) - ip = ntohl(iph->saddr); - else - ip = ntohl(iph->daddr); + if (info->flags & XT_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 "xt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n", - COUNTRY(info->cc[i])); - - continue; - } + spin_lock_bh(&geoip_lock); + for (i = 0; i < info->count; i++) { + if ((node = info->mem[i]) == NULL) { + printk(KERN_ERR "xt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n", + COUNTRY(info->cc[i])); - 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 & XT_GEOIP_INV) ? 0 : 1; - } - } - - spin_unlock_bh(&geoip_lock); - return (info->flags & XT_GEOIP_INV) ? 1 : 0; + 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 & XT_GEOIP_INV) ? 0 : 1; + } + } + + spin_unlock_bh(&geoip_lock); + return (info->flags & XT_GEOIP_INV) ? 1 : 0; } -static bool xt_geoip_mt_checkentry(const char *tablename, - const void *ip, - const struct xt_match *match, - void *matchinfo, - unsigned int hook_mas) +static bool xt_geoip_mt_checkentry(const char *table, const void *entry, + const struct xt_match *match, void *matchinfo, unsigned int hook_mask) { - - struct xt_geoip_match_info *info = matchinfo; - struct geoip_info *node; - u_int8_t i; + struct xt_geoip_match_info *info = matchinfo; + struct geoip_info *node; + u_int8_t i; - 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 - "xt_geoip: unable to load '%c%c' into memory\n", - COUNTRY(info->cc[i])); - return 0; - } + for (i = 0; i < info->count; 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; - } + 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 + "xt_geoip: unable to load '%c%c' into memory\n", + COUNTRY(info->cc[i])); + return 0; + } - return 1; + /* 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; + } + + return 1; } -static void xt_geoip_mt_destroy(const struct xt_match *matcn, - void *matchinfo) +static void xt_geoip_mt_destroy(const struct xt_match *match, void *matchinfo) { - struct xt_geoip_match_info *info = matchinfo; - struct geoip_info *node; /* this keeps the code sexy */ - u_int8_t i; - - /* 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); + struct xt_geoip_match_info *info = matchinfo; + struct geoip_info *node; /* this keeps the code sexy */ + u_int8_t i; - /* 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 - "xt_geoip: What happened peejix ? What happened acidfu ?\n" - "xt_geoip: please report this bug to the maintainers\n"); - 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 + "xt_geoip: What happened peejix ? What happened acidfu ?\n" + "xt_geoip: please report this bug to the maintainers\n"); + return; } static struct xt_match xt_geoip_match __read_mostly = { - .family = AF_INET, - .name = "geoip", - .match = &xt_geoip_mt, - .checkentry = &xt_geoip_mt_checkentry, - .destroy = &xt_geoip_mt_destroy, - .matchsize = sizeof(struct xt_geoip_match_info), - .me = THIS_MODULE, + .family = AF_INET, + .name = "geoip", + .match = xt_geoip_mt, + .checkentry = xt_geoip_mt_checkentry, + .destroy = xt_geoip_mt_destroy, + .matchsize = sizeof(struct xt_geoip_match_info), + .me = THIS_MODULE, }; static int __init xt_geoip_mt_init(void) { - return xt_register_match(&xt_geoip_match); + return xt_register_match(&xt_geoip_match); } static void __exit xt_geoip_mt_fini(void) { - xt_unregister_match(&xt_geoip_match); + xt_unregister_match(&xt_geoip_match); } module_init(xt_geoip_mt_init); diff --git a/extensions/xt_geoip.h b/extensions/xt_geoip.h index 8169f6d..e99bfd7 100644 --- a/extensions/xt_geoip.h +++ b/extensions/xt_geoip.h @@ -1,5 +1,5 @@ /* ipt_geoip.h header file for libipt_geoip.c and ipt_geoip.c - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -10,8 +10,8 @@ * Samuel Jean * Nicolas Bouliane */ -#ifndef _XT_GEOIP_H -#define _XT_GEOIP_H +#ifndef _LINUX_NETFILTER_XT_GEOIP_H +#define _LINUX_NETFILTER_XT_GEOIP_H 1 #define XT_GEOIP_SRC 0x01 /* Perform check on Source IP */ #define XT_GEOIP_DST 0x02 /* Perform check on Destination IP */ @@ -20,29 +20,28 @@ #define XT_GEOIP_MAX 15 /* Maximum of countries */ struct geoip_subnet { - u_int32_t begin; - u_int32_t end; + 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 geoip_subnet *subnets; + u_int32_t count; + u_int32_t ref; + u_int16_t cc; + struct geoip_info *next; + struct geoip_info *prev; }; struct xt_geoip_match_info { - u_int8_t flags; - u_int8_t count; - u_int16_t cc[XT_GEOIP_MAX]; + u_int8_t flags; + u_int8_t count; + u_int16_t cc[XT_GEOIP_MAX]; - /* Used internally by the kernel */ - struct geoip_info *mem[XT_GEOIP_MAX]; + /* Used internally by the kernel */ + struct geoip_info *mem[XT_GEOIP_MAX]; }; #define COUNTRY(cc) (cc >> 8), (cc & 0x00FF) -#endif - +#endif /* _LINUX_NETFILTER_XT_GEOIP_H */ From d480ea2b1f556bff97483d3d8e32a90854525931 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 14:12:40 +0100 Subject: [PATCH 05/18] geoip: sort #include list --- extensions/libxt_geoip.c | 8 +++----- extensions/xt_geoip.c | 13 ++++++------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/extensions/libxt_geoip.c b/extensions/libxt_geoip.c index 38e7660..44a5a4b 100644 --- a/extensions/libxt_geoip.c +++ b/extensions/libxt_geoip.c @@ -12,18 +12,16 @@ * Samuel Jean * Nicolas Bouliane */ - +#include +#include #include #include #include #include -#include #include +#include #include #include -#include -#include - #include #include "xt_geoip.h" diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index ecb38f7..8666838 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -8,16 +8,15 @@ * Copyright (c) 2004, 2005, 2006, 2007, 2008 * Samuel Jean & Nicolas Bouliane */ -#include -#include -#include -#include #include +#include +#include #include -#include -#include - +#include +#include #include +#include +#include #include "xt_geoip.h" #include "compat_xtables.h" From df063ab61c256f6c3508fd4c6665b07943315fff Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 14:11:26 +0100 Subject: [PATCH 06/18] geoip: add missing kfree in error path --- extensions/xt_geoip.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 8666838..5e393a7 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -35,12 +35,16 @@ static struct geoip_info *add_node(struct geoip_info *memcpy) struct geoip_subnet *s; - if ((p == NULL) || (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0)) + if (p == NULL) return NULL; + if (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0) + goto free_p; s = 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; + if (s == NULL) + goto free_p; + if (copy_from_user(s, p->subnets, p->count * sizeof(struct geoip_subnet)) != 0) + goto free_s; spin_lock_bh(&geoip_lock); @@ -54,6 +58,11 @@ static struct geoip_info *add_node(struct geoip_info *memcpy) spin_unlock_bh(&geoip_lock); return p; + free_s: + kfree(s); + free_p: + kfree(p); + return NULL; } static void remove_node(struct geoip_info *p) From 93c7d0ac4702cd9d30a5bd6a6f7c85797076c619 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 14:37:37 +0100 Subject: [PATCH 07/18] geoip: lock timing correctness find_node: The reference count needs to be increased while the lock is held. Otherwise, the node may disappear right after the lock was released and increase was attempted, leading to an oops. remove_node: The reference count needs to be checked while the lock is held. Otherwise, the node may be used in the match function or returned from find_node while it has a zero refcount. --- extensions/xt_geoip.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 5e393a7..5989276 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -65,9 +65,13 @@ static struct geoip_info *add_node(struct geoip_info *memcpy) return NULL; } -static void remove_node(struct geoip_info *p) +static void geoip_try_remove_node(struct geoip_info *p) { spin_lock_bh(&geoip_lock); + if (!atomic_dec_and_test((atomic_t *)&p->ref)) { + spin_unlock_bh(&geoip_lock); + return; + } if (p->next) { /* Am I following a node ? */ p->next->prev = p->prev; @@ -99,6 +103,7 @@ static struct geoip_info *find_node(u_int16_t cc) while (p) { if (p->cc == cc) { + atomic_inc((atomic_t *)&p->ref); spin_unlock_bh(&geoip_lock); return p; } @@ -150,10 +155,8 @@ static bool xt_geoip_mt_checkentry(const char *table, const void *entry, u_int8_t i; 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 + node = find_node(info->cc[i]); + if (node == NULL) if ((node = add_node(info->mem[i])) == NULL) { printk(KERN_ERR "xt_geoip: unable to load '%c%c' into memory\n", @@ -185,12 +188,9 @@ static void xt_geoip_mt_destroy(const struct xt_match *match, void *matchinfo) 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); + geoip_try_remove_node(node); } else /* Something strange happened. There's no memory allocated for this From 8c58a61f52da2e9c6c3ae925c9b3cd8a1299bd6c Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 15:36:59 +0100 Subject: [PATCH 08/18] geoip: address comparison is inclusive subnet is somewhat a wrong term, geoip actually uses ipranges. Either way, the comparison needs to be >= and <= instead of > <. --- extensions/xt_geoip.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 5989276..0b15b83 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -137,7 +137,8 @@ static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, } for (j = 0; j < node->count; j++) - if ((ip > node->subnets[j].begin) && (ip < node->subnets[j].end)) { + if (ip >= node->subnets[j].begin && + ip <= node->subnets[j].end) { spin_unlock_bh(&geoip_lock); return (info->flags & XT_GEOIP_INV) ? 0 : 1; } From 848484c08cb4ad161074262994410387585259ff Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 22:34:00 +0100 Subject: [PATCH 09/18] geoip: use a binary search to replace the current linear one Certain countries have lots (around 10000) of IP address ranges (US,GB,DE,...). The current linear search is really bad: No firewall: 3000 packets transmitted, 3000 received, 0% packet loss, time 1992ms Testing against the countries with top 50 IP ranges: 3000 packets transmitted, 3000 received, 0% packet loss, time 8998ms With binary search: 3000 packets transmitted, 3000 received, 0% packet loss, time 2358ms --- extensions/xt_geoip.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 0b15b83..9c080a7 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -113,6 +113,25 @@ static struct geoip_info *find_node(u_int16_t cc) return NULL; } +static bool geoip_bsearch(const struct geoip_subnet *range, + uint32_t addr, int lo, int hi) +{ + int mid; + + if (hi < lo) + return false; + mid = (lo + hi) / 2; + if (range[mid].begin <= addr && addr <= range[mid].end) + return true; + if (range[mid].begin > addr) + return geoip_bsearch(range, addr, lo, mid - 1); + else if (range[mid].end < addr) + return geoip_bsearch(range, addr, mid + 1, hi); + + WARN_ON(true); + return false; +} + static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const struct xt_match *match, const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) @@ -120,7 +139,7 @@ static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, const struct xt_geoip_match_info *info = matchinfo; const struct geoip_info *node; /* This keeps the code sexy */ const struct iphdr *iph = ip_hdr(skb); - u_int32_t ip, i, j; + uint32_t ip, i; if (info->flags & XT_GEOIP_SRC) ip = ntohl(iph->saddr); @@ -136,12 +155,10 @@ static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, 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 & XT_GEOIP_INV) ? 0 : 1; - } + if (geoip_bsearch(node->subnets, ip, 0, node->count)) { + spin_unlock_bh(&geoip_lock); + return (info->flags & XT_GEOIP_INV) ? 0 : 1; + } } spin_unlock_bh(&geoip_lock); From 65eeb7f1f69e575f4b1ba203290a19b23a0708f2 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Mon, 17 Mar 2008 14:16:14 +0100 Subject: [PATCH 10/18] geoip: use vmalloc due to potential list size The subnet list may become really large (United States: ~15000 entries), which means a use of roughly 120 KB, and kmalloc may fail to find a contiguous block in physical memory. Virtual contiguity is enough, so use vmalloc/vfree. vfree may not be called within a spin_lock_bh area, so release the lock first, it is safe to do so. --- extensions/xt_geoip.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 9c080a7..17768cc 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,7 @@ static struct geoip_info *add_node(struct geoip_info *memcpy) if (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0) goto free_p; - s = kmalloc(p->count * sizeof(struct geoip_subnet), GFP_KERNEL); + s = vmalloc(p->count * sizeof(struct geoip_subnet)); if (s == NULL) goto free_p; if (copy_from_user(s, p->subnets, p->count * sizeof(struct geoip_subnet)) != 0) @@ -59,7 +60,7 @@ static struct geoip_info *add_node(struct geoip_info *memcpy) spin_unlock_bh(&geoip_lock); return p; free_s: - kfree(s); + vfree(s); free_p: kfree(p); return NULL; @@ -88,11 +89,9 @@ static void geoip_try_remove_node(struct geoip_info *p) /* 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); + vfree(p->subnets); + kfree(p); return; } From fd5321c7d8d19be183e79f639f9fe28c5bfb4733 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Mar 2008 02:31:57 +0100 Subject: [PATCH 11/18] geoip: split user/kernel-visible parts of struct geoip_info --- extensions/libxt_geoip.c | 10 ++++---- extensions/xt_geoip.c | 49 ++++++++++++++++++++++++++-------------- extensions/xt_geoip.h | 14 ++++++++---- 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/extensions/libxt_geoip.c b/extensions/libxt_geoip.c index 44a5a4b..c093a28 100644 --- a/extensions/libxt_geoip.c +++ b/extensions/libxt_geoip.c @@ -122,11 +122,11 @@ get_country_subnets(u_int16_t cc, u_int32_t *count) return subnets; } -static struct geoip_info * +static struct geoip_country_user * load_geoip_cc(u_int16_t cc) { - static struct geoip_info *ginfo; - ginfo = malloc(sizeof(struct geoip_info)); + struct geoip_country_user *ginfo; + ginfo = malloc(sizeof(struct geoip_country_user)); if (!ginfo) return NULL; @@ -175,7 +175,7 @@ check_geoip_cc(char *cc, u_int16_t cc_used[], u_int8_t count) /* 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) +parse_geoip_cc(const char *ccstr, u_int16_t *cc, union geoip_country_group *mem) { char *buffer, *cp, *next; u_int8_t i, count = 0; @@ -192,7 +192,7 @@ parse_geoip_cc(const char *ccstr, u_int16_t *cc, struct geoip_info **mem) if (next) *next++ = '\0'; if ((cctmp = check_geoip_cc(cp, cc, count)) != 0) { - if ((mem[count++] = load_geoip_cc(cctmp)) == NULL) + if ((mem[count++].user = load_geoip_cc(cctmp)) == NULL) exit_error(OTHER_PROBLEM, "geoip: insufficient memory available"); cc[count-1] = cctmp; diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 17768cc..dcca0f6 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -27,24 +27,39 @@ MODULE_AUTHOR("Samuel Jean"); MODULE_DESCRIPTION("xtables module for geoip match"); MODULE_ALIAS("ipt_geoip"); -struct geoip_info *head = NULL; +struct geoip_country_kernel { + struct geoip_subnet *subnets; + u_int32_t count; + u_int32_t ref; + u_int16_t cc; + struct geoip_country_kernel *next; + struct geoip_country_kernel *prev; +}; + +struct geoip_country_kernel *head = NULL; static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED; -static struct geoip_info *add_node(struct geoip_info *memcpy) +static struct geoip_country_kernel * +geoip_add_node(const struct geoip_country_user __user *umem_ptr) { - struct geoip_info *p = kmalloc(sizeof(struct geoip_info), GFP_KERNEL); - + struct geoip_country_user umem; + struct geoip_country_kernel *p; struct geoip_subnet *s; + if (copy_from_user(&umem, umem_ptr, sizeof(umem)) != 0) + return NULL; + + p = kmalloc(sizeof(struct geoip_country_kernel), GFP_KERNEL); if (p == NULL) return NULL; - if (copy_from_user(p, memcpy, sizeof(struct geoip_info)) != 0) - goto free_p; + + p->count = umem.count; + p->cc = umem.cc; s = vmalloc(p->count * sizeof(struct geoip_subnet)); if (s == NULL) goto free_p; - if (copy_from_user(s, p->subnets, p->count * sizeof(struct geoip_subnet)) != 0) + if (copy_from_user(s, umem.subnets, p->count * sizeof(struct geoip_subnet)) != 0) goto free_s; spin_lock_bh(&geoip_lock); @@ -66,7 +81,7 @@ static struct geoip_info *add_node(struct geoip_info *memcpy) return NULL; } -static void geoip_try_remove_node(struct geoip_info *p) +static void geoip_try_remove_node(struct geoip_country_kernel *p) { spin_lock_bh(&geoip_lock); if (!atomic_dec_and_test((atomic_t *)&p->ref)) { @@ -95,9 +110,9 @@ static void geoip_try_remove_node(struct geoip_info *p) return; } -static struct geoip_info *find_node(u_int16_t cc) +static struct geoip_country_kernel *find_node(u_int16_t cc) { - struct geoip_info *p = head; + struct geoip_country_kernel *p = head; spin_lock_bh(&geoip_lock); while (p) { @@ -136,7 +151,7 @@ static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop) { const struct xt_geoip_match_info *info = matchinfo; - const struct geoip_info *node; /* This keeps the code sexy */ + const struct geoip_country_kernel *node; const struct iphdr *iph = ip_hdr(skb); uint32_t ip, i; @@ -147,7 +162,7 @@ static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, spin_lock_bh(&geoip_lock); for (i = 0; i < info->count; i++) { - if ((node = info->mem[i]) == NULL) { + if ((node = info->mem[i].kernel) == NULL) { printk(KERN_ERR "xt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n", COUNTRY(info->cc[i])); @@ -168,13 +183,13 @@ static bool xt_geoip_mt_checkentry(const char *table, const void *entry, const struct xt_match *match, void *matchinfo, unsigned int hook_mask) { struct xt_geoip_match_info *info = matchinfo; - struct geoip_info *node; + struct geoip_country_kernel *node; u_int8_t i; for (i = 0; i < info->count; i++) { node = find_node(info->cc[i]); if (node == NULL) - if ((node = add_node(info->mem[i])) == NULL) { + if ((node = geoip_add_node(info->mem[i].user)) == NULL) { printk(KERN_ERR "xt_geoip: unable to load '%c%c' into memory\n", COUNTRY(info->cc[i])); @@ -186,7 +201,7 @@ static bool xt_geoip_mt_checkentry(const char *table, const void *entry, * This avoids searching for a node in the match() and * destroy() functions. */ - info->mem[i] = node; + info->mem[i].kernel = node; } return 1; @@ -195,7 +210,7 @@ static bool xt_geoip_mt_checkentry(const char *table, const void *entry, static void xt_geoip_mt_destroy(const struct xt_match *match, void *matchinfo) { struct xt_geoip_match_info *info = matchinfo; - struct geoip_info *node; /* this keeps the code sexy */ + struct geoip_country_kernel *node; u_int8_t i; /* This entry has been removed from the table so @@ -204,7 +219,7 @@ static void xt_geoip_mt_destroy(const struct xt_match *match, void *matchinfo) */ for (i = 0; i < info->count; i++) - if ((node = info->mem[i]) != NULL) { + if ((node = info->mem[i].kernel) != NULL) { /* Free up some memory if that node isn't used * anymore. */ geoip_try_remove_node(node); diff --git a/extensions/xt_geoip.h b/extensions/xt_geoip.h index e99bfd7..f6b7330 100644 --- a/extensions/xt_geoip.h +++ b/extensions/xt_geoip.h @@ -24,13 +24,17 @@ struct geoip_subnet { u_int32_t end; }; -struct geoip_info { +struct geoip_country_user { 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 geoip_country_kernel; + +union geoip_country_group { + struct geoip_country_user *user; + struct geoip_country_kernel *kernel; }; struct xt_geoip_match_info { @@ -39,7 +43,7 @@ struct xt_geoip_match_info { u_int16_t cc[XT_GEOIP_MAX]; /* Used internally by the kernel */ - struct geoip_info *mem[XT_GEOIP_MAX]; + union geoip_country_group mem[XT_GEOIP_MAX]; }; #define COUNTRY(cc) (cc >> 8), (cc & 0x00FF) From 7aae90da5a891936a768c2d011bf0c297df9221d Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Mar 2008 03:12:07 +0100 Subject: [PATCH 12/18] geoip: use local-portable aligned_u64 pointer values A 64-bit kernel will interpret the pointer with 64 bits width, while a 32-bit userspace filled in only 32 of it, leaving the other 32 undefined. This must be avoided. --- extensions/libxt_geoip.c | 14 ++++++-------- extensions/xt_geoip.c | 5 +++-- extensions/xt_geoip.h | 4 ++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/extensions/libxt_geoip.c b/extensions/libxt_geoip.c index c093a28..5a99f4e 100644 --- a/extensions/libxt_geoip.c +++ b/extensions/libxt_geoip.c @@ -56,7 +56,7 @@ struct geoip_index { u_int32_t offset; } __attribute__ ((packed)); -struct geoip_subnet * +static struct geoip_subnet * get_country_subnets(u_int16_t cc, u_int32_t *count) { FILE *ixfd, *dbfd; @@ -122,8 +122,7 @@ get_country_subnets(u_int16_t cc, u_int32_t *count) return subnets; } -static struct geoip_country_user * -load_geoip_cc(u_int16_t cc) +static struct geoip_country_user *geoip_load_cc(unsigned short cc) { struct geoip_country_user *ginfo; ginfo = malloc(sizeof(struct geoip_country_user)); @@ -131,7 +130,7 @@ load_geoip_cc(u_int16_t cc) if (!ginfo) return NULL; - ginfo->subnets = get_country_subnets(cc, &ginfo->count); + ginfo->subnets = (unsigned long)get_country_subnets(cc, &ginfo->count); ginfo->cc = cc; return ginfo; @@ -173,9 +172,8 @@ check_geoip_cc(char *cc, u_int16_t cc_used[], u_int8_t count) return cc_int16; } -/* Based on libipt_multiport.c parsing code. */ -static u_int8_t -parse_geoip_cc(const char *ccstr, u_int16_t *cc, union geoip_country_group *mem) +static unsigned int parse_geoip_cc(const char *ccstr, uint16_t *cc, + union geoip_country_group *mem) { char *buffer, *cp, *next; u_int8_t i, count = 0; @@ -192,7 +190,7 @@ parse_geoip_cc(const char *ccstr, u_int16_t *cc, union geoip_country_group *mem) if (next) *next++ = '\0'; if ((cctmp = check_geoip_cc(cp, cc, count)) != 0) { - if ((mem[count++].user = load_geoip_cc(cctmp)) == NULL) + if ((mem[count++].user = (unsigned long)geoip_load_cc(cctmp)) == 0) exit_error(OTHER_PROBLEM, "geoip: insufficient memory available"); cc[count-1] = cctmp; diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index dcca0f6..caf9309 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -59,7 +59,8 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr) s = vmalloc(p->count * sizeof(struct geoip_subnet)); if (s == NULL) goto free_p; - if (copy_from_user(s, umem.subnets, p->count * sizeof(struct geoip_subnet)) != 0) + if (copy_from_user(s, (const void __user *)(unsigned long)umem.subnets, + p->count * sizeof(struct geoip_subnet)) != 0) goto free_s; spin_lock_bh(&geoip_lock); @@ -189,7 +190,7 @@ static bool xt_geoip_mt_checkentry(const char *table, const void *entry, for (i = 0; i < info->count; i++) { node = find_node(info->cc[i]); if (node == NULL) - if ((node = geoip_add_node(info->mem[i].user)) == NULL) { + if ((node = geoip_add_node((const void __user *)(unsigned long)info->mem[i].user)) == NULL) { printk(KERN_ERR "xt_geoip: unable to load '%c%c' into memory\n", COUNTRY(info->cc[i])); diff --git a/extensions/xt_geoip.h b/extensions/xt_geoip.h index f6b7330..1f9801e 100644 --- a/extensions/xt_geoip.h +++ b/extensions/xt_geoip.h @@ -25,7 +25,7 @@ struct geoip_subnet { }; struct geoip_country_user { - struct geoip_subnet *subnets; + aligned_u64 subnets; u_int32_t count; u_int16_t cc; }; @@ -33,7 +33,7 @@ struct geoip_country_user { struct geoip_country_kernel; union geoip_country_group { - struct geoip_country_user *user; + aligned_u64 user; struct geoip_country_kernel *kernel; }; From e45cb21ad6bb0867865dd84f8c525e99bfee02ee Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Mar 2008 03:28:08 +0100 Subject: [PATCH 13/18] geoip: use struct list_head instead of self-cooked list --- extensions/xt_geoip.c | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index caf9309..adcc7a0 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -10,6 +10,7 @@ */ #include #include +#include #include #include #include @@ -28,15 +29,14 @@ MODULE_DESCRIPTION("xtables module for geoip match"); MODULE_ALIAS("ipt_geoip"); struct geoip_country_kernel { + struct list_head list; struct geoip_subnet *subnets; u_int32_t count; u_int32_t ref; u_int16_t cc; - struct geoip_country_kernel *next; - struct geoip_country_kernel *prev; }; -struct geoip_country_kernel *head = NULL; +static LIST_HEAD(geoip_head); static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED; static struct geoip_country_kernel * @@ -67,11 +67,8 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr) p->subnets = s; p->ref = 1; - p->next = head; - p->prev = NULL; - if (p->next) - p->next->prev = p; - head = p; + INIT_LIST_HEAD(&p->list); + list_add_tail(&p->list, &geoip_head); spin_unlock_bh(&geoip_lock); return p; @@ -90,17 +87,7 @@ static void geoip_try_remove_node(struct geoip_country_kernel *p) return; } - 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 */ + list_del(&p->list); /* So now am unlinked or the only one alive, right ? * What are you waiting ? Free up some memory! @@ -113,17 +100,16 @@ static void geoip_try_remove_node(struct geoip_country_kernel *p) static struct geoip_country_kernel *find_node(u_int16_t cc) { - struct geoip_country_kernel *p = head; + struct geoip_country_kernel *p; spin_lock_bh(&geoip_lock); - while (p) { + list_for_each_entry(p, &geoip_head, list) if (p->cc == cc) { atomic_inc((atomic_t *)&p->ref); spin_unlock_bh(&geoip_lock); return p; } - p = p->next; - } + spin_unlock_bh(&geoip_lock); return NULL; } From 000d813171b4948aa827c2e051f3bcdf592a61c0 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Mar 2008 03:31:07 +0100 Subject: [PATCH 14/18] geoip: use real atomic_t and remove casts from uint32_t --- extensions/xt_geoip.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index adcc7a0..9cc1879 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -31,8 +31,8 @@ MODULE_ALIAS("ipt_geoip"); struct geoip_country_kernel { struct list_head list; struct geoip_subnet *subnets; + atomic_t ref; u_int32_t count; - u_int32_t ref; u_int16_t cc; }; @@ -66,7 +66,7 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr) spin_lock_bh(&geoip_lock); p->subnets = s; - p->ref = 1; + atomic_set(&p->ref, 1); INIT_LIST_HEAD(&p->list); list_add_tail(&p->list, &geoip_head); @@ -82,7 +82,7 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr) static void geoip_try_remove_node(struct geoip_country_kernel *p) { spin_lock_bh(&geoip_lock); - if (!atomic_dec_and_test((atomic_t *)&p->ref)) { + if (!atomic_dec_and_test(&p->ref)) { spin_unlock_bh(&geoip_lock); return; } @@ -105,7 +105,7 @@ static struct geoip_country_kernel *find_node(u_int16_t cc) list_for_each_entry(p, &geoip_head, list) if (p->cc == cc) { - atomic_inc((atomic_t *)&p->ref); + atomic_inc(&p->ref); spin_unlock_bh(&geoip_lock); return p; } From 52a0ed7f152d7404adb2c2c935d01e7048e1d39b Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Mar 2008 03:46:56 +0100 Subject: [PATCH 15/18] geoip: use rcu to reduce time spinlocks are held spin_lock_bh does not look safe (only disables preempt on current CPU?). Change to spin_lock, that also avoids the management overhead of spin_lock_bh. to spin_lock to avoid management overhead. Use rcu in match and destroy function. --- extensions/xt_geoip.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 9cc1879..8d4d58f 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,7 @@ struct geoip_country_kernel { }; static LIST_HEAD(geoip_head); -static spinlock_t geoip_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(geoip_lock); static struct geoip_country_kernel * geoip_add_node(const struct geoip_country_user __user *umem_ptr) @@ -63,15 +64,16 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr) p->count * sizeof(struct geoip_subnet)) != 0) goto free_s; - spin_lock_bh(&geoip_lock); - p->subnets = s; atomic_set(&p->ref, 1); INIT_LIST_HEAD(&p->list); - list_add_tail(&p->list, &geoip_head); - spin_unlock_bh(&geoip_lock); + spin_lock(&geoip_lock); + list_add_tail_rcu(&p->list, &geoip_head); + spin_unlock(&geoip_lock); + return p; + free_s: vfree(s); free_p: @@ -81,18 +83,19 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr) static void geoip_try_remove_node(struct geoip_country_kernel *p) { - spin_lock_bh(&geoip_lock); + spin_lock(&geoip_lock); if (!atomic_dec_and_test(&p->ref)) { - spin_unlock_bh(&geoip_lock); + spin_unlock(&geoip_lock); return; } - list_del(&p->list); - /* So now am unlinked or the only one alive, right ? * What are you waiting ? Free up some memory! */ - spin_unlock_bh(&geoip_lock); + list_del_rcu(&p->list); + spin_unlock(&geoip_lock); + + synchronize_rcu(); vfree(p->subnets); kfree(p); return; @@ -101,16 +104,16 @@ static void geoip_try_remove_node(struct geoip_country_kernel *p) static struct geoip_country_kernel *find_node(u_int16_t cc) { struct geoip_country_kernel *p; - spin_lock_bh(&geoip_lock); + spin_lock(&geoip_lock); - list_for_each_entry(p, &geoip_head, list) + list_for_each_entry_rcu(p, &geoip_head, list) if (p->cc == cc) { atomic_inc(&p->ref); - spin_unlock_bh(&geoip_lock); + spin_unlock(&geoip_lock); return p; } - spin_unlock_bh(&geoip_lock); + spin_unlock(&geoip_lock); return NULL; } @@ -147,7 +150,7 @@ static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, else ip = ntohl(iph->daddr); - spin_lock_bh(&geoip_lock); + rcu_read_lock(); for (i = 0; i < info->count; i++) { if ((node = info->mem[i].kernel) == NULL) { printk(KERN_ERR "xt_geoip: what the hell ?? '%c%c' isn't loaded into memory... skip it!\n", @@ -157,12 +160,12 @@ static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, } if (geoip_bsearch(node->subnets, ip, 0, node->count)) { - spin_unlock_bh(&geoip_lock); + rcu_read_unlock(); return (info->flags & XT_GEOIP_INV) ? 0 : 1; } } - spin_unlock_bh(&geoip_lock); + rcu_read_unlock(); return (info->flags & XT_GEOIP_INV) ? 1 : 0; } From f4c4208e75a4fac42f008ce93779ddce3c90809d Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Mar 2008 04:02:07 +0100 Subject: [PATCH 16/18] geoip: use appropriate and normal types For the header file, we need __u32 and so on because they are exported to userspace and rather constitute a kernel header. Use normal types instead of uintXX_t in the main code. --- extensions/xt_geoip.c | 23 +++++++++++------------ extensions/xt_geoip.h | 15 ++++++++------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/extensions/xt_geoip.c b/extensions/xt_geoip.c index 8d4d58f..8b37b17 100644 --- a/extensions/xt_geoip.c +++ b/extensions/xt_geoip.c @@ -33,8 +33,8 @@ struct geoip_country_kernel { struct list_head list; struct geoip_subnet *subnets; atomic_t ref; - u_int32_t count; - u_int16_t cc; + unsigned int count; + unsigned short cc; }; static LIST_HEAD(geoip_head); @@ -98,10 +98,9 @@ static void geoip_try_remove_node(struct geoip_country_kernel *p) synchronize_rcu(); vfree(p->subnets); kfree(p); - return; } -static struct geoip_country_kernel *find_node(u_int16_t cc) +static struct geoip_country_kernel *find_node(unsigned short cc) { struct geoip_country_kernel *p; spin_lock(&geoip_lock); @@ -143,7 +142,8 @@ static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, const struct xt_geoip_match_info *info = matchinfo; const struct geoip_country_kernel *node; const struct iphdr *iph = ip_hdr(skb); - uint32_t ip, i; + unsigned int i; + uint32_t ip; if (info->flags & XT_GEOIP_SRC) ip = ntohl(iph->saddr); @@ -161,12 +161,12 @@ static bool xt_geoip_mt(const struct sk_buff *skb, const struct net_device *in, if (geoip_bsearch(node->subnets, ip, 0, node->count)) { rcu_read_unlock(); - return (info->flags & XT_GEOIP_INV) ? 0 : 1; + return !(info->flags & XT_GEOIP_INV); } } rcu_read_unlock(); - return (info->flags & XT_GEOIP_INV) ? 1 : 0; + return info->flags & XT_GEOIP_INV; } static bool xt_geoip_mt_checkentry(const char *table, const void *entry, @@ -174,7 +174,7 @@ static bool xt_geoip_mt_checkentry(const char *table, const void *entry, { struct xt_geoip_match_info *info = matchinfo; struct geoip_country_kernel *node; - u_int8_t i; + unsigned int i; for (i = 0; i < info->count; i++) { node = find_node(info->cc[i]); @@ -183,7 +183,7 @@ static bool xt_geoip_mt_checkentry(const char *table, const void *entry, printk(KERN_ERR "xt_geoip: unable to load '%c%c' into memory\n", COUNTRY(info->cc[i])); - return 0; + return false; } /* Overwrite the now-useless pointer info->mem[i] with @@ -194,14 +194,14 @@ static bool xt_geoip_mt_checkentry(const char *table, const void *entry, info->mem[i].kernel = node; } - return 1; + return true; } static void xt_geoip_mt_destroy(const struct xt_match *match, void *matchinfo) { struct xt_geoip_match_info *info = matchinfo; struct geoip_country_kernel *node; - u_int8_t i; + unsigned int i; /* This entry has been removed from the table so * decrease the refcount of all countries it is @@ -220,7 +220,6 @@ static void xt_geoip_mt_destroy(const struct xt_match *match, void *matchinfo) printk(KERN_ERR "xt_geoip: What happened peejix ? What happened acidfu ?\n" "xt_geoip: please report this bug to the maintainers\n"); - return; } static struct xt_match xt_geoip_match __read_mostly = { diff --git a/extensions/xt_geoip.h b/extensions/xt_geoip.h index 1f9801e..4188a71 100644 --- a/extensions/xt_geoip.h +++ b/extensions/xt_geoip.h @@ -19,15 +19,16 @@ #define XT_GEOIP_MAX 15 /* Maximum of countries */ +/* Yup, an address range will be passed in with host-order */ struct geoip_subnet { - u_int32_t begin; - u_int32_t end; + __u32 begin; + __u32 end; }; struct geoip_country_user { aligned_u64 subnets; - u_int32_t count; - u_int16_t cc; + __u32 count; + __u16 cc; }; struct geoip_country_kernel; @@ -38,9 +39,9 @@ union geoip_country_group { }; struct xt_geoip_match_info { - u_int8_t flags; - u_int8_t count; - u_int16_t cc[XT_GEOIP_MAX]; + __u8 flags; + __u8 count; + __u16 cc[XT_GEOIP_MAX]; /* Used internally by the kernel */ union geoip_country_group mem[XT_GEOIP_MAX]; From 5d431b45f134545263bdc8f90c0a27780bb06d00 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Tue, 18 Mar 2008 15:47:10 +0100 Subject: [PATCH 17/18] geoip: use simpler, preprocessed integer vector lists and fix endian issue The old database format was in unknown byteorder -- if you run the converter program yourself, you got a host order file, but if you downloaded the preprocessed DB file (geoipdb.bin), you got a little-endian file. Use a new database format. Instead of having an index and a DB file, do away with the index and let the filesystem do the indexing, using one file per country. Also access the database files with a known endianess type. The converter script now produces two distinct variants (especially needed for IA-64). All of this reduces the touched code by half. --- extensions/libxt_geoip.c | 97 ++++++++++++-------------------------- extensions/libxt_geoip.man | 13 ++--- 2 files changed, 38 insertions(+), 72 deletions(-) diff --git a/extensions/libxt_geoip.c b/extensions/libxt_geoip.c index 5a99f4e..c1aeb0c 100644 --- a/extensions/libxt_geoip.c +++ b/extensions/libxt_geoip.c @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -24,6 +26,7 @@ #include #include #include "xt_geoip.h" +#define GEOIP_DB_DIR "/var/geoip" static void geoip_help(void) { @@ -51,78 +54,40 @@ static struct option geoip_opts[] = { { 0 }, }; -struct geoip_index { - u_int16_t cc; - u_int32_t offset; -} __attribute__ ((packed)); - -static struct geoip_subnet * -get_country_subnets(u_int16_t cc, u_int32_t *count) +static struct geoip_subnet *geoip_get_subnets(const char *code, uint32_t *count) { - FILE *ixfd, *dbfd; struct geoip_subnet *subnets; - struct geoip_index *index; - struct stat buf; + struct stat sb; + char buf[256]; + int fd; - size_t idxsz; - u_int16_t i; + /* Use simple integer vector files */ +#if __BYTE_ORDER == _BIG_ENDIAN + snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/BE/%s.iv0", code); +#else + snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/LE/%s.iv0", code); +#endif - 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: cannot open geoip's database index file"); + if ((fd = open(buf, O_RDONLY)) < 0) { + fprintf(stderr, "Could not open %s: %s\n", buf, strerror(errno)); + exit_error(OTHER_PROBLEM, "Could not read geoip database"); } - stat("/var/geoip/geoipdb.idx", &buf); - idxsz = buf.st_size/sizeof(struct geoip_index); - 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: 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: 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 = malloc(db_nsubnets * sizeof(struct geoip_subnet)); - - if (!subnets) - exit_error(OTHER_PROBLEM, - "geoip: insufficient memory available"); - - fread(subnets, db_nsubnets * sizeof(struct geoip_subnet), 1, dbfd); - - fclose(dbfd); - free(index); - *count = db_nsubnets; + fstat(fd, &sb); + if (sb.st_size % sizeof(struct geoip_subnet) != 0) + exit_error(OTHER_PROBLEM, "Database file %s seems to be " + "corrupted", buf); + subnets = malloc(sb.st_size); + if (subnets == NULL) + exit_error(OTHER_PROBLEM, "geoip: insufficient memory"); + read(fd, subnets, sb.st_size); + close(fd); + *count = sb.st_size / sizeof(struct geoip_subnet); return subnets; } - -static struct geoip_country_user *geoip_load_cc(unsigned short cc) + +static struct geoip_country_user *geoip_load_cc(const char *code, + unsigned short cc) { struct geoip_country_user *ginfo; ginfo = malloc(sizeof(struct geoip_country_user)); @@ -130,7 +95,7 @@ static struct geoip_country_user *geoip_load_cc(unsigned short cc) if (!ginfo) return NULL; - ginfo->subnets = (unsigned long)get_country_subnets(cc, &ginfo->count); + ginfo->subnets = (unsigned long)geoip_get_subnets(code, &ginfo->count); ginfo->cc = cc; return ginfo; @@ -190,7 +155,7 @@ static unsigned int parse_geoip_cc(const char *ccstr, uint16_t *cc, if (next) *next++ = '\0'; if ((cctmp = check_geoip_cc(cp, cc, count)) != 0) { - if ((mem[count++].user = (unsigned long)geoip_load_cc(cctmp)) == 0) + if ((mem[count++].user = (unsigned long)geoip_load_cc(cp, cctmp)) == 0) exit_error(OTHER_PROBLEM, "geoip: insufficient memory available"); cc[count-1] = cctmp; diff --git a/extensions/libxt_geoip.man b/extensions/libxt_geoip.man index bc3b517..5f7ca04 100644 --- a/extensions/libxt_geoip.man +++ b/extensions/libxt_geoip.man @@ -1,15 +1,16 @@ Match a packet by its source or destination country. .TP -[\fB!\fR] \fB--src-cc\fR, \fB--source-country \fIcountry\fR[\fB,\fIcountry\fB,\fIcountry\fB,\fI...\fR] +[\fB!\fP] \fB--src-cc\fP, \fB--source-country\fP \fIcountry\fP[\fB,\fP\fIcountry\fP\fB...\fP] Match packet coming from (one of) the specified country(ies) .TP -[\fB!\fR] \fB--dst-cc\fR, \fB--destination-country \fIcountry\fR[\fB,\fIcountry\fB,\fIcountry\fB,\fI...\fR] +[\fB!\fP] \fB--dst-cc\fP, \fB--destination-country\fP \fIcountry\fP[\fB,\fP\fIcountry\fP\fB...\fP] Match packet going to (one of) the specified country(ies) .TP NOTE: The country is inputed by its ISO3166 code. .P -The only extra files you need is a binary db (geoipdb.bin) & its index file (geoipdb.idx). -Both files are generated from a countries & subnets database with the csv2bin tool, -available at http://people.netfilter.org/peejix/geoip/. Both files MUST also be moved in /var/geoip/ -as the shared library is statically looking for that pathname (ex.: /var/geoip/geoipdb.bin). +The extra files you will need is the binary database files. They are generated +from a country-subnet database with the geoip_csv_iv0.pl tool, available at +http://jengelh.hopto.org/files/geoip/ . The files MUST be moved to /var/geoip/ +as the shared library is statically looking for this pathname (e.g. +/var/geoip/LE/de.iv0). From cd7c8fc4fac6e2d7bd5944111c696a47a5b702b0 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Fri, 21 Mar 2008 06:11:22 +0100 Subject: [PATCH 18/18] geoip: minor cleanups in help, opts and logic --- extensions/libxt_geoip.c | 28 ++++++++++++---------------- extensions/xt_geoip.h | 10 ++++++---- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/extensions/libxt_geoip.c b/extensions/libxt_geoip.c index c1aeb0c..1783a85 100644 --- a/extensions/libxt_geoip.c +++ b/extensions/libxt_geoip.c @@ -31,27 +31,23 @@ static void geoip_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" + "geoip match options:\n" + "[!] --src-cc, --source-country country[,country...]\n" + " Match packet coming from (one of) the specified country(ies)\n" + "[!] --dst-cc, --destination-country country[,country...]\n" + " Match packet going to (one of) 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" + "NOTE: The country is inputed by its ISO3166 code.\n" "\n" - " NOTE: The country is inputed by its ISO3166 code.\n" - "\n" - "\n", XTABLES_VERSION ); } static struct option geoip_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 }, + {.name = "dst-cc", .has_arg = true, .val = '2'}, + {.name = "destination-country", .has_arg = true, .val = '2'}, + {.name = "src-cc", .has_arg = true, .val = '1'}, + {.name = "source-country", .has_arg = true, .val = '1'}, + {NULL}, }; static struct geoip_subnet *geoip_get_subnets(const char *code, uint32_t *count) @@ -127,7 +123,7 @@ check_geoip_cc(char *cc, u_int16_t cc_used[], u_int8_t count) * going to change someday, this whole * match will need to be rewritten, anyway. * - SJ */ - cc_int16 = (cc[0]<<8) + cc[1]; + cc_int16 = (cc[0] << 8) | cc[1]; // Check for presence of value in cc_used for (i = 0; i < count; i++) diff --git a/extensions/xt_geoip.h b/extensions/xt_geoip.h index 4188a71..291c108 100644 --- a/extensions/xt_geoip.h +++ b/extensions/xt_geoip.h @@ -13,11 +13,13 @@ #ifndef _LINUX_NETFILTER_XT_GEOIP_H #define _LINUX_NETFILTER_XT_GEOIP_H 1 -#define XT_GEOIP_SRC 0x01 /* Perform check on Source IP */ -#define XT_GEOIP_DST 0x02 /* Perform check on Destination IP */ -#define XT_GEOIP_INV 0x04 /* Negate the condition */ +enum { + XT_GEOIP_SRC = 1 << 0, /* Perform check on Source IP */ + XT_GEOIP_DST = 1 << 1, /* Perform check on Destination IP */ + XT_GEOIP_INV = 1 << 2, /* Negate the condition */ -#define XT_GEOIP_MAX 15 /* Maximum of countries */ + XT_GEOIP_MAX = 15, /* Maximum of countries */ +}; /* Yup, an address range will be passed in with host-order */ struct geoip_subnet {