mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-07 05:05:12 +02:00
Merge reworked geoip extension
This commit is contained in:
@@ -11,6 +11,7 @@ obj-${build_ECHO} += xt_ECHO.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
|
||||
|
278
extensions/libxt_geoip.c
Normal file
278
extensions/libxt_geoip.c
Normal file
@@ -0,0 +1,278 @@
|
||||
/* 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 <peejix@people.netfilter.org>
|
||||
* Nicolas Bouliane <peejix@people.netfilter.org>
|
||||
*/
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <ctype.h>
|
||||
#include <endian.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <xtables.h>
|
||||
#include "xt_geoip.h"
|
||||
#define GEOIP_DB_DIR "/var/geoip"
|
||||
|
||||
static void geoip_help(void)
|
||||
{
|
||||
printf (
|
||||
"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"
|
||||
"NOTE: The country is inputed by its ISO3166 code.\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static struct option geoip_opts[] = {
|
||||
{.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)
|
||||
{
|
||||
struct geoip_subnet *subnets;
|
||||
struct stat sb;
|
||||
char buf[256];
|
||||
int fd;
|
||||
|
||||
/* 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
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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(const char *code,
|
||||
unsigned short cc)
|
||||
{
|
||||
struct geoip_country_user *ginfo;
|
||||
ginfo = malloc(sizeof(struct geoip_country_user));
|
||||
|
||||
if (!ginfo)
|
||||
return NULL;
|
||||
|
||||
ginfo->subnets = (unsigned long)geoip_get_subnets(code, &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;
|
||||
}
|
||||
|
||||
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;
|
||||
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++].user = (unsigned long)geoip_load_cc(cp, cctmp)) == 0)
|
||||
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;
|
||||
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,
|
||||
};
|
||||
|
||||
static void _init(void)
|
||||
{
|
||||
xtables_register_match(&geoip_match);
|
||||
}
|
16
extensions/libxt_geoip.man
Normal file
16
extensions/libxt_geoip.man
Normal file
@@ -0,0 +1,16 @@
|
||||
Match a packet by its source or destination country.
|
||||
.TP
|
||||
[\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!\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 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).
|
246
extensions/xt_geoip.c
Normal file
246
extensions/xt_geoip.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/* 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 <linux/ip.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/uaccess.h>
|
||||
#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_country_kernel {
|
||||
struct list_head list;
|
||||
struct geoip_subnet *subnets;
|
||||
atomic_t ref;
|
||||
unsigned int count;
|
||||
unsigned short cc;
|
||||
};
|
||||
|
||||
static LIST_HEAD(geoip_head);
|
||||
static DEFINE_SPINLOCK(geoip_lock);
|
||||
|
||||
static struct geoip_country_kernel *
|
||||
geoip_add_node(const struct geoip_country_user __user *umem_ptr)
|
||||
{
|
||||
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;
|
||||
|
||||
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, (const void __user *)(unsigned long)umem.subnets,
|
||||
p->count * sizeof(struct geoip_subnet)) != 0)
|
||||
goto free_s;
|
||||
|
||||
p->subnets = s;
|
||||
atomic_set(&p->ref, 1);
|
||||
INIT_LIST_HEAD(&p->list);
|
||||
|
||||
spin_lock(&geoip_lock);
|
||||
list_add_tail_rcu(&p->list, &geoip_head);
|
||||
spin_unlock(&geoip_lock);
|
||||
|
||||
return p;
|
||||
|
||||
free_s:
|
||||
vfree(s);
|
||||
free_p:
|
||||
kfree(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void geoip_try_remove_node(struct geoip_country_kernel *p)
|
||||
{
|
||||
spin_lock(&geoip_lock);
|
||||
if (!atomic_dec_and_test(&p->ref)) {
|
||||
spin_unlock(&geoip_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* So now am unlinked or the only one alive, right ?
|
||||
* What are you waiting ? Free up some memory!
|
||||
*/
|
||||
list_del_rcu(&p->list);
|
||||
spin_unlock(&geoip_lock);
|
||||
|
||||
synchronize_rcu();
|
||||
vfree(p->subnets);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
static struct geoip_country_kernel *find_node(unsigned short cc)
|
||||
{
|
||||
struct geoip_country_kernel *p;
|
||||
spin_lock(&geoip_lock);
|
||||
|
||||
list_for_each_entry_rcu(p, &geoip_head, list)
|
||||
if (p->cc == cc) {
|
||||
atomic_inc(&p->ref);
|
||||
spin_unlock(&geoip_lock);
|
||||
return p;
|
||||
}
|
||||
|
||||
spin_unlock(&geoip_lock);
|
||||
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)
|
||||
{
|
||||
const struct xt_geoip_match_info *info = matchinfo;
|
||||
const struct geoip_country_kernel *node;
|
||||
const struct iphdr *iph = ip_hdr(skb);
|
||||
unsigned int i;
|
||||
uint32_t ip;
|
||||
|
||||
if (info->flags & XT_GEOIP_SRC)
|
||||
ip = ntohl(iph->saddr);
|
||||
else
|
||||
ip = ntohl(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)) {
|
||||
rcu_read_unlock();
|
||||
return !(info->flags & XT_GEOIP_INV);
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
return info->flags & XT_GEOIP_INV;
|
||||
}
|
||||
|
||||
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_country_kernel *node;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < info->count; i++) {
|
||||
node = find_node(info->cc[i]);
|
||||
if (node == 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]));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 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].kernel = node;
|
||||
}
|
||||
|
||||
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;
|
||||
unsigned int 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].kernel) != NULL) {
|
||||
/* Free up some memory if that node isn't used
|
||||
* anymore. */
|
||||
geoip_try_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");
|
||||
}
|
||||
|
||||
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);
|
54
extensions/xt_geoip.h
Normal file
54
extensions/xt_geoip.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* 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 _LINUX_NETFILTER_XT_GEOIP_H
|
||||
#define _LINUX_NETFILTER_XT_GEOIP_H 1
|
||||
|
||||
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 */
|
||||
|
||||
XT_GEOIP_MAX = 15, /* Maximum of countries */
|
||||
};
|
||||
|
||||
/* Yup, an address range will be passed in with host-order */
|
||||
struct geoip_subnet {
|
||||
__u32 begin;
|
||||
__u32 end;
|
||||
};
|
||||
|
||||
struct geoip_country_user {
|
||||
aligned_u64 subnets;
|
||||
__u32 count;
|
||||
__u16 cc;
|
||||
};
|
||||
|
||||
struct geoip_country_kernel;
|
||||
|
||||
union geoip_country_group {
|
||||
aligned_u64 user;
|
||||
struct geoip_country_kernel *kernel;
|
||||
};
|
||||
|
||||
struct xt_geoip_match_info {
|
||||
__u8 flags;
|
||||
__u8 count;
|
||||
__u16 cc[XT_GEOIP_MAX];
|
||||
|
||||
/* Used internally by the kernel */
|
||||
union geoip_country_group mem[XT_GEOIP_MAX];
|
||||
};
|
||||
|
||||
#define COUNTRY(cc) (cc >> 8), (cc & 0x00FF)
|
||||
|
||||
#endif /* _LINUX_NETFILTER_XT_GEOIP_H */
|
Reference in New Issue
Block a user