本文共 15234 字,大约阅读时间需要 50 分钟。
《》的第3.3节,并没有给出如何配置一个pool,那是因为在Linux 2.6.10之上,已经不再支持配置不连续IP地址的pool了,如果看iptables的man手册,将会得到以下信息:
In Kernels up to 2.6.10 you can add several --to-destination options. For those kernels, if you specify more than one destination address, either via an address range or multiple --to-destination options, a simple round-robin (one after another in cycle) load balancing takes place between these addresses. Later Kernels (>= 2.6.11-rc1) don't have the ability to NAT to multiple ranges anymore./* POOL. 将目的地址随机映射到不连续的IP地址池 */ /* (C) 2011/06/28 By ZhaoYa * * 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. */ #include#include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("ZhaoYa "); MODULE_DESCRIPTION("Xtables: DNAT from ip-pool"); #define MAX 100 struct ip_addr_pool { unsigned int size; __be32 ips[MAX]; }; static bool pool_check(const struct xt_tgchk_param *par) { //TODO return true; } static unsigned int pool_target(struct sk_buff *skb, const struct xt_target_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; const struct ip_addr_pool *mr = par->targinfo; //以时钟嘀嗒作为随机源,理由是你不知道何时数据包会过来 unsigned int indx = jiffies%(mr->size); struct nf_nat_range newrange; newrange.min_ip = mr->ips[indx]; newrange.max_ip = mr->ips[indx]; newrange.flags = IP_NAT_RANGE_MAP_IPS; NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || par->hooknum == NF_INET_LOCAL_OUT); ct = nf_ct_get(skb, &ctinfo); NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); return nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_DST); } static struct xt_target pool_reg __read_mostly = { .name = "POOL", .family = NFPROTO_IPV4, .target = pool_target, .targetsize = sizeof(struct ip_addr_pool), .table = "nat", .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), .checkentry = pool_check, .me = THIS_MODULE, }; static int __init pool_target_init(void) { return xt_register_target(&pool_reg); } static void __exit pool_target_exit(void) { xt_unregister_target(&pool_reg); } module_init(pool_target_init); module_exit(pool_target_exit);
/* POOL. 将目的地址随机映射到不连续的IP地址池 -用户态iptables模块*/ /* (C) 2011/06/28 By ZhaoYa * * 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. */ #include#include #include #include #include #include #include #include #include enum { //目前只支持一个配置 FROM_POOL = 0, }; //最多一个池中有100个地址 #define MAX 100 struct ip_addr_pool { unsigned int size; u_int32_t ips[MAX]; }; struct ipt_natinfo { struct xt_entry_target t; struct ip_addr_pool mr; }; static void POOL_help(void) { printf( "POOL target options:\n" " --pool [ [, <...>]>]]\n"); } static const struct xt_option_entry POOL_opts[] = { { .name = "pool", .id = FROM_POOL, .type = XTTYPE_STRING, .flags = XTOPT_MAND | XTOPT_MULTI }, XTOPT_TABLEEND, }; static struct ipt_natinfo * set_contents(struct ipt_natinfo *info, const char *arg) { unsigned int size; char *tok; unsigned int i = 0; size = XT_ALIGN(sizeof(struct ipt_natinfo)); info = realloc(info, size); if (!info) xtables_error(OTHER_PROBLEM, "Out of memory\n"); tok = strtok(arg, ","); if (tok){ while (tok && i < MAX) { info->mr.ips[i] = (u_int32_t)inet_addr(tok); info->mr.size++; tok = strtok(NULL, ","); i ++; } } else { info->mr.ips[i] = (u_int32_t)inet_addr(arg); info->mr.size++; } return info; } static void POOL_parse(struct xt_option_call *cb) { const struct ipt_entry *entry = cb->xt_entry; struct ipt_natinfo *info = (void *)(*cb->target); int portok; if (entry->ip.proto == IPPROTO_TCP || entry->ip.proto == IPPROTO_UDP || entry->ip.proto == IPPROTO_SCTP || entry->ip.proto == IPPROTO_DCCP || entry->ip.proto == IPPROTO_ICMP) portok = 1; else portok = 0; xtables_option_parse(cb); switch (cb->entry->id) { case FROM_POOL: { char *arg ; arg = strdup(cb->arg); if (arg == NULL) xtables_error(RESOURCE_PROBLEM, "strdup"); info = set_contents(info, arg); free(arg); *cb->target = &(info->t); break; } } } static void POOL_save(const void *ip, const struct xt_entry_target *target) { const struct ipt_natinfo *info = (const void *)target; unsigned int i = 0; printf(" --pool "); for (i = 0; i < info->mr.size; i++) { struct in_addr ia; char *addr; ia.s_addr = info->mr.ips[i]; addr = inet_ntoa(ia); if (i == info->mr.size-1) printf("%s", addr); else printf("%s,", addr); } } static struct xtables_target pool_tg_reg = { .name = "POOL", .version = XTABLES_VERSION, .family = NFPROTO_IPV4, .size = XT_ALIGN(sizeof(struct ip_addr_pool)), .userspacesize = XT_ALIGN(sizeof(struct ip_addr_pool)), .help = POOL_help, .x6_parse = POOL_parse, .save = POOL_save, .x6_options = POOL_opts, }; void _init(void) { xtables_register_target(&pool_tg_reg); }
CC=gcc IPTABLES_SRC=/root/iptables/iptables-1.4.12 INCLUDE=-I$(IPTABLES_SRC)/include KERNEL_SRC=/lib/modules/`uname -r`/build MOD=ipt_POOL.ko all: modules libipt_POOL.so modules: $(MOD) ipt_POOL.ko: ipt_POOL.c $(MAKE) -C $(KERNEL_SRC) SUBDIRS=$(PWD) modules libipt_POOL.so: libipt_POOL.c $(CC) $(INCLUDE) -fPIC -c libipt_POOL.c ld -shared -o libipt_POOL.so libipt_POOL.o clean: -rm -f *.o *.so *.ko .*.cmd *.mod.c *.symvers *.order install: all cp -rf libipt_POOL.so /usr/local/lib/xtables/ cp -rf $(MOD) /lib/modules/`uname -r`/kernel/net/ipv4/netfilter/ @depmod -a
struct pool-info { unsigned char pool_name[MAXNAMELEN]; }
#include#include #include #include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("ZhaoYa "); MODULE_DESCRIPTION("Xtables: DNAT from ip-pool"); #define MAX 100 #define MAXNAMELEN 100 #define MAXPOOL 5 struct ip_addr_pool { unsigned char pool_name[MAXNAMELEN]; //该字段用于target中的具体POOL的查找 unsigned int size; __be32 ips[MAX]; }; struct pool_info { unsigned char pool_name[MAXNAMELEN]; }; //定义一个只有MAXPOOL个POOL的静态数组替代复杂的链表操作,标准的做法是用链表实现 struct ip_addr_pool gpoolset[MAXPOOL]; static bool poolset_check(const struct xt_tgchk_param *par) { //TODO return true; } //poolset_target很简单,就是根据iptables配置的pool名称,定位一个要使用的pool,也就是一个ip_addr_pool结构体。 static unsigned int poolset_target(struct sk_buff *skb, const struct xt_target_param *par) { struct nf_conn *ct; enum ip_conntrack_info ctinfo; struct nf_nat_range newrange; const struct pool_info *mr = par->targinfo; struct ip_addr_pool res = {}; unsigned int i = 0; unsigned int indx; //以下的循环找到根据name索引的ip_addr_pool,也就是一个POOL for (i = 0; i < MAXPOOL; i++) { if (!strcmp(mr->pool_name, gpoolset[i].pool_name)) res = gpoolset[i]; } //找到POOL后,在此POOL中随机取出一个IP地址用于DNAT indx = jiffies%(res.size); newrange.min_ip = res.ips[indx]; newrange.max_ip = res.ips[indx]; newrange.flags = IP_NAT_RANGE_MAP_IPS; NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING || par->hooknum == NF_INET_LOCAL_OUT); ct = nf_ct_get(skb, &ctinfo); NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); return nf_nat_setup_info(ct, &newrange, IP_NAT_MANIP_DST); } static struct xt_target poolset_reg __read_mostly = { .name = "POOLSET", .family = NFPROTO_IPV4, .target = poolset_target, .targetsize = sizeof(struct pool_info), .table = "nat", .hooks = (1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT), .checkentry = poolset_check, .me = THIS_MODULE, }; struct proc_dir_entry *poolset_entry; static ssize_t write_poolset(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { //TODO //在这里实现将buf转换为特定pool中的IP地址 return 0; } static struct file_operations proc_poolset_operations = { .write = write_poolset, }; static int __init poolset_target_init(void) { unsigned int i = 0; memset(gpoolset, 0, sizeof(gpoolset)); poolset_entry = proc_mkdir("poolset", NULL); for (i = 0; i < MAXPOOL; i++) { struct proc_dir_entry *entry; char buf[100] = {0}; sprintf(buf, "%d", i); strcpy(gpoolset[i].pool_name, buf); entry = create_proc_entry(buf, S_IWUSR, poolset_entry); poolset_entry->proc_fops = &proc_poolset_operations; } return xt_register_target(&poolset_reg); } static void __exit poolset_target_exit(void) { unsigned int i = 0; for (i = 0; i < MAXPOOL; i++) { remove_proc_entry(gpoolset[i].pool_name, poolset_entry); } xt_unregister_target(&poolset_reg); } module_init(poolset_target_init); module_exit(poolset_target_exit);
#include#include #include #include #include #include #include #include #include enum { //目前只支持一个配置 TO_POOLSET_SET = 0, }; #define MAXNAMELEN 100 struct pool_info { unsigned char pool_name[MAXNAMELEN]; }; struct ipt_poolsetinfo { struct xt_entry_target t; struct pool_info mr; }; static const struct xt_option_entry POOLSET_opts[] = { { .name = "poolset", .id = TO_POOLSET_SET, .type = XTTYPE_STRING, .flags = XTOPT_MAND | XTOPT_MULTI }, XTOPT_TABLEEND, }; static struct ipt_poolsetinfo * set_contents(struct ipt_poolsetinfo *info, const char *arg) { unsigned int size; size = XT_ALIGN(sizeof(struct ipt_poolsetinfo)); info = realloc(info, size); if (!info) xtables_error(OTHER_PROBLEM, "Out of memory\n"); //内核将只需要一个名称即可,根据pool名称,内核会在poolset中定位到具体的pool strcpy(info->mr.pool_name, arg); return info; } static void POOLSET_parse(struct xt_option_call *cb) { const struct ipt_entry *entry = cb->xt_entry; struct ipt_poolsetinfo *info = (void *)(*cb->target); int portok; if (entry->ip.proto == IPPROTO_TCP || entry->ip.proto == IPPROTO_UDP || entry->ip.proto == IPPROTO_SCTP || entry->ip.proto == IPPROTO_DCCP || entry->ip.proto == IPPROTO_ICMP) portok = 1; else portok = 0; xtables_option_parse(cb); switch (cb->entry->id) { case TO_POOLSET_SET: { char *arg ; arg = strdup(cb->arg); if (arg == NULL) xtables_error(RESOURCE_PROBLEM, "strdup"); info = set_contents(info, arg); free(arg); *cb->target = &(info->t); break; } } } static struct xtables_target poolset_tg_reg = { .name = "POOLSET", .version = XTABLES_VERSION, .family = NFPROTO_IPV4, .size = XT_ALIGN(sizeof(struct pool_info)), .userspacesize = XT_ALIGN(sizeof(struct pool_info)), .x6_parse = POOLSET_parse, .x6_options = POOLSET_opts, }; void _init(void) { xtables_register_target(&poolset_tg_reg); }
Linux的很多特性都是机制和策略分离的,机制在内核中实现而策略靠用户态程序配置。Netfilter和iptables就是这方面的最佳体现之一,它们互相配合,联合的很默契。因此如果想实现一个自定义的iptables策略,那么就必须首先实现一个内核模块,然后再实现一个iptables模块,幸运的是,这件事是很简单的,因为netfilter也好,iptables也罢,其核心框架逻辑都已经成型了,你需要做的仅仅是注册一些数据结构,实现一些回调函数而已,正如本文所体现的,一切很非常简单。
本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1270965
转载地址:http://juwva.baihongyu.com/