这是第一次在这一系列文章里面详细介绍一个数据包格式,在计算机网络编程上面,数据包格式可能是接触的最多的一个概念了。如果你要实现一个什么协议,第一件事应该就是搞清楚数据格式,我的做法一般都是先看一遍数据包的文档,在纸上画一遍数据包,然后再打开wireshark,对着抓到的包多鼓捣几遍一般就差不多能掌握了。
IPv4头数据包
一个IPv4数据包格式如下所示(倾情献上我的手绘版):
除去最后的data部分就是一个IP头数据包了,那么就从最低位开始一一开始详细介绍,下面的单位都是位,不是字节:
基本信息
0-3: 版本,标识了IP头的版本,IPv4里面自然这个值是4(0100)。
4-7: IP头的长度,以32bit word为单位,一共四位,所以最大长度是15*32bit,也就是60个字节,注意这是头的长度,而不是IP数据包最大的长度。按照图中的格式,去掉Data和If any的option部分,可以一共数出来是5行,也就是5个32bit长度,那么可以得出IP头最少也要20个字节,所以这个字段最小值是5,小于这个值都是不合法的。
8-15: TOS,全称Type of service, 这个字段是根据有些特殊的网络状况的不同来定义想IP层提供怎样的服务的。比如在有的网络上希望有的数据包可以优先得到传输,因为他们可能含有相对于其他数据包来说,比较重要的信息。这三个字段来完成这一服务的本质就是在三个方面提供一个平衡,这三个方面就是,低延迟,高可靠性和高吞吐量。具体来说可以将这个8位分为5个部分:
- 8-10: 优先级
- 11: 1代表开启低延迟模式,0代表关闭
- 12: 1代表开启高吞吐量模式,0代表关闭
- 13: 1代表开启高可靠性模式,0代表关闭
- 14-15: 保留位
优先级的仍然可以展开成为8个不同的级别,不过一般这种单纯罗列的东西超过三层就基本上会产生发散注意力的尿性,所以就不展开了,只要能记住这个3位用来表示数据包的不同的重要性或优先级就行了。后面三位就是从三个不同的角度来促使某个数据包能够因为其重要性最先被处理。但是在大多数网络中,真的要同设置了三位,那么反而会导致更差的后果。因为在很多情况下,这三个本身就是鱼和熊掌不可兼得的,所以一般只需要选择一个就好,很少的情况下需要打开两个。
16-31: 总长度字段,总长度是以字节(8 bit)为单位的,包括头部长度,所以说一个IP数据包最长是65,535个字节。不过一般这么长的数据包一般都是不切合实际,除非有特殊目的,所以说在最初的标准里推荐的是不超过576个字节,包括头部的长度。
标识与分片
32-47:标识符,用来标识和归组数据包,为什么要归组呢?因为如果一个数据包比较大,往往就需要分片,分片简单的说就是把一个大的IP数据包分成好几包小的,为了标哪些是哪一个大包的组成部分,就需要一个标识符来组织他们,这样对端才能把分开的数据包重新组合成为一个整的数据包。但是可以看到,这个字段只有16个bits的长度,所以标识最多只可能有65536个不同的数,在现在一个数据包可能以十万百万计的网络通信上,就会出现一个问题,标识符在循环了一圈之后又要出现重复的数啦。这个问题咋办呢?答案是简单的丢弃数据包并且发送标识错误的信息给发送端。
48-50:分片标识,上面说了如果一个IP数据包过大,就需要分片,那么这个3个bit就用来表示该数据包的分片情况。其中
- 48: 保留位,必须为0
- 49: 0表示是分片的数据包,1表示不是分片的数据包
- 50: 0表示是一组分片数据包的最后一片,1表示后面还有分片的数据包
总结一下,其实就是这三位如果是000,就是表示这只是一组分片数据包中间的一片,如果是001就表示这是最后一片了,IP层可以根据需要组装数据包交给上一层,只要是01,后面以为就可以忽略了,因为这是一片完整的数据包。
51-63:偏移量,这个是配合标识符字段使用的,标识出改数据包在整个分片数据包的位置,这样才能让IP层按照正确的顺序拼凑出数据包。
生存和上层信息
64-71: TTL,数据包还剩的生存时间,这样翻译感觉有点挫,一般这个名词都不会被翻译,直接用英文的原文Time to live。这个值最初被设计出来的时候是以秒作为单位的,也就是该数据包还能存活多少秒,如果这个字段的值到0了,那么这个数据包就得消失了并且向发送端发送能标识错误的消息。但是由于现在路由器的处理时间和发送时间都远远小于1秒,所以这个慢慢的就被用作标识还能经过多少个路由器转发,俗称跳数,现在一般都标为128。但是在一些特殊的测试目的的场景下,我们就可以利用这个字段来测试出发送主机和目的主机之间的路径信息。
72-79:协议,就是标识这个IP数据包的data是啥协议,常见的比如TCP,UDP。
80-95:头部效验和,这是使用CRC算法来检测头部,注意,仅仅时检测头部信息有没有错,如果有错那么该数据包将不得通过。
地址信息
96-127: 发送端IP地址,32位。
128-159:接收端IP地址,32位。
选项(如果有)
这个选项字段是IP层数据包为了配合一些其他协议的发送或者应答而应运而生的,如果只是单纯的列出有哪些选项保证就会被滚轮滚过去,而且大部分是根本不会遇到的,所以我决定再介绍ICMP的时候再介绍我能接触到的。
如何记忆数据包格式
我觉得这是如果涉及网络编程的一个难题,因为有太多协议了,不可能把所有数据包的格式都记住,但是这也不代表啥数据包格式都记不住。在我看来,最基础的比如IPv4的头数据包格式,TCP的格式,UDP的格式,ICMP,ARP大概的样式都应该记住,虽然说这些玩意儿一百度就能百度出来,但是我觉得在面试时这些都是能反应你熟练程度的一个参考吧。
经过我的几次探索,我觉得我找到了一条适合我的方法,就是用笔画一画。我觉得计算机一个最大的弊端就是让人们忘了笔和纸的作用,很多时候动动手真的能加深大脑的记忆,虽然我也不知为什么。第二个就是分类,比如这个IP头吧,我会把他主要分为上面黑体字的那几类,结合着几类,其实再加一些简单的逻辑推理就不难记住这些。比如基本信息,基本信息嘛,总要有长度,版本号嘛,再对照书一看,真有,但是少了一个TOS,没关系,这是特殊的功能,不在人脑一般认知的基本信息之内,这一次的miss hit就可以让你记住有一个TOS,强化了记忆。
抓个活物来看看
在对这个包头格式有一定了解的基础上就可以找一个真实的IP数据包看看,一是加深印象,二是看看随着年代的发展,标准里会不会有坑。我还是用哪个SMB数据包来观察一下:
展开IP这一层的信息,wireshark已经把一串二进制翻译成了人眼可见的信息,当然,你要是有兴趣也可以在二进制信息里自己寻找和发掘。我感觉这东西和考古差不多,在一堆一眼看上去差不多的东西里面发现自己需要的,偶尔弄弄还是很有成就感的,搞多了伤眼睛。
对比第一个部分介绍的,可以看到这个IP头是20个字节,也就最小长度,没有选项。有一个天蓝色的字段上面貌似没有找到,但其实这就是TOS字段,有兴趣可以搜一下这个名词,还是很复杂的。不过因为是0,就认为啥也没有发生就行。还可以看到标识符,这个数据包并不分片,是一个整体。还有就是这个IP数据包里所蕴含的是一个TCP的数据,也可以看到两个IP地址。
所以说,至少在相信wireshark的情况下,标准并没有坑我,或者说SMB发送的IP包是符合标准的额,我们可元气满满的额进入下一个话题了。