Import of xt_DNETMAP

This commit is contained in:
Marek Kierdelewicz
2011-01-07 00:02:59 +01:00
committed by Jan Engelhardt
parent 0168f8e8a2
commit c5d4dd0bcf
9 changed files with 1367 additions and 0 deletions

View File

@@ -10,6 +10,7 @@ obj-${build_CHAOS} += xt_CHAOS.o
obj-${build_CHECKSUM} += xt_CHECKSUM.o obj-${build_CHECKSUM} += xt_CHECKSUM.o
obj-${build_DELUDE} += xt_DELUDE.o obj-${build_DELUDE} += xt_DELUDE.o
obj-${build_DHCPMAC} += xt_DHCPMAC.o obj-${build_DHCPMAC} += xt_DHCPMAC.o
obj-${build_DNETMAP} += xt_DNETMAP.o
obj-${build_ECHO} += xt_ECHO.o obj-${build_ECHO} += xt_ECHO.o
obj-${build_IPMARK} += xt_IPMARK.o obj-${build_IPMARK} += xt_IPMARK.o
obj-${build_LOGMARK} += xt_LOGMARK.o obj-${build_LOGMARK} += xt_LOGMARK.o

View File

@@ -5,6 +5,7 @@ obj-${build_CHAOS} += libxt_CHAOS.so
obj-${build_CHECKSUM} += libxt_CHECKSUM.so obj-${build_CHECKSUM} += libxt_CHECKSUM.so
obj-${build_DELUDE} += libxt_DELUDE.so obj-${build_DELUDE} += libxt_DELUDE.so
obj-${build_DHCPMAC} += libxt_DHCPMAC.so libxt_dhcpmac.so obj-${build_DHCPMAC} += libxt_DHCPMAC.so libxt_dhcpmac.so
obj-${build_DNETMAP} += libxt_DNETMAP.so
obj-${build_ECHO} += libxt_ECHO.so obj-${build_ECHO} += libxt_ECHO.so
obj-${build_IPMARK} += libxt_IPMARK.so obj-${build_IPMARK} += libxt_IPMARK.so
obj-${build_LOGMARK} += libxt_LOGMARK.so obj-${build_LOGMARK} += libxt_LOGMARK.so

245
extensions/libxt_DNETMAP.c Normal file
View File

@@ -0,0 +1,245 @@
/* Shared library add-on to iptables to add DNETMAP support.
* (C) 2010 Marek Kierdelewicz <marek@koba.pl>
*
* uses some code from libipt_NETMAP by:
* Svenning Soerensen <svenning@post5.tele.dk>
*/
#include <stdio.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <xtables.h>
#include <net/netfilter/nf_nat.h>
#include "xt_DNETMAP.h"
#define MODULENAME "DNETMAP"
static const struct option DNETMAP_opts[] = {
{"prefix", 1, NULL, 'p'},
{"reuse", 0, NULL, 'r'},
{"ttl", 1, NULL, 't'},
{.name = NULL}
};
static void DNETMAP_help(void)
{
printf(MODULENAME " target options:\n"
" --%s address[/mask]\n"
" Network subnet to map to. If not specified, all existing prefixes are used.\n"
" --%s\n"
" Reuse entry for given prenat-ip from any prefix despite bindings ttl < 0.\n"
" --%s seconds\n"
" Regenerate bindings ttl value to seconds. If negative value is specified,\n"
" bindings ttl is kept unchanged. If not specified then default ttl value (600s)\n"
" is used.\n\n",
DNETMAP_opts[0].name, DNETMAP_opts[1].name,
DNETMAP_opts[2].name);
}
static u_int32_t bits2netmask(int bits)
{
u_int32_t netmask, bm;
if (bits >= 32 || bits < 0)
return ~0;
for (netmask = 0, bm = 0x80000000; bits; bits--, bm >>= 1)
netmask |= bm;
return htonl(netmask);
}
static int netmask2bits(u_int32_t netmask)
{
u_int32_t bm;
int bits;
netmask = ntohl(netmask);
for (bits = 0, bm = 0x80000000; netmask & bm; netmask <<= 1)
bits++;
if (netmask)
return -1; /* holes in netmask */
return bits;
}
static void DNETMAP_init(struct xt_entry_target *t)
{
struct xt_DNETMAP_tginfo *tginfo = (struct xt_DNETMAP_tginfo *)&t->data;
struct nf_nat_multi_range *mr = &tginfo->prefix;
/* Actually, it's 0, but it's ignored at the moment. */
mr->rangesize = 1;
tginfo->ttl = 0;
tginfo->flags = 0;
}
/* Parses network address */
static void parse_prefix(char *arg, struct nf_nat_range *range)
{
char *slash;
const struct in_addr *ip;
u_int32_t netmask;
unsigned int bits;
range->flags |= IP_NAT_RANGE_MAP_IPS;
slash = strchr(arg, '/');
if (slash)
*slash = '\0';
ip = xtables_numeric_to_ipaddr(arg);
if (!ip)
xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
arg);
range->min_ip = ip->s_addr;
if (slash) {
if (strchr(slash + 1, '.')) {
ip = xtables_numeric_to_ipmask(slash + 1);
if (!ip)
xtables_error(PARAMETER_PROBLEM,
"Bad netmask \"%s\"\n",
slash + 1);
netmask = ip->s_addr;
} else {
if (!xtables_strtoui(slash + 1, NULL, &bits, 0, 32))
xtables_error(PARAMETER_PROBLEM,
"Bad netmask \"%s\"\n",
slash + 1);
netmask = bits2netmask(bits);
}
/* Don't allow /0 (/1 is probably insane, too) */
if (netmask == 0)
xtables_error(PARAMETER_PROBLEM, "Netmask needed\n");
/* Mask should be <= then /16 */
if (bits < 16)
xtables_error(PARAMETER_PROBLEM,
"Max netmask size is /16\n");
} else
netmask = ~0;
if (range->min_ip & ~netmask) {
if (slash)
*slash = '/';
xtables_error(PARAMETER_PROBLEM, "Bad network address \"%s\"\n",
arg);
}
range->max_ip = range->min_ip | ~netmask;
}
static int DNETMAP_parse(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct xt_DNETMAP_tginfo *tginfo =
(struct xt_DNETMAP_tginfo *)(*target)->data;
struct nf_nat_multi_range *mr = &tginfo->prefix;
char *end;
switch (c) {
case 'p':
xtables_param_act(XTF_ONLY_ONCE, MODULENAME, "--prefix",
*flags & XT_DNETMAP_PREFIX);
xtables_param_act(XTF_NO_INVERT, MODULENAME, "--prefix",
invert);
/* TO-DO use xtables_ipparse_any instead? */
parse_prefix(optarg, &mr->range[0]);
*flags |= XT_DNETMAP_PREFIX;
tginfo->flags |= XT_DNETMAP_PREFIX;
return 1;
case 'r':
xtables_param_act(XTF_ONLY_ONCE, MODULENAME, "--reuse",
*flags & XT_DNETMAP_REUSE);
xtables_param_act(XTF_NO_INVERT, MODULENAME, "--reuse", invert);
*flags |= XT_DNETMAP_REUSE;
tginfo->flags |= XT_DNETMAP_REUSE;
return 1;
case 't':
xtables_param_act(XTF_ONLY_ONCE, MODULENAME, "--ttl",
*flags & XT_DNETMAP_TTL);
xtables_param_act(XTF_NO_INVERT, MODULENAME, "--ttl", invert);
*flags |= XT_DNETMAP_TTL;
tginfo->flags |= XT_DNETMAP_TTL;
tginfo->ttl = strtol(optarg, &end, 10);
if (*end != '\0')
return 0;
return 1;
default:
return 0;
}
}
static void DNETMAP_print_addr(const void *ip,
const struct xt_entry_target *target,
int numeric)
{
struct xt_DNETMAP_tginfo *tginfo =
(struct xt_DNETMAP_tginfo *)&target->data;
const struct nf_nat_multi_range *mr = &tginfo->prefix;
const struct nf_nat_range *r = &mr->range[0];
struct in_addr a;
int bits;
a.s_addr = r->min_ip;
printf("%s", xtables_ipaddr_to_numeric(&a));
a.s_addr = ~(r->min_ip ^ r->max_ip);
bits = netmask2bits(a.s_addr);
if (bits < 0)
printf("/%s", xtables_ipaddr_to_numeric(&a));
else
printf("/%d", bits);
}
static void DNETMAP_print(const void *ip, const struct xt_entry_target *target,
int numeric)
{
struct xt_DNETMAP_tginfo *tginfo =
(struct xt_DNETMAP_tginfo *)&target->data;
const __u8 *flags = &tginfo->flags;
printf("prefix ");
if (*flags & XT_DNETMAP_PREFIX)
DNETMAP_print_addr(ip, target, numeric);
else
printf("any");
printf(" reuse %i", (*flags & XT_DNETMAP_REUSE) > 0);
if (*flags & XT_DNETMAP_TTL)
printf(" ttl %i", tginfo->ttl);
else
printf(" ttl default");
}
static void DNETMAP_save(const void *ip, const struct xt_entry_target *target)
{
struct xt_DNETMAP_tginfo *tginfo =
(struct xt_DNETMAP_tginfo *)&target->data;
const __u8 *flags = &tginfo->flags;
if (*flags & XT_DNETMAP_PREFIX) {
printf("--%s", DNETMAP_opts[0].name);
DNETMAP_print_addr(ip, target, 0);
}
printf(" --reuse %i", *flags & XT_DNETMAP_REUSE);
/* ommited because default value can change as kernel mod param */
if (*flags & XT_DNETMAP_TTL)
printf(" --ttl %i", tginfo->ttl);
}
static struct xtables_target dnetmap_tg_reg = {
.name = MODULENAME,
.version = XTABLES_VERSION,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct xt_DNETMAP_tginfo)),
.userspacesize = XT_ALIGN(sizeof(struct xt_DNETMAP_tginfo)),
.help = DNETMAP_help,
.init = DNETMAP_init,
.parse = DNETMAP_parse,
.print = DNETMAP_print,
.save = DNETMAP_save,
.extra_opts = DNETMAP_opts,
};
static void _init(void)
{
xtables_register_target(&dnetmap_tg_reg);
}

View File

@@ -0,0 +1,91 @@
The \fBDNETMAP\fR target allows dynamic two-way 1:1 mapping of IPv4 subnets.
Single rule can map private subnet to shorter public subnet creating and
maintaining unambigeous private-public ip bindings. Second rule can be used to
map new flows to private subnet according to maintained bindings. Target allows
efficient public IPv4 space usage and unambigeous NAT at the same time.
Target can be used only in \fBnat\fR table in \fBPOSTROUTING\fR or \fBOUTPUT\fR
chains for SNAT and in \fBPREROUTING\fR for DNAT. Only flows directed to bound
IPs will be DNATed. Packet continues chain traversal if there is no free
postnat-ip to be assigned to prenat-ip. Default binding \fBttl\fR is \fI10
minutes\fR and can be changed using \fBdefault_ttl\fR module option. Default ip
hash size is 256 and can be changed using \fBhash_size\fR module option.
.TP
\fB\-\-prefix\fR \fIaddr\fR\fB/\fR\fImask\fR
Network subnet to map to. If not specified, all existing prefixes are used.
.TP
\fB\-\-reuse\fR
Reuse entry for given prenat-ip from any prefix despite bindings ttl < 0.
.TP
\fB\-\-ttl\fR \fIseconds\fR
Regenerate bindings ttl value to \fIseconds\fR. If negative value is specified,
bindings ttl is kept unchanged. If not specified then default ttl value (600s)
is used.
.PP
\fB* /proc interface\fR
Module creates following entries for each new specified subnet:
.TP
\fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR
Contains binding table for subnet/mask. Each line contains \fBprenat-ip\fR,
\fBpostnat-ip\fR,\fBttl\fR (seconds till entry times out), \fBlasthit\fR (last
entry hit in seconds relative to system boot time).
.TP
\fB/proc/net/xt_DNETMAP/\fR\fIsubnet\fR\fB_\fR\fImask\fR\fB_stat\fR
Contains statistics for given subnet/mask. Line contains contains three
numerical values separated by spaces. First one is number of currently used
addresses (bindings with negative ttl excluded), second one is number of all
usable addresses in subnet and third one is mean \fBttl\fR value for all active
entries.
.PP
Entries are removed if the last iptables rule for a specific subnet is deleted.
\fB* Logging\fR
Module logs binding add/timeout events to klog. This behaviour can be disabled
using \fBdisable_log\fR module parameter.
\fB* Examples\fR
\fB1.\fR Map subnet 192.168.0.0/24 to subnets 20.0.0.0/26. SNAT only:
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
Active hosts from 192.168.0.0/24 subnet are mapped to 20.0.0.0/26. If packet
from not yet bound prenat-ip hits the rule and there are no free or timed-out
(ttl<0) entries in prefix 20.0.0.0/28, then notice is logged to klog and chain
traversal continues. If packet from already bound prenat-ip hits the rule,
bindings ttl value is regenerated to default_ttl and SNAT is performed.
\fB2.\fR Use of \fB\-\-reuse\fR and \fB\-\-ttl\fR switches, multiple rule
interaction:
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix
20.0.0.0/26 --reuse --ttl 200
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 30.0.0.0/26
Active hosts from 192.168.0.0/24 subnet are mapped to 20.0.0.0/26 with ttl =
200 seconds. If there are no free addresses in first prefix the next one
(30.0.0.0/26) is used with default ttl. It's important to note that the first
rule SNATs all flows whose source IP is already actively (ttl>0) bound to ANY
prefix. Parameter \fB\-\-reuse\fR makes this functionality work even for
inactive (ttl<0) entries.
If both subnets are exhaused, then chain traversal continues.
\fB3.\fR Map 192.168.0.0/24 to subnets 20.0.0.0/26 bidirectional way:
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j DNETMAP --prefix 20.0.0.0/26
iptables -t nat -A PREROUTING -j DNETMAP
If host 192.168.0.10 generates some traffic, it gets bound to first free IP in
subnet - 20.0.0.0. Now any traffic directed to 20.0.0.0 gets DNATed to
192.168.0.10 as long as there's an active (ttl>0) binding. There's no need to
specify \fB\-\-prefix\fR parameter in PREROUTING rule, because this way it DNATs
traffic to all active prefixes. You could specify prefix it you'd like to make
DNAT work for specific prefix only.
.

704
extensions/xt_DNETMAP.c Normal file
View File

@@ -0,0 +1,704 @@
/* DNETMAP - dynamic two-way 1:1 NAT mapping of IPv4 network addresses.
* The mapping can be applied to source (POSTROUTING|OUTPUT)
* or destination (PREROUTING),
*/
/* (C) 2010 Marek Kierdelewicz <marek@koba.pl>
*
* module is dedicated to my wife Eliza and my daughters Jula and Ola :* :* :*
*
* module uses some code and ideas from following modules:
* - "NETMAP" module by Svenning Soerensen <svenning@post5.tele.dk>
* - "recent" module by Stephen Frost <sfrost@snowman.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter/x_tables.h>
#include <net/netfilter/nf_nat_rule.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include "xt_DNETMAP.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marek Kierdelewicz <marek@koba.pl>");
MODULE_DESCRIPTION(
"Xtables: dynamic two-way 1:1 NAT mapping of IPv4 addresses");
static unsigned int default_ttl = 600;
static unsigned int proc_perms = 0644;
static unsigned int proc_uid;
static unsigned int proc_gid;
static unsigned int default_hash_size = 256;
static unsigned int hash_size = 256;
static unsigned int disable_log;
static unsigned int whole_prefix = 1;
module_param(default_ttl, uint, 0400);
MODULE_PARM_DESC(default_ttl,
" default ttl value to be used if rule doesn't specify any (default: 600)");
module_param(hash_size, uint, 0400);
MODULE_PARM_DESC(hash_size,
" hash size for ip lists, needs to be power of 2 (default: 256)");
module_param(disable_log, uint, 0400);
MODULE_PARM_DESC(disable_log,
" disables logging of bind/timeout events (default: 0)");
module_param(whole_prefix, uint, 0400);
MODULE_PARM_DESC(whole_prefix,
" use network and broadcast addresses of specified prefix for bindings (default: 1)");
static unsigned int jtimeout;
struct dnetmap_entry {
struct list_head list;
/* priv2entry */
struct list_head glist;
/* pub2entry */
struct list_head grlist;
struct list_head lru_list;
__be32 prenat_addr;
__be32 postnat_addr;
unsigned long stamp;
struct dnetmap_prefix *prefix;
};
struct dnetmap_prefix {
struct nf_nat_multi_range_compat prefix;
char prefix_str[16];
struct list_head list;
unsigned int refcnt;
/* lru entry list */
struct list_head lru_list;
/* hash based on prenat-ips */
struct list_head iphash[0];
};
struct dnetmap_net {
struct list_head prefixes;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *xt_dnetmap;
#endif
/* global hash */
struct list_head *dnetmap_iphash;
};
static int dnetmap_net_id;
static inline struct dnetmap_net *dnetmap_pernet(struct net *net)
{
return net_generic(net, dnetmap_net_id);
}
static DEFINE_SPINLOCK(dnetmap_lock);
static DEFINE_MUTEX(dnetmap_mutex);
#ifdef CONFIG_PROC_FS
static const struct file_operations dnetmap_tg_fops;
#endif
static int dnetmap_stat_proc_read(char __user *buffer, char **start,
off_t offset, int length, int *eof,
void *data);
static inline unsigned int dnetmap_entry_hash(const __be32 addr)
{
return ntohl(addr) & (hash_size - 1);
}
static struct dnetmap_entry *dnetmap_entry_lookup(struct dnetmap_net
*dnetmap_net,
const __be32 addr)
{
struct dnetmap_entry *e;
unsigned int h;
h = dnetmap_entry_hash(addr);
list_for_each_entry(e, &dnetmap_net->dnetmap_iphash[h], glist)
if (memcmp(&e->prenat_addr, &addr, sizeof(e->prenat_addr)) == 0)
return e;
return NULL;
}
static struct dnetmap_entry *dnetmap_entry_rlookup(struct dnetmap_net
*dnetmap_net,
const __be32 addr)
{
struct dnetmap_entry *e;
unsigned int h;
h = dnetmap_entry_hash(addr);
list_for_each_entry(e, &dnetmap_net->dnetmap_iphash[hash_size + h],
grlist)
if (memcmp(&e->postnat_addr, &addr, sizeof(e->postnat_addr)) == 0)
return e;
return NULL;
}
static struct dnetmap_prefix *dnetmap_prefix_lookup(struct dnetmap_net
*dnetmap_net,
const struct
nf_nat_multi_range_compat
*mr)
{
struct dnetmap_prefix *p;
list_for_each_entry(p, &dnetmap_net->prefixes, list)
if (!memcmp(&p->prefix, mr, sizeof(*mr)))
return p;
return NULL;
}
static void dnetmap_prefix_flush(struct dnetmap_net *dnetmap_net,
struct dnetmap_prefix *p)
{
struct dnetmap_entry *e, *next;
unsigned int i;
for (i = 0; i < hash_size; i++) {
list_for_each_entry_safe(e, next,
&dnetmap_net->dnetmap_iphash[i], glist)
if (e->prefix == p)
list_del(&e->glist);
list_for_each_entry_safe(e, next,
&dnetmap_net->
dnetmap_iphash[hash_size + i], grlist)
if (e->prefix == p)
list_del(&e->grlist);
list_for_each_entry_safe(e, next, &p->iphash[i], list) {
list_del(&e->list);
list_del(&e->lru_list);
kfree(e);
}
}
}
static int dnetmap_tg_check(const struct xt_tgchk_param *par)
{
struct dnetmap_net *dnetmap_net = dnetmap_pernet(par->net);
const struct xt_DNETMAP_tginfo *tginfo = par->targinfo;
const struct nf_nat_multi_range_compat *mr = &tginfo->prefix;
struct dnetmap_prefix *p;
struct dnetmap_entry *e;
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *pde_data, *pde_stat;
char proc_str_data[20];
char proc_str_stat[25];
#endif
int ret = -EINVAL;
int i;
__be32 a;
__u32 ip_min, ip_max, ip;
/* prefix not specified - no need to do anything */
if (!(tginfo->flags & XT_DNETMAP_PREFIX)) {
ret = 0;
return ret;
}
if (!(mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)) {
pr_debug("DNETMAP:check: bad MAP_IPS.\n");
return -EINVAL;
}
if (mr->rangesize != 1) {
pr_debug("DNETMAP:check: bad rangesize %u.\n", mr->rangesize);
return -EINVAL;
}
mutex_lock(&dnetmap_mutex);
p = dnetmap_prefix_lookup(dnetmap_net, mr);
if (p != NULL) {
p->refcnt++;
ret = 0;
goto out;
}
p = kzalloc(sizeof(*p) + sizeof(struct list_head) * hash_size * 2,
GFP_KERNEL);
if (p == NULL) {
ret = -ENOMEM;
goto out;
}
p->refcnt = 1;
memcpy(&p->prefix, mr, sizeof(*mr));
INIT_LIST_HEAD(&p->lru_list);
for (i = 0; i < hash_size * 2; i++)
INIT_LIST_HEAD(&p->iphash[i]);
ip_min = ntohl(mr->range[0].min_ip) + (whole_prefix == 0);
ip_max = ntohl(mr->range[0].max_ip) - (whole_prefix == 0);
sprintf(p->prefix_str, "%pI4/%i", &mr->range[0].min_ip,
33 - ffs(~(ip_min ^ ip_max)));
#ifdef CONFIG_PROC_FS
sprintf(proc_str_data, "%pI4_%i", &mr->range[0].min_ip,
33 - ffs(~(ip_min ^ ip_max)));
sprintf(proc_str_stat, "%pI4_%i_stat", &mr->range[0].min_ip,
33 - ffs(~(ip_min ^ ip_max)));
#endif
printk(KERN_INFO KBUILD_MODNAME ": new prefix %s\n", p->prefix_str);
for (ip = ip_min; ip <= ip_max; ip++) {
a = htonl(ip);
e = kmalloc(sizeof(*e), GFP_ATOMIC);
if (e == NULL)
return 0;
e->postnat_addr = a;
e->prenat_addr = 0;
e->stamp = jiffies;
e->prefix = p;
list_add_tail(&e->lru_list, &p->lru_list);
}
#ifdef CONFIG_PROC_FS
/* data */
pde_data =
proc_create_data(proc_str_data, proc_perms, dnetmap_net->xt_dnetmap,
&dnetmap_tg_fops, p);
if (pde_data == NULL) {
kfree(p);
ret = -ENOMEM;
goto out;
}
pde_data->uid = proc_uid;
pde_data->gid = proc_gid;
/* statistics */
pde_stat =
create_proc_entry(proc_str_stat, proc_perms,
dnetmap_net->xt_dnetmap);
if (pde_stat == NULL) {
kfree(p);
ret = -ENOMEM;
goto out;
}
pde_stat->data = p;
pde_stat->read_proc = dnetmap_stat_proc_read;
pde_stat->uid = proc_uid;
pde_stat->gid = proc_gid;
#endif
spin_lock_bh(&dnetmap_lock);
list_add_tail(&p->list, &dnetmap_net->prefixes);
spin_unlock_bh(&dnetmap_lock);
ret = 0;
out:
mutex_unlock(&dnetmap_mutex);
return ret;
}
static unsigned int
dnetmap_tg(struct sk_buff *skb, const struct xt_action_param *par)
{
struct net *net = dev_net(par->in ? par->in : par->out);
struct dnetmap_net *dnetmap_net = dnetmap_pernet(net);
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
__be32 prenat_ip, postnat_ip, prenat_ip_prev;
const struct xt_DNETMAP_tginfo *tginfo = par->targinfo;
const struct nf_nat_multi_range_compat *mr = &tginfo->prefix;
struct nf_nat_range newrange;
struct dnetmap_entry *e;
struct dnetmap_prefix *p;
__s32 jttl;
NF_CT_ASSERT(par->hooknum == NF_INET_POST_ROUTING ||
par->hooknum == NF_INET_LOCAL_OUT ||
par->hooknum == NF_INET_PRE_ROUTING);
ct = nf_ct_get(skb, &ctinfo);
jttl = tginfo->flags & XT_DNETMAP_TTL ? tginfo->ttl * HZ : jtimeout;
/* in prerouting we try to map postnat-ip to prenat-ip */
if (par->hooknum == NF_INET_PRE_ROUTING) {
postnat_ip = ip_hdr(skb)->daddr;
spin_lock_bh(&dnetmap_lock);
e = dnetmap_entry_rlookup(dnetmap_net, postnat_ip);
if (e == NULL)
goto no_rev_map; /* no binding found */
/* if prefix is specified, we check if
it matches lookedup entry */
if (tginfo->flags & XT_DNETMAP_PREFIX) {
if (memcmp(mr, &e->prefix, sizeof(*mr)))
goto no_rev_map;
}
/* don't reset ttl if flag is set */
if (jttl >= 0) {
p = e->prefix;
e->stamp = jiffies + jttl;
list_move_tail(&e->lru_list, &p->lru_list);
}
spin_unlock_bh(&dnetmap_lock);
newrange = ((struct nf_nat_range) {
mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
e->prenat_addr, e->prenat_addr,
mr->range[0].min, mr->range[0].max});
/* Hand modified range to generic setup. */
return nf_nat_setup_info(ct, &newrange,
HOOK2MANIP(par->hooknum));
}
prenat_ip = ip_hdr(skb)->saddr;
spin_lock_bh(&dnetmap_lock);
p = dnetmap_prefix_lookup(dnetmap_net, mr);
e = dnetmap_entry_lookup(dnetmap_net, prenat_ip);
if (e == NULL) { /* need for new binding */
bind_new_prefix:
e = list_entry(p->lru_list.next, struct dnetmap_entry,
lru_list);
if (e->prenat_addr != 0 && time_before(jiffies, e->stamp)) {
if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME
": ip %pI4 - no free adresses in prefix %s\n",
&prenat_ip, p->prefix_str);
goto no_free_ip;
}
postnat_ip = e->postnat_addr;
if (e->prenat_addr != 0) {
prenat_ip_prev = e->prenat_addr;
if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME
": timeout binding %pI4 -> %pI4\n",
&prenat_ip_prev, &postnat_ip);
list_del(&e->list);
list_del(&e->glist);
list_del(&e->grlist);
}
e->prenat_addr = prenat_ip;
e->stamp = jiffies + jttl;
list_move_tail(&e->lru_list, &p->lru_list);
list_add_tail(&e->list,
&p->iphash[dnetmap_entry_hash(prenat_ip)]);
list_add_tail(&e->glist,
&dnetmap_net->
dnetmap_iphash[dnetmap_entry_hash(prenat_ip)]);
list_add_tail(&e->grlist,
&dnetmap_net->dnetmap_iphash[hash_size +
dnetmap_entry_hash
(postnat_ip)]);
if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME
": add binding %pI4 -> %pI4\n", &prenat_ip,
&postnat_ip);
} else {
if (!(tginfo->flags & XT_DNETMAP_REUSE)) {
if (time_before(e->stamp, jiffies) && p != e->prefix) {
if (!disable_log)
printk(KERN_INFO KBUILD_MODNAME
": timeout binding %pI4 -> %pI4\n",
&e->prenat_addr,
&e->postnat_addr);
list_del(&e->list);
list_del(&e->glist);
list_del(&e->grlist);
e->prenat_addr = 0;
goto bind_new_prefix;
}
}
/* don't reset ttl if flag is set */
if (jttl >= 0) {
e->stamp = jiffies + jttl;
p = e->prefix;
list_move_tail(&e->lru_list, &p->lru_list);
}
postnat_ip = e->postnat_addr;
}
spin_unlock_bh(&dnetmap_lock);
newrange = ((struct nf_nat_range) {
mr->range[0].flags | IP_NAT_RANGE_MAP_IPS,
postnat_ip, postnat_ip,
mr->range[0].min, mr->range[0].max});
/* Hand modified range to generic setup. */
return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(par->hooknum));
no_rev_map:
no_free_ip:
spin_unlock_bh(&dnetmap_lock);
return XT_CONTINUE;
}
static void dnetmap_tg_destroy(const struct xt_tgdtor_param *par)
{
struct dnetmap_net *dnetmap_net = dnetmap_pernet(par->net);
const struct xt_DNETMAP_tginfo *tginfo = par->targinfo;
const struct nf_nat_multi_range_compat *mr = &tginfo->prefix;
struct dnetmap_prefix *p;
#ifdef CONFIG_PROC_FS
char str[25];
#endif
if (!(tginfo->flags & XT_DNETMAP_PREFIX))
return;
mutex_lock(&dnetmap_mutex);
p = dnetmap_prefix_lookup(dnetmap_net, mr);
if (--p->refcnt == 0) {
spin_lock_bh(&dnetmap_lock);
list_del(&p->list);
spin_unlock_bh(&dnetmap_lock);
#ifdef CONFIG_PROC_FS
sprintf(str, "%pI4_%i", &mr->range[0].min_ip,
33 - ffs(~(ntohl(mr->range[0].min_ip ^
mr->range[0].max_ip))));
remove_proc_entry(str, dnetmap_net->xt_dnetmap);
sprintf(str, "%pI4_%i_stat", &mr->range[0].min_ip,
33 - ffs(~(ntohl(mr->range[0].min_ip ^
mr->range[0].max_ip))));
remove_proc_entry(str, dnetmap_net->xt_dnetmap);
#endif
dnetmap_prefix_flush(dnetmap_net, p);
kfree(p);
}
mutex_unlock(&dnetmap_mutex);
}
#ifdef CONFIG_PROC_FS
struct dnetmap_iter_state {
const struct dnetmap_prefix *p;
unsigned int bucket;
};
static void *dnetmap_seq_start(struct seq_file *seq, loff_t * pos)
__acquires(dnetmap_lock)
{
struct dnetmap_iter_state *st = seq->private;
const struct dnetmap_prefix *prefix = st->p;
struct dnetmap_entry *e;
loff_t p = *pos;
spin_lock_bh(&dnetmap_lock);
list_for_each_entry(e, &prefix->lru_list, lru_list)
if (p-- == 0)
return e;
return NULL;
}
static void *dnetmap_seq_next(struct seq_file *seq, void *v, loff_t * pos)
{
struct dnetmap_iter_state *st = seq->private;
const struct dnetmap_prefix *prefix = st->p;
const struct dnetmap_entry *e = v;
const struct list_head *head = e->lru_list.next;
if (head == &prefix->lru_list)
return NULL;
(*pos)++;
return list_entry(head, struct dnetmap_entry, lru_list);
}
static void dnetmap_seq_stop(struct seq_file *s, void *v)
__releases(dnetmap_lock)
{
spin_unlock_bh(&dnetmap_lock);
}
static int dnetmap_seq_show(struct seq_file *seq, void *v)
{
const struct dnetmap_entry *e = v;
seq_printf(seq, "%pI4 -> %pI4 --- ttl: %i lasthit: %lu\n",
&e->prenat_addr, &e->postnat_addr,
(int)(e->stamp - jiffies) / HZ, (e->stamp - jtimeout) / HZ);
return 0;
}
static const struct seq_operations dnetmap_seq_ops = {
.start = dnetmap_seq_start,
.next = dnetmap_seq_next,
.stop = dnetmap_seq_stop,
.show = dnetmap_seq_show,
};
static int dnetmap_seq_open(struct inode *inode, struct file *file)
{
struct proc_dir_entry *pde = PDE(inode);
struct dnetmap_iter_state *st;
st = __seq_open_private(file, &dnetmap_seq_ops, sizeof(*st));
if (st == NULL)
return -ENOMEM;
st->p = pde->data;
return 0;
}
static const struct file_operations dnetmap_tg_fops = {
.open = dnetmap_seq_open,
.read = seq_read,
.release = seq_release_private,
.owner = THIS_MODULE,
};
/* for statistics */
static int dnetmap_stat_proc_read(char __user *buffer, char **start,
off_t offset, int length, int *eof,
void *data)
{
const struct dnetmap_prefix *p = data;
struct dnetmap_entry *e;
unsigned int used, all;
long int ttl, sum_ttl;
used = 0;
all = 0;
sum_ttl = 0;
spin_lock_bh(&dnetmap_lock);
list_for_each_entry(e, &p->lru_list, lru_list) {
ttl = e->stamp - jiffies;
if (e->prenat_addr != 0 && ttl >= 0) {
used++;
sum_ttl += ttl;
}
all++;
}
sum_ttl = used > 0 ? sum_ttl / (used * HZ) : 0;
sprintf(buffer, "%u %u %li\n", used, all, sum_ttl);
if (length >= strlen(buffer))
*eof = true;
spin_unlock_bh(&dnetmap_lock);
return strlen(buffer);
}
static int __net_init dnetmap_proc_net_init(struct net *net)
{
struct dnetmap_net *dnetmap_net = dnetmap_pernet(net);
dnetmap_net->xt_dnetmap = proc_mkdir("xt_DNETMAP", net->proc_net);
if (!dnetmap_net->xt_dnetmap)
return -ENOMEM;
return 0;
}
static void __net_exit dnetmap_proc_net_exit(struct net *net)
{
proc_net_remove(net, "xt_DNETMAP");
}
#else
static inline int dnetmap_proc_net_init(struct net *net)
{
return 0;
}
static inline void dnetmap_proc_net_exit(struct net *net)
{
}
#endif /* CONFIG_PROC_FS */
static int __net_init dnetmap_net_init(struct net *net)
{
struct dnetmap_net *dnetmap_net = dnetmap_pernet(net);
int i;
dnetmap_net->dnetmap_iphash =
kmalloc(sizeof(struct list_head) * hash_size * 2, GFP_ATOMIC);
if (dnetmap_net->dnetmap_iphash == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&dnetmap_net->prefixes);
for (i = 0; i < hash_size * 2; i++)
INIT_LIST_HEAD(&dnetmap_net->dnetmap_iphash[i]);
return dnetmap_proc_net_init(net);
}
static void __net_exit dnetmap_net_exit(struct net *net)
{
struct dnetmap_net *dnetmap_net = dnetmap_pernet(net);
BUG_ON(!list_empty(&dnetmap_net->prefixes));
kfree(dnetmap_net->dnetmap_iphash);
dnetmap_proc_net_exit(net);
}
static struct pernet_operations dnetmap_net_ops = {
.init = dnetmap_net_init,
.exit = dnetmap_net_exit,
.id = &dnetmap_net_id,
.size = sizeof(struct dnetmap_net),
};
static struct xt_target dnetmap_tg_reg __read_mostly = {
.name = "DNETMAP",
.family = NFPROTO_IPV4,
.target = dnetmap_tg,
.targetsize = sizeof(struct xt_DNETMAP_tginfo),
.table = "nat",
.hooks = (1 << NF_INET_POST_ROUTING) | (1 << NF_INET_LOCAL_OUT) |
(1 << NF_INET_PRE_ROUTING),
.checkentry = dnetmap_tg_check,
.destroy = dnetmap_tg_destroy,
.me = THIS_MODULE
};
static int __init dnetmap_tg_init(void)
{
int err;
/* verify parameters */
if (ffs(hash_size) != fls(hash_size) || hash_size <= 0) {
pr_info("bad hash_size parameter value - using defaults");
hash_size = default_hash_size;
}
jtimeout = default_ttl * HZ;
err = register_pernet_subsys(&dnetmap_net_ops);
if (err)
return err;
err = xt_register_target(&dnetmap_tg_reg);
if (err)
unregister_pernet_subsys(&dnetmap_net_ops);
return err;
}
static void __exit dnetmap_tg_exit(void)
{
xt_unregister_target(&dnetmap_tg_reg);
unregister_pernet_subsys(&dnetmap_net_ops);
}
module_init(dnetmap_tg_init);
module_exit(dnetmap_tg_exit);

16
extensions/xt_DNETMAP.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef _LINUX_NETFILTER_XT_DNETMAP_H
#define _LINUX_NETFILTER_XT_DNETMAP_H 1
enum {
XT_DNETMAP_TTL = 1 << 0,
XT_DNETMAP_REUSE = 1 << 1,
XT_DNETMAP_PREFIX = 1 << 2,
};
struct xt_DNETMAP_tginfo {
struct nf_nat_multi_range_compat prefix;
__u8 flags;
__s16 ttl;
};
#endif

View File

@@ -0,0 +1,215 @@
/*
* Definitions and Declarations for tuple.
*
* 16 Dec 2003: Yasuyuki Kozakai @USAGI <yasuyuki.kozakai@toshiba.co.jp>
* - generalize L3 protocol dependent part.
*
* Derived from include/linux/netfiter_ipv4/ip_conntrack_tuple.h
*/
#ifndef _NF_CONNTRACK_TUPLE_H
#define _NF_CONNTRACK_TUPLE_H
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/nf_conntrack_tuple_common.h>
/*#include <linux/list_nulls.h>*/
/* A `tuple' is a structure containing the information to uniquely
identify a connection. ie. if two packets have the same tuple, they
are in the same connection; if not, they are not.
We divide the structure along "manipulatable" and
"non-manipulatable" lines, for the benefit of the NAT code.
*/
#define NF_CT_TUPLE_L3SIZE ARRAY_SIZE(((union nf_inet_addr *)NULL)->all)
/* The protocol-specific manipulable parts of the tuple: always in
network order! */
union nf_conntrack_man_proto {
/* Add other protocols here. */
__be16 all;
struct {
__be16 port;
} tcp;
struct {
__be16 port;
} udp;
struct {
__be16 id;
} icmp;
struct {
__be16 port;
} dccp;
struct {
__be16 port;
} sctp;
struct {
__be16 key; /* GRE key is 32bit, PPtP only uses 16bit */
} gre;
};
/* The manipulable part of the tuple. */
struct nf_conntrack_man {
union nf_inet_addr u3;
union nf_conntrack_man_proto u;
/* Layer 3 protocol */
u_int16_t l3num;
};
/* This contains the information to distinguish a connection. */
struct nf_conntrack_tuple {
struct nf_conntrack_man src;
/* These are the parts of the tuple which are fixed. */
struct {
union nf_inet_addr u3;
union {
/* Add other protocols here. */
__be16 all;
struct {
__be16 port;
} tcp;
struct {
__be16 port;
} udp;
struct {
u_int8_t type, code;
} icmp;
struct {
__be16 port;
} dccp;
struct {
__be16 port;
} sctp;
struct {
__be16 key;
} gre;
} u;
/* The protocol. */
u_int8_t protonum;
/* The direction (for tuplehash) */
u_int8_t dir;
} dst;
};
struct nf_conntrack_tuple_mask {
struct {
union nf_inet_addr u3;
union nf_conntrack_man_proto u;
} src;
};
#ifdef __KERNEL__
static inline void nf_ct_dump_tuple_ip(const struct nf_conntrack_tuple *t)
{
#ifdef DEBUG
printk("tuple %p: %u %pI4:%hu -> %pI4:%hu\n",
t, t->dst.protonum,
&t->src.u3.ip, ntohs(t->src.u.all),
&t->dst.u3.ip, ntohs(t->dst.u.all));
#endif
}
static inline void nf_ct_dump_tuple_ipv6(const struct nf_conntrack_tuple *t)
{
#ifdef DEBUG
printk("tuple %p: %u %pI6 %hu -> %pI6 %hu\n",
t, t->dst.protonum,
t->src.u3.all, ntohs(t->src.u.all),
t->dst.u3.all, ntohs(t->dst.u.all));
#endif
}
static inline void nf_ct_dump_tuple(const struct nf_conntrack_tuple *t)
{
switch (t->src.l3num) {
case AF_INET:
nf_ct_dump_tuple_ip(t);
break;
case AF_INET6:
nf_ct_dump_tuple_ipv6(t);
break;
}
}
/* If we're the first tuple, it's the original dir. */
#define NF_CT_DIRECTION(h) \
((enum ip_conntrack_dir)(h)->tuple.dst.dir)
/* Connections have two entries in the hash table: one for each way */
struct nf_conntrack_tuple_hash {
struct hlist_nulls_node hnnode;
struct nf_conntrack_tuple tuple;
};
static inline bool __nf_ct_tuple_src_equal(const struct nf_conntrack_tuple *t1,
const struct nf_conntrack_tuple *t2)
{
return (nf_inet_addr_cmp(&t1->src.u3, &t2->src.u3) &&
t1->src.u.all == t2->src.u.all &&
t1->src.l3num == t2->src.l3num);
}
static inline bool __nf_ct_tuple_dst_equal(const struct nf_conntrack_tuple *t1,
const struct nf_conntrack_tuple *t2)
{
return (nf_inet_addr_cmp(&t1->dst.u3, &t2->dst.u3) &&
t1->dst.u.all == t2->dst.u.all &&
t1->dst.protonum == t2->dst.protonum);
}
static inline bool nf_ct_tuple_equal(const struct nf_conntrack_tuple *t1,
const struct nf_conntrack_tuple *t2)
{
return __nf_ct_tuple_src_equal(t1, t2) &&
__nf_ct_tuple_dst_equal(t1, t2);
}
static inline bool
nf_ct_tuple_mask_equal(const struct nf_conntrack_tuple_mask *m1,
const struct nf_conntrack_tuple_mask *m2)
{
return (nf_inet_addr_cmp(&m1->src.u3, &m2->src.u3) &&
m1->src.u.all == m2->src.u.all);
}
static inline bool
nf_ct_tuple_src_mask_cmp(const struct nf_conntrack_tuple *t1,
const struct nf_conntrack_tuple *t2,
const struct nf_conntrack_tuple_mask *mask)
{
int count;
for (count = 0; count < NF_CT_TUPLE_L3SIZE; count++) {
if ((t1->src.u3.all[count] ^ t2->src.u3.all[count]) &
mask->src.u3.all[count])
return false;
}
if ((t1->src.u.all ^ t2->src.u.all) & mask->src.u.all)
return false;
if (t1->src.l3num != t2->src.l3num ||
t1->dst.protonum != t2->dst.protonum)
return false;
return true;
}
static inline bool
nf_ct_tuple_mask_cmp(const struct nf_conntrack_tuple *t,
const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_tuple_mask *mask)
{
return nf_ct_tuple_src_mask_cmp(t, tuple, mask) &&
__nf_ct_tuple_dst_equal(t, tuple);
}
#endif /* __KERNEL__ */
#endif /* _NF_CONNTRACK_TUPLE_H */

View File

@@ -0,0 +1,93 @@
#ifndef _NF_NAT_H
#define _NF_NAT_H
#include <linux/netfilter_ipv4.h>
#include <net/netfilter/nf_conntrack_tuple.h>
#define NF_NAT_MAPPING_TYPE_MAX_NAMELEN 16
enum nf_nat_manip_type {
IP_NAT_MANIP_SRC,
IP_NAT_MANIP_DST
};
/* SRC manip occurs POST_ROUTING or LOCAL_IN */
#define HOOK2MANIP(hooknum) ((hooknum) != NF_INET_POST_ROUTING && \
(hooknum) != NF_INET_LOCAL_IN)
#define IP_NAT_RANGE_MAP_IPS 1
#define IP_NAT_RANGE_PROTO_SPECIFIED 2
#define IP_NAT_RANGE_PROTO_RANDOM 4
#define IP_NAT_RANGE_PERSISTENT 8
/* NAT sequence number modifications */
struct nf_nat_seq {
/* position of the last TCP sequence number modification (if any) */
u_int32_t correction_pos;
/* sequence number offset before and after last modification */
int16_t offset_before, offset_after;
};
/* Single range specification. */
struct nf_nat_range {
/* Set to OR of flags above. */
unsigned int flags;
/* Inclusive: network order. */
__be32 min_ip, max_ip;
/* Inclusive: network order */
union nf_conntrack_man_proto min, max;
};
/* For backwards compat: don't use in modern code. */
struct nf_nat_multi_range_compat {
unsigned int rangesize; /* Must be 1. */
/* hangs off end. */
struct nf_nat_range range[1];
};
#ifdef __KERNEL__
#include <linux/list.h>
#include <linux/netfilter/nf_conntrack_pptp.h>
#include <net/netfilter/nf_conntrack_extend.h>
/* per conntrack: nat application helper private data */
union nf_conntrack_nat_help {
/* insert nat helper private data here */
struct nf_nat_pptp nat_pptp_info;
};
struct nf_conn;
/* The structure embedded in the conntrack structure. */
struct nf_conn_nat {
struct hlist_node bysource;
struct nf_nat_seq seq[IP_CT_DIR_MAX];
struct nf_conn *ct;
union nf_conntrack_nat_help help;
#if defined(CONFIG_IP_NF_TARGET_MASQUERADE) || \
defined(CONFIG_IP_NF_TARGET_MASQUERADE_MODULE)
int masq_index;
#endif
};
/* Set up the info structure to map into this range. */
extern unsigned int nf_nat_setup_info(struct nf_conn *ct,
const struct nf_nat_range *range,
enum nf_nat_manip_type maniptype);
/* Is this tuple already taken? (not by us)*/
extern int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple,
const struct nf_conn *ignored_conntrack);
static inline struct nf_conn_nat *nfct_nat(const struct nf_conn *ct)
{
return nf_ct_ext_find(ct, NF_CT_EXT_NAT);
}
#else /* !__KERNEL__: iptables wants this to compile. */
#define nf_nat_multi_range nf_nat_multi_range_compat
#endif /*__KERNEL__*/
#endif

View File

@@ -27,3 +27,4 @@ build_lscan=m
build_pknock=m build_pknock=m
build_psd=m build_psd=m
build_quota2=m build_quota2=m
build_DNETMAP=m