• IP头部信息出现在每个IP数据报中,用于指定IP通信的源端和目的端IP地址,指导IP分片和重组,指定部分通信行为
  • IP数据报的路由和转发发生在除目标机器之外的所有主机和路由器上,它们决定数据报如何转发
  • 32位的IPv4地址已经耗尽,可使用128位IPv6地址

IP服务的特点

  • IP协议为上层协议提供无状态、无连接、不可靠的服务
  • 无状态(stateless):IP通信双方不同步传输数据的状态,即所有IP数据报都是相互独立、无上下文关系
    • 缺点:无法处理乱序和重复的IP数据报,因为IP协议无法检测乱序和重复。(IP数据报头部也有提供标识字段来唯一标识一个IP数据报,但它仅用于处理IP数据报的分片和重组,不用于指定接收顺序)
    • 优点:简单高效,不需为保持通信状态而分配资源,也不需每次传输都携带状态信息。(UDP和HTTP也是无状态,HTTP浏览器连续两次网页请求之间无关联)
  • 无连接(connectionless):IP通信双方都不维持对方的任何信息,即上层协议每次发送数据都必须指定对方的IP地址
  • 不可靠:IP协议不能保证IP数据报准确地到达接收端,它只承诺尽最大努力。上层协议需实现数据确认、超时重传等机制以实现可靠传输
    • IP数据报发送失败的例子:
      • 中转路由器根据IP数据报头部TTL字段发现它在网络上存活太久,会将其丢弃,并返回ICMP错误消息(超时错误)给发送端
      • 接收端通过校验发现IP数据报不正确,会将其丢弃,并返回ICMP错误消息(IP头部参数错误)给发送端

IPv4头部结构

IPv4头部结构

  • IPv4的头部通常为20字节,除非含有可变长的选项。结构如图2.1 fig_2_1
  • 4位版本号指定IP协议版本:IPv4的版本号是4,其他IPv4的扩展(如SIP和PIP)有不同的版本号,头部结构也不同
  • 4位头部长度是该头部的长度,单位是32bit(4字节),IP头部的最大长度是15*4=60字节
  • 8位服务类型(Type Of Service,TOS)包括:
    • 3位优先权字段,现已被忽略
    • 4位TOS字段,分别为:最小延时最大吞吐量最高可靠性最小费用。根据实际需要,至多能将其中一个置为1,如ssh和telnet需要最小延时,ftp需要最大吞吐量
    • 1位保留字段,必须置0
  • 16位总长度是整个IP数据报的长度,单位是字节,IP数据报的最大长度为65535字节,但长度超过MTU的会被分片传输
  • 16位标识唯一地标识主机发送的每一个数据报,其初始值随机,每发送一个数据报就+1,分片时每一个分片的标识都相同
  • 3位标志字段:
    • 第一位保留
    • 第二位DF(Don’t Fragment)表示禁止分片,此时若IP数据报长度超过MTU将被丢弃并返回ICMP差错报文
    • 第三位MF(More Fragment)表示更多分片,除数据报的最后一个分片外,其他分片都要将其置1
  • 13位分片偏移是当前分片相对原始IP数据报开始处的偏移,实际偏移是该值*8(左移3位)得到的。因此除最后一个分片外,其他所有分片的数据部分长度必须是8的整数倍
  • 8位生存时间(Time To Live,TTL)是数据报到达目的地之前允许经过的路由器跳数,每经过一个路由器该值就-1,减为0时路由器丢弃该数据报并向源端发送ICMP差错报文。TTL可防止数据报陷入路由循环
  • 8位协议字段用于区分上层协议,在/etc/protocols文件中定义所有上层协议的该字段值,该文件是RFC1700的子集。例如:ICMP是1,TCP是6,UDP是17
  • 16位头部校验和由发送端填充,接收端对其使用CRC来校验IP数据报的头部
  • 32位源端IP地址目的端IP地址用于标识发送端和接收端,在整个传递过程中一般不变
  • 可变长的选项字段最多包含40字节(因为IP数据报头部最长是60字节,固定部分占用20字节),该字段使用不多,详细信息见RFC791:
    • 记录路由用于告诉传播途经的所有路由器,将其IP地址填入IP头部的选项部分,这样可追踪数据报的传递路径
    • 时间戳告诉每个路由器都将数据报被转发的时间填入IP头部的选项部分,这样可测量途径路由间传递的时间
    • 松散源路由选择指定一个路由器IP列表,使得IP数据报传递过程必须经过这些路由器
    • 严格源路由选择指定一个路由器IP列表,使得IP数据报传递过程只能经过这些路由器

使用tcpdump观察IPv4头部结构

  • 在A机上使用telnet登陆到本机,并用tcpdump抓取第一个包
1
2
3
4
5
6
7
8
$ sudo tcpdump -ntx -i lo
$ telnet 127.0.0.1
# 输出:
# IP 127.0.0.1.45422 > 127.0.0.1.23: Flags [S], seq 4231039042, win 65495, options [mss 65495,sackOK,TS val 3775535807 ecr 0,nop,wscale 7], length 0
#         0x0000:  4510 003c 3038 4000 4006 0c72 7f00 0001
#         0x0010:  7f00 0001 b16e 0017 fc30 8842 0000 0000
#         0x0020:  a002 ffd7 fe30 0000 0204 ffd7 0402 080a
#         0x0030:  e10a 1abf 0000 0000 0103 0307
  • tcpdump使用-x输出数据包的二进制码,该输出有60字节:前20字节是IP头部,后40字节是TCP头部,此处没有应用程序数据
  • 该输出描述一个IP数据报,源端和目的端IP都是127.0.0.1,telnet服务器的端口号是23,Flag、seq、win、options都是TCP头部信息,length指出IP数据报携带的应用程序数据长度为0
  • 前20字节的IP头部含义见表2.1,对照图2.1看。其中数据报标识和头部校验和这两项与我的输出不同 tab_2_1
  • 看出telnet选择使用最小延时服务,传输层默认用TCP协议

IP分片

  • IP数据报长度超过帧的MTU时会被分片,它可能发生在发送端或中转路由,可能在传输中被多次分片,只有在目标机上才会完成分片的重组
  • IP头部的数据报标识、标志、分片偏移这3个字段为分片重组提供信息
  • IP数据报的每个分片都有自己的头部:
    • 每个分片的标识相同
    • 除最后一个分片外,标志中的MF都被设置
    • 每个分片的分片偏移不同
    • 每个分片的总长度字段被设置为该分片的长度
  • 以太网帧的MTU是1500字节,故携带的IP数据报的数据部分最多1480字节。
  • 如图2.2是传递一个长未1481字节的ICMP报文时的IP分片: fig_2_2
  • 对上图进行实验:
    • 使用ping来传输ICMP报文,tcpdump抓取前两个包
1
2
3
4
5
6
$ sudo tcpdump -ntv -i eth0 icmp
$ ping 192.168.1.109 -s 1473
# IP (tos 0x0, ttl 64, id 36528, offset 0, flags [+], proto ICMP (1), length 1500)
#     192.168.1.108 > 192.168.1.109: ICMP echo request, id 2164, seq 1, length 1480
# IP (tos 0x0, ttl 64, id 36528, offset 1480, flags [none], proto ICMP (1), length 21)
#     192.168.1.108 > 192.168.1.109: ip-proto-1
  • 命令:
    • tcpdump使用-v输出更多内容(IP头部),使用icp指定只抓取ICMP报文
    • ping使用-s指定每次发送的ICMP报文的数据部分是1473字节(ping使用的ICMP头部长为8,ICMP报文总长1481)
  • 输出:
    • 两个IP分片的标识值id都是36528,故是同一IP数据报的分片
    • 第一个分片的偏移offset是0,第二个分片的偏移是1480(第一个分片的数据部分的长度)
    • 第一个分片flags为[+]表明设置了MF标志,而第二个分片flag为[none]表明未设置标志位
    • 两分片长度分别为1500和21
  • IP层传递给数据链路层的数据可能是一个完整的IP数据报,也可能是一个分片,它们统称为IP分组(packet)

IP路由

  • IP协议的核心之一是数据报的路由,即决定发送数据报到目标机器的路径

IP模块工作流程

  • IP模块的工作流程如图2.3 fig_2_3
  • IP模块接收到来自链路层的数据报时,首先对头部做CRC校验
  • 决定转发或内部处理:
    • 若该数据报头部设置了源站选路(即松散/严格源路由选择),则将其传给数据报转发子模块
    • 若该数据报的目的IP地址是本机或广播地址,即该数据报发送给本机,则根据头部的协议字段来分发给本机的上层应用(分用)
    • 若该数据报不是发送给本机的,则也传给数据报转发子模块
  • 数据报转发子模块首先检测系统是否允许转发
    • 若不允许转发,则将该数据报丢弃
    • 若允许转发,则对该数据报执行一些操作,并传给IP数据报输出子模块
  • IP路由:决定IP数据报应发送至哪个下一跳路由或目标机器,以及经过哪个网卡发送。
  • 路由表是实现IP路由的核心数据结构:
    • 路由表按照数据报的目标IP地址分类,同一类型的IP数据报将被发往相同的下一跳路由或目标机器
    • IP路由策略:可通过路由协议或route命令调整路由表,使之适应新的网络拓扑结构
  • IP输出队列中存放所有等待发送的IP数据报,包括本机发送的数据报和转发的数据报

路由机制

  • 可使用route命令或netstat命令查看路由表
  • 使用route命令输出的路由表中每项包含8个字段,如表2.2 tab_2_2
1
2
3
4
5
6
7
$ route
# 输出:
# Kernel IP routing table
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# default         _gateway        0.0.0.0         UG    0      0        0 eth0
# link-local      0.0.0.0         255.255.0.0     U     1000   0        0 eth0
# 192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0
  • 上述输出:
    • 第一项的目的地址是default,即默认路由。flag中包含G说明下一跳是网关
    • 第三项的目的地址是本地局域网,数据报不需要路由中转可直接发送到目标机器
  • IP路由机制的步骤:
    1. 查找路由表中与数据报目的IP完全匹配的主机的IP地址
    2. 查找路由表中与数据报目的IP具有相同网络ID的网络IP地址
    3. 选择默认路由,通常意味着下一跳路由是网关

路由表更新

  • route命令可静态修改路由表
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ 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
$ route
# 输出:
# Kernel IP routing table
# Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
# default         _gateway        0.0.0.0         UG    0      0        0 eth0
# link-local      0.0.0.0         255.255.0.0     U     1000   0        0 eth0
# _gateway        0.0.0.0         255.255.255.255 UH    0      0        0 eth0
  • 对于大型路由器,通常用BGP(Border Gateway Protocol,边际网关协议)、RIP(Routing Information Protocol,路由信息协议)、OSPF等协议来发现路径以自动地动态更新路由表

IP转发

  • 不发送给本机的IP数据报交给数据转发子模块处理
  • 路由器都能执行数据报的转发操作,但主机一般只发送/接收数据报,因为主机的内核参数/proc/sys/net/ipv4/ip_forward默认被设为0,可将其修改为1来让主机开启数据报转发功能
  • 之前将B机设为A机的默认路由,但B机是主机,需开启转发功能才可让A机上网
1
2
3
$ sudo sysctl -w net.ipv4.ip_forward=1
# 输出:
# net.ipv4.ip_forward = 1
  • 对于允许IP数据报转发的系统(路由或主机),数据报转发子模块将对IP数据报执行操作:
    1. 检查头部TTL值,为0则丢弃
    2. 检查头部严格源路由选项,若该项被设置,则检测目标IP是否是本机,若不是则向发送端发送ICMP源站选路失败报文
    3. 若有必要,向源端发送ICMP重定向报文,告诉它更合理的下一跳路由
    4. 将TTL值减1
    5. 处理IP头部选项
    6. 若有必要,执行分片

重定向

  • ICMP重定向报文也可用于更新路由表(图2.3)

ICMP重定向报文

  • ICMP重定向报文如图2.4 fig_2_4
  • 在1.1节讨论过ICMP报文的格式有3个固定字段
  • 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重定向报文,路由器只能发送ICMP重定向报文

主机重定向实例

  • 将B机设为A机的网关,并在B机上开启数据转发功能,此时A机通过B机上网,在A机上ping百度
1
2
$ ping www.baidu.com
# 输出:
  • 从ping的输出发现,B机给A机发送了ICMP重定向报文,告知A机通过192.168.1.1来访问目标机器。
  • A机收到这个ICMP重定向报文后,更新自己的路由表缓存,使用新的路由方式发送后续数据报
  • 如图2.5 fig_2_5

IPv6头部结构

  • IPv6解决了IPv4地址不够用的问题,还做了很大改进,例如:
    • 增加多播和流的功能,精细控制多媒体内容的质量
    • 引入自动配置功能,更方便管理局域网
    • 增加了专门的网络安全
  • IPv6的规范参见RFC2460

IPv6固定头部结构

  • IPv6头部由40字节的固定头部和可变长的扩展头部组成,如图2.6是固定头部 fig_2_6
  • 4位版本号指定IP协议的版本,IPv6的版本号是6
  • 8位通信类型指定数据流的通信类型或优先级,类似IPv4的TOS
  • 20位流标签是IPv6新增字段,用于某些对连接的服务质量有特殊要求的通信,例如音视频实时传输
  • 16位净荷长度是IPv6扩展头部和应用程序数据长度之和,不包括固定头部长度
  • 8位下一个头部指出紧跟IPv6固定头部后的包头类型,如扩展头,或上层协议头部。它类似IPv4的协议字段
  • 8位跳数限制类似IPv4的TTL
  • IPv6的地址是128位
    • IPv4的地址是32位,用点分十进制分割为4组。而IPv6的地址用十六进制和:分割为8组,每组2字节
    • 通常使用零压缩法简写IPv6地址,即省略连续的全0组。不过对一个IPv6地址只能省略一个全0组

IPv6扩展头部

  • 可变长扩展头部使IPv6可支持更多选项,且便于未来扩展
  • 可变长扩展头部的长度可以是0,表示不使用扩展头部
  • 一个IPv6数据报可包含多个扩展头部,每个扩展头部的类型由前一个头部(固定头部或扩展头部)中的下一个头部字段指定
  • 可使用的扩展头部见表2.3 tab_2_3
  • IPv6协议并不是IPv4的扩展,它是完全独立的协议
  • 以太网帧封装的IPv6数据报和IPv4数据报拥有不同的类型值,IPv4是0x800,IPv6是0x86dd