IP协议

IP协议是TCP/IP协议族的核心协议,也是socket网络编程基础之一。本章从两个方面讨论IP协议:

  1. IP头部信息。IP头部信息出现在每个IP数据报中,用于指定IP通信的源端IP地址、目的端IP地址,指导IP分片和重组,以及指定部分通信行为。
  2. IP数据报的路由和转发。IP数据报的路由和转发发生在除目标机器之为的所有主机和路由器上。它们决定数据报是否应该转发以及如何转发。

1、IP服务的特点

IP协议是TCP/IP协议族的动力,为上层协议提供无状态、无连接、不可靠的服务。

  • 无状态(stateless)是指IP通信双方不同步传输数据的状态信息,所有IP数据报的发生、传输和接收都是相互独立、没有上下文关系的。这种服务最大的缺点是无法处理乱序和重复的IP数据报。接收端的IP模块只要收到完整的IP数据报(如果是IP分片的话,IP模块会闲执行重组),就将其数据部分(TCP报文端、UDP数据包或者ICMP报文)上交给上层协议。那么在上层协议来看,这些数据可能是乱序的、重复的、面向连接的协议,比如TCP协议、能够自己处理乱序 的、重复的报文段,它递交给上层协议的内容绝对是有序的、正确的。IP数据报头部提供了一个标志段用于唯一标志一个IP数据报,用来处理IP分片和重组,但不能用来知识接收顺序。无状态服务的有点很明显:简单、高效。无需为保证通信的状态而分配一些内核资源吗,也无需每次传输数据时都携带状态信息。网络中无状态协议有UDP和HTTP等协议。
  • 无连接(connectionless)指IP通信双方都不长久维持对方的任何信息。因此,上层协议每次发送数据的时候,都必须明确指定对方的IP地址。
  • 不可靠指IP协议不能保证IP数据报准确的到达接收端,它知识承诺尽最大努力(best effort)。很多情况都能导致IP数据报发送失败。比如:某个中转路由器发现IP数据报在网络上存活的时间太长,则会将其丢弃,并返回一个ICMP错误信息(超时错误)给发送端。接收端发现收到的IP数据报不正确(通过校验机制),也会将其丢弃,并返回一个ICMP错误信息(IP头部参数错误)给发送端。无论哪种情况,发送端的Ip模块一旦检测到IP数据报发送失败就通知上层协议发送失败,而不会试图重传。以此,使用IP服务的上层协议(比如:TCP协议)需要自己实现数据确认、超时重传等机制来达到可靠传输的目的。

2、IPv4头部结构

IPv4头部结构如下图,其长度通常为20字节,除非含有可变长的选项部分。

  • 4位版本号(version)指定IP协议的版本,对IPv4来说,值=4。
  • 4位头部长度(header length)标志该IP头部有多少个32bit字(4字节)。英文4位最大能表示15,所以IP头部最长位为60字节。
  • 8位服务类型(Type Of Service,TOS)包括一个3位的优先权限字段(现在已经被忽略),4位的TOS字段和1位保留字段(必须置0)。4位的TOS字段分别表示:最小延时、最大吞吐量、最高可靠性和最小费用。一种最多有一个能置为1,应用程序应该根据实际需求来设置它。比如像ssh和telent这样的登录程序需要的是最小延时的服务,而文件传输程序ftp则需要最大吞吐量的服务。
  • 16位总长度(total length)是指整个IP数据报的长度,以字节为单位,以此IP数据报的最大长度为65535字节。但由于MTU的限制,长度超过MTU的数据报将被分片传输,所以实际传输的IP数据报(或分片)的长度都远远没有达到这个最大值。接下来的3个字段则描述了如何实现分片。
  • 16位标志(identification)唯一标志主机发送的每一个数据报。其初始值由系统随机生成,每发送一个数据报,其值就加1。该值在数据分片时,被复制到每个分片中,因此同一个数据报的所有分片都具有相同的标识值。
  • 3位标识字段的第一位保留。第二位(Don't Fragement,DF)表示“禁止分片”。如果设置了这个位,IP模块将不对数据报进行分片。在这种情况下,如果IP数据报长度超过MTU的话,IP模块将丢弃该数据报并返回一个ICMP差错报文。第三位(More Fragment,MF)表示“更多分片”。除数据报的最后一个分片外,其他分片都要把它置1。
  • 13位分片偏移(fragmentation offset)时分片相对原始IP数据报开始处(仅指数据部分)的偏移。实际的偏移值时该值左移3位(乘8)后得到的。由于这个原因,除了最后一个IP分片外,每个IP分片的数据部分的长度必须是8的整数倍(这样才能保障后面的IP分片拥有一个合适的偏移值)。
  • 8位生存时间(Time To Live,TTL)是数据报到达目的地之前允许经过的路由器跳数。TTL值被发送端设置(常见的值是64).数据报在转发过程中没经过一个路由,该值就被路由器减1。当TTL值减为0时,路由器将丢弃数据报,并向源端发送一个ICMP差错报文。TTL值可以防止数据报陷入路由循环。
  • 8位协议(protocol)用来区分上层协议。/etc/protocls文件定义了所有上层协议对应的protocol字段数值。其中,ICMP=1,TCP=6,UDP=17。/etc/protocols文件时RFC1700的一个子集。
  • 16位头部校验和(header checksum)由发送端填充,接收端对其使用CRC算法以检验IP数据报头部(注意,仅检验头部)在传输过程中是否损坏。
  • 32位源端IP地址和目的端IP地址用来标识数据报的发送端和接收端。一般情况下,这两个地址在整个数据报的传递中保持不变,而不论它中间经过多少个中转路由器。
  • 最后一个选项字段(option)是可变长的可选信息。这部分最多包含40字节,因为IP头部最长是60个字节(包含前面讨论的20个字节的固定部分)。可用的IP选项包括:
    • 记录路由(record route),告诉数据报途径的所有的路由器都将自己的IP地址填入IP头部的选项部分,这个我们可以跟踪数据报的传递路径。
    • 时间戳(timestamp),告诉每个路由器都将数据报被转发时的时间(或时间与IP地址对)填入IP头部选项部分,这个就可以测量途径路由之间的数据报传输的时间。
    • 松散源路由选择(loose source routing),指定一个路由器IP地址列表,数据报发送过程中必须经过其中所有的路由器。
    • 严格源路由选择(strict source routing),和松散源路由选择类似,不过数据报只经过被指定的路由器。

3、分片

当Ip数据报的长度超过帧的MTU时,将被分片传输。分片可能发生在发送端,也可能发生在中转路由器上,而且可能在传输过程中被多次分片,但只有在最终的目标机器上,这些分片才会被内核中的IP模块重新组装。

IP头部中的如下三个字段给IP的分片和重组提供了足够的信息:数据报标识、标志和片偏移。

一个数据报的每个分片都具有自己的IP头部,它们具有相同的标识值,但具有不同的片偏移。并且除了最后一个分片外,其他分片都将设置MF标识。此外,每个分片的IP头部的总长度字段将被设置为该分片的长度。

以太网的MTU是1500字节(可通过ifconfig或netstat命令查看),以此它携带的IP数据报的数据部分最多是1480字节(IP头部占用20字节)。考虑用IP数据报封装一个长度为1481字节的ICMP报文(包括8字节的ICMP头部,所以其数据部分长度为1473字节),则该数据报在使用以太网传输时必须被分片,如下图所示:

注意:

ICMP报文的头部长度取决于报文的类型,其变化范围很大。上图以8字节为例,后面例子用到的ping程序使用的ICMP回显和应答报文的头部长度是8字节。

sudo tcpdump -ntv -i enp2s0 icmp
ping 192.168.6.16 -s 1473

tcpdump输出一个IP数据报的两个分片内容如下:

这两个分片的标识值都是11816,说明它们是同一个IP数据报分片。第一个分片的片偏移值为0,而第二个则是1480。很显然,第二个分片的偏移值是加上也是第一个分片的ICMP报文的长度。而一个分片设置了MF标识表示还有后续分片,所以tcpdump输出“flags[+]”。而第二个分片则没有设置任何标识,所以tcpdump输出“flags[none]”。这两个分片长度分片分别是1500字节和21字节,与IP分片示意图描述的一致。

最后,IP层传递给数据链路层的数据可能是一个完整的Ip数据报,也可能是一个IP分片,它们统称为IP分组(packet)。

4、IP路由

IP模块工作流程:

IP协议的一个核心任务是数据报的路由,即决定发送数据报到目标机器的路径。IP模块的基本工作流程如下:

首先从右往左来分析上图,当IP模块接收到来自数据链路层的IP数据报时,首先对该数据报的头部进行CRC校验,确认无误后就分析其头部的具体信息。

  • 如果该IP数据报的头部设置了源站选路选项(松散原路由选择或严格源路由选择),则IP模块调用数据报转发子模块处理该数据。
  • 如果IP数据报头部中的IP地址是本机的某个IP地址吗,或者是广播地址,即该数据报是发给本机的,则IP模块就根据数据报头部中的协议字段来决定将它派发给哪个上层应用(分用)。
  • 如果IP模块发现这个数据报不是发送给本机的,则调用数据报转发子模块来处理该数据报。
  • 数据报转发子模块将首先检测系统是否允许转发。如不允许,IP模块则将数据丢弃;如允许,数据报转发子模块将对该数据报执行一些操作,然后将它交给IP数据报输出子模块。

IP数据报应该发送到哪个下一跳路由(或者目标机器),以及经过哪个网卡来发送,就是IP路由过程(即上图中“计算下一跳路由”子模块)。IP模块实现数据报路由的核心数据结构是路由表。这个表安装数据报的目标IP地址分类,同一类型的IP数据报将被发往相同的下一跳路由器(或目标机器)。

IP输出队列中存放的是所有等待发送的IP数据报,其中除了需要转发的IP数据报外,还包括封装了本机上层数据(ICMP报文、TCP报文段和UDP数据报)的IP数据报。

IP路由策略:上图中虚线箭头显示了路由表更新过程。这一过程是指通过路由协议或者route命令调整路由表,使之更适应最新的网络拓扑结构。

路由机制:

路由表实例说明:

该路由表包括三项,每项都包含8个字段,如下所示:

  • 目标(Destination):目标网络或主机
  • 网关(Gateway):网关地址,*表示目标与本机在同一个网络,不要需要路由。
  • 子网掩码(Genmask):网络掩码
  • 标识(Flags):路由项标志,常见有如下五种:
    • U:该路由项是活动的。
    • H:该路由项的目标是一台主机。
    • G:该路由项的目标是网关。
    • D:该路由项是由重定向生成的。
    • M:该路由项被重定向修改过。
  • 活跃点(Metric):路由距离,即到达指定网络所需的中转数。
  • 引用(Ref):路由项被引用的次数(Linux未使用)。
  • 使用(Use):该路由项被使用的次数。
  • 接口(Iface):该路由项对应的输出网卡接口。

说明:

第一项的目标地址是default,即默认路由项。该路由项包含G标志,说明路由下一跳目标是网关,其地址为192.168.6.1。另一个路由项目标地址是192.168.6.0,它只本地局域网。该路由项网关地址为* ,说明数据报不需要路由中转,可以直接发送到目标机器。

IP路由机制的工作步骤:

  1. 查找路由表中和数据报的目标IP地址完全匹配的主机IP地址。如果找到,则使用该路由项,没有则转步骤2。
  2. 查找路由表中和数据报的目标IP地址具有相同网络ID的网络IP地址,比如上图中第三项。如果找到则使用该路由项,否则转步骤3。
  3. 选择默认路由项,这通常意为中数据报下一跳路由是网关。

因此,对于上图而言,所有发送到IP地址为192.168.6.*的机器的IP数据报都可以直接发送到目标机器,而所有访问因特网的请求都将通过网关来转发(匹配默认路由项)

路由表更新:

sudo route add -host 192.168.1.109 dev enp1s0
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 enp1s0

第1行表示添加主机192.168.1.109对应的路由项。设置后,从本机发往109的IP数据报缴通过网卡enp1s0直接发送至目标机器的接收网卡。

第2行表示删除网络192.168.1.0对应的路由项。此时除了109机器外,本机无法访问局域网内任何其他机器。

第3行表示删除默认的路由项。这样做的结果是无法访问英特网。

第4行表示重新设置默认路由项,不过这次网关是机器109,而不是能直接访问的因特网路由器。经过上述修改后路由表内容如下:

Destination         Gateway         Genmask       Flags     Metric     Ref     Use     Iface
ken-machine          *              255.255.255.255   UH        0       0      0      enp1s0
default             109-machine     0.0.0.0        UG        0       0      0      enp1s0  

新路由表中,第一个路由项是主机路由项,所以被设置了H标志。

通过route命令或其他工具手工修改路由表是静态路由更新方式。但对于大型的路由器,通常通过BGP(Border Gateway Protocol,边际网关协议)、RIP(Routing Information Protocol,路由信息协议)、OSPF等协议来发现路径,并更新自己的路由表。这种更新方式是动态的、自动的。

5、IP转发

不是发给本机的IP数据报将由数据报转发子模块来处理。路由器都能执行数据报的转发操作,而主机一般只发送和接收数据报,因为主机上/proc/sys/net/ipv4/ip_forward内核参数默认为0。我们可通过修改其值来激活主机的数据报转发功能。

echo 1 > /proc/sys/net/ipv4/ip_forward

对于允许IP数据报转发的系统(主机或路由器),数据报转发子模块将对期望转发的数据报执行如下操纵:

  1. 检查数据报头部信息的TTL值。如果TTL=0,则丢弃该数据报。
  2. 查看数据报头部信息的严格源路由选项。如果该选项被设置,则检测数据报的目标IP地址是不是本机的某个IP地址,如果不是,则发送一个ICMP源站选路失败报文给发送端。
  3. 如果有必要,则给源端发送一个ICMP重定向报文,以告知它一个更合理的下一跳路由器。
  4. 将TTL值减1。
  5. 处理IP头部选项。
  6. 如有必要,则执行IP分片操作。

6、重定向

ICMP重定向报文

格式如下:

ICMP报文头部包含3个固定字段:8为类型、8为代码和16为校验和。ICMP重定向报文的类型值=5,代码段有4个值可选,用来区分不同的重定向类型。主机重定向时,代码值=1。

ICMP重定向的数据部分给接收方提供如下两个信息:

  • 引起重定向的IP数据报的源端IP地址。
  • 应该使用的路由器IP地址。

接收主机根据这连个信息就可以断定引起重定向的IP数据报应该使用哪个路由器来转发,并且以此来更新路由表(通常是更新路由表缓冲,而不是直接更改路由表)。

/proc/sys/net/ipv4/conf/all/send_redirects内核参数指定是否允许发送ICMP重定向报文,而/proc/sys/net/ipv4/conf/all/accept_redirects内核参数则指定是否允许接收重定向报文。一般来说,主机只能接收ICMP重定向报文,而路由器只能发送ICMP重定向报文。

主机重定向应用举例:

第4章中,我们把机器ken-machine网关设置为109-machine,第5章中又设置了109-machine的数据报转发功能,因此机器ken-machine将通过109-machine来访问因特网。比如在ken-machine执行ping命令:

$ ping www.baidu.com
PING www.a.shifen.com (119.75.217.56) 56(84) bytes of data.
From 109-machine(192.168.1.109): icmp_sep=1 Redirect Host(New nexthop:192.168.1.1) 64 bytes from 119.75.217.56: icmp_seq=1 ttl=54 time=6.78 ms

---------www.a.shifen.com ping statistics---------
1 packets transmitted ,1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 6.789/ 6.789/ 6.789/0.000 ms

从ping命令的输出来看,109-machine给ken-machine发送了一个ICMP重定向报文,告诉它请通过192.168.1.1来访问目标机器,因为这对于ken-machine来说是更合理的路由方式。当主机ken-machine收到这样的ICMP重定向报文后,它将更新其路由表缓冲(使用命令 route -Cn查看),并使用新的路由方式来发送后续数据报。总结如下图所示:

注:笔记来源《Linux高性能服务器编程》

posted @ 2021-06-01 15:32  钟齐峰  阅读(686)  评论(0编辑  收藏  举报