SNAT 实验
运行给定网络拓扑(nat_topo.py)
在 n1, h1, h2, h3 上运行相应脚本
在 n1 上运行 nat 程序: n1# ./nat exp1.conf
在 h3 上运行 HTTP 服务:h3# python ./http_server.py
在 h1, h2 上分别访问 h3 的 HTTP 服务
DNAT 实验
运行给定网络拓扑(nat_topo.py)
在 n1, h1, h2, h3 上运行相应脚本
在 n1 上运行 nat 程序: n1# ./nat exp2.conf
在 h1, h2 上分别运行 HTTP Server: h1/h2# python ./http_server.py
在 h3 上分别请求 h1, h2 页面
手动构造一个包含两个 nat 的拓扑
节点 n1 作为 SNAT, n2 作为 DNAT,主机 h2 提供 HTTP 服务,主机 h1 穿过两个 nat 连接到 h2 并获取相应页面
int parse_config(const char *filename)
函数负责根据 config 文件中读取的每一行字符串,分别配置 external-iface
,internal-iface
以及 DNAT Rules
。 具体实现如下:
int parse_config(const char *filename) {
FILE * fd = fopen(filename, "r");
if (fd == NULL) {
return 0;
}
char * line = (char *)malloc(MAX_LINE);
while (fgets(line, MAX_LINE, fd) != NULL) {
if (strstr(line, internal_iface_des)) {
parse_internal_iface(line);
} else if (strstr(line, external_iface_des)) {
parse_external_iface(line);
} else if (strstr(line, dnat_rules_des)) {
parse_dnat_rules(line);
}
}
return 0;
}
其中调用了三个自己编写的 parse 函数,负责具体条目的配置。
static int get_packet_direction(char *packet)
函数负责返回数据包方向:
当源地址为内部地址,且目的地址为外部地址时,方向为 DIR_OUT
当源地址为外部地址,且目的地址为 external_iface 地址时,方向为 DIR_IN
具体设计时,可以根据源地址进行判断。具体实现如下:
static int get_packet_direction(char *packet)
{
struct iphdr * ip = packet_to_ip_hdr(packet);
rt_entry_t * rt = longest_prefix_match(ntohl(ip->saddr));
if (rt->iface->index == nat.internal_iface->index) {
return DIR_OUT;
} else if (rt->iface->index == nat.external_iface->index) {
return DIR_IN;
}
return DIR_INVALID;
}
实验结果如下:
上图可知,H1 和 H2 作为客户端向服务器端 H3 请求了两个网页。请求结果如下:
H3->H1:
H3->H2:
由上述结果可知,SNAT 转换实验成功。
实验结果如下:
上图可知,H1 和 H2 作为服务器端,H3 作为客户端分别向 H1 和 H2 请求了两个网页。请求结果如下:
H1 -> H3:
H2 -> H3:
由上述结果可知,DNAT 转换实验成功。
自建的网络拓扑文件如下:
h1.cmd('ifconfig h1-eth0 10.21.0.1/16')
h1.cmd('route add default gw 10.21.0.254')
h2.cmd('ifconfig h2-eth0 10.22.0.1/16')
h2.cmd('route add default gw 10.22.0.254')
n1.cmd('ifconfig n1-eth0 10.21.0.254/16')
n1.cmd('ifconfig n1-eth1 159.226.39.43/24')
n2.cmd('ifconfig n2-eth0 10.22.0.254/16')
n2.cmd('ifconfig n2-eth1 159.226.39.123/24')
即 H1 <-> N1 <-> N2 <-> H2
节点 N1 作为 SNAT, N2 作为 DNAT,主机 H2 提供 HTTP 服务,主机 H1 穿过两个 nat 连接到 h2 并获取相应页面。
实验结果如下:
上图可知,H1 和 H2 作为客户端向服务器端 H3 请求了两个网页。请求结果如下:
H2 -> H1:
由上述结果可知,实验三成功。
ICMP 的报文格式如下:
类型(Type) | 代码(Code) | 检验和(Checksum) |
---|---|---|
标识符(Identifier) | 序列号(Sequence number) | |
选项(Option) |
当主机发送 ICMP 报文的时候,会根据 Type+Code 的值,来生成源端口号,根据 Identifier 的值生成目的端口号,即发送到路由器的报文如下:
源报文:
源 IP | 源端口 | 目的 IP | 目的端口 |
---|---|---|---|
192.168.0.2 | (Type+Code) | 200.10.2.1 | Identifier |
在路由器上进行 SNAT,源 IP 更改后 ICMP 报文中的 Identifier 会改变,记作 IDENTIFIER。
此时报文如下:
源 IP | 源端口 | 目的 IP | 目的端口 |
---|---|---|---|
188.10.1.2 | IDENTIFIER | 200.10.2.1 | Identifier |
对应的 NAT 表:
源 IP | 源端口 | 协议 | 目的 IP | 目的端口 |
---|---|---|---|---|
192.168.0.2 | (Type+Code) | ICMP | 188.10.1.2 | IDENTIFIER |
在服务器收到 ICMP 请求后,生成 ICMP 响应报文,响应报文中的(Type+Code)会作为源端口,IDENTIFIER 作为目的端口。
源报文
源 IP | 源端口 | 目的 IP | 目的端口 |
---|---|---|---|
200.10.2.1 | (Type+Code) | 188.10.1.2 | IDENTIFIER |
报文到达路由器后,根据 NAT 表中,查询目的 IP 和目的端口为 188.10.1.2 和 IDENTIFIER 的信息。将目的 IP 和目的端口换为 192.168.0.2 和(Type+Code),报文就可以成功的到达客户端了。 |