1.NAT网络知识的学习

1.1.创建虚拟机

本次我使用的是VirtualBox软件,抛弃了VmWare。因为VBox是开源的,而VmWare则需要序列号,网上关于如何安装VBox的教程非常多,我个人认为最好的莫过于其自身的使用手册,需要时多翻一翻总是一件好事。

我安装的Linux操作系统是Ubuntu12_04版本,分配的内存至少为512M,分配的硬盘为10G,具体分区如下所示:

/boot   ext3     主分区     512M

       swap     主分区     Host主机内存的2

/       ext3     逻辑分区至少5G

/home   ext3     逻辑分区剩下的空间

在以前玩耍虚拟机的时候我就发现一个奇怪的现象,当使用NAT方式上网的时候,虚拟机里的Client可以pingHost,但是Host却无法pingClient。个人感觉这个现象很是让人惊奇,ping这玩意儿不是双向的吗,为什么会出现这种“莫名其妙”的情况呢?由此触发我对NAT的深入学习。

1.2.初识使用NAT上网的Ubuntu

1.2.1.有用的快捷键

使用VBox创建好虚拟机并在其上安装好Ubuntu之后,Ubuntu操作系统默认就是以NAT的方式来上网的,可以使用快捷键Ctrl+Alt+T的方式在图形界面里调出命令符操作界面,如下图所示:

144642741.jpg

显然命令符界面的字体太小了,可以使用快捷键Ctrl+Shift+‘+’来放大界面与字体,对应的使用快捷键Ctrl+‘-’来缩小字体。

可以使用另外一种方法彻底的进入命令符界面,即快捷键Ctrl+Alt+F1,如下图所示:

161943927.jpg

使用快捷键Ctrl+Alt+F7可以回到图形界面。

可以看到命令输入提示符是‘~$’,这表示此时登陆的用户不是root用户,如果要切换到root账号的话,那么需要使用命令‘sudo -i’,切换到root账号之后的命令输入提示符是‘~#’

1.2.2.使用NAT上网时的网络参数

以上都是关于Ubuntu的操作方面的一些小技巧,下面我们来点“真刀×××”的东东。前面说了,安装Ubuntu之后Client系统默认是以NAT的方式来上网的,那么此时Ubuntu的网络参数是怎么样的呢?我们可以使用命令‘ifconfig’来得到最基本的信息,如下图所示:

162841170.jpg

核心的信息都在红线框之内,我们可以看出使用该命令查询到的网络参数与Windows比较起来在人性化方面差一些,因为从上图中我们不知道Client系统的网关地址,DNS地址和DHCP地址,要想知道这些基本参数就要使用其他的命令了。

使用命令route -n查询Client上的路由信息,如下所示:

144809151.jpg

可以看出,Client系统的默认网关地址是10.0.2.2Flags当中的U表示该路由是启动的,G表示需要通过外部主机来传递数据包。

查询DNS地址的时候,几乎所有的书籍和网上的资料都指向文件/etc/resolv.conf,描述DNS地址都是在该文件里设置的,可是当我查阅该文件内容时却让我大跌眼镜。如下所示:

163504405.jpg

可以看到ClientDNS地址是本地内循环地址,我不禁长叹一句“How it can be?”。当我接下去继续查找DHCP地址的时候却发现了有趣的现象,如下所示:

163759364.jpg

首先,DHCP服务器给Client分配IP地址的时候都会有租赁信息,该信息存放在目录/var/lib/dhcp下面,严格意义说来应该是在文件dhclient.leases里面,但是我却发现这个文件是空文件,所以就查询了那个很长名字的文件(dhclient-9f…….lease),在得到了DHCP地址的同时却意外发现了DNS的地址,正所谓无心插柳柳成荫啊,更让我惊奇的是这三个DNS地址居然与Host主机(我是在我的Dell笔记本上安装的虚拟机和Ubuntu,这里就把Host主机认为是我的Dell笔记本电脑哈)的DNS地址完全相同,如下所示:

164310844.jpg

这不禁让我“浮想联翩”,VBox究竟是怎么样获取到Host主机的DNS地址信息并配置给Client系统的呢?如有哪位大虾能够代为解答,小弟不胜感激!

还有一个问题,/etc/resolv.conf中明明将ClientDNS地址设置为本地内循环地址,可DHCP服务器配置给ClientIP地址租赁信息里却含有真正起作用的DNS地址,这二者究竟是一个什么样的联系?DHCP除了给Client分配IP地址之外,还分配了哪些额外的有分量的信息呢?这两个问题还请高手解答。

最后,我在网络上终于查到命令nm-tool可以获取到所有的网络参数,如下所示:

100449350.jpg

也许你注意到了一给Client上电,它就会从DHCP服务器处获取IP地址,那么是什么文件告诉Client以这样的方式获取IP地址呢?答案就是文件/etc/network/interfaces,如下图所示:

100649921.jpg

如果我要自己设置ClientIP地址,不让它自动从DHCP处获取IP地址,怎么办?简单,修改这个文件即可,具体细节在下面的章节描述。

1.3.初步分析ClientHost之间的通信

1.3.1.开通SSH服务

我喜欢在Host里面使用工具Secure Shell Client登陆Client系统进行各种操作,但前提条件是Client系统要开放SSH服务,此时的Client系统等同于SSH服务器。那么如何在Client上开通SSH服务呢?如下图所示:

100822520.jpg

可以看到命令apt-get install openssh-server是用来安装SSH服务,不过需要注意的是该命令要使用root账号执行。

那么怎么知道SSH服务确实是运行起来了呢?可以使用命令netstat来查询Client监听了哪些本地端口,以此确认SSH服务的正常运行。如下图所示:

100939609.jpg

可以看到详细的命令是netstat –l -n,其中的-l表示列出正在监听的端口,-n表示使用IPPort来显示(不要使用Hostname)。从红线框内可以看到,本地端口号22已经处于监听的状态了,证明SSH服务已经正常运行起来。

此处插入一个题外话,端口号22对应的是SSH服务,那么其他端口号对应的又是些什么服务呢,这样的信息可以在哪里获取到?答案就是/etc/services。如下图所示:

101044250.jpg

好了,回归正题。SSH服务既然已经开通了,那么在Host里我就使用Secure Shell Client来登陆了,可是真能登陆的上吗?看看下面的截图:

101227692.jpg

101243925.jpg

这是怎么回事,网络不通吗?由此很容易联想到使用ping命令来检查ClientHost之间是否通信顺畅。

1.3.2.Clientping Host

首先,这里先奉上Host的网络参数,如下图所示:

101341658.jpg

可以看到HostIP10.121.125.44/255.255.255.0,而ClientIP10.0.2.15/255.255.255.0,二者并不处于一个网段当中。

我先在Client里尝试pingHost,为了清楚的知道数据包的传递细节,我在Client里使用tcpdump工具抓取数据包,在Host里使用Wireshark抓取数据包,看看会有什么样的结果。操作流程如下:

   (1)Client里开启两个命令符输入窗口,在第一个窗口里输入命令ping –c 1 10.121.125.44但不要执行。参数-c 1的意思是只ping一个数据包;

   (2)在第二个命令符窗口输入命令tcpdump–e –i eth0 –n –XX > testPingHost,暂不执行;

(3)Host里运行软件Wireshark,开始抓取数据包(对于该软件的操作方法网上多的是,自行查阅)

(4)执行步骤(2)

(5)执行步骤(1)

我们先来看看Client端都抓到了什么样的数据包,如下图所示:

101603829.jpg

在具体分析上面的测试结果之前,我们先从理论上分析一下数据包应该怎么走才能从Client到达Host呢?首先,因为ClientHost并不在同一个网段之内,所以ICMP echo request的数据包肯定是要先送到网关处,由网关将该数据包转给Host主机,Host收到该数据包,做出响应,将ICMP echo reply数据包传递给网关,再由网关回传给Client,以此完成整个ping的数据通信过程。

以太网上传递的都是MAC数据帧,该数据帧的格式如下图所示:

101712512.jpg

回到testPingHost这个测试文件,我们可以清楚的看到第一个MAC数据帧是发送给网关(10.0.2.2)的,因为目的MAC地址是52:54:00:12:35:02,这正好是Client默认网关(10.0.2.2)MAC地址。

Client的网关收到该数据包之后解析其中IP数据报文表头,发现目的IP地址是10.121.125.44,于是乎它就想尽一切办法将该数据包转给HostHost收到ICMP echo request数据包之后做出处理,并将响应ICMP echo reply数据包传给Client的网关,再由Client的网关回传给Client。实际上,这里VBox肯定对我隐藏了某些细节,例如Host的网关是10.121.125.1,如果有一个ICMP echo request数据包被传递到Host(我在想,这个数据包真的是到了Host的物理网卡了吗?),且该包的来源是10.0.2.15,那么HostICMP echo reply数据包应该是传递给Host的网关去处理,但是实际上Host直接将响应数据包传递给了Client的网关。VBox究竟耍了什么样的“把戏”,使得这样的结果看似情理之中,却意料之外呢?我个人感觉虚拟机在底层的实现还有很多东东要学习啊,有待研究。

此处我并未对文件testPingHost中的每个字节做出详尽的分析,这是一件很繁琐且枯燥的事情,虽然如此,如果真正去做了,那么对于MAC帧、IP数据报文和ICMP数据报文必将有一个“踏实”的感觉和认识。

另外一个细节是,Client与它的网关相互通信时,并没有发送ARP广播消息,这证明二者知道彼此的MAC地址。关于ARP的学习,后面的内容会做详细分析。

我们再来看看在Host里抓取的数据包,如下图所示:

101944945.jpg

没有ICMP的数据包,甚至连TCP的数据包都没有。这从一个侧面在印证着我的猜测,即来自于ClientICMP echo request数据包,由于其目的地就是Host主机,所以该数据包并未真正到达也没有必要到达Host的物理网卡,一切的一切都在Host主机内部做了处理,否则工具Wireshark肯定能抓住这样的包包。

1.3.3.ClientPing Host的邻居

在上一小节我猜想Client发到Host主机的ICMP echo request数据包并未真正到达Host主机的物理网卡,所以Wireshark没有抓到这个数据包。那么自然地,我想到了如果Client去尝试pingHost主机所处局域网里的“邻居”,那么ICMP echo request数据包毫无疑问要“经过”Host主机的物理网卡,这样的话Wireshark不就能抓到这样的数据包了吗?理论分析的结果还是需要靠实践的证明。

140453944.jpg

首先确定主机AIP地址是10.121.125.66Host主机与主机A同属于一个网段,它哥们两肯定能相互通信的哈,于是开始在Client里尝试ping通主机A,操作步骤如下:

   (1)Client里开启两个命令符输入窗口,在第一个窗口里输入命令ping –c 1 10.121.125.66但不要执行。参数-c 1的意思是只ping一个数据包;

   (2)在第二个命令符窗口输入命令tcpdump–e –i eth0 –n –XX > testPingNeibor,暂不执行;

(3)Host里运行软件Wireshark,开始抓取数据包(对于该软件的操作方法网上多的是,自行查阅)

(4)执行步骤(2)

(5)执行步骤(1)

我们先来看看Client抓到的数据包,如下图所示:

140717872.jpg

这个结果与上一小节的测试结果相比,最主要的不同就是目的IP地址不一样,上一小节的目的IP地址是10.121.125.44,而此次的目的IP地址是10.121.125.66

来看看在Host主机里抓到了什么包包,如下图所示:

140809744.jpg

果然不出所料,在Host主机的物理网卡上我们终于抓到了ICMP的数据包。不过这里我要解释一下为什么Host主机的IP地址由前一小节的10.121.125.44变成了10.121.125.143,由于我是在公司的局域网里做的这些实验,而我的电脑(Host主机)IP是通过公司的DHCP服务器分配的,由于IP地址租赁时间的原因,我的电脑上的IP地址隔一段时间会被重新分配一次,所以IP地址就变成了上图所示的10.121.125.l43。不过这并不影响我对NAT网络知识的学习。

分析到这里可以清楚的看到,发起ping通信的是Client端,其源IP地址是10.0.2.15ICMP echo request数据包通过Client的默认网关(10.0.2.2)转到Host主机(10.121.125.143),再由Host主机发送到目的主机A(10.121.125.66)上,可我们在Host主机上抓到的数据包却显示该ICMP echo request数据包的源IP地址是Host主机本身,由此促发人们这样想:这是怎么回事?Host主机太“卑鄙”了,居然做这种“偷梁换柱”的事情,而且我们可以看到从主机A回来的响应数据包只传递到Host主机,可是Client明明收到了来自主机A的响应包,那么这究竟是怎么回事呢?

其实当前这个现象就是NAT技术的执行结果,这个问题的答案等同于NAT是什么,以及它做了什么事情。

经过1.3小节的学习,可以回答一个潜在的问题:MAC地址和公共IP地址都是全球唯一的,那么计算机之间的通信只需要一种地址来标识自己就可以了嘛,为什么这两种全球唯一的地址都要存在呢?聪明的你已经知道了吧j_0028.gif

1.4.NAT的理论知识

关于NAT的理论知识,网上多的是,甚至有不少大牛的巨著都讲到了。我这里只是班门弄斧的对大牛们的描述做一个自我的总结而已。

这里遵从Douglas E.Comber这位大牛的著作,将运行NAT软件的电脑称之为NAT Box。一般情况下要求NAT Box的一端连接具有私有IP地址的电脑(称之为Internal Host),而另一端至少具备一个在公共因特网上的有效地址G。如下图所示:

141157303.jpg

NAT Box对来自于Internal Host的数据包以及从公共Internet传来的数据包进行了IP地址转换,具体说来,如果Internal Host A想要访问公共Internet网上的资源,当请求数据包到达NAT Box之后,NAT Box将该数据包的源IP地址由192.168.1.100替换成公共InternetIP地址G;当公共Internet将资源返回到NAT Box时,NAT Box又将该数据包的目的IP地址替换成内部的Internal Host A地址。

内部Internal Host通过NAT上网时,公共Internet看到是NAT Box在索取资源,它看不到真正请求资源的是Internal Host,即Internal Host相对于公共Internet来说是透明的。我们都知道Internal HostIP是私有IP,这样的IP在公共Internet上是不能够出现的,但是因为NAT的缘故,即便是私有IPInternal Host也一样可以访问公共Internet的资源。

由此可见,NAT Box内部必然存在一张表格,内含Internal Host与公共Internet地址相互之间的映射,内部Internal Host访问外部资源的时候建立这张表格。如果我们在私有网络里只有Internal Host A的话,那么它与G的对应就是一对一的,即这张表格可以是下面的样子:

(G, 192.168.1.100)

但实际情况却是私有网络里有很多Internal Host,如果仅仅还是与G对应而没有其他的辅助的话,那么这张表格就乱了,因为从公共Internet上回来的多个数据包使得NAT Box不知道究竟将数据包传递到哪个Internal Host处,因为这些数据包的目的地址都是G,如下所示:

(G, 192.168.1.100)

(G, 192.168.1.101)

(G, 192.168.1.102)

所以,NAT的衍生技术NAPT就出现在了人们眼前,NAPT主要是把TCP的端口号给加了进来,从而导致NAT Box维护的表格变成下面的样子:

141405139.jpg

从上表可以看到,Host AHost B需要同时访问百度服务器(百度的IP220.181.112.143),而Host C则访问谷歌服务器(谷歌的IP173.194.44.127),三者启动各自的TCP端口号21023123451274。由于这些端口号来源于Host本身,所以有可能它们都采用了相同的端口号。三个Host的访问请求可以简化成如下的形式:

(192.168.1.10021023; 220.181.112.143, 80)

(192.168.1.101, 12345; 220.181.112.143, 80)

(192.168.1.102, 1274; 173.194.44.127, 21)

当上面的请求到达NAT Box之后,NAT Box又做了什么“龌龊”的事情呢?NAT Box将这些请求中的源IP地址和源TCP端口号替换成如下的形式:

(G14003; 220.181.112.143, 80)

(G, 14010; 220.181.112.143, 80)

                                                                                                   (G, 14012;173.194.44.127, 21)

看到了吧,如果此时百度服务器和谷歌服务器都将响应数据包返回给NAT Box的话,那么NAT Box是否知道该怎么样分发对应的数据包到正确的Host了呢?答案是肯定的,如果你还没明白的话,请再想想。

如果你明白了的话,那么这里有个问题考考你。上面表格中的三个NAT Port号是否能相同呢?答案是否定的哈。

NAT理论知识我只总结了一个大概,更为详细的细节性知识请查阅网络和相关的书籍。

现在回过头来再看看1.3.3小节,对于NAT的认识是不是变得更“踏实”了一些呢?毕竟经历了理论知识的洗礼了哈,不过这里我要强调的是ICMP协议并不是TCP协议,换句话说1.3.3小节演示的内容其实并不包含TCP端口号这样的内容。那么聪明的你也许会问如果有两个Client同时ping我们的Host主机或是主机A的话,VBox如何将响应数据包正确的分发到对应的Client端呢?由于我暂时没有办法去研究NAT的实现代码,所以这里我做如下的推测:ICMP echo request数据包当中有两个字段IDENTIFIERSEQUENCE NUMBER,这两个字段唯一标识了这个request数据包,reply的数据包含有相同字段相同的值,所以VBox借助对这两个字段的分析可以正确的分发reply数据包到对应的Client端。该推断是否正确还要待以后的验证。如果有高手能确认并提出comments的话,小弟不升感激!

还有一个明显的问题:那就是NAT Box什么时候释放掉映射表当中的条目呢?在众多的相关资料中,我尚未找到关于这个问题的直接答案。我自己推测如下,如果NAT Box也作为DHCP服务器的话,那么当其中一个内部私有的Client关机之后,其IP地址自然会被释放,那么这个时候NAT Box(也是DHCP Server)肯定知道有个IP地址回到了它可以分配的地址池当中,这样NAT Box就可以释放掉与这个私有IP地址相联系的映射表当中的条目;那么如果NATBox不是DHCP服务器呢,这种情况下如何释放?我尚未考虑清楚,高手能否解答?

1.5.为什么Host Ping不通Client

分析到这里,你可能还是不明白为什么Host就是无法pingClient。这里我要求你将上一小节中学到的NAT(暂不考虑NAPT)理论知识类比到我们实际的操作上:Client对于Host而言,甚至对于主机A而言都是透明的,只有Client发起的对外通信,其响应信息才能通过Host到达ClientHost以及其他的主机并不知道Client的存在(暂不考虑NAPT),所以当它尝试pingClient时会议失败告终,那么又如何来解析此时Host ping出的数据包呢?

Host主机有自己的网络参数,当我在Host里执行命令‘ping –c 110.0.2.15时,Host主机发现10.0.2.15这个东东与自己并不处于同一网段,于是Host主机将该数据包传递到其默认网关10.121.125.1处,由于Host主机的网关也找不到10.0.2.15这个终端,所以ping的操作以失败结束。

发生这种情况的原因就是Host主机并不知道Client(10.0.2.15)其实就“藏”在它内部(之后),此时你也许回想,如果Host主机能够“绕”到它自身的内部那该多好!可以吗?当然可以,上一小节介绍的NAPT就是为此而生。

1.6.Host里使用SSH登录Client

那么根据NAPT的知识,如何操作VBox以达到外部能够主动发起与Client的通信呢?在这里我们就以在Host主机里使用Secure Shell Client工具登录Client为例子来讲解。

VBox里使用端口映射的功能就可以达到我们的目的,这个端口映射实际上就是NAPT的具体体现。如下图所示:

141927218.jpg

核心在于“Port Forwarding Rules”对话框中所添加的那条item,基于对1.4小节的学习,这里很容易联想到VBox建立了如下的映射项目:

(10.0.2.15, 22; 10.121.125.143, 4000)

即当有请求发送到10.121.125.143:4000,那么VBox就会将此请求转发到10.0.2.15:22那里。这样Host主机就成功“绕”到了它自己的内部(或是后花园j_0005.gif)。我自己推测,其实真正的情况是VBox能够监控Host主机的4000端口,当有数据包到达这个端口时,VBox就将此数据包转发给Client,因为Client就在VBox里面,它当然能够做到这样的事情。

重新启动Client,在Host主机里SSH登陆Client的结果如下图所示:

142302232.jpg

142411764.jpg

大功告成矣!

1.7.TCP三次握手的详细分析

这一小节的内容实际上是个题外话,1.6小节当中我在Host主机里使用Secure Shell Client工具成功登录Client,这个通信过程涉及到了我们耳熟能详的TCP的三次握手通信,那么深入到数据流的情况下,这三次握手的具体交互是怎么样的呢?出于兴趣,我做了总结。

理论指导实践,实践验证理论。我们首先从理论的角度复习一下TCP的三次握手,如下所示:

142520314.jpg

(1)A:数据包发起。当Client端想要与Host主机连接时,它需要发出一个要求连接的数据包,Client需要选取大于1024的某个端口来发起连接,同时在TCP表头当中置位SYN,表示Client端请求连接。Client端记下发出连接请求的序列号Seq=10001;

(2)B:数据包接收与确认数据包传送。当Host主机确认接收Client发过来的连接请求数据包之后,它会置位TCP表头当中的ACK,表示Host主机正在制作一个响应数据包,同时将TCP表头当中的ack响应序列号置成10001+1=10002,即Client发出的连接请求数据包的序列号加1。此时仅是Client要求与Host主机建立连接,由于连接都是双向的,所以Host主机同样需要与 Client端建立连接。所以你看到了TCP表头中的SYN被置位,请求序列号Seq=20001;

(3)C:回送确认数据包。当Client收到来自于Host主机的数据包之后,它发现ACK=1,所以它知道这是个响应数据包,同时它又发现响应序列号ack=10002,所以它能够确定稍早之前发送给Host主机的连接请求数据包是被收到的;另外,Client又发现该数据包的SYN=1Seq=20001,所以Client知道这同样是个请求连接的数据包,这次是Host主机请求与Client建立连接,Client如果同意建立连接(当然同意,要不然Client就有“病”了),那么它就回复一个确认数据包给Host主机,这个回复数据包中ACK=1, ack=20001+1=20002

(4)D:取得最后确认。若一切顺利,Host主机收到带有ACK=1ack=20002序号的数据包之后,就能完全建立起这次的连接了,之后的通信该怎么样就怎么样了。

OK,复习完三次握手的理论知识之后,我们来看看实际的测试结果是怎么样的呢?如下图所示:

142608393.jpg

TCP三次握手的完整数据流程就包含在上图中的三个数据包里,是不是有点让人头晕?我们一个包一个包的分析。

(1)A:数据包发起。

142652566.jpg

第一个红线框内的数据是MAC帧的数据头;第二个红线框内的数据是TCP表头当中的源端口号和目的端口号,可以看到源端口号是0xe30d = 58125,目的端口号是0x0016 = 22;第三个红线框内的数据是Seq序列号(请求数据包的序列号),即0x0154d201 = 22336001;第四个红线框内的数据是响应数据包的序列号,因为该包不是响应包,所以为0;第五个红线框内的数据含有标识位SYN(已被置位)ACK的信息。

第一个红线框和第二个红线框之间的数据是IP表头,共20个字节;第五个红线框之后的数据是TCP表头的剩余信息,此处不再赘述。

(2)B:数据包接收与确认数据包传送。

142752516.jpg

这个数据包既是响应包也是请求建立连接的数据包,第三个红框内的数据指明这个信息;第一个框内的数据是Host主机请求与Client建立连接的Seq序列号,即0x24cfca3d = 617597501;第二个框内的数据是Host主机返回给Client的响应确认ack序列号,即0x0154d202 = 22336002 = 22336001 + 1

(3)C:回送确认数据包。

142841393.jpg

红线框内的数据是Client响应Host主机的响应序列号ack,即0x24cfca3e = 617597502 = 617597501 + 1

也许你已经注意到了,我是在Host主机里面请求与Client建立TCP连接,可是在抓到的第一个数据包里却显示源IP地址是10.0.2.2:58125,这个可是Client的默认网关啊,这是怎么回事?

另外,如果想深一层,我不从Host主机发起请求,而是从主机A里发起与ClientTCP连接,那么数据包显示的源IP地址就应该是主机AIP地址10.121.125.66了吗?答案是否定的,这个现象好不让人困惑,原因待究。

OK,各位朋友,今天就先写到这里。这是小弟第一次再51CTO上发文,看看效果吧,如果还能引起读者的共鸣,那么小弟我就继续写出后半部分的总结。这里顺便提一句,我在VBox里通过创建3个虚拟机来实现NAT服务器,拓扑结构如下图所示:

143647788.jpg