mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-07 21:25:12 +02:00
Merge branch 'geoip'
This commit is contained in:
@@ -6,6 +6,7 @@ Fixes:
|
||||
- build: fix objdir builds for ipset-5 (xt-a specific)
|
||||
- xt_LOGMARK: fix detection of untracked connection for Linux >= 2.6.36
|
||||
Enhancements:
|
||||
- IPv6 support for xt_geoip
|
||||
- Update to ipset 5.3
|
||||
* make IPv4 and IPv6 address handling similar
|
||||
* show correct line numbers in restore output for parser errors
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* "geoip" match extension for iptables
|
||||
* Copyright © Samuel Jean <peejix [at] people netfilter org>, 2004 - 2008
|
||||
* Copyright © Nicolas Bouliane <acidfu [at] people netfilter org>, 2004 - 2008
|
||||
* Jan Engelhardt <jengelh [at] medozas de>, 2008
|
||||
* Jan Engelhardt <jengelh [at] medozas de>, 2008-2011
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License; either
|
||||
@@ -49,19 +49,28 @@ static struct option geoip_opts[] = {
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static struct geoip_subnet *geoip_get_subnets(const char *code, uint32_t *count)
|
||||
static void *
|
||||
geoip_get_subnets(const char *code, uint32_t *count, uint8_t nfproto)
|
||||
{
|
||||
struct geoip_subnet *subnets;
|
||||
void *subnets;
|
||||
struct stat sb;
|
||||
char buf[256];
|
||||
int fd;
|
||||
|
||||
/* Use simple integer vector files */
|
||||
if (nfproto == NFPROTO_IPV6) {
|
||||
#if __BYTE_ORDER == _BIG_ENDIAN
|
||||
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/BE/%s.iv0", code);
|
||||
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/BE/%s.iv6", code);
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/LE/%s.iv0", code);
|
||||
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/LE/%s.iv6", code);
|
||||
#endif
|
||||
} else {
|
||||
#if __BYTE_ORDER == _BIG_ENDIAN
|
||||
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/BE/%s.iv4", code);
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), GEOIP_DB_DIR "/LE/%s.iv4", code);
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((fd = open(buf, O_RDONLY)) < 0) {
|
||||
fprintf(stderr, "Could not open %s: %s\n", buf, strerror(errno));
|
||||
@@ -69,20 +78,31 @@ static struct geoip_subnet *geoip_get_subnets(const char *code, uint32_t *count)
|
||||
}
|
||||
|
||||
fstat(fd, &sb);
|
||||
if (sb.st_size % sizeof(struct geoip_subnet) != 0)
|
||||
xtables_error(OTHER_PROBLEM, "Database file %s seems to be "
|
||||
"corrupted", buf);
|
||||
*count = sb.st_size;
|
||||
switch (nfproto) {
|
||||
case NFPROTO_IPV6:
|
||||
if (sb.st_size % sizeof(struct geoip_subnet6) != 0)
|
||||
xtables_error(OTHER_PROBLEM,
|
||||
"Database file %s seems to be corrupted", buf);
|
||||
*count /= sizeof(struct geoip_subnet6);
|
||||
break;
|
||||
case NFPROTO_IPV4:
|
||||
if (sb.st_size % sizeof(struct geoip_subnet4) != 0)
|
||||
xtables_error(OTHER_PROBLEM,
|
||||
"Database file %s seems to be corrupted", buf);
|
||||
*count /= sizeof(struct geoip_subnet4);
|
||||
break;
|
||||
}
|
||||
subnets = malloc(sb.st_size);
|
||||
if (subnets == NULL)
|
||||
xtables_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(const char *code,
|
||||
unsigned short cc)
|
||||
unsigned short cc, uint8_t nfproto)
|
||||
{
|
||||
struct geoip_country_user *ginfo;
|
||||
ginfo = malloc(sizeof(struct geoip_country_user));
|
||||
@@ -90,7 +110,8 @@ static struct geoip_country_user *geoip_load_cc(const char *code,
|
||||
if (!ginfo)
|
||||
return NULL;
|
||||
|
||||
ginfo->subnets = (unsigned long)geoip_get_subnets(code, &ginfo->count);
|
||||
ginfo->subnets = (unsigned long)geoip_get_subnets(code,
|
||||
&ginfo->count, nfproto);
|
||||
ginfo->cc = cc;
|
||||
|
||||
return ginfo;
|
||||
@@ -133,7 +154,7 @@ check_geoip_cc(char *cc, u_int16_t cc_used[], u_int8_t count)
|
||||
}
|
||||
|
||||
static unsigned int parse_geoip_cc(const char *ccstr, uint16_t *cc,
|
||||
union geoip_country_group *mem)
|
||||
union geoip_country_group *mem, uint8_t nfproto)
|
||||
{
|
||||
char *buffer, *cp, *next;
|
||||
u_int8_t i, count = 0;
|
||||
@@ -150,7 +171,8 @@ 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(cp, cctmp)) == 0)
|
||||
if ((mem[count++].user =
|
||||
(unsigned long)geoip_load_cc(cp, cctmp, nfproto)) == 0)
|
||||
xtables_error(OTHER_PROBLEM,
|
||||
"geoip: insufficient memory available");
|
||||
cc[count-1] = cctmp;
|
||||
@@ -169,11 +191,9 @@ static unsigned int parse_geoip_cc(const char *ccstr, uint16_t *cc,
|
||||
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, bool invert, unsigned int *flags,
|
||||
const char *arg, struct xt_geoip_match_info *info, uint8_t nfproto)
|
||||
{
|
||||
struct xt_geoip_match_info *info = (void *)(*match)->data;
|
||||
|
||||
switch (c) {
|
||||
case '1':
|
||||
if (*flags & (XT_GEOIP_SRC | XT_GEOIP_DST))
|
||||
@@ -185,7 +205,8 @@ static int geoip_parse(int c, char **argv, int invert, unsigned int *flags,
|
||||
if (invert)
|
||||
*flags |= XT_GEOIP_INV;
|
||||
|
||||
info->count = parse_geoip_cc(argv[optind-1], info->cc, info->mem);
|
||||
info->count = parse_geoip_cc(arg, info->cc, info->mem,
|
||||
nfproto);
|
||||
info->flags = *flags;
|
||||
return true;
|
||||
|
||||
@@ -199,7 +220,8 @@ static int geoip_parse(int c, char **argv, int invert, unsigned int *flags,
|
||||
if (invert)
|
||||
*flags |= XT_GEOIP_INV;
|
||||
|
||||
info->count = parse_geoip_cc(argv[optind-1], info->cc, info->mem);
|
||||
info->count = parse_geoip_cc(arg, info->cc, info->mem,
|
||||
nfproto);
|
||||
info->flags = *flags;
|
||||
return true;
|
||||
}
|
||||
@@ -207,6 +229,20 @@ static int geoip_parse(int c, char **argv, int invert, unsigned int *flags,
|
||||
return false;
|
||||
}
|
||||
|
||||
static int geoip_parse6(int c, char **argv, int invert, unsigned int *flags,
|
||||
const void *entry, struct xt_entry_match **match)
|
||||
{
|
||||
return geoip_parse(c, invert, flags, optarg,
|
||||
(void *)(*match)->data, NFPROTO_IPV6);
|
||||
}
|
||||
|
||||
static int geoip_parse4(int c, char **argv, int invert, unsigned int *flags,
|
||||
const void *entry, struct xt_entry_match **match)
|
||||
{
|
||||
return geoip_parse(c, invert, flags, optarg,
|
||||
(void *)(*match)->data, NFPROTO_IPV4);
|
||||
}
|
||||
|
||||
static void
|
||||
geoip_final_check(unsigned int flags)
|
||||
{
|
||||
@@ -259,22 +295,39 @@ geoip_save(const void *ip, const struct xt_entry_match *match)
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
static struct xtables_match geoip_match = {
|
||||
.family = NFPROTO_IPV4,
|
||||
.name = "geoip",
|
||||
.revision = 1,
|
||||
.version = XTABLES_VERSION,
|
||||
.size = XT_ALIGN(sizeof(struct xt_geoip_match_info)),
|
||||
.userspacesize = 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,
|
||||
static struct xtables_match geoip_match[] = {
|
||||
{
|
||||
.family = NFPROTO_IPV6,
|
||||
.name = "geoip",
|
||||
.revision = 1,
|
||||
.version = XTABLES_VERSION,
|
||||
.size = XT_ALIGN(sizeof(struct xt_geoip_match_info)),
|
||||
.userspacesize = offsetof(struct xt_geoip_match_info, mem),
|
||||
.help = geoip_help,
|
||||
.parse = geoip_parse6,
|
||||
.final_check = geoip_final_check,
|
||||
.print = geoip_print,
|
||||
.save = geoip_save,
|
||||
.extra_opts = geoip_opts,
|
||||
},
|
||||
{
|
||||
.family = NFPROTO_IPV4,
|
||||
.name = "geoip",
|
||||
.revision = 1,
|
||||
.version = XTABLES_VERSION,
|
||||
.size = XT_ALIGN(sizeof(struct xt_geoip_match_info)),
|
||||
.userspacesize = offsetof(struct xt_geoip_match_info, mem),
|
||||
.help = geoip_help,
|
||||
.parse = geoip_parse4,
|
||||
.final_check = geoip_final_check,
|
||||
.print = geoip_print,
|
||||
.save = geoip_save,
|
||||
.extra_opts = geoip_opts,
|
||||
},
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void geoip_mt_ldr(void)
|
||||
{
|
||||
xtables_register_match(&geoip_match);
|
||||
xtables_register_matches(geoip_match,
|
||||
sizeof(geoip_match) / sizeof(*geoip_match));
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@
|
||||
* Samuel Jean & Nicolas Bouliane
|
||||
*/
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
@@ -27,25 +28,49 @@ MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Nicolas Bouliane");
|
||||
MODULE_AUTHOR("Samuel Jean");
|
||||
MODULE_DESCRIPTION("xtables module for geoip match");
|
||||
MODULE_ALIAS("ip6t_geoip");
|
||||
MODULE_ALIAS("ipt_geoip");
|
||||
|
||||
enum geoip_proto {
|
||||
GEOIPROTO_IPV6,
|
||||
GEOIPROTO_IPV4,
|
||||
__GEOIPROTO_MAX,
|
||||
};
|
||||
|
||||
/**
|
||||
* @list: anchor point for geoip_head
|
||||
* @subnets: packed ordered list of ranges (either v6 or v4)
|
||||
* @count: number of ranges
|
||||
* @cc: country code
|
||||
*/
|
||||
struct geoip_country_kernel {
|
||||
struct list_head list;
|
||||
struct geoip_subnet *subnets;
|
||||
void *subnets;
|
||||
atomic_t ref;
|
||||
unsigned int count;
|
||||
unsigned short cc;
|
||||
};
|
||||
|
||||
static LIST_HEAD(geoip_head);
|
||||
static struct list_head geoip_head[__GEOIPROTO_MAX];
|
||||
static DEFINE_SPINLOCK(geoip_lock);
|
||||
|
||||
static const enum geoip_proto nfp2geo[] = {
|
||||
[NFPROTO_IPV6] = GEOIPROTO_IPV6,
|
||||
[NFPROTO_IPV4] = GEOIPROTO_IPV4,
|
||||
};
|
||||
static const size_t geoproto_size[] = {
|
||||
[GEOIPROTO_IPV6] = sizeof(struct geoip_subnet6),
|
||||
[GEOIPROTO_IPV4] = sizeof(struct geoip_subnet4),
|
||||
};
|
||||
|
||||
static struct geoip_country_kernel *
|
||||
geoip_add_node(const struct geoip_country_user __user *umem_ptr)
|
||||
geoip_add_node(const struct geoip_country_user __user *umem_ptr,
|
||||
enum geoip_proto proto)
|
||||
{
|
||||
struct geoip_country_user umem;
|
||||
struct geoip_country_kernel *p;
|
||||
struct geoip_subnet *s;
|
||||
size_t size;
|
||||
void *subnet;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&umem, umem_ptr, sizeof(umem)) != 0)
|
||||
@@ -57,30 +82,30 @@ geoip_add_node(const struct geoip_country_user __user *umem_ptr)
|
||||
|
||||
p->count = umem.count;
|
||||
p->cc = umem.cc;
|
||||
|
||||
s = vmalloc(p->count * sizeof(struct geoip_subnet));
|
||||
if (s == NULL) {
|
||||
size = p->count * geoproto_size[proto];
|
||||
subnet = vmalloc(size);
|
||||
if (subnet == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto free_p;
|
||||
}
|
||||
if (copy_from_user(s, (const void __user *)(unsigned long)umem.subnets,
|
||||
p->count * sizeof(struct geoip_subnet)) != 0) {
|
||||
if (copy_from_user(subnet,
|
||||
(const void __user *)(unsigned long)umem.subnets, size) != 0) {
|
||||
ret = -EFAULT;
|
||||
goto free_s;
|
||||
}
|
||||
|
||||
p->subnets = s;
|
||||
p->subnets = subnet;
|
||||
atomic_set(&p->ref, 1);
|
||||
INIT_LIST_HEAD(&p->list);
|
||||
|
||||
spin_lock(&geoip_lock);
|
||||
list_add_tail_rcu(&p->list, &geoip_head);
|
||||
list_add_tail_rcu(&p->list, &geoip_head[proto]);
|
||||
spin_unlock(&geoip_lock);
|
||||
|
||||
return p;
|
||||
|
||||
free_s:
|
||||
vfree(s);
|
||||
vfree(subnet);
|
||||
free_p:
|
||||
kfree(p);
|
||||
return ERR_PTR(ret);
|
||||
@@ -105,12 +130,13 @@ static void geoip_try_remove_node(struct geoip_country_kernel *p)
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
static struct geoip_country_kernel *find_node(unsigned short cc)
|
||||
static struct geoip_country_kernel *find_node(unsigned short cc,
|
||||
enum geoip_proto proto)
|
||||
{
|
||||
struct geoip_country_kernel *p;
|
||||
spin_lock(&geoip_lock);
|
||||
|
||||
list_for_each_entry_rcu(p, &geoip_head, list)
|
||||
list_for_each_entry_rcu(p, &geoip_head[proto], list)
|
||||
if (p->cc == cc) {
|
||||
atomic_inc(&p->ref);
|
||||
spin_unlock(&geoip_lock);
|
||||
@@ -121,7 +147,73 @@ static struct geoip_country_kernel *find_node(unsigned short cc)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool geoip_bsearch(const struct geoip_subnet *range,
|
||||
static inline int
|
||||
ipv6_cmp(const struct in6_addr *p, const struct in6_addr *q)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (p->s6_addr32[i] < q->s6_addr32[i])
|
||||
return -1;
|
||||
else if (p->s6_addr32[i] > q->s6_addr32[i])
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool geoip_bsearch6(const struct geoip_subnet6 *range,
|
||||
const struct in6_addr *addr, int lo, int hi)
|
||||
{
|
||||
int mid;
|
||||
|
||||
if (hi <= lo)
|
||||
return false;
|
||||
mid = (lo + hi) / 2;
|
||||
if (ipv6_cmp(&range[mid].begin, addr) <= 0 &&
|
||||
ipv6_cmp(addr, &range[mid].end) <= 0)
|
||||
return true;
|
||||
if (ipv6_cmp(&range[mid].begin, addr) > 0)
|
||||
return geoip_bsearch6(range, addr, lo, mid);
|
||||
else if (ipv6_cmp(&range[mid].end, addr) < 0)
|
||||
return geoip_bsearch6(range, addr, mid + 1, hi);
|
||||
|
||||
WARN_ON(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
xt_geoip_mt6(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
{
|
||||
const struct xt_geoip_match_info *info = par->matchinfo;
|
||||
const struct geoip_country_kernel *node;
|
||||
const struct ipv6hdr *iph = ipv6_hdr(skb);
|
||||
unsigned int i;
|
||||
struct in6_addr ip;
|
||||
|
||||
memcpy(&ip, (info->flags & XT_GEOIP_SRC) ? &iph->saddr : &iph->daddr,
|
||||
sizeof(ip));
|
||||
for (i = 0; i < 4; ++i)
|
||||
ip.s6_addr32[i] = ntohl(ip.s6_addr32[i]);
|
||||
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < info->count; i++) {
|
||||
if ((node = info->mem[i].kernel) == NULL) {
|
||||
pr_err("'%c%c' is not loaded into memory... skip it!\n",
|
||||
COUNTRY(info->cc[i]));
|
||||
continue;
|
||||
}
|
||||
if (geoip_bsearch6(node->subnets, &ip, 0, node->count)) {
|
||||
rcu_read_unlock();
|
||||
return !(info->flags & XT_GEOIP_INV);
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
return info->flags & XT_GEOIP_INV;
|
||||
}
|
||||
|
||||
static bool geoip_bsearch4(const struct geoip_subnet4 *range,
|
||||
uint32_t addr, int lo, int hi)
|
||||
{
|
||||
int mid;
|
||||
@@ -132,16 +224,16 @@ static bool geoip_bsearch(const struct geoip_subnet *range,
|
||||
if (range[mid].begin <= addr && addr <= range[mid].end)
|
||||
return true;
|
||||
if (range[mid].begin > addr)
|
||||
return geoip_bsearch(range, addr, lo, mid);
|
||||
return geoip_bsearch4(range, addr, lo, mid);
|
||||
else if (range[mid].end < addr)
|
||||
return geoip_bsearch(range, addr, mid + 1, hi);
|
||||
return geoip_bsearch4(range, addr, mid + 1, hi);
|
||||
|
||||
WARN_ON(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
xt_geoip_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
xt_geoip_mt4(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
{
|
||||
const struct xt_geoip_match_info *info = par->matchinfo;
|
||||
const struct geoip_country_kernel *node;
|
||||
@@ -149,21 +241,15 @@ xt_geoip_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
unsigned int i;
|
||||
uint32_t ip;
|
||||
|
||||
if (info->flags & XT_GEOIP_SRC)
|
||||
ip = ntohl(iph->saddr);
|
||||
else
|
||||
ip = ntohl(iph->daddr);
|
||||
|
||||
ip = ntohl((info->flags & XT_GEOIP_SRC) ? iph->saddr : iph->daddr);
|
||||
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",
|
||||
COUNTRY(info->cc[i]));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (geoip_bsearch(node->subnets, ip, 0, node->count)) {
|
||||
if (geoip_bsearch4(node->subnets, ip, 0, node->count)) {
|
||||
rcu_read_unlock();
|
||||
return !(info->flags & XT_GEOIP_INV);
|
||||
}
|
||||
@@ -180,9 +266,10 @@ static int xt_geoip_mt_checkentry(const struct xt_mtchk_param *par)
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < info->count; i++) {
|
||||
node = find_node(info->cc[i]);
|
||||
node = find_node(info->cc[i], nfp2geo[par->family]);
|
||||
if (node == NULL) {
|
||||
node = geoip_add_node((const void __user *)(unsigned long)info->mem[i].user);
|
||||
node = geoip_add_node((const void __user *)(unsigned long)info->mem[i].user,
|
||||
nfp2geo[par->family]);
|
||||
if (IS_ERR(node)) {
|
||||
printk(KERN_ERR
|
||||
"xt_geoip: unable to load '%c%c' into memory: %ld\n",
|
||||
@@ -227,25 +314,41 @@ static void xt_geoip_mt_destroy(const struct xt_mtdtor_param *par)
|
||||
"xt_geoip: please report this bug to the maintainers\n");
|
||||
}
|
||||
|
||||
static struct xt_match xt_geoip_match __read_mostly = {
|
||||
.name = "geoip",
|
||||
.revision = 1,
|
||||
.family = NFPROTO_IPV4,
|
||||
.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 struct xt_match xt_geoip_match[] __read_mostly = {
|
||||
{
|
||||
.name = "geoip",
|
||||
.revision = 1,
|
||||
.family = NFPROTO_IPV6,
|
||||
.match = xt_geoip_mt6,
|
||||
.checkentry = xt_geoip_mt_checkentry,
|
||||
.destroy = xt_geoip_mt_destroy,
|
||||
.matchsize = sizeof(struct xt_geoip_match_info),
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "geoip",
|
||||
.revision = 1,
|
||||
.family = NFPROTO_IPV4,
|
||||
.match = xt_geoip_mt4,
|
||||
.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);
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(geoip_head); ++i)
|
||||
INIT_LIST_HEAD(&geoip_head[i]);
|
||||
return xt_register_matches(xt_geoip_match, ARRAY_SIZE(xt_geoip_match));
|
||||
}
|
||||
|
||||
static void __exit xt_geoip_mt_fini(void)
|
||||
{
|
||||
xt_unregister_match(&xt_geoip_match);
|
||||
xt_unregister_matches(xt_geoip_match, ARRAY_SIZE(xt_geoip_match));
|
||||
}
|
||||
|
||||
module_init(xt_geoip_mt_init);
|
||||
|
@@ -22,11 +22,15 @@ enum {
|
||||
};
|
||||
|
||||
/* Yup, an address range will be passed in with host-order */
|
||||
struct geoip_subnet {
|
||||
struct geoip_subnet4 {
|
||||
__u32 begin;
|
||||
__u32 end;
|
||||
};
|
||||
|
||||
struct geoip_subnet6 {
|
||||
struct in6_addr begin, end;
|
||||
};
|
||||
|
||||
struct geoip_country_user {
|
||||
aligned_u64 subnets;
|
||||
__u32 count;
|
||||
@@ -36,7 +40,7 @@ struct geoip_country_user {
|
||||
struct geoip_country_kernel;
|
||||
|
||||
union geoip_country_group {
|
||||
aligned_u64 user;
|
||||
aligned_u64 user; /* struct geoip_country_user * */
|
||||
struct geoip_country_kernel *kernel;
|
||||
};
|
||||
|
||||
@@ -49,6 +53,6 @@ struct xt_geoip_match_info {
|
||||
union geoip_country_group mem[XT_GEOIP_MAX];
|
||||
};
|
||||
|
||||
#define COUNTRY(cc) (cc >> 8), (cc & 0x00FF)
|
||||
#define COUNTRY(cc) ((cc) >> 8), ((cc) & 0x00FF)
|
||||
|
||||
#endif /* _LINUX_NETFILTER_XT_GEOIP_H */
|
||||
|
@@ -1,3 +1,5 @@
|
||||
# -*- Makefile -*-
|
||||
|
||||
pkglibexec_SCRIPTS = geoip_build_db.pl geoip_download.sh
|
||||
pkglibexec_SCRIPTS = xt_geoip_build xt_geoip_dl
|
||||
|
||||
man1_MANS = xt_geoip_build.1 xt_geoip_dl.1
|
||||
|
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Converter for MaxMind CSV database to binary, for xt_geoip
|
||||
# Copyright © Jan Engelhardt <jengelh@medozas.de>, 2008
|
||||
#
|
||||
# Use -b argument to create big-endian tables.
|
||||
#
|
||||
use Getopt::Long;
|
||||
use IO::Handle;
|
||||
use Text::CSV_XS; # or trade for Text::CSV
|
||||
use strict;
|
||||
|
||||
my %country;
|
||||
my %names;
|
||||
my $csv = Text::CSV_XS->new({binary => 0, eol => $/}); # or Text::CSV
|
||||
my $mode = "VV";
|
||||
my $target_dir = ".";
|
||||
|
||||
&Getopt::Long::Configure(qw(bundling));
|
||||
&GetOptions(
|
||||
"D=s" => \$target_dir,
|
||||
"b" => sub { $mode = "NN"; },
|
||||
);
|
||||
|
||||
if (!-d $target_dir) {
|
||||
print STDERR "Target directory $target_dir does not exist.\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
while (my $row = $csv->getline(*ARGV)) {
|
||||
if (!defined($country{$row->[4]})) {
|
||||
$country{$row->[4]} = [];
|
||||
$names{$row->[4]} = $row->[5];
|
||||
}
|
||||
my $c = $country{$row->[4]};
|
||||
push(@$c, [$row->[2], $row->[3]]);
|
||||
if ($. % 4096 == 0) {
|
||||
print STDERR "\r\e[2K$. entries";
|
||||
}
|
||||
}
|
||||
|
||||
print STDERR "\r\e[2K$. entries total\n";
|
||||
|
||||
foreach my $iso_code (sort keys %country) {
|
||||
printf "%5u ranges for %s %s\n",
|
||||
scalar(@{$country{$iso_code}}),
|
||||
$iso_code, $names{$iso_code};
|
||||
|
||||
open(my $fh, "> $target_dir/".uc($iso_code).".iv0");
|
||||
foreach my $range (@{$country{$iso_code}}) {
|
||||
print $fh pack($mode, $range->[0], $range->[1]);
|
||||
}
|
||||
close $fh;
|
||||
}
|
143
geoip/xt_geoip_build
Executable file
143
geoip/xt_geoip_build
Executable file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/perl
|
||||
#
|
||||
# Converter for MaxMind CSV database to binary, for xt_geoip
|
||||
# Copyright © Jan Engelhardt <jengelh@medozas.de>, 2008-2011
|
||||
#
|
||||
# Use -b argument to create big-endian tables.
|
||||
#
|
||||
use Getopt::Long;
|
||||
use IO::Handle;
|
||||
use Text::CSV_XS; # or trade for Text::CSV
|
||||
use strict;
|
||||
|
||||
my $csv = Text::CSV_XS->new({
|
||||
allow_whitespace => 1,
|
||||
binary => 1,
|
||||
eol => $/,
|
||||
}); # or Text::CSV
|
||||
my $target_dir = ".";
|
||||
|
||||
&Getopt::Long::Configure(qw(bundling));
|
||||
&GetOptions(
|
||||
"D=s" => \$target_dir,
|
||||
);
|
||||
|
||||
if (!-d $target_dir) {
|
||||
print STDERR "Target directory $target_dir does not exist.\n";
|
||||
exit 1;
|
||||
}
|
||||
foreach (qw(LE BE)) {
|
||||
my $dir = "$target_dir/$_";
|
||||
if (!-e $dir && !mkdir($dir)) {
|
||||
print STDERR "Could not mkdir $dir: $!\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
&dump(&collect());
|
||||
|
||||
sub collect
|
||||
{
|
||||
my %country;
|
||||
|
||||
while (my $row = $csv->getline(*ARGV)) {
|
||||
if (!defined($country{$row->[4]})) {
|
||||
$country{$row->[4]} = {
|
||||
name => $row->[5],
|
||||
pool_v4 => [],
|
||||
pool_v6 => [],
|
||||
};
|
||||
}
|
||||
my $c = $country{$row->[4]};
|
||||
if ($row->[0] =~ /:/) {
|
||||
push(@{$c->{pool_v6}},
|
||||
[&ip6_pack($row->[0]), &ip6_pack($row->[1])]);
|
||||
} else {
|
||||
push(@{$c->{pool_v4}}, [$row->[2], $row->[3]]);
|
||||
}
|
||||
if ($. % 4096 == 0) {
|
||||
print STDERR "\r\e[2K$. entries";
|
||||
}
|
||||
}
|
||||
|
||||
print STDERR "\r\e[2K$. entries total\n";
|
||||
return \%country;
|
||||
}
|
||||
|
||||
sub dump
|
||||
{
|
||||
my $country = shift @_;
|
||||
|
||||
foreach my $iso_code (sort keys %$country) {
|
||||
&dump_one($iso_code, $country->{$iso_code});
|
||||
}
|
||||
}
|
||||
|
||||
sub dump_one
|
||||
{
|
||||
my($iso_code, $country) = @_;
|
||||
my($file, $fh_le, $fh_be);
|
||||
|
||||
printf "%5u IPv6 ranges for %s %s\n",
|
||||
scalar(@{$country->{pool_v6}}),
|
||||
$iso_code, $country->{name};
|
||||
|
||||
$file = "$target_dir/LE/".uc($iso_code).".iv6";
|
||||
if (!open($fh_le, "> $file")) {
|
||||
print STDERR "Error opening $file: $!\n";
|
||||
exit 1;
|
||||
}
|
||||
$file = "$target_dir/BE/".uc($iso_code).".iv6";
|
||||
if (!open($fh_be, "> $file")) {
|
||||
print STDERR "Error opening $file: $!\n";
|
||||
exit 1;
|
||||
}
|
||||
foreach my $range (@{$country->{pool_v6}}) {
|
||||
print $fh_be $range->[0], $range->[1];
|
||||
print $fh_le &ip6_swap($range->[0]), &ip6_swap($range->[1]);
|
||||
}
|
||||
close $fh_le;
|
||||
close $fh_be;
|
||||
|
||||
printf "%5u IPv4 ranges for %s %s\n",
|
||||
scalar(@{$country->{pool_v4}}),
|
||||
$iso_code, $country->{name};
|
||||
|
||||
$file = "$target_dir/LE/".uc($iso_code).".iv4";
|
||||
if (!open($fh_le, "> $file")) {
|
||||
print STDERR "Error opening $file: $!\n";
|
||||
exit 1;
|
||||
}
|
||||
$file = "$target_dir/BE/".uc($iso_code).".iv4";
|
||||
if (!open($fh_be, "> $file")) {
|
||||
print STDERR "Error opening $file: $!\n";
|
||||
exit 1;
|
||||
}
|
||||
foreach my $range (@{$country->{pool_v4}}) {
|
||||
print $fh_le pack("VV", $range->[0], $range->[1]);
|
||||
print $fh_be pack("NN", $range->[0], $range->[1]);
|
||||
}
|
||||
close $fh_le;
|
||||
close $fh_be;
|
||||
}
|
||||
|
||||
sub ip6_pack
|
||||
{
|
||||
my $addr = shift @_;
|
||||
$addr =~ s{::}{:!:};
|
||||
my @addr = split(/:/, $addr);
|
||||
my @e = (0) x 8;
|
||||
foreach (@addr) {
|
||||
if ($_ eq "!") {
|
||||
$_ = join(':', @e[0..(8-scalar(@addr))]);
|
||||
}
|
||||
}
|
||||
@addr = split(/:/, join(':', @addr));
|
||||
$_ = hex($_) foreach @addr;
|
||||
return pack("n*", @addr);
|
||||
}
|
||||
|
||||
sub ip6_swap
|
||||
{
|
||||
return pack("V*", unpack("N*", shift @_));
|
||||
}
|
35
geoip/xt_geoip_build.1
Normal file
35
geoip/xt_geoip_build.1
Normal file
@@ -0,0 +1,35 @@
|
||||
.TH xt_geoip_build 1 "2010-12-17" "xtables-addons" "xtables-addons"
|
||||
.SH Name
|
||||
.PP
|
||||
xt_geoip_build \(em convert GeoIP.csv to packed format for xt_geoip
|
||||
.SH Syntax
|
||||
.PP
|
||||
\fI/usr/libexec/xt_geoip/\fP\fBxt_geoip_build\fP [\fB\-D\fP
|
||||
\fItarget_dir\fP] [\fIfile\fP...]
|
||||
.SH Description
|
||||
.PP
|
||||
xt_geoip_build is used to build packed raw representations of the range
|
||||
database that the xt_geoip module relies on. Since kernel memory is precious,
|
||||
much of the preprocessing is done in userspace by this very building tool. One
|
||||
file is produced for each country, so that no more addresses than needed are
|
||||
required to be loaded into memory. The ranges in the packed database files are
|
||||
also ordered, as xt_geoip relies on this property for its bisection approach to
|
||||
work.
|
||||
.PP
|
||||
Input is processed from the listed files, or if none is given, from stdin.
|
||||
.PP
|
||||
Since the script is usually installed to the libexec directory of the
|
||||
xtables-addons package and this is outside $PATH (on purpose), invoking the
|
||||
script requires it to be called with a path.
|
||||
.PP Options
|
||||
.TP
|
||||
\fB\-D\fP \fItarget_dir\fP
|
||||
Specify a target directory into which the files are to be put.
|
||||
.SH Application
|
||||
.PP
|
||||
Shell commands to build the databases and put them to where they are expected:
|
||||
.PP
|
||||
xt_geoip_build -D /usr/share/xt_geoip
|
||||
.SH See also
|
||||
.PP
|
||||
xt_geoip_dl(1)
|
21
geoip/xt_geoip_dl.1
Normal file
21
geoip/xt_geoip_dl.1
Normal file
@@ -0,0 +1,21 @@
|
||||
.TH xt_geoip_dl 1 "2010-12-17" "xtables-addons" "xtables-addons"
|
||||
.SH Name
|
||||
.PP
|
||||
xt_geoip_dl \(em download GeoIP database files
|
||||
.SH Syntax
|
||||
.PP
|
||||
\fI/usr/libexec/xt_geoip/\fP\fBxt_geoip_dl\fP
|
||||
.SH Description
|
||||
.PP
|
||||
Downloads and unpacks the MaxMind GeoIP Country Lite databases for IPv4 and
|
||||
IPv6 and unpacks them to the current directory.
|
||||
.PP
|
||||
Since the script is usually installed to the libexec directory of the
|
||||
xtables-addons package and this is outside $PATH (on purpose), invoking the
|
||||
script requires it to be called with a path.
|
||||
.SH Options
|
||||
.PP
|
||||
None.
|
||||
.SH See also
|
||||
.PP
|
||||
xt_geoip_build(1)
|
Reference in New Issue
Block a user