0.说明
本文为我个人计划撰写的博客专题《在实践中深入理解常见网络协议》中关于IP协议的一篇,有兴趣的朋友可以继续关注我的博客,我将会陆续撰写各种协议的实践分析文章。
TCP/IP协议栈其实当然不止有TCP和IP两个协议,但以这两个协议作为这个协议栈的统称,足以体现出其在TCP/IP协议栈中的重要性,正是因为有了IP协议,才使得不同网络间可以进行通信。当然要提及IP协议,其实还需要分析与其相关的配套协议,如ICMP、IGMP、ARP,这些协议与IP协议的关系可如下:
不过这里我们只关注于IP协议中的各个字段内容,其它的协议分析可以参考本专题博客文章。
下面就来结合搭建的网络环境,通过一些手段来深入理解IP协议中各字段的具体含义,相信这会比纯看理论的书籍要容易理解得多。
本次使用的环境如下:
操作系统:Ubuntu 15.10
网络设备模拟器:GNS3
抓包软件:Wireshark
这里使用的操作系统为Ubuntu而不是Windows,只是出于个人的喜好,实际上使用Windows操作系统也是完全没有问题的。
1.网络环境搭建
对于IP协议的分析,并不需要太复杂的网络环境,我们只需要建立下面一个拥有两个节点的网络环境即可:
在Linux操作系统上正确配置IP地址192.168.1.1/24,然后在思科路由器3600上做如下配置:
Router>en Router#conf t Router(config)#int f0/0 Router(config-if)#no shu Router(config-if)#ip add 192.168.1.2 255.255.255.0
这样之后就搭建好了环境,我们可以在Linux操作系统上执行ping来测试一下网络的可达性:
xpleaf@leaf:~$ ping 192.168.1.2 PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. 64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=2.39 ms 64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=2.11 ms 64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=1.65 ms 64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=2.04 ms ^C --- 192.168.1.2 ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3004ms rtt min/avg/max/mdev = 1.658/2.052/2.398/0.267 ms
OK,没有问题,网络正常!
需要注意的是,在这个网络环境中,Linux操作系统是真实的操作系统,思科路由器也跟真实的不差多少,这利益于GNS3的强大功能!没有用过GNS3的朋友请务必尝试一下。
2.IP协议报文格式
如下:
报文格式中“首部”即是我们要讨论的内容,在下面的实践分析中,我们主要讨论的字段如下:
版本、首部长度、区分服务、总长度
标识、标志、片偏移
生存时间、协议、首部检验和、源地址、目的地址
为了实验的方便,我们将会分成上面的三组来说明各字段的详细含义与实际中的数据包内容,不过可以看到的是,这里并没有提及可变部分的内容,下面的讨论都是在首部长度固定(20个字节)的情况下去讨论的。
3.在实践中分析IP协议各字段含义
(1)版本、首部长度、区分服务、总长度
我们将会抓取一个数据包来分析这4个字段的内容,启动抓包软件Wireshark(GNS3软件中,在Linux操作系统和思科路由器之间的链路上可以直接启动Wireshark,这是GNS3软件的功能之一),以监测Linux操作系统的网络接口:
然后在Linux操作系统上执行如下命令:
xpleaf@leaf:~$ ping 192.168.1.2 PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. 64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=1.75 ms ^C --- 192.168.1.2 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 1.755/1.755/1.755/0.000 ms
我们只向路由器发送了一个ICMP包,可以在Wireshark软件上看到的抓包情况:
可以看到Wireshark抓到了两个包,为什么呢?数据通信是有去有回的,我们发送一个ICMP请求包,当然也会收到一个ICMP回复包,正如上面所看到的,一个是request,而另外一个是reply,关于ICMP协议的分析,请关注本专题的其它文章。
以我们发送的包为例,我们来分析IP协议中的版本、首部长度、区分服务和总长度这四个字段的详细含义,如下:
版本
占4位,指IP协议的版本,目前存在两种IP协议版本,分别为IPv4和IPv6,而广泛使用的是IPv4,IPv6目前正在部署,但估计IPv6的全部普及还需要一段时间。显然,在我们的网络环境中使用的是IPv4,可以查看我们抓取到的数据包内容:
可以看到,版本号字段Version为4,代表正在使用的正是IPv4协议。
首部长度
占4位,可表示的最大十进制数值是15,不过需要注意的是,它的单位的4字节,也就是说,如果这4位是0101(十进制表示5),那么也就意味着首部长度为5*4字节=20字节,又因为单位是4字节,所以首部长度的大小应该为4字节的整数倍。很显然,因为这4位表示的最大1十进制数为15,所以首部长度最大可以为15*4字节=60字节。由于这里我们只讨论固定长度的首部长度(可变部分实际上用到不多),所以大小肯定是20字节, 可以查看我们抓取到的数据包内容:
区分服务
占8位,但是一般情况下都不会使用这个字段,看一下数据包内容即可:
总长度
占16位,指的是首部长度和数据长度之各,单位为字节。由于16位二进制能表示的最大十进制数为65535,这也意味着数据报的最大长度为65535*1字节=65535字节,尽管如此,但由于在数据链路层有MTU(最大传输单元)的限制(举以太网为例,MTU为1500字节,也就是数据报的长度不能超过1500字节),因此肯定会进行分片。不过,正常情况下是很少有这么大的数据报的。
提示: 尽管存在着标准的MTU设定,但是一个设备的MTU通常是可以手工设定。MTU是基于接口进行设定的,可以在Windows或者Linux系统上修改,或者在管理路由器的界面上修改。(选自《Wireshark数据包分析实战》P107,不过我没有测试过) |
查看一下我们抓取到的数据包:
可以看到总长度为84字节,减去了IP首部的20字节,也就是说ICMP数据的长度为64字节,而从我们刚刚执行命令时的提示:
PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. 64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=1.75 ms
从这里也可以知道,ICMP数据的长度确实为64字节(56字节的实际数据+8字节的ICMP首部),这进一步验证了我们的计算结果。
OK,关于版本、首部长度、区分服务和总长度的分析就到这里,相对来说还是比较简单的,而比较麻烦的是分片的问题,我们下面就要分析分片的数据包的IP首部到底是怎么样的。
(2)标识、标志、片偏移
为了观察和分析带有这些特性的IP首部,我们需要重新发送比较大的ICMP包,不过在执行这个命令之前,先观察一下前面的数据包中的这些字段:
基于此,我们分别提及各字段的作用,然后再进行分片的实践:
标识
占16位。IP软件在存储器中维持一个计数器,每产生一个数据报,计数器就加1,然后并将该值赋给该字段,如上面看到的47440。这也就意味着,产生的每一个数据报的标识字段的值都是不一样的,那么这个字段又有什么用呢?考虑需要分片的情况:当一个数据报需要分片时,就说明需要把一个数据报拆分成多个进行发送,而每一个拆分后的数据报都会有自己的IP首部,当然都有一个标识字段,而它们的标识字段就跟原来产生的那个数值(IP软件的计数器生产的数字)是一样,这样的好处是,这些拥有相同标识字段的被拆分的数据分片最后就在在目的地重装成为原来的数据。通过后面的实践,这一点就会很好理解了。
标志
占3位,只有最低两位(DF位和MF位)有意义,Reserved没有实际意义。上面我们抓取的数据包显然是没有进行分片的,因为比较小,观察Flags的值,为0x02(十六进制表示,二进制为10),这是因为MF位为0(MF=1表示后面还有分片,MF=0表示这是若干数据报片中的最后一个,显然由于这里没有分片,就只有一个数据报而已,所以MF位肯定为0),而DF位为1(DF=0时才表示分片),所以Flags的值就为10.
片偏移
占13位。这里直接给出《计算机网络》一书中的解释,片偏移指出,较长的分组在分片后,某片在原分组中的相对位置,也就是说,相对于用户数据字段的起点,该片从何处开始。片偏移以8个字节为偏移单位。也就是说,每个分片的长度一定是8字节(64位)的整数倍。
一个经典的例子: 在进行实践之前,不得不说的是,谢希仁的《计算机网络》中,P128有一个关于数据分片的非常经典的例子,虽然这只是以习题的形式给出,但在实践分析之前能够看一下这个例子,那么相信对于分片的原理也会有一个相对比较清晰的了解。 |
OK,基于上面的基础,我们来进行数据分片的实践。
在Linux操作系统上执行如下命令:
xpleaf@leaf:~$ ping 192.168.1.2 -s 3800 PING 192.168.1.2 (192.168.1.2) 3800(3828) bytes of data. 3808 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=18.0 ms ^C --- 192.168.1.2 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 18.000/18.000/18.000/0.000 ms
在这里,我们指定了发送的数据大小为3800字节,再加上ICMP首部的8字节,实际上从ICMP交给IP的数据部分大小为3808字节(非常重要,务必先弄清楚),这样一来的话,就必须要进行分片了,根据MTU=1500字节来计算,分片的数量应该为3。
我们在Wireshark上观察抓取到的数据包,如下:
可以看到,确实是分了3个包发送。在分析每个数据分片之后,我们不妨来预测一下每个分片中,IP首部的总长度字段的值:
第1个数据分片:总长度应该为1500字节(MTU的限制,这里为以太网),因为3800可以至少划出两个1500,由于IP首部本身有20个字节的大小,所以上层交给IP的数据部分长度应该为1480字节(准确来说应该是分片得到的数据)
第2个数据分片:总长度应该为1500字节(MTU的限制,这里为以太网),因为3800可以至少划出两个1500,由于IP首部本身有20个字节的大小,所以上层交给IP的数据部分长度应该为1480字节(准确来说应该是分片得到的数据)
第3个数据分片:总长度应该为868字节,因为减去两个1480字节后,ICMP数据剩下848字节,由于IP首部本身有20个字节的大小,所以总长度为848字节+20字节=868字节
那么实际上是不是这样的呢?肯定是的,相信我们的理论是没有错的!可以分别查看一下这三个数据分片中的总长度字段的值:
a.分片1
b.分片2
c.分片3
可以看到,这跟我们预测的完全一样!所以不用怀疑,虽然理论跟实践可能会存在一定的差别,但是现在的计算机网络通信的实现没有基于这些理论的分析,有可能实现吗?所以说理论也是非常重要的!
前面讨论的是分片之后的总长度大小的关系,下面要说的就是标识、标志、片偏移这三个字段之间的关系和含义信息,我们分别给出上面三个分片的这三个字段的数据包信息,并进行对比分析,如下:
分片 | 对比分析 |
分片1 |
|
分片2 | |
分片3 |
OK,这样一来的话,算是把IP协议中最难的部分都搞定了,是的,是否能够把IP协议弄得较为清晰,很重要的一点是要看你对于数据分片的理解是否到位。
(3)生存时间、协议、首部检验和、源地址、目的地址
出于不做太多重复无意义的事情的目的,我们不妨直接把第一次抓的包来出来进行分析,如下:
分析如下:
生存时间
占8位,习惯称为TTL,用以表明数据报文在网络传输过程中能经过的跳数。因为不可否认一种情况,就是一个数据包可能在网络中无休止地进行传输,但仍然找不到目的地,这是绝对存在的,路由环路就是其中一种情况。既然如此,如果还是让这个数据包不停地传输,显然就太浪费时间了。那么这时可以考虑在IP首部中加入一个TTL字段,并设定初始值,每经过一跳路由器,该TTL的值就减1,如果TTL的值变为0还没有找到目的地,那么将不再转发该数据包,也就是说会进行丢弃,这样一来的话就可以解决上面提到的问题。这个字段除了这个作用以外,还有一个非常重要的作用,那就用来作路由追踪时使用,我们常使用的tracert或traceroute命令,就是利用了TTL的原理,有兴趣的不妨自行研究一下。
协议
占8位,指明此数据报携带的数据是使用何种协议,也就是说IP层应该把重装好的数据(如果有分片的话)交给哪一个进程进行处理(其实所谓的ICMP其实就是开启一个进程,计算机网络中的通信本质上就是进程和进程之间的通信)。比如上面的协议字段为1,就表示IP层应该把数据处理好后(去掉IP首部,这是数据封装中的解封装过程)交给ICMP进程进行处理。关于常用的协议字段,如下:
协议名 | ICMP | IGMP | IP | TCP | EGP | IGP | UDP | IPv6 | ESP |
协议字段值 | 1 | 2 | 4 | 6 | 8 | 9 | 17 | 41 | 50 |
图示如下:
另外,如果你对为什么在协议名中竟然还会出现IP协议感到困惑,建议有时间研究一下IPSec ***的原理,这里简单提一下,IPSec ***是一种通过用公网IP来封装私网IP以实现跨公网的私网之间进行局域网通信的一种隧道技术。
其实关于所谓的协议字段,不仅在IP协议中会有,如果查看过数据链路层的帧或者传输层的数据段,会发现同样有个类似的字段,我们把这样一种现象称为数据分用,相信研究过数据封装和解封装的朋友就会清楚。
首部检验和
占16位,这个字段只检验数据报的首部,但不包括数据部分,这样做是为了减小计算检验和的工作量,加快数据包在进行路由转发时的速度。需要注意的是,这种检验只是简单的检验,不像数据链路层中使用的CRC检验码那么复杂,具体的计算方法可以查看谢希仁的《计算机网络》P130,这里就不提及了。
源IP地址
占32位,提及一点的是,在数据转发的过程中,源IP地址是不变的。
目的IP地址
占32位,提及一点的是,在数据转发的过程中,目的IP地址是不变的。
OK,到这里的话所有的字段都分析完成了。
4.下一步要做什么
实际上对于IP协议的理解不可能是只理解了它你就算理解IP协议,也就是说,TCP/IP协议栈是一个不可分隔的整体,在对网络协议进行学习和分析的过程中,请务必在头脑中有这样一个概念:任何一种协议都不是单独而存在的。
如果你感兴趣,不妨详细分析一下数据转发的一个完整流程,其实也就是数据封装和解封装的一个完整过程,建议的话,如果有能力,自己根据上面的内容来进行实践一遍!如果你真的理解了,我相信不需要我上面的这些环境,你自己也完全可以跟我做出一模一样的效果来。
请继续关注香飘叶子51cto博客的博客专题《在实践中深入理解常见网络协议》的系列文章,有问题可以留言提问,谢谢大家!