iptables之snat
2020-09-06 本文已影响0人
分享放大价值
看内核snat部分,有如下几个标志位,有点混淆,所以看下下发规则命令行iptables是如何指定的。
#define NF_NAT_RANGE_MAP_IPS (1 << 0)
#define NF_NAT_RANGE_PROTO_SPECIFIED (1 << 1)
#define NF_NAT_RANGE_PROTO_RANDOM (1 << 2)
#define NF_NAT_RANGE_PERSISTENT (1 << 3)
#define NF_NAT_RANGE_PROTO_RANDOM_FULLY (1 << 4)
这里先说下结论,后面再编译ipbales源码,gdb查看一下流程。
可以使用如下命令查看snat的参数列表
root@master:~/iptables-1.8.5# iptables -j SNAT -h
...
SNAT target options:
--to-source [<ipaddr>[-<ipaddr>]][:port[-port]]
Address to map source to.
[--random] [--random-fully] [--persistent]
--to-source 指定了ip/ip范围和port/port范围,
只要指定了ip或者ip范围就会设置 NF_NAT_RANGE_MAP_IPS,
只要指定了port或者port范围就会设置 NF_NAT_RANGE_PROTO_SPECIFIED。
--random 指定了这个参数就会设置NF_NAT_RANGE_PROTO_RANDOM,
--random-fully 指定了这个参数就会设置 NF_NAT_RANGE_PROTO_RANDOM_FULLY,
--persistent 指定了这个参数就会设置 NF_NAT_RANGE_PERSISTENT
参数--to-source比较简单,就是指定了ip和port,后面三个参数的作用得结合内核代码看。
--persistent 参数只在下面代码用到,通过 jhash2 计算j值时的最后一个参数 jhash2(const u32 *k, u32 length, u32 initval),如果指定了此参数,则直接写0,如果不指定,需要根据dst ip计算出一个值。
nf_nat_setup_info->get_unique_tuple->find_best_ips_proto
{
j = jhash2((u32 *)&tuple->src.u3, sizeof(tuple->src.u3) / sizeof(u32),
range->flags & NF_NAT_RANGE_PERSISTENT ?
0 : (__force u32)tuple->dst.u3.all[max] ^ zone);
full_range = false;
for (i = 0; i <= max; i++) {
/* If first bytes of the address are at the maximum, use the
* distance. Otherwise use the full range.
*/
if (!full_range) {
minip = ntohl((__force __be32)range->min_addr.all[i]);
maxip = ntohl((__force __be32)range->max_addr.all[i]);
dist = maxip - minip + 1;
} else {
minip = 0;
dist = ~0;
}
var_ipp->all[i] = (__force __u32)
htonl(minip + reciprocal_scale(j, dist));
if (var_ipp->all[i] != range->max_addr.all[i])
full_range = true;
if (!(range->flags & NF_NAT_RANGE_PERSISTENT))
j ^= (__force u32)tuple->dst.u3.all[i];
}
}
如果不指定--random和--random-fully,则优先判断报文自带的ip或者port是否在规则指定的范围内。
如果指定了这两个标志,就会直接调用find_best_ips_proto选择合适的ip,调用四层协议提供的 unique_tuple 函数,选择合适的port。下面代码是内核中选择端口时流程
nf_nat_setup_info->get_unique_tuple
{
...
if (maniptype == NF_NAT_MANIP_SRC &&
!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
/* try the original tuple first */
if (in_range(l3proto, l4proto, orig_tuple, range)) {
if (!nf_nat_used_tuple(orig_tuple, ct)) {
*tuple = *orig_tuple;
goto out;
}
} else if (find_appropriate_src(net, zone, l3proto, l4proto,
orig_tuple, tuple, range)) {
pr_debug("get_unique_tuple: Found current src map\n");
if (!nf_nat_used_tuple(tuple, ct))
goto out;
}
}
/* 2) Select the least-used IP/proto combination in the given range */
*tuple = *orig_tuple;
find_best_ips_proto(zone, tuple, range, ct, maniptype);
/* Only bother mapping if it's not already in range and unique */
if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL)) {
//规则指定了port,则判断报文自带的port是否在指定的port range内。
//如果在range内,并且只指定了一个port,或者没被使用,则返回,否则还得调用unique_tuple选择合适的port
if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
if (l4proto->in_range(tuple, maniptype,
&range->min_proto,
&range->max_proto) &&
(range->min_proto.all == range->max_proto.all ||
!nf_nat_used_tuple(tuple, ct)))
goto out;
//规则没有指定port,则还使用报文中自带的port
} else if (!nf_nat_used_tuple(tuple, ct)) {
goto out;
}
}
/* Last change: get protocol to try to obtain unique tuple. */
//调用四层协议提供的 unique_tuple,对于udp来说,此函数为udp_unique_tuple
l4proto->unique_tuple(l3proto, tuple, range, maniptype, ct);
out:
rcu_read_unlock();
}
static void
udp_unique_tuple(const struct nf_nat_l3proto *l3proto,
struct nf_conntrack_tuple *tuple,
const struct nf_nat_range *range,
enum nf_nat_manip_type maniptype,
const struct nf_conn *ct)
{
nf_nat_l4proto_unique_tuple(l3proto, tuple, range, maniptype, ct,
&udp_port_rover);
}
void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto,
struct nf_conntrack_tuple *tuple,
const struct nf_nat_range *range,
enum nf_nat_manip_type maniptype,
const struct nf_conn *ct,
u16 *rover)
{
unsigned int range_size, min, i;
__be16 *portptr;
u_int16_t off;
if (maniptype == NF_NAT_MANIP_SRC)
portptr = &tuple->src.u.all;
else
portptr = &tuple->dst.u.all;
//规则没有指定port range,如果是dnat,则不修改port。如果是
//snat,根据报文自带的源port选择合适的范围
/* If no range specified... */
if (!(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) {
/* If it's dst rewrite, can't change port */
if (maniptype == NF_NAT_MANIP_DST)
return;
if (ntohs(*portptr) < 1024) {
/* Loose convention: >> 512 is credential passing */
if (ntohs(*portptr) < 512) {
min = 1;
range_size = 511 - min + 1;
} else {
min = 600;
range_size = 1023 - min + 1;
}
} else {
min = 1024;
range_size = 65535 - 1024 + 1;
}
} else {
min = ntohs(range->min_proto.all);
range_size = ntohs(range->max_proto.all) - min + 1;
}
if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
off = l3proto->secure_port(tuple, maniptype == NF_NAT_MANIP_SRC
? tuple->dst.u.all
: tuple->src.u.all);
} else if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
off = prandom_u32();
} else {
off = *rover;
}
for (i = 0; ; ++off) {
*portptr = htons(min + off % range_size);
if (++i != range_size && nf_nat_used_tuple(tuple, ct))
continue;
if (!(range->flags & NF_NAT_RANGE_PROTO_RANDOM_ALL))
*rover = off;
return;
}
}
iptables
- 使用源码安装iptables,可以加gdb断点,方便调式
操作系统版本
root@ubuntu:~/iptables-1.8.5# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.3 LTS
Release: 18.04
Codename: bionic
root@ubuntu:~/iptables-1.8.5# uname -a
Linux ubuntu 4.15.0-109-generic #110-Ubuntu SMP Tue Jun 23 02:39:32 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
下载安装依赖包
wget https://launchpad.net/ubuntu/+archive/primary/+files/libnftnl-dev_1.1.7-1_amd64.deb
wget https://launchpad.net/ubuntu/+archive/primary/+files/libnftnl11_1.1.7-1_amd64.deb
dpkg -i ./libnftnl11_1.1.7-1_amd64.deb
dpkg -i ./libnftnl-dev_1.1.7-1_amd64.deb
解压编译iptables
tar -jxf iptables-1.8.5.tar.bz2
cd iptables-1.8.5/
./configure
//指定 -g -o0,方便gdb设置断点
export EXTRA_CFLAGS="-g -o0"
//编译
make
//将iptables命令安装到系统中
make install
编译成功后,会在iptables目录下生成两个脚本: xtables-nft-multi和xtables-legacy-multi。它们都会分别调用 iptables/.libs/下的xtables-nft-multi和xtables-legacy-multi。其中nft是新命令,legacy是iptables命令。
root@ubuntu:~/iptables-1.8.5# ls iptables/xtables-nft-multi
iptables/xtables-nft-multi
root@ubuntu:~/iptables-1.8.5# ls iptables/xtables-legacy-multi
iptables/xtables-legacy-multi
root@ubuntu:~/iptables-1.8.5# ls iptables/.libs/
xtables-legacy-multi xtables-nft-multi
root@ubuntu:~/iptables-1.8.5# file iptables/xtables-nft-multi
iptables/xtables-nft-multi: Bourne-Again shell script, ASCII text executable
root@ubuntu:~/iptables-1.8.5# file iptables/.libs/xtables-legacy-multi
iptables/.libs/xtables-legacy-multi: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=875ac2a4a56de055f109563e8cce1e61f7f2b69d, with debug_info, not stripped
- snat和dnat的用法
ip和port可以指定单个,也可以指定一个范围
static void SNAT_help(void)
{
printf(
"SNAT target options:\n"
" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
" Address to map source to.\n"
"[--random] [--random-fully] [--persistent]\n");
}
./extensions/libipt_SNAT.t
:POSTROUTING
*nat
-j SNAT --to-source 1.1.1.1;=;OK
-j SNAT --to-source 1.1.1.1-1.1.1.10;=;OK
-j SNAT --to-source 1.1.1.1:1025-65535;;FAIL
-j SNAT --to-source 1.1.1.1 --to-source 2.2.2.2;;FAIL
-p tcp -j SNAT --to-source 1.1.1.1:1025-65535;=;OK
-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65535;=;OK
-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65536;;FAIL
-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65535 --to-source 2.2.2.2-2.2.2.20:1025-65535;;FAIL
-j SNAT;;FAIL
static void DNAT_help(void)
{
printf(
"DNAT target options:\n"
" --to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
" Address to map destination to.\n"
"[--random] [--persistent]\n");
}
static void DNAT_help_v2(void)
{
printf(
"DNAT target options:\n"
" --to-destination [<ipaddr>[-<ipaddr>]][:port[-port[/port]]]\n"
" Address to map destination to.\n"
"[--random] [--persistent]\n");
}
./extensions/libipt_DNAT.t
:PREROUTING
*nat
-j DNAT --to-destination 1.1.1.1;=;OK
-j DNAT --to-destination 1.1.1.1-1.1.1.10;=;OK
-j DNAT --to-destination 1.1.1.1:1025-65535;;FAIL
-j DNAT --to-destination 1.1.1.1 --to-destination 2.2.2.2;;FAIL
-p tcp -j DNAT --to-destination 1.1.1.1:1025-65535;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1-1.1.1.10:1025-65535;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1-1.1.1.10:1025-65536;;FAIL
-p tcp -j DNAT --to-destination 1.1.1.1-1.1.1.10:1025-65535 --to-destination 2.2.2.2-2.2.2.20:1025-65535;;FAIL
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/1000;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/3000;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/65535;=;OK
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/0;;FAIL
-p tcp -j DNAT --to-destination 1.1.1.1:1000-2000/65536;;FAIL
-j DNAT;;FAIL
- 注册target
注册 snat target
static struct xtables_target snat_tg_reg = {
.name = "SNAT",
.version = XTABLES_VERSION,
.family = NFPROTO_IPV4,
.size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
.userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
.help = SNAT_help,
.x6_parse = SNAT_parse,
.x6_fcheck = SNAT_fcheck,
.print = SNAT_print,
.save = SNAT_save,
.x6_options = SNAT_opts,
.xlate = SNAT_xlate,
};
xtables_register_target(&snat_tg_reg);
注册 dnat target
static struct xtables_target dnat_tg_reg[] = {
{
.name = "DNAT",
.version = XTABLES_VERSION,
.family = NFPROTO_IPV4,
.revision = 0,
.size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
.userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
.help = DNAT_help,
.print = DNAT_print,
.save = DNAT_save,
.x6_parse = DNAT_parse,
.x6_fcheck = DNAT_fcheck,
.x6_options = DNAT_opts,
.xlate = DNAT_xlate,
},
{
.name = "DNAT",
.version = XTABLES_VERSION,
.family = NFPROTO_IPV4,
.revision = 2,
.size = XT_ALIGN(sizeof(struct nf_nat_range2)),
.userspacesize = XT_ALIGN(sizeof(struct nf_nat_range2)),
.help = DNAT_help_v2,
.print = DNAT_print_v2,
.save = DNAT_save_v2,
.x6_parse = DNAT_parse_v2,
.x6_fcheck = DNAT_fcheck_v2,
.x6_options = DNAT_opts,
.xlate = DNAT_xlate_v2,
},
};
xtables_register_targets(dnat_tg_reg, ARRAY_SIZE(dnat_tg_reg));
- 命令行流程
legacy命令实现
static const struct subcommand multi_subcommands[] = {
#ifdef ENABLE_IPV4
{"iptables", iptables_main},
{"main4", iptables_main},
{"iptables-save", iptables_save_main},
{"save4", iptables_save_main},
{"iptables-restore", iptables_restore_main},
{"restore4", iptables_restore_main},
{"iptables-legacy", iptables_main},
{"iptables-legacy-save",iptables_save_main},
{"iptables-legacy-restore",iptables_restore_main},
#endif
{"iptables-xml", iptables_xml_main},
{"xml", iptables_xml_main},
#ifdef ENABLE_IPV6
{"ip6tables", ip6tables_main},
{"main6", ip6tables_main},
{"ip6tables-save", ip6tables_save_main},
{"save6", ip6tables_save_main},
{"ip6tables-restore", ip6tables_restore_main},
{"restore6", ip6tables_restore_main},
{"ip6tables-legacy", ip6tables_main},
{"ip6tables-legacy-save",ip6tables_save_main},
{"ip6tables-legacy-restore",ip6tables_restore_main},
#endif
{NULL},
};
int main(int argc, char **argv)
{
return subcmd_main(argc, argv, multi_subcommands);
}
新命令实现
static const struct subcommand multi_subcommands[] = {
{"iptables-xml", iptables_xml_main},
{"xml", iptables_xml_main},
{"iptables", xtables_ip4_main},
{"iptables-nft", xtables_ip4_main},
{"main4", xtables_ip4_main},
{"save4", xtables_ip4_save_main},
{"restore4", xtables_ip4_restore_main},
{"iptables-save", xtables_ip4_save_main},
{"iptables-restore", xtables_ip4_restore_main},
{"iptables-nft-save", xtables_ip4_save_main},
{"iptables-nft-restore", xtables_ip4_restore_main},
{"ip6tables", xtables_ip6_main},
{"ip6tables-nft", xtables_ip6_main},
{"main6", xtables_ip6_main},
{"save6", xtables_ip6_save_main},
{"restore6", xtables_ip6_restore_main},
{"ip6tables-save", xtables_ip6_save_main},
{"ip6tables-restore", xtables_ip6_restore_main},
{"ip6tables-nft-save", xtables_ip6_save_main},
{"ip6tables-nft-restore", xtables_ip6_restore_main},
{"iptables-translate", xtables_ip4_xlate_main},
{"ip6tables-translate", xtables_ip6_xlate_main},
{"iptables-restore-translate", xtables_ip4_xlate_restore_main},
{"ip6tables-restore-translate", xtables_ip6_xlate_restore_main},
{"arptables", xtables_arp_main},
{"arptables-nft", xtables_arp_main},
{"arptables-restore", xtables_arp_restore_main},
{"arptables-nft-restore", xtables_arp_restore_main},
{"arptables-save", xtables_arp_save_main},
{"arptables-nft-save", xtables_arp_save_main},
{"ebtables-translate", xtables_eb_xlate_main},
{"ebtables", xtables_eb_main},
{"ebtables-restore", xtables_eb_restore_main},
{"ebtables-save", xtables_eb_save_main},
{"ebtables-nft", xtables_eb_main},
{"ebtables-nft-restore", xtables_eb_restore_main},
{"ebtables-nft-save", xtables_eb_save_main},
{"xtables-monitor", xtables_monitor_main},
{NULL},
};
//main函数
int main(int argc, char **argv)
{
return subcmd_main(argc, argv, multi_subcommands);
}
每个命令对应一个函数,比如执行 iptables 命令时就会调用 xtables_ip4_main。
下面是main函数流程和参数解析
xtables_ip4_main -->xtables_main --> do_commandx -->do_parse
重点看下解析参数部分
do_parse
{
while ((cs->c = getopt_long(argc, argv,
"-:A:C:D:R:I:L::S::M:F::Z::N:X::E:P:Vh::o:p:s:d:j:i:fbvw::W::nt:m:xc:g:46",
opts, NULL)) != -1) {
switch (cs->c) {
case 'j':
//获取-j指定的target
command_jump(cs, optarg);
break;
default:
//获取match或者target的参数,参见下面的分析a
if (command_default(cs, &xtables_globals) == 1)
continue;
break;
}
}
//调用target的final_check函数,参见下面的分析b。tfcall全称为target final call
if (cs->target != NULL)
xtables_option_tfcall(cs->target);
}
a.
int command_default(struct iptables_command_state *cs,
struct xtables_globals *gl)
{
struct xtables_rule_match *matchp;
struct xtables_match *m;
if (cs->target != NULL &&
(cs->target->parse != NULL || cs->target->x6_parse != NULL) &&
cs->c >= cs->target->option_offset &&
cs->c < cs->target->option_offset + XT_OPTION_OFFSET_SCALE) {
xtables_option_tpcall(cs->c, cs->argv, cs->invert,
cs->target, &cs->fw);
return 0;
}
}
/**
* Dispatch arguments to the appropriate parse function, based upon the
* extension's choice of API.
*/
//tpcall 全称 target parse call
void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
struct xtables_target *t, void *fw)
{
struct xt_option_call cb;
if (t->x6_parse == NULL) {
if (t->parse != NULL)
t->parse(c - t->option_offset, argv, invert,
&t->tflags, fw, &t->t);
return;
}
c -= t->option_offset;
cb.entry = xtables_option_lookup(t->x6_options, c);
if (cb.entry == NULL)
xtables_error(OTHER_PROBLEM,
"Extension does not know id %u\n", c);
cb.arg = optarg;
cb.invert = invert;
cb.ext_name = t->name;
cb.data = t->t->data;
cb.xflags = t->tflags;
cb.target = &t->t;
cb.xt_entry = fw;
cb.udata = t->udata;
t->x6_parse(&cb); //SNAT_parse
t->tflags = cb.xflags;
}
static void SNAT_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 O_TO_SRC:
if (cb->xflags & F_X_TO_SRC) {
if (!kernel_version)
get_kernel_version();
//如果内核版本大于2.6.10,则不能指定多个 --to-source
if (kernel_version > LINUX_VERSION(2, 6, 10))
xtables_error(PARAMETER_PROBLEM,
"SNAT: Multiple --to-source not supported");
}
//解析ip和port
//只要指定了port(单个port或者port范围),就会设置 range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
//只要指定了ip(单个ip或者ip范围),就会设置 range.flags |= NF_NAT_RANGE_MAP_IPS;
*cb->target = parse_to(cb->arg, portok, info);
cb->xflags |= F_X_TO_SRC;
break;
//如果命令行指定了--persistent,则设置NF_NAT_RANGE_PERSISTENT
case O_PERSISTENT:
info->mr.range[0].flags |= NF_NAT_RANGE_PERSISTENT;
break;
}
}
/* Ranges expected in network order. */
static struct xt_entry_target *
parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info)
{
struct nf_nat_ipv4_range range;
char *arg, *colon, *dash, *error;
const struct in_addr *ip;
//-p tcp -j SNAT --to-source 1.1.1.1-1.1.1.10:1025-65535
//假如设置了上面的规则,则 orig_arg 指向 --to-source 的参数,即"1.1.1.1-1.1.1.10:1025-65535"
arg = strdup(orig_arg);
if (arg == NULL)
xtables_error(RESOURCE_PROBLEM, "strdup");
memset(&range, 0, sizeof(range));
//colon指向 ":1025-65535"
colon = strchr(arg, ':');
//只要colon不为空,说明指定了port
if (colon) {
int port;
if (!portok)
xtables_error(PARAMETER_PROBLEM,
"Need TCP, UDP, SCTP or DCCP with port specification");
range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
port = atoi(colon+1);
if (port <= 0 || port > 65535)
xtables_error(PARAMETER_PROBLEM,
"Port `%s' not valid\n", colon+1);
//不能使用":"分隔port,得使用"-"分隔
error = strchr(colon+1, ':');
if (error)
xtables_error(PARAMETER_PROBLEM,
"Invalid port:port syntax - use dash\n");
//没有"-",说明只指定了一个port
dash = strchr(colon, '-');
if (!dash) {
range.min.tcp.port
= range.max.tcp.port
= htons(port);
} else {
//指定了最小和最大port
int maxport;
maxport = atoi(dash + 1);
if (maxport <= 0 || maxport > 65535)
xtables_error(PARAMETER_PROBLEM,
"Port `%s' not valid\n", dash+1);
if (maxport < port)
/* People are stupid. */
xtables_error(PARAMETER_PROBLEM,
"Port range `%s' funky\n", colon+1);
range.min.tcp.port = htons(port);
range.max.tcp.port = htons(maxport);
}
/* Starts with a colon? No IP info...*/
//如果colon和arg地址相同,说明只指定了port,没有指定ip
if (colon == arg) {
free(arg);
return &(append_range(info, &range)->t);
}
*colon = '\0';
}
//解析指定了ip,可以指定一个ip,或者通过"-"指定ip范围
range.flags |= NF_NAT_RANGE_MAP_IPS;
dash = strchr(arg, '-');
if (colon && dash && dash > colon)
dash = NULL;
if (dash)
*dash = '\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 (dash) {
ip = xtables_numeric_to_ipaddr(dash+1);
if (!ip)
xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
dash+1);
range.max_ip = ip->s_addr;
} else
range.max_ip = range.min_ip;
free(arg);
return &(append_range(info, &range)->t);
}
b.
/**
* Dispatch arguments to the appropriate final_check function, based upon the
* extension's choice of API.
*/
void xtables_option_tfcall(struct xtables_target *t)
{
if (t->x6_fcheck != NULL) {
struct xt_fcheck_call cb;
cb.ext_name = t->name;
cb.data = t->t->data;
cb.xflags = t->tflags;
cb.udata = t->udata;
t->x6_fcheck(&cb); //SNAT_fcheck
} else if (t->final_check != NULL) {
t->final_check(t->tflags);
}
if (t->x6_options != NULL)
xtables_options_fcheck(t->name, t->tflags, t->x6_options); //SNAT_opts
}
static void SNAT_fcheck(struct xt_fcheck_call *cb)
{
static const unsigned int f = F_TO_SRC | F_RANDOM;
static const unsigned int r = F_TO_SRC | F_RANDOM_FULLY;
struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
//如果命令行指定了 --random,则设置NF_NAT_RANGE_PROTO_RANDOM
if ((cb->xflags & f) == f)
mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
//如果命令行指定了--random-fully,则设置NF_NAT_RANGE_PROTO_RANDOM_FULLY
if ((cb->xflags & r) == r)
mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
}
snat选项
enum {
O_TO_SRC = 0,
O_RANDOM,
O_RANDOM_FULLY,
O_PERSISTENT,
O_X_TO_SRC,
F_TO_SRC = 1 << O_TO_SRC,
F_RANDOM = 1 << O_RANDOM,
F_RANDOM_FULLY = 1 << O_RANDOM_FULLY,
F_X_TO_SRC = 1 << O_X_TO_SRC,
};
static const struct xt_option_entry SNAT_opts[] = {
{.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
.flags = XTOPT_MAND | XTOPT_MULTI},
{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
{.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
XTOPT_TABLEEND,
};