2.4.4 IP协议
网络层中最主要的协议是IP协议(Internet Protocol)。IP协议的数据包中包含了目的地的地址信息,我们之前提到的数据转发就是根据这个目的地址来决定的。在发送数据时,如果数据包无法直接发送给对端,我们就需要通过路由器来进行数据中转,IP协议会根据目的地址决定向哪一个后继路由器(或节点)进行数据转发。这个过程会一直持续,直到数据包发送到对端为止。
现在,我们考虑一种特殊情况,当IP数据包发送到某一个路由器时,吧嗒,停电了。这时问题就来了,我们这个数据包并没有成功发送到对端,而此时数据发送的源端也可能不知道数据有没有发送成功,怎么办呢?ICMP协议(Internet Control Message Protocol)在这个时候就可以大显身手了。我们把这种协议称为Internet控制报文协议,其作用就是探测网络连接。该协议提供了简单的出错报告信息,发送的出错报文会返回到发送数据的源端。发送端随后可根据ICMP报文确定发生错误的类型,并确定如何才能更好地重发失败的数据包。需要注意的是,ICMP只负责报告错误,而如何处理某个特定错误,则由发送端自己决定,如图2-10所示。
图2-10 ICMP原理
1.协议详解
接下来,我们来一起了解一下IP协议头部的构造。首先是IPv4版本的IP协议头,如图2-11所示。
图2-11 IP头部
1)版本:该字段的长度为4 bit。对于IPv4版本的IP协议来说,其值是4。
2)头部长度:该字段的长度为4 bit。用于指定当前IP头部的总长度(单位是32 bit)。由于该字段总长度为4位,用二进制表示的最大十进制数字为15(用二进制位表示是1111),因此IP协议头部的总长度最大为15×32/8=60 Bytes,即60字节。
3)服务类型:该字段的长度为8 bit。用于分配优先级、延迟、吞吐量以及可靠性。该字段的最高3 bit用于定义IP优先级,同理,我们可以用这3 bit来定义8种不同的服务优先级。其次,第3至第6 bit用于定义最低延迟、最高吞吐量、可靠性和最小开销。而最后1 bit则必须置0。需要注意的是,第3至第6 bit的定义中,至多只能有一位置1,应用程序需要根据特定需求来进行置位。
4)长度:该字段的长度为16 bit。用于定义整个IP数据报的总字长,同理,我们可以通过16 bit计算出一个IP数据报的最大字长,即216-1=65535 Bytes。不过,低层(链路层)会限制能支持的IP数据报长度。例如,Ethernet协议包含一个名为MTU(Maximum Transfer Unit,最大传输单元)的协议参数,其值为1518字节,Ethernet的帧首部使用18字节,因此只能携带1500字节的数据。因此,实际情况下的IP数据报总长度远比我们计算出来的65535 Bytes小。
现在需要考虑一个问题,我们怎样才能发送超过MTU的IP数据报呢?最简单和直接的想法就是把超过MTU长度的数据拆分成多个数据报再分别发送。嗯,这种方法完全可行!
5)认证:该字段的长度为16 bit。用于对IP数据报进行唯一标识,其值从一个随机数开始,随着发送的IP数据报每次加1。不过,对于字长超过MTU的IP数据报,拆分后发送的数据报具有相同的认证。
6)标志:该字段的长度为3 bit。第1位为保留字段,第2位置1则表示禁止对IP数据报拆分。如果将该位置为1,那么网络层将不会对IP数据报进行拆分,取而代之的是返回一个ICMP差错报文。第3位表示是否还包含更多分片,除了最后一个分片以外,其他IP数据报都应将该值置为1。
7)段偏移量:该字段的长度为13 bit。用于表示分片相对原始IP数据报开始处的偏移量。
8)TTL:该字段的长度为8 bit。用于指定数据报到达目的地之前允许经过的最大路由跳数。
9)协议:该字段的长度为8 bit。用于表示当前上层所使用的协议类型。在Linux操作系统下,/etc/protocols文件定义了所有上层协议使用的协议字段值,如表2-1所示。
表2-1 协议字段
10)校验和:该字段的长度为16 bit。通过CRC算法计算出来,用于检查接收到的数据是否正确。
11)源IP地址:该字段的长度为32 bit。用于标识发送端。
12)目的IP地址:该字段的长度为32 bit。用于标识接收端。
13)选项:该字段的长度为40字节。虽然这个字段的总长度不能超过40字节,但是在这个范围内,该字段的长度是可变的,我们可以具体定义多种类型的选项。
IPv6头部由40个字节的定长头部与不定长的扩展字段组成,其结构如图2-12所示。
图2-12 IPv6头部
IPv6逐跳头:此扩展头必须紧随在IPv6头之后,它包含包所经路径上的每个节点都必须检查的可选数据。到目前为止,只定义了一个选项:巨型净荷选项。该选项指明,此包的净荷长度超出了IPv6的16位净荷长度字段。只要包的净荷(包括逐跳选项头)超出65535字节,就必须包含该选项。如果节点不能转发此包,则必须返回一个ICMPv6出错报文。逐跳头的定义如图2-13所示。
图2-13 IPv6逐跳头
IPv6路由头:此扩展头指明包在到达目的地途中将经过的特殊的节点。它包含包沿途经过的各节点的地址列表。IPv6头的最初目的地址不是包的最终目的地址,而是选路头中所列的第一个地址。此地址对应的节点接收到该包后,对IPv6头和选路头进行处理,然后将包发送到选路头列表中的第二个地址。如此继续,直至该包到达最终目的地。路由头的定义如图2-14所示。
图2-14 IPv6路由头
IPv6目的选项头:此扩展头包含只能由最终目的地节点所处理的选项。目前,只定义了填充选项,将该头填充为64位边界,以备将来所用。目的选项头的定义如图2-15所示。
图2-15 IPv6目的选项头
IPv6封装安全载荷头:这是最后一个扩展头,不进行加密,它指明剩余的净荷已经加密,并为已获得授权的目的节点提供足够的解密信息。封装安全载荷头的定义如图2-16所示。
图2-16 IPv6封装安全载荷头
2.路由与转发规则
IP协议的路由转发规则就是依照我们之前描述的路由算法实现的。流程如图2-17所示。
图2-17 转发规则流程
转发的具体流程如下。
1)收到IP报文,查找路由表。
2)如果路由表中有路由项和报文目标IP匹配,则认为路由项匹配,并转发到对应的端口上。
3)如果没有找到任何匹配项,则将IP报文丢弃。
其中比较复杂的就是路由表项的匹配过程。我们一般将路由表中的目的地址与路由表中的子网掩码进行逻辑与操作,得到一个网络地址,然后将该地址与路由表中的地址进行比较,如果相同则表示匹配。
如果发现匹配的路由表项,则看路由器是否与该目标节点直接相连,如果没有,则以该匹配的路由网络地址为目标地址,再次进行匹配,直至找出与该路由器直接相连的匹配项为止。
确定了路由项之后,我们就将IP报文封装成数据链路层的帧并转发到对应端口上。
由于我们有多重方式可以匹配到不同的路由,因此为了统一,业界将路由匹配规则按照优先级分为以下3种匹配规则。
1)路由最长匹配原则:我们优先选择子网掩码最长的路由表项进行匹配。因为子网掩码越长,表示网络号越长,代表其网络范围越小,越精确。
2)路由迭代查找原则:上文阐述过,如果当前匹配结果不与路由器直接相连,那么就以匹配结果为目标节点。
3)默认路由匹配原则:一般地,如果IP报文与任何表项都无法匹配,我们会将其丢弃。但是我们可以设置一种默认原则,当报文不与其他表项匹配时,我们转发到某个特定端口上。其原理很简单,我们在路由器中加入一条IP地址和子网掩码均为0.0.0.0的路由表项,由于该表项长度最短,因此处于最低优先级。另外,该子网掩码与任何地址做与运算都是0.0.0.0,肯定与目的地址相匹配。因此只有在没有其他任何路由匹配IP报文的情况下,系统才会按照默认路由转发。