IP协议是TCP/IP协议族的核心协议,也是socket网络编程的基础之一。
我们将从以下两个方面较为深入的了解IP协议:
IP头部信息:IP头部信息出现在每个IP数据报中,用于指定IP通信的源端IP地址,目的端IP地址,指导IP分片和重组,以及指定部分通信行为。
IP数据报的路由和转发:IP数据报的路由和转发发生在除目标机器之外的所有主机和路由器上。它们决定数据报是否应该转发以及如何转发
IP服务的特点
IP协议是TCP/IP协议族的动力,它为上层提供无状态,无连接,不可靠的服务。
无状态:指IP通信的双方不同步传输数据的状态信息,因此所有IP数据报的发送,传输和接收都是相互独立,没有上下文关系的。IP通信的最大缺点是无法处理乱序和重复的IP数据报,因为这些IP数据报之间没有任何上下文关系。接收端的IP模块只要收到了完整的IP数据报(如果是IP分片的话,IP模块将先执行重组),就将其数据部分(TCP报文段,UDP报文段和ICMP报文)上交给上层协议。
虽然IP数据报头部提供了一个标识字段用以唯一标识一个IP数据报,但它是被用来处理IP分片和重组的,而不是用来指示接受顺序的。
TCP协议能够自己处理乱序的,重复的报文段,TCP递交给上层协议的内容绝对是有序的正确的。
无状态服务的优点也很明显,就是简单和高效。并且UDP和HTTP都是无状态的协议。无需为了保持通信的状态而分配一些内核资源,也无需每次传输数据时都携带状态信息。无连接:指IP通信双方都不长久的维持对方的任何信息。这样,上层协议每次发送数据的时候,都必须明确指定对方的IP地址。
不可靠:IP协议不能保证IP数据报准确地到达接收端,它只是尽最大的努力。
很多情况都能导致IP数据报发送失败,但无论什么情况,发送端的IP模块一旦检测到IP数据报发送错误,就通知上层协议发送失败,而不会试图重传。因此,使用IP服务的上层协议(例如:TCP)需要自己实现数据确认,超时重传等机制以达到可靠传输的目的。
IPv4头部结构
IPv4长度通常位20字节,除非含有可变长度的选项部分。
4位版本号:指定IP协议的版本,IPv4值为4。
4位头部长度:标识该IP头部有多少个4字节。因为4位2进制最大为15,所以IP头部最长为60个字节 。
8位服务类型:包括一个3位的优先权字段(现已被忽略),4位的TOS字段和1位保留字段(必须设置为0)。4位的TOS字段分别表示:最小延时,最大吞吐量,最高可靠性和最小费用。其中最多有一个能置为1。应用程序根据实际需要设置它。
例如:ssh和telnet这样的登录程序需要的是最小延时服务,而文件传输程序ftp则需要最大吞吐量服务。16位总长度:指整个IP数据报的长度,以字节为单位,虽然最大长度为2^16-1个字节,但是MTU为1500字节,长度超过MTU的数据报都将被分片传输。
16位标识:唯一标识主机发送的每一个数据报。初始值由系统随机生成;每发送一个数据报,其值就加1,该值在数据报分片时被复制到每个分片中,因此同一个数据报的所有分片都具有相同的标识值。
3位标志字段的第一位保留。第二位表示禁止分片,如果设置为1,IP模块将不对数据报进行分片。在禁止分片的情况下,如果IP数据报长度超过了MTU,IP模块将丢弃该数据报并返回一个ICMP差错报文。第三位表示更多分片,除了数据报的最后一个分片外,其他分片都要把该位设置为1。
13位分片偏移:是分片相对原始IP数据报开始处(仅指数据部分)的偏移,实际的偏移值是该值左移3位(乘8)后得到的。因此,除了最后一个IP分片,每个IP分片的数据部分长度必须为8的整数倍(这样才能保证后面的IP分片拥有一个合适的偏移值)。
8位生存时间(Time To Live,TTL)是数据报到达目的地之前允许经过的路由器跳数。TTL值被发送端设置,数据报在转发过程中每经过一个路由,该值就被路由器减1,当TTL值减为0时,路由器将丢弃数据报,并向源端发送一个ICMP差错报文,TTL可以防止数据报陷入路由循环。
8位协议:用来区分上层协议,/etc/protocols 文件定义了所有上层协议对应的protocol字段的数值,例:ICMP是1,TCP是6,UDP是17 。
16位头部校验和由发送端填充,接收端对其使用CRC算法以检验IP数据报头部(仅检验头部)在传输过程中是否损坏。
32位的源端IP地址和目的端IP地址用来标识数据报的发送端和接收端,一般情况下,这两个地址在整个传递过程中保持不变,而不论它中间经过多少个中转路由器。
IPv4最后一个选项字段是可变长的可选信息。这部分最多可包含40字节,因为IP头部最长是60字节。
可用选项:
1.记录路由:告诉数据报途经的所有路由器都将自己的IP地址填入头部的选项部分,这样我们就可以跟踪数据报的传递路径。
2.时间戳:告诉每个路由器都将数据报被转发的时间(或时间与IP地址对)填入IP头部的选项部分,这样就可以测量途经路由之间的数据报传输的时间。
3.松散源路由选择:指定一个路由器IP地址列表,数据报发送过程中必须经过其中所有的路由器。IP包必须沿着这些IP地址传送,但是允许在相继的两个IP地址之间跳过多个路由器。
4.严格源路由选择:和松散源路由选择类似,不过数据报只能经过被指定的路由器。IP包必须沿着这些IP地址传送,如果下一跳不在IP地址表中则表示发生错误。
IP分片
当IP数据报的长度超过帧的MTU时,将被分片传输。分片可能发生在发送端,也可能发生在中转路由器上,而且可能在传输过程中被多次分片,但是只有在最终的目标机器上,这些分片才会被内核中的IP模块重新组装。
IP头部中的如下三个字段给IP的分片重组提供了足够的信息:数据报标识,标志和片偏移。一个IP数据报的每个分片都具有自己的IP头部,它们具有相同的数据报标识值,但是具有不同的片偏移,除了最后一个分片,其它分片都将设置MF标志,每个分片的IP头部的总长度字段将被设置为该分片的长度。
MTU最大值为1500字节,因此携带的IP数据报的数据部分最多1480字节(IP头部占用20字节)。
如下图,长度位1501字节的IP数据报被拆分为两个IP分片,第一个IP分片长度为1500字节,第二个IP分片长度为21字节。每个IP分片都包含自己的IP头部(20个字节),且第一个IP分片的IP头部设置了MF标志,而第二个IP分片的头部则没有设置该标志,因为第二个分片已经是最后一个分片了。原始IP数据报中的ICMP头部全部被复制到第一个分片,第二个分片不包含ICMP头部信息。
所以,IP层传递给数据链路层的数据可能是一个完整的IP数据报,也可能是一个IP分片,他们统称为IP分组。
IP路由
IP协议的一个核心任务是数据报的路由,即决定发送数据报到目标机器的路径。
IP模块工作流程
我们从右往左分析该图。
当IP模块接收来自数据链路层的IP数据报时,他首先对该数据报的头部做CRC校验,确认无误之后就分析其头部的具体信息。
如果该IP数据报的头部设置了源站选路选项(松散源路由选择或严格源路由选择),则IP模块调用数据报转发子模块来处理该数据报。
如果该IP数据报是发送给本机的,则IP模块就根据数据报头部中的协议字段来决定将它派发给哪个上层应用(分用)。
如果IP模块发现这个数据报不是发送给本机的,则也调用数据报转发子模块来处理该数据报。
数据报转发子模块将首先检测系统是否允许转发,如果不允许,IP模块就将数据报丢弃;如果允许,数据报转发子模块将对该数据报执行一些操作,然后将它交给IP数据报输出子模块。
IP数据报应该发送至哪个下一跳路由(或者目标机器),以及经过哪个网卡来发送,就是IP路由过程,即图中“计算下一跳路由”子模块。
IP模块实现数据报路由的核心数据结构是路由表。这个表按照数据报的目标IP地址分类,同一类型的IP数据报将被发送往相同的下一跳路由器。
IP输出队列中存放的是所有等待发送的IP数据报,其中除了需要转发的IP数据报外,还包括封装了本机上层数据的IP数据报。
虚线箭头显示了路由表更新的过程,这一过程是指通过路由协议或者route命令调整路由表,使之更适应最新的网络拓扑结构,称为IP路由策略。
路由机制
路由表是如何按照IP地址分类的呢?或者说给定数据报的目标IP地址,它将匹配路由表中的哪一项呢?这就是IP的路由机制,分为三个步骤:
1.查找路由表中和数据报的目标IP地址完全匹配的主句IP地址;如果找到,就使用该路由项;没找到则转步骤2
2.查找路由表中和数据报的目标IP地址具有相同网路ID的网络IP地址,如果找到,就使用该路由项;没有则转步骤3
3.选择默认路由项,这通常意味着数据报的下一跳路由是网关。
路由表更新
路由表必须能够更新,以反映网络连接的变化,这样IP模块才能准确,高效的转发数据报。
route命令可以修改路由表。
eg:
$sudo route add -host 192.168.1.109 dev eth0
$sudo route del -net 192.168.1.0 netmask 255.255.255.0
$sudo route del default
$sudo route add default gw 192.168.1.109 dev eth0
IP转发
不是发送给本机的IP数据报将由数据报转发子模块来处理。路由器都能执行数据报的转发操作,而主机一般只发送和接收数据报,这是因为主机上/proc/sys/net/ipv4/ip_forward 内核参数默认被设置为0,我们可以通过修改它来使能主机的数据报的转发功能。
$ echo 1 > /proc/sys/net/ipv4/ip_foeward
对于允许IP数据报转发的系统(主机或路由器),数据报转发子模块将对期望转发的数据报执行如下操作:
1.检查数据报头部TTL值,为0就丢弃。
2.查看数据报头部的严格源路由选择选项,如果该选项被设置,则检测数据报目标IP地址是否是本机的某个IP地址,如果不是,则发送一个ICMP源站选路失败报文给发送端。
3.如果有必要,则给源端发送一个ICMP重定向报文,以告诉它一个更合理的下一跳路由器。
4.将TTL值减1。
5.处理IP头部选项。
6.如果有必要,则执行IP分片操作。
ICPM重定向报文
8位类型字段用于区分报文类型,它将ICMP报文分为两大类: 一类是差错报文,这类报文主要用来回应网络错误(例如:目标不可到达(类型值为3)和重定向(类型值为5) )。
另一类是查询报文,这类报文用来查询网络信息(用ping程序就是使用ICMP报文查看目标是否到达(类型值为8) )。
有的ICMP报文还使用8位代码字段来进一步细分不同的条件。
ICMP报文使用16位校验和字段对整个报文(包括头部和内容部分)进行循环冗余校验(CRC),以检验报文在传输过程中是否损坏。
ICMP重定向报文的数据部分含义很明确,它给接收方提供了如下两个信息:
1.引起重定向的IP数据报的源端IP地址。
2.应该使用的路由器的IP地址。
接收主机根据这两个信息就可以断定引起重定向的IP数据报应该使用哪个路由器来转发,并且以此来更新路由表(通常是更新路由表缓冲,而不是直接更改路由表)。
一般来说,主机只能接收ICMP重定向报文,而路由器只能发送ICMP重定向报文。