Merge branch 'geoip'

This commit is contained in:
Jan Engelhardt
2011-02-02 04:48:40 +01:00
10 changed files with 438 additions and 130 deletions

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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

View File

@@ -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
View 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
View 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
View 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)