如上图所示,IPv4数据报首部有多个字段,由20个字节的固定字段和40字节的可选字段组成。最少20个字节,最多60个字节。
版本号(version, VER):版本号字段的长度为4比特。这4比特规定了数据报的IP协议版本,路由器需要查看版本号来确定如何解释IP数据报的剩余部分(IPv4和IPv6的解释方法是完全不同的)。目前就IPv4和IPv6两个版本号,IPv4的版本号字段为:0100 = 4。
首部长度(internet header length, IHL):首部长度字段的长度也为4比特。它的单位是32bit的字(1个字 = 4字节),由于一共只有4比特,所以最大值为15字 = 60字节。之所以需要这个字段是因为IPv4的首部有选项这种可选的字段,所以IP数据报首部长度是可变的。当没有选项时,首部长度是20个字节 = 5个字,也就是0101,这5个字(20字节)是固定的首部长度,也是首部长度最小值,是最常见的情况。
首部长度限制为60字节(15字)可能不够用,会导致有的选项几乎无法使用。IPv6就不存在这个字段。
区分服务(differentiated service, DS):区分服务字段的长度为8比特,这个字段以前叫服务类型(TOS),但IETF已经改变了这8个比特的解释和名称。
区分服务字段希望在不改变网络基础结构的前提下,让路由器可以对不同的数据报进行区别服务。路由器会根据DS字段的值来对分组进行区别处理。也就是说,数据报不再都是通过尽力交付的策略被分发,在DS字段做了特殊标记的数据报,会以不同于一般数据报的方式(比如以更高的优先级)被处理。
DS字段分为2部分,前6位为区分服务码点DSCP(differentiated services codepoint),后面两位叫做ECN( Explicit Congestion Notification),翻译为显式拥塞通知。
准确说以前的8位TOS字段已经变成了现在的DSCP字段和ECN字段这2个字段,前者用于为数据报区分服务类型,后者用于拥塞控制。
先介绍2bit的ECN字段,ECN字段用于为数据报标记拥塞。假如一个持续拥塞的路由器希望告知发送方自己的情况,让发送方降低发送速度来缓解拥塞,它就需要利用ECN字段。拥塞的路由器会将ECN字段的值设置为拥塞。当接收方接受到被标记ECN为拥塞的分组时,它会让它的上层协议(比如TCP协议)将这种情况通知发送方。发送方接收到接收方响应报文的通知后,就会降低发送速度,路由器的拥塞情况就会缓解。
再来介绍前6bit的DSCP字段。
DSCP可以分为2种情况理解:
1.当后3位都为0时,最左边前三位与TOS的前三位优先位的解释相同。
如下表所示:
名称 | 值 | 描述 |
---|---|---|
CS0 | 000000 | 类别选择(尽力而为) |
CS1 | 001000 | 类别选择(优先) |
CS2 | 010000 | 类别选择(立即) |
CS3 | 011000 | 类别选择(瞬间) |
CS4 | 100000 | 类别选择(瞬间覆盖) |
CS5 | 101000 | 类别选择(严重) |
CS6 | 110000 | 类别选择(网间控制) |
CS7 | 111000 | 类别选择(网络控制) |
在路由器出现拥塞时,路由器可以根据数据报该字段的优先级,优先丢弃低优先级的数据报。
2.当后3位不全为0时,这6位由因特网或本地机构根据情况定义了多种服务。
类 | 值 | 分配机构 |
---|---|---|
1 | XXXXX0 | 因特网 |
2 | XXXX11 | 本地的或实验的 |
3 | XXXX01 | 本地的或实验的,但最终会走向标准化 |
根据最右边的数字不同,来区分这些服务是由因特网,本地的还是科研人员做实验定义的。
可以看出,如果最后一位为0,则表示标准服务。也就是有32个DSCP被正式用于因特网中。除开和TOS前三位优先位兼容的8个,还有24个代码点。
而最后一位为1的代码点则用于本地或者实验用途。其中01结尾的DSCP最初打算用于本地或者实验用途,但是最终会走向标准化。
我们只需要掌握标准的32个DSCP的分类。
其实并不难,只需要把6bit的DSCP分为3部分。
如上图所示,左边前3位为类别位,和TOS的优先位兼容。但是这里最好把它理解为不同的类别,不同类别的优先级是不同的。数值越大,优先级越高。
第4和5两位表示丢弃概率,假如前三位是同一类别,那么分组的优先级则由这2位决定。DSCP2和DSCP1这两位的值越大,表示越不容易被丢弃。
这32个标准DSCP大部分都是按这个规律来表示。比如011 100这个代码点,表示数据报的流量类别为3(对应前3位011),丢弃优先级为2(对应10这两位),最后一位0表示这是标准的代码点,已经应用在因特网上了。
但是也有少部分标准代码点有特殊用途。
比如101 110代码点的含义是加速转发(EF),DSCP值为101 110的分组,会享受较低的延时、抖动和丢包率。EF流量是优先级最高的,它在路由器队列中只会排在其他EF流量后面。
总长度(total length, TL): 总长度字段有16个bit = 2个字节,它表示整个数据报的长度(首部+数据),单位为字节。
通过总长度字段和首部长度字段,可以计算出有效数据从哪里开始,到哪里结束。
虽然这个字段有16位长,理论上IPv4数据报的长度最多为65535( 2 16 2^{16} 216 - 1)字节 ,但是数据链路层协议规定了一个数据帧的数据字段的最大长度,称为MTU(Maximum Transmission Unit),所以数据报在被链路层封装为帧时,数据报的总长度一定要小于对应链路层规定的MUT的值。比如以太网规定MUT值为1500字节(远远小于65535字节)。对于过长的数据报长度,就必须要进行分片处理。
IP数据报的长度最好不要太短,否则传输效率就太低了(首部长度相比有效数据长度的比例过大了),但也不要太长,长的数据报对路由器的转发速度有更高的要求。IP协议规定,互联网上所有的主机和路由器,必须接受长度不超过576字节的数据报,换句话说,所有的链路层的MTU必须超过576字节。576这个数字是512字节的有效数据(合理的长度)+可能的最长IP首部60字节+4字节的富余。
之所以需要这个字段是因为链路层协议可能会进行填充,必须靠总长度字段计算出实际的数据长度和填充字节的长度帮助分离帧。
有三个字段都和数据报分片相关:标识、标志、片偏移,下面会分别介绍。
IP数据报需要分片是因为数据链路层的帧的最大长度是不同的。一个链路层帧能承载的最大数据量叫做最大传送单元(maximum transmission unit, MTU)。IP数据报在传递时可能会通过多个链路层协议,当数据报的总长度大于某个帧的MTU,就必须分片。当原始数据报分片后,每个分片会作为一个单独的数据报(也就首部和数据段)发送,它们可以在转发过程中走到了不同的路径。而且分片可能不止一次,当分片自身到达某个链路层,该链路层的MTU比分片的长度更小时,还得让分片继续分片,以通过该段链路。
分开了必定需要重组。重组的工作是在端系统,而不是在路由器中完成的。
原因有2个:
1.为了保持网络内核简单的原则,减轻路由器转发分组的负担。
2.更重要的原因是,同一个数据报的不同分片会经过不同的路径到达相同的目的地,这样路由器只能得到所有分片的一部分,它是没有能力重组分片的。
如何实现分片与重组就需要下面3个字段:标识,标志,片偏移。
标识(identification):标识字段有16比特,它相当于每个数据报的id,标识字段与IP地址一起唯一的定义了每个数据报。在发送端的主存储器中会有一个计数器,每发送一个报文,计数器就会加1,并且计数器的当前值会被复制到标识字段中(也就是每个新发的报文的标识字段都是唯一的)。数据报在网络中转发时,如果需要分片,每个分片都会重新加上一个首部,首部的标识字段是完全一样的。等所有分片都到接收端后,标识相同的数据报就一定是同一个原始数据报的分片。
标记(flag):标记字段有3比特。
第一位保留为以后使用,第一位目前都固定为0。
第二位叫不分片位(Don’t Fragment, DF),如果DF为1就不允许分片,DF=0才允许分片。对于使用UDP传输层协议的分组,可能为了效率会禁止分片。
第三位叫更多分片位(More Fragment, MF),MF=1表示该分片后还有更多的分片,MF=0表示该分片是最后一个分片或者该数据报没有分片过,是原始数据报。
用上面的理论举例:
如果一个数据报禁止分片,它的标记位就是010。
如果一个分片不是最后一个分片,它的标记位就是001。
如果该分片是最后一个分片,它的标记位就是000。
片偏移(Fragment Offset):片偏移字段有13比特。该字段用来记录当前分片的有效数据在原数据报有效数据中的相对位置,相对位置就是偏移量。它的单位是8字节,这是因为总长度字段有16bit,而片偏移只有13bit。这也导致分片中,除了最后一个分片以外,其他分片的有效数据一定是8字节(64bit)的整数倍。
举例:一个总长度4000字节的数据报(20字节首部+3980字节有效数据)需要通过一段以太网链路,以太网的MTU为1500字节。设置标识为777(随便设定的)。假设让每个分片总长度为1500字节,去掉20字节的IP首部,则每个分片的有效数据为1480字节。
分片后的情况如下:
数据报 | 总长度 | 有效数据 | 标识 | 片偏移 | 标记 |
---|---|---|---|---|---|
原数据报 | 4000字节 | 3980字节 | 777 | offset = 0 | 000 |
第一片 | 1500字节 | 1480字节 | 777 | offset = 0 | 001 |
第二片 | 1500字节 | 1480字节 | 777 | offset = 185 (1480字节 = 185 x 8) | 001 |
第三片 | 1040字节 | 1020字节 | 777 | offset = 370 (2960字节 = 370 x 8) | 000 |
其实很好理解,先去掉首部20字节,原数据报一共有3980的有效数据,而MTU为1500字节,所以分段的有效数据为1480字节(还有20字节是IP首部)。
所以,需要分片为3段,前2段的有效数据都是1480字节,第三段为剩下的1020字节,一共为1480+1480+1020 = 3980字节的有效数据。
所有分段的标识必须和原数据报一致为777,片偏移计算的是分片的有效数据在原数据报的有效数据中的相对位置,第一片的offset=0,第二片的开始有效数据为原数据报的1480字节,所以offset = 1480/8 = 185,同理,第三片的offset=2960/8 = 370。前2片不是最后一片,所以它们的标志都为001,最后一片标志为000。
如果第三个分片在之后的传输中遇到了MTU为576字节的链路层协议,它需要再次分片,那应该如何分片呢?
其实只需要记住一点,分片不管分了多少次,它的片偏移一定是记录在“原始分组有效数据 ”的相对位置。
假设让进一步的分片总长度就为572字节,去掉20字节的IP首部,分片的有效数据应该为552字节(不能让总长度为576字节,因为556字节不能被8整除)
第三个分片进一步分片后的情况如下:
数据报 | 总长度 | 有效数据 | 标识 | 片偏移 | 标记 |
---|---|---|---|---|---|
原本第三个分片 | 1040字节 | 1020字节 | 777 | offset = 370 (2960字节 = 370 x 8) | 000 |
进一步的第一片 | 572字节 | 552字节 | 777 | offset = 370 (2960字节 = 370 x 8) | 001 |
进一步的第二片 | 488字节 | 468字节 | 777 | offset = 439 (3512字节 =439 x 8) | 000 |
算法很简单:
1.找到片偏移值为0,标志位为001的数据报,标志位为001说明它是第一个或者中间的分片,片偏移值为0说明它是第一个分片。
2.将第一个分片的有效数据长度除以8,其结果就是第二个分片的片偏移值。
3.以此类推依次找到第三第四等等分片的偏移值。
4.直到发现有一个分片的标志位为000,说明这是最后一个分片。
5.将所有的分片按偏移值重组,就可以恢复原始的数据报。
生存时间(time to live, TTL): 生存时间字段有8个比特,它表示数据报一共会经过多少个路由器。数据报每到达一个路由器,路由器就会将该字段的值先减1,如果减1后等于0,就丢弃该数据报,并用一个ICMP报文通知发送方。其实TTL的功能更像是限制数据报的跳数,每经过一个路由器就算跳一次,它的值就减1。
从这个字段可知,数据报在互联网中能经过的路由器最大数值为255。如果把TTL初始值设为1,则该数据报只能在本地局域网中传送,只要到达第一个路由器就会被丢弃。
这个字段的作用是防止类似数据报因为路由环路在网络中永远循环等问题。
TTL的值的设置是由发送端决定的,通常这个数值为发送端到接收端之间的路由器数量的两倍左右。
协议(protocol):协议字段有8个比特。它的作用是定义使用该IPv4服务的高层协议,比如TCP,UDP,ICMP,IGMP等。以便让目的主机的IP层知道应将数据部分上交给哪个协议处理。
常见的协议对应取值如下表:
值 | 协议 |
---|---|
1 | ICMP |
2 | IGMP |
4 | IPv4-in-IPv4 |
6 | TCP |
17 | UDP |
89 | OSPF |
首部校验和(header checksum):首部校验和字段有16个比特。它只是用来计算IPv4头部,不检查有效载荷部分的正确性(这是因为封装在数据报中的高层协议都有对整个分组的校验和,而且数据报经过每个路由器只会改变数据报首部的部分字段,不会改变有效载荷,只重新计算首部校验和足够)。数据报每经过一个路由器都需要重新计算首部校验和字段(因为生存时间、标志、片偏移等值每经过一个路由器都可能发生变化,TTL是一定会变化)。
首部校验和的算法非常简单,为了在路由器上更高效的运行,它并没有采取更复杂的循环冗余校验(CRC)。
发送方:
1.发送方将校验和字段设置为0。
2.将整个头部划分为16位为单位的数字,对各部分计算16位二进制反码和。
3.将计算结果插入到校验和字段中。
接收方:
1.接收方直接对整个头部进行16位二进制反码和计算(包括首部校验和字段)
如果没有错误,计算出的结果应该为0。如果最终结果不为0,说明首部出现了差错。
当接收方如果发现IP首部有错,就会丢弃该数据报。
这两个ip地址各自占4个字节,它们的寻址方式我在一篇博文详细介绍了IPv4的三种寻址方式(分类寻址,子网寻址到最新的CIDR寻址)
选项:选项字段是可选的,最长可达40字节。它对每个数据报来说不是必须的,它们可用于网络测试和调试等。选项字段虽然增加了IPv4数据报的功能,但是也让IPv4数据报的首部长度是可变的,这增加了路由器处理IP数据报的开销。
选项字段长度从1字节到40字节不等。如果选项长度不是4字节的倍数,则需要在末尾填充足够的0,让首部长度为4字节的倍数。
比如如果选项字段为1字节,则需要填充3字节的0,让IP首部变成4字节的倍数。
这是因为首部长度字段是以4字节的字为单位的。