Netfilter学习之NAT类型动态配置(二)NAT类型介绍及MASQUERADE
在上一节中,我们介绍了iptables和netfilter的基本关系,在这里我们会进一步介绍现有的NAT以及Linux中大多已实现的MASQUERADE实现原理。
1. NAT类型介绍
常用的NAT类型主要有Full Cone NAT(全锥型), Restricted NAT(限制型锥型), Port Restricted NAT(端口限制型锥型), Symmetric NAT(对称型)四种。
四种类型的主要区别在于对外界访问内部IP的控制力度。为方便解释,我们使用如下的用语来说明四种NAT类型的不同之处。
用语定义
- 内部Tuple:指内部主机的私有地址和端口号所构成的二元组,即内部主机所发送报文的源地址、端口所构成的二元组
- 外部Tuple:指内部Tuple经过NAT的源地址/端口转换之后,所获得的外部地址、端口所构成的二元组,即外部主机收到经NAT转换之后的报文时,它所看到的该报文的源地址(通常是NAT设备的地址)和源端口
- 目标Tuple:指外部主机的地址、端口所构成的二元组,即内部主机所发送报文的目标地址、端口所构成的二元组
详细释义
Full Cone NAT:所有来自同一 个内部Tuple X的请求均被NAT转换至同一个外部Tuple Y,而不管这些请求是不是属于同一个应用或者是多个应用的。除此之外,当X-Y的转换关系建立之后,任意外部主机均可随时将Y中的地址和端口作为目标地址 和目标端口,向内部主机发送UDP报文,由于对外部请求的来源无任何限制,因此这种方式虽然足够简单,但却不那么安全
Restricted Cone NAT: 它是Full Cone的受限版本:所有来自同一个内部Tuple X的请求均被NAT转换至同一个外部Tuple Y,这与Full Cone相同,但不同的是,只有当内部主机曾经发送过报文给外部主机(假设其IP地址为Z)后,外部主机才能以Y中的信息作为目标地址和目标端口,向内部 主机发送UDP请求报文,这意味着,NAT设备只向内转发(目标地址/端口转换)那些来自于当前已知的外部主机的UDP报文,从而保障了外部请求来源的安 全性
Port Restricted Cone NAT:它是Restricted Cone NAT的进一步受限版。只有当内部主机曾经发送过报文给外部主机(假设其IP地址为Z且端口为P)之后,外部主机才能以Y中的信息作为目标地址和目标端 口,向内部主机发送UDP报文,同时,其请求报文的源端口必须为P,这一要求进一步强化了对外部报文请求来源的限制,从而较Restrictd Cone更具安全性
Symmetric NAT:这是一种比所有Cone NAT都要更为灵活的转换方式:在Cone NAT中,内部主机的内部Tuple与外部Tuple的转换映射关系是独立于内部主机所发出的UDP报文中的目标地址及端口的,即与目标Tuple无关; 在Symmetric NAT中,目标Tuple则成为了NAT设备建立转换关系的一个重要考量:只有来自于同一个内部Tuple 、且针对同一目标Tuple的请求才被NAT转换至同一个外部Tuple,否则的话,NAT将为之分配一个新的外部Tuple;打个比方,当内部主机以相 同的内部Tuple对2个不同的目标Tuple发送UDP报文时,此时NAT将会为内部主机分配两个不同的外部Tuple,并且建立起两个不同的内、外部 Tuple转换关系。与此同时,只有接收到了内部主机所发送的数据包的外部主机才能向内部主机返回UDP报文,这里对外部返回报文来源的限制是与Port Restricted Cone一致的。不难看出,如果说Full Cone是要求最宽松NAT UDP转换方式,那么,Symmetric NAT则是要求最严格的NAT方式,其不仅体现在转换关系的建立上,而且还体现在对外部报文来源的限制方面。
在Netfilter中,不同NAT的实现在内核中进行。而通常情况下,Linux系统自带的NAT类型多为端口限制型锥型NAT或者对称型。对此,我们参照着内核和用户层的源码进行说明。这里之所以说为端口限制型锥型NAT或对称型NAT是因为系统自带的MASQUERADE部分模块根据配置命令行的不同可以实现上述两种不同类型的NAT。由上一篇,大家应该都知道了iptables和Netfilter的关系,这里我们先看看在用户层,Iptables命令中关于MASQUERADE的实现。
源码名libipt_MASQUERADE.c,位置的话不同版本的linux系统可能不大一样。该部分主要是实现对配置masquerade命令的解析以及注册,注册之后即可激活内核空间中的masquerade相应钩子函数实现masquerade功能。关于钩子函数,在下一讲中将会简单介绍。
首先看一下该源码的大体框架。
<div align=center>
由图可见,该部分主要由help, opts, init, parse_ports, parse, final_check, print, save, masq, _init组成。按
各个函数、结构的名称大致可以猜测到该函数做的是什么功能,之后实现其他几种NAT均需要模仿该模块完成。
首先需要介绍的是masq。源码如下
static struct iptables_target masq = {
NULL,
.name= "MASQUERADE",
.version= IPTABLES_VERSION,
.size= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
.userspacesize= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
.help= &help,
.init= &init,
.parse= &parse,
.final_check= &final_check,
.print= &print,
.save= &save,
.extra_opts= opts
};
该部分是整个MASQUERADE的注册,给定了该结构体的各个元素(名称、版本、函数实现等)。所有其他的iptables命令行参数均以此形式添加。
help部分即命令行常用的帮助部分,在MASQUERADE中,实现如下。
/* Function which prints out usage message. */
static void
help(void)
{
printf(
"MASQUERADE v%s options:\n"
" --to-ports <port>[-<port>]\n"
"Port (range) to map to.\n"
" --random\n"
"Randomize source port.\n"
"\n"
,
IPTABLES_VERSION);
}
这里我们可以很清晰的了解iptables中MASQUERADE如何配置。之前所说的MASQUERADE可以配置两种不同类型的NAT也来源于此,提供的--random功能即为对称型NAT,其他两种形式命令行则为端口限制型锥形NAT的配置。
另外还需要说明的是这里parse是解析命令行,使用的也是大名鼎鼎的opt, getlongopt系列函数,需要了解的可以自行百度谷歌搜索。parse_ports部分主要是实现将输入的" --to-ports <port>[-<port>]"
加以识别。其余都是很容易弄懂的一些函数,在此不做过多的介绍,有问题的可以联系我。本篇结束。