《Linux高性能服务器编程》学习总结(二)——IP协议详解
第二章 IP协议详解
IP协议是TCP/IP协议族中的核心协议,也是socket网络编程的基础之一。IP协议的特点是为上层提供无状态、无连接、不可靠的服务。
无状态是指IP通信双方不同步传输数据的状态信息,通俗一些说就是双方发送的IP数据报是相互独立的,没有任何上下文关系。这样的特性缺点在于无法处理重复和乱序的IP数据报,举个例子,假设由于网络原因或者IP选路的原因导致第N个数据报比第N+1个数据报晚到达目的主机或同一个IP数据报经过不同的路径多次到达目的端,此时接收端的IP模块无法检测到乱序和重复,当向上层协议交付数据的时候,就会导致失序。当然,无状态的优点也很明显,简单而高效,我们没有必要像TCP协议一样为了维持连接而占用一部分内核资源,通信双方也不用在IP层交换状态信息。
无连接是指通信双方都不长久维持对方的任何信息,所以上层协议在发送数据的时候都必须指明对方的IP地址。
不可靠是指IP协议不能保证对方收到IP数据包,只能保证尽最大努力交付,而一旦发送方检测到发送失败,只会通知上层,并不会重传,所以对于数据的可靠传输需要由上层协议完成。
在IPv4的首部中,值得注意的几个要点有,首先4位首部长度是标识该IP首部有多少个32bit,也就是有多少个4字节,所以,IP首部的最大长度为(24-1)*4=60字节,而16位总长度是以字节数为单位的,所以IP数据报的最大长度为(216-1)=65535字节,但是由于路径MTU的存在,一般实际传输中长度远没有达到这个最大值。下面的三位标志中第一位保留,第二位是DF位,表示禁止分片,若此位置一,则IP模块将强制不分片,第三位是MF位,表示更多分片,除了数据报的最后一个分片以外其余分片都应将此为置一。接下来的13位片偏移是以8字节为单位的,换句话说就是实际的片偏移是这个位的值左移三位,所以,除了最后一个分片外,其余分片长度都应该是8的整数倍。
关于IP分片,若IP数据报的长度大于了MTU,则他将被分片传输,而分片可能发生在发送端,可能发生在传输过程中,数据报也可能被多次分片,但是只会在接收端的IP模块被重新组装。下面我们用一个例子来看一下IP分片的过程和组装。
以太网的MTU是1500,所以除去IP首部,数据部分最大能传输1480字节。假设我们现在有一个1481字节的ICMP报文,其中包含8字节的ICMP首部和1473字节的数据部分,则发送端IP模块会讲包含ICMP首部的前1480字节截取,加上IP首部作为第一个分片,将MF置1,最后剩余的1字节加上MF置0的IP首部发送到接收端。所以我们看到,对于IP模块而言,上层的协议首部和数据部分是一样的,在本层都是数据部分。而当两个数据包传送到接收端时,IP模块依次剥离两报文的IP首部将其组装起来。但是前面我们提到过,IP协议是无状态的,也就是两个IP报文是没有上下文联系的,如果第二个报文段先一步到达接收端,那么接收端还能正确将其组装吗?答案是肯定的,因为前面我们讲到了IP首部有片偏移字段,根据这个字段,IP模块就能将IP分片准确地放在应该放在的地方了。
下一个重要的问题就是,IP协议是如何选路的呢?为了研究这个问题,我们就要先理解IP模块的工作流程。
IP模块的工作流程看起来很复杂,但是其实很简单,我们从右向左分析。当主机收到一个数据报后,先对数据报头部进行CRC校验,确认后开始分析头部信息;如果该数据报设置了源站选路选项则调用数据报转发子模块进行处理;否则继续看目的IP地址是不是本机的IP地址或者广播地址,即这个数据报是不是发送给自己的,如果是,则向上层协议交付数据,若不是,继续调用数据报转发子模块;当数据报转发子模块收到一个报文时,先检测本机系统是否允许转发,如果不允许则丢弃之,若允许则将对其进行一些操作后交给IP数据报输出子模块。
而我们略过的转发过程、选择下一跳路由以及ICMP重定向等就是IP选路的核心。IP模块实现数据报路由的核心数据结构是路由表,这个表按照数据报的目标IP地址进行分类,同一类型的IP数据报会发给相同的下一跳,而路由表对IP地址进行分类,就是IP的路由机制,共分为三个步骤:1)查找路由表中和数据报的目标IP地址完全匹配的主机地址,如果找到就使用该路由表项,否则转向步骤2。2)查找路由表中和数据报的目的IP地址具有相同网络ID的网络IP地址,即是同一网段的IP地址,如果找到则使用该路由表项,否则转向步骤3。3)选择默认路由,通常意味着下一跳路由是网关。
读到这里我们会想,那么路由表又是如何生成的呢,主机又是如何知道发往哪些IP地址的数据包应该转发给谁呢?路由表的更新有两种方式,第一种是手工静态更新,但是一般应用很少,只在本机实验时会用到,第二种就是通过BGP、RIP、OSPF协议等进行更新路由表。
数据转发子模块在收到需要转发的数据报并且本机允许转发的情况下,将执行如下操作:1)检查数据报头的TTL,如果TTL为0则将之抛弃。2)查看数据报头部的严格源站选路选项,若已经被设置则检查是否是本机的某个IP地址,如果不是则发送一个ICMP源站选路失败报文给发送端。3)如果有必要则向源端发送ICMP重定向报文。4)将TTL减一。5)处理IP头部选项。6)如果有必要则进行IP分片。
前面提到了ICMP重定向报文,这个是做什么的呢?其实就是主机告诉源端,这个数据报从我这里走绕远了,并且告诉他应该怎么走。举个例子,在一个网段上有主机A和主机B,网关是路由器C,连接着外网,我们把A的网关地址改成B的IP地址,并将B的转发功能打开,这时用A向外网发送一个数据报,数据报先发给默认网关即主机B,但是主机B发现它自己接收到这个包的端口和它即将转发的端口是同一个,而且源地址主机A和他的下一跳路由器C是同一网段内,他就会告诉A,你可以直接走C,不经过我,这样A就可以利用ICMP重定向来对自己的路由表进行更新。