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可以ping通Host,但是Host却无法ping通Client。个人感觉这个现象很是让人惊奇,ping这玩意儿不是双向的吗,为什么会出现这种“莫名其妙”的情况呢?由此触发我对NAT的深入学习。
1.2.初识使用NAT上网的Ubuntu
1.2.1.有用的快捷键
使用VBox创建好虚拟机并在其上安装好Ubuntu之后,Ubuntu操作系统默认就是以NAT的方式来上网的,可以使用快捷键Ctrl+Alt+T的方式在图形界面里调出命令符操作界面,如下图所示:
显然命令符界面的字体太小了,可以使用快捷键Ctrl+Shift+‘+’来放大界面与字体,对应的使用快捷键Ctrl+‘-’来缩小字体。
可以使用另外一种方法彻底的进入命令符界面,即快捷键Ctrl+Alt+F1,如下图所示:
使用快捷键Ctrl+Alt+F7可以回到图形界面。
可以看到命令输入提示符是‘~$’,这表示此时登陆的用户不是root用户,如果要切换到root账号的话,那么需要使用命令‘sudo -i’,切换到root账号之后的命令输入提示符是‘~#’。
1.2.2.使用NAT上网时的网络参数
以上都是关于Ubuntu的操作方面的一些小技巧,下面我们来点“真刀×××”的东东。前面说了,安装Ubuntu之后Client系统默认是以NAT的方式来上网的,那么此时Ubuntu的网络参数是怎么样的呢?我们可以使用命令‘ifconfig’来得到最基本的信息,如下图所示:
核心的信息都在红线框之内,我们可以看出使用该命令查询到的网络参数与Windows比较起来在人性化方面差一些,因为从上图中我们不知道Client系统的网关地址,DNS地址和DHCP地址,要想知道这些基本参数就要使用其他的命令了。
使用命令‘route -n’查询Client上的路由信息,如下所示:
可以看出,Client系统的默认网关地址是10.0.2.2,Flags当中的U表示该路由是启动的,G表示需要通过外部主机来传递数据包。
查询DNS地址的时候,几乎所有的书籍和网上的资料都指向文件/etc/resolv.conf,描述DNS地址都是在该文件里设置的,可是当我查阅该文件内容时却让我大跌眼镜。如下所示:
可以看到Client的DNS地址是本地内循环地址,我不禁长叹一句“How it can be?”。当我接下去继续查找DHCP地址的时候却发现了有趣的现象,如下所示:
首先,DHCP服务器给Client分配IP地址的时候都会有租赁信息,该信息存放在目录/var/lib/dhcp下面,严格意义说来应该是在文件dhclient.leases里面,但是我却发现这个文件是空文件,所以就查询了那个很长名字的文件(dhclient-9f…….lease),在得到了DHCP地址的同时却意外发现了DNS的地址,正所谓无心插柳柳成荫啊,更让我惊奇的是这三个DNS地址居然与Host主机(我是在我的Dell笔记本上安装的虚拟机和Ubuntu,这里就把Host主机认为是我的Dell笔记本电脑哈)的DNS地址完全相同,如下所示:
这不禁让我“浮想联翩”,VBox究竟是怎么样获取到Host主机的DNS地址信息并配置给Client系统的呢?如有哪位大虾能够代为解答,小弟不胜感激!
还有一个问题,/etc/resolv.conf中明明将Client的DNS地址设置为本地内循环地址,可DHCP服务器配置给Client的IP地址租赁信息里却含有真正起作用的DNS地址,这二者究竟是一个什么样的联系?DHCP除了给Client分配IP地址之外,还分配了哪些额外的有分量的信息呢?这两个问题还请高手解答。
最后,我在网络上终于查到命令‘nm-tool’可以获取到所有的网络参数,如下所示:
也许你注意到了一给Client上电,它就会从DHCP服务器处获取IP地址,那么是什么文件告诉Client以这样的方式获取IP地址呢?答案就是文件/etc/network/interfaces,如下图所示:
如果我要自己设置Client的IP地址,不让它自动从DHCP处获取IP地址,怎么办?简单,修改这个文件即可,具体细节在下面的章节描述。
1.3.初步分析Client与Host之间的通信
1.3.1.开通SSH服务
我喜欢在Host里面使用工具Secure Shell Client登陆Client系统进行各种操作,但前提条件是Client系统要开放SSH服务,此时的Client系统等同于SSH服务器。那么如何在Client上开通SSH服务呢?如下图所示:
可以看到命令‘apt-get install openssh-server’是用来安装SSH服务,不过需要注意的是该命令要使用root账号执行。
那么怎么知道SSH服务确实是运行起来了呢?可以使用命令‘netstat’来查询Client监听了哪些本地端口,以此确认SSH服务的正常运行。如下图所示:
可以看到详细的命令是‘netstat –l -n’,其中的-l表示列出正在监听的端口,-n表示使用IP和Port来显示(不要使用Hostname)。从红线框内可以看到,本地端口号22已经处于监听的状态了,证明SSH服务已经正常运行起来。
此处插入一个题外话,端口号22对应的是SSH服务,那么其他端口号对应的又是些什么服务呢,这样的信息可以在哪里获取到?答案就是‘/etc/services’。如下图所示:
好了,回归正题。SSH服务既然已经开通了,那么在Host里我就使用Secure Shell Client来登陆了,可是真能登陆的上吗?看看下面的截图:
这是怎么回事,网络不通吗?由此很容易联想到使用ping命令来检查Client与Host之间是否通信顺畅。
1.3.2.Client去ping Host
首先,这里先奉上Host的网络参数,如下图所示:
可以看到Host的IP是10.121.125.44/255.255.255.0,而Client的IP是10.0.2.15/255.255.255.0,二者并不处于一个网段当中。
我先在Client里尝试ping通Host,为了清楚的知道数据包的传递细节,我在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端都抓到了什么样的数据包,如下图所示:
在具体分析上面的测试结果之前,我们先从理论上分析一下数据包应该怎么走才能从Client到达Host呢?首先,因为Client与Host并不在同一个网段之内,所以ICMP echo request的数据包肯定是要先送到网关处,由网关将该数据包转给Host主机,Host收到该数据包,做出响应,将ICMP echo reply数据包传递给网关,再由网关回传给Client,以此完成整个ping的数据通信过程。
以太网上传递的都是MAC数据帧,该数据帧的格式如下图所示:
回到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,于是乎它就想尽一切办法将该数据包转给Host。Host收到ICMP echo request数据包之后做出处理,并将响应ICMP echo reply数据包传给Client的网关,再由Client的网关回传给Client。实际上,这里VBox肯定对我隐藏了某些细节,例如Host的网关是10.121.125.1,如果有一个ICMP echo request数据包被传递到Host(我在想,这个数据包真的是到了Host的物理网卡了吗?),且该包的来源是10.0.2.15,那么Host的ICMP echo reply数据包应该是传递给Host的网关去处理,但是实际上Host直接将响应数据包传递给了Client的网关。VBox究竟耍了什么样的“把戏”,使得这样的结果看似情理之中,却意料之外呢?我个人感觉虚拟机在底层的实现还有很多东东要学习啊,有待研究。
此处我并未对文件testPingHost中的每个字节做出详尽的分析,这是一件很繁琐且枯燥的事情,虽然如此,如果真正去做了,那么对于MAC帧、IP数据报文和ICMP数据报文必将有一个“踏实”的感觉和认识。
另外一个细节是,Client与它的网关相互通信时,并没有发送ARP广播消息,这证明二者知道彼此的MAC地址。关于ARP的学习,后面的内容会做详细分析。
我们再来看看在Host里抓取的数据包,如下图所示:
没有ICMP的数据包,甚至连TCP的数据包都没有。这从一个侧面在印证着我的猜测,即来自于Client的ICMP echo request数据包,由于其目的地就是Host主机,所以该数据包并未真正到达也没有必要到达Host的物理网卡,一切的一切都在Host主机内部做了处理,否则工具Wireshark肯定能抓住这样的包包。
1.3.3.Client去Ping Host的邻居
在上一小节我猜想Client发到Host主机的ICMP echo request数据包并未真正到达Host主机的物理网卡,所以Wireshark没有抓到这个数据包。那么自然地,我想到了如果Client去尝试ping通Host主机所处局域网里的“邻居”,那么ICMP echo request数据包毫无疑问要“经过”Host主机的物理网卡,这样的话Wireshark不就能抓到这样的数据包了吗?理论分析的结果还是需要靠实践的证明。
首先确定主机A的IP地址是10.121.125.66,Host主机与主机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抓到的数据包,如下图所示:
这个结果与上一小节的测试结果相比,最主要的不同就是目的IP地址不一样,上一小节的目的IP地址是10.121.125.44,而此次的目的IP地址是10.121.125.66。
来看看在Host主机里抓到了什么包包,如下图所示:
果然不出所料,在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.15,ICMP 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地址都是全球唯一的,那么计算机之间的通信只需要一种地址来标识自己就可以了嘛,为什么这两种全球唯一的地址都要存在呢?聪明的你已经知道了吧
1.4.NAT的理论知识
关于NAT的理论知识,网上多的是,甚至有不少大牛的巨著都讲到了。我这里只是班门弄斧的对大牛们的描述做一个自我的总结而已。
这里遵从Douglas E.Comber这位大牛的著作,将运行NAT软件的电脑称之为NAT Box。一般情况下要求NAT Box的一端连接具有私有IP地址的电脑(称之为Internal Host),而另一端至少具备一个在公共因特网上的有效地址G。如下图所示:
NAT Box对来自于Internal Host的数据包以及从公共Internet传来的数据包进行了IP地址转换,具体说来,如果Internal Host A想要访问公共Internet网上的资源,当请求数据包到达NAT Box之后,NAT Box将该数据包的源IP地址由192.168.1.100替换成公共Internet的IP地址G;当公共Internet将资源返回到NAT Box时,NAT Box又将该数据包的目的IP地址替换成内部的Internal Host A地址。
内部Internal Host通过NAT上网时,公共Internet看到是NAT Box在索取资源,它看不到真正请求资源的是Internal Host,即Internal Host相对于公共Internet来说是透明的。我们都知道Internal Host的IP是私有IP,这样的IP在公共Internet上是不能够出现的,但是因为NAT的缘故,即便是私有IP,Internal 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维护的表格变成下面的样子:
从上表可以看到,Host A与Host B需要同时访问百度服务器(百度的IP:220.181.112.143),而Host C则访问谷歌服务器(谷歌的IP:173.194.44.127),三者启动各自的TCP端口号21023、12345和1274。由于这些端口号来源于Host本身,所以有可能它们都采用了相同的端口号。三个Host的访问请求可以简化成如下的形式:
(192.168.1.100,21023; 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端口号替换成如下的形式:
(G,14003; 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数据包当中有两个字段IDENTIFIER和SEQUENCE 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就是无法ping通Client。这里我要求你将上一小节中学到的NAT(暂不考虑NAPT)理论知识类比到我们实际的操作上:Client对于Host而言,甚至对于主机A而言都是透明的,只有Client发起的对外通信,其响应信息才能通过Host到达Client。Host以及其他的主机并不知道Client的存在(暂不考虑NAPT),所以当它尝试ping通Client时会议失败告终,那么又如何来解析此时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的具体体现。如下图所示:
核心在于“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主机就成功“绕”到了它自己的内部(或是后花园)。我自己推测,其实真正的情况是VBox能够监控Host主机的4000端口,当有数据包到达这个端口时,VBox就将此数据包转发给Client,因为Client就在VBox里面,它当然能够做到这样的事情。
重新启动Client,在Host主机里SSH登陆Client的结果如下图所示:
大功告成矣!
1.7.TCP三次握手的详细分析
这一小节的内容实际上是个题外话,1.6小节当中我在Host主机里使用Secure Shell Client工具成功登录Client,这个通信过程涉及到了我们耳熟能详的TCP的三次握手通信,那么深入到数据流的情况下,这三次握手的具体交互是怎么样的呢?出于兴趣,我做了总结。
理论指导实践,实践验证理论。我们首先从理论的角度复习一下TCP的三次握手,如下所示:
(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=1,Seq=20001,所以Client知道这同样是个请求连接的数据包,这次是Host主机请求与Client建立连接,Client如果同意建立连接(当然同意,要不然Client就有“病”了),那么它就回复一个确认数据包给Host主机,这个回复数据包中ACK=1, ack=20001+1=20002;
(4)D:取得最后确认。若一切顺利,Host主机收到带有ACK=1且ack=20002序号的数据包之后,就能完全建立起这次的连接了,之后的通信该怎么样就怎么样了。
OK,复习完三次握手的理论知识之后,我们来看看实际的测试结果是怎么样的呢?如下图所示:
TCP三次握手的完整数据流程就包含在上图中的三个数据包里,是不是有点让人头晕?我们一个包一个包的分析。
(1)A:数据包发起。
第一个红线框内的数据是MAC帧的数据头;第二个红线框内的数据是TCP表头当中的源端口号和目的端口号,可以看到源端口号是0xe30d = 58125,目的端口号是0x0016 = 22;第三个红线框内的数据是Seq序列号(请求数据包的序列号),即0x0154d201 = 22336001;第四个红线框内的数据是响应数据包的序列号,因为该包不是响应包,所以为0;第五个红线框内的数据含有标识位SYN(已被置位)和ACK的信息。
第一个红线框和第二个红线框之间的数据是IP表头,共20个字节;第五个红线框之后的数据是TCP表头的剩余信息,此处不再赘述。
(2)B:数据包接收与确认数据包传送。
这个数据包既是响应包也是请求建立连接的数据包,第三个红框内的数据指明这个信息;第一个框内的数据是Host主机请求与Client建立连接的Seq序列号,即0x24cfca3d = 617597501;第二个框内的数据是Host主机返回给Client的响应确认ack序列号,即0x0154d202 = 22336002 = 22336001 + 1。
(3)C:回送确认数据包。
红线框内的数据是Client响应Host主机的响应序列号ack,即0x24cfca3e = 617597502 = 617597501 + 1。
也许你已经注意到了,我是在Host主机里面请求与Client建立TCP连接,可是在抓到的第一个数据包里却显示源IP地址是10.0.2.2:58125,这个可是Client的默认网关啊,这是怎么回事?
另外,如果想深一层,我不从Host主机发起请求,而是从主机A里发起与Client的TCP连接,那么数据包显示的源IP地址就应该是主机A的IP地址10.121.125.66了吗?答案是否定的,这个现象好不让人困惑,原因待究。
OK,各位朋友,今天就先写到这里。这是小弟第一次再51CTO上发文,看看效果吧,如果还能引起读者的共鸣,那么小弟我就继续写出后半部分的总结。这里顺便提一句,我在VBox里通过创建3个虚拟机来实现NAT服务器,拓扑结构如下图所示: