网络层的数据包是由首部和数据两部分组成,其中数据很多时候都是由传输层传递下来的数据段(Segment)
注意,一般来说网络层的数据包,最常见的是由传输层传递下来,但是有的可能只是三层协议(如ARP、IP、ICMP),或者两层协议(如CSMA/CD、PPP)等。
以下图例就是网络层数据包的组成:

在WireShark中抓包的具体字段显示为:

接下来针对首部的每一个字段去讲解具体作用是干什么的
占4位,代表是IPv4(0b0100)还是IPv6(0b0110)
占4位,代表的是整个首部的长度(包含可变部分),不过首部的真实长度等于该字段的二进制乘以4。因为首部的最小长度为20字节(5x4),该字段的最小值为0b0101,而4个二进制最大值表示为0b1111,因此首部的最大长度为60字节(15x4)。那么由此可以得出,首部的可变部分最大长度为40字节(最大总长度60 - 固定长度20)
占8位,可以用于提高网络的服务质量(QoS,Quality of Service)。简单来说就是在网络传输过程中做一下差异化服务,例如某些传输可以优先通过等,以保证通过量、延迟等因素
占16位,代表是首部+数据部分的总长度(也就是整个IP数据包的长度),最大表示值为65535。
但是在前面数据链层章节所了解到,数据链层帧的数据部分其实就是网络层的IP数据包,而帧的数据部分是有个最大值限制的(最大值为1500字节),如下图所示

那么当网络层的数据包超出1500之后,会怎么处理呢?这里网络层会将数据部分进行分片处理,而且每一片都会有自己的网络层首部(IP首部),然后将每一片传递给数据链路层封装成帧(有几片就封装成几个帧)。
到这里问题又来了,数据链路层怎么知道哪几帧需要组成一个完整的网络层数据包呢(在接收方时,数据链路层需要找到对应几片,再还原成一个完整的IP数据)?那么接下来就需要用到另外几个字段(标识、标志、片偏移等)了
占16位,代表的是一个数据包的ID,当数据包过大进行分片时,同一个数据包的所有片的标识是一样的。该标识ID会由一个计数器专门进行管理,每发出一个数据包,ID就会+1,如果超出了最大值(65535),又会从0开始计数
占3位,每一位的作用以及在WireShark中表示如下:

占13位,代表的是数据包分片后每一片的数据偏移位置,可以参考如下图例

当数据包分片后,每一片都有自己的首部,而且每片的总长度(首部+数据)不能超过1500字节。那么为了能保证接收方将这些片按顺序拼接成一个完整的数据包,就使用片偏移来处理这个问题
在上图中,完整数据包的数据部分总长度为3800字节,然后被分成三个片,分别是1400、1400、1000字节,而片偏移字段的值实际是0、175、350,片偏移乘以8就是实际的字节偏移,这是由于首部的片偏移字段只有13字节,最多只能表示8191,而一个数据包最多是65535,是不够表示的,因此需要乘以8,那么也说明了分片后的数据包长度一定是8的倍数
占8位,每个路由器在转发之前会将该字段减1,一旦发现TTL减为0,路由器就会返回错误报告。该字段可以跟踪数据包经过了哪些路由器,能够解决手动设置路由表错误,路由器死循环等常见问题
不同操作系统,默认的TTL值是不同的,因此也可以通过TTL来推测对方的操作系统

例如在终端调用 ping baidu.com 命令后:

可以看到从百度服务器到我这台电脑,TTL变成了54,因此可以推敲百度的服务器是部署在Linux上,而且百度服务器到我电脑之间经历了14个路由器
通过
tracert IP地址或者pathping IP地址命令,可以跟踪数据包经过了哪些路由器
占8位,表明所封装的数据是使用了什么协议

占16位,将网络层的首部根据算法生成一个值,然后填充到该字段,用于检查首部是否有错误,其实跟数据链路层协议里面的FCS差不多
接下来通过一个具体的例子,在wireshark中看看数据包分片后的具体首部信息
首先在终端中输入 ping ke.qq.com -l 4000命令,给腾讯课堂发送一个4000字节的数据包

然后打开wireshark,输入 ip.addr == 125.94.62.250将数据过滤一下

由于ping了4次,因此会发发送4个数据包,而且还会接受4个数据包,每个数据包在发送过程中被分成了三片(图中红框部分)。我们看到被框的部分,第一次和第二次wireshark误以为是IPv4协议,当第三片(也就是最后一片)发送过去的时候,wireshark就知道这三片属于一个完整数据包,而且是ICMP协议。
那么接下来每个片单独分析
第一片:

可以看到第一片的总长度是1500(首部20+数据1480)标识是61666,标志位的第3位为1(表示不是最后一片),片偏移为0(说明是第一片)
第二片:

可以看到第二片的总长度也是1500(首部20+数据1480),而且标识也是61666(说明跟第一片为同一数据包),标志位的第3位为1(表示不是最后一片),片偏移为1480(说明是紧接着第一片,也就是第二片)
注意,这里的片偏移是wireshark将原始片偏移字段乘以8得到的数据,也就是片偏移具体的字节数据

注意,当最后一片时,wireshark发现这是ICMP协议,会给该数据包加上ICMP的首部,8个字节