TCP/IP 协议栈分析
本文参考内容 https://www.bilibili.com/video/BV1ducszjEWs/
发展过程
多家网络公司、机构(IEEE)等均推出了自己的网络协议栈,导致市面有多种网络协议栈。但随着发展,TCP/IP 协议栈是如今最主流的协议栈
- 网络的诞生:美国国防部在冷战中设计与使用的阿帕网(ARPANET)
- 单点网络:一个设备出现故障,整个网络瘫痪
- 不能跨军种共享,陆军使用DEC,海军使用Honeywell,空军使用IBM。不同公司使用不同的协议栈
- 75年,TCP/IP协议栈诞生
- 82年,TCP/IP规范诞生,Unix使用TCP/IP通信
- 83年,美国国防部将TCP/IP作为阿帕网通信标准
- 随着阿帕网商业化,TCP/IP成为事实通信标准
因此,协议栈有很多,随着发展,最终胜出的是 TCP/IP 协议栈
模型: DoD模型
4层,早于 OSI 7层模型
- 应用层: HTTP FTP TELNET SMTP DHCP DNS TFTP
- 传输层: TCP UDP
- 网络层: ARP RARP IP ICMP
- 链路层: Ethernet
本文将从底向上进行协议分析,采用用 GNS3 软件模拟路由交换环境。该网络模拟软件可直接使用 Wireshark 抓取软件中模拟网络设备的数据包
以太网协议
局域网使用最广泛的协议,部署简单,价格低廉。局域网链路层还有令牌网、令牌总线网、FDDI网等,随着以太网的发展,其他协议被抛弃
介质
- 共享介质型: 多个网络终端通过同轴线缆与集线器连接,等待没有数据传输时才会发送数据。常用标准: 10BASE-T(数字表示10M BASE表示基带,T表示线缆)
- 独享介质型: 多个网络终端通过网线与交换机连接,每个终端处于不同的冲突域中(不会冲突)常用标准: 100BASE-TX(100M 基带的 以太网线)
标准
IEEE 802.3 (有线以太网协议都是 802.3,无线以太网是 802.1)
- CSMA/CD: 用于解决链路数据包冲突
- 10BASE-T: 以太网标准。数字表示速度,BASE表示,T表示线缆
- 10BASE-F: F表示光纤
- 100BASE-TX: 快速以太网标准
- 100BASE-FX
- 1000BASE-T
- 1000BASE-LX: Gb以太网标准
封装
数据帧封装分为两种格式。其中的数据部分的长度与 MTU(最大传输单元: IP头部+TCP头部+数据)有关
RFC 894: Ethernet帧(Wireshark标记为 Ethernet)。各大网络公司刚推出以太网协议采用的格式。大部分常采用此封装,更简洁高效。可查看 ARP 协议的数据包详情中的链路层内容

- 目的地址: 6字节
- 源地址: 6字节
- 类型: 2字节。标明该数据包类型。如图,0806 表示 ARP 、8035 表示 RARP
- 内容: 如图 46 - 1500 字节,ARP/RARP 内容的长度 28+18 共 46 字节
RFC 1042: 802.3帧(Wireshark标记为 IEEE 802.3 Ethernet)。IEEE 标准化创立的格式。CDP/VTP/PAGP等,一些Cisco等推出的私有协议常在此基础上设计。Wireshark中可查看 CDP/VTP... 等协议的数据包详情中的链路层内容
ARP 协议
地址解析协议,实现从 IP 地址到 MAC 地址的映射
过程
- 当发送方向接收方发送数据包时,需要得知对方的 MAC 地址。因此发送 ARP request(广播请求,目的地址全F),询问指定 IP 的 MAC 地址(Who has 12.1.1.1? Tell 12.1.1.1)
- 目标 IP(接收方)发送 ARP Reply(单播回复),告知指定 IP 的 MAC 地址值(12.1.1.2 is as cc:01:01:68:00:00)
- 此时发送方的 ARP 缓存表中记录了目标 IP 的 MAC 地址
- 发送方查询自己的 ARP 缓存表,封装数据包
- 发送方发送数据包
封装

- Hardware type: 硬件类型,标识链路层协议
- Protocol type: 协议类型,标识网络层协议
- Hardware size: 硬件地址大小,标识 MAC 地址长度(通常为6)
- Protocol size: 协议地址大小,标识 IP 地址长度(通常为4,ipv4 地址长度 )
- Opcode: 操作代码,标识 ARP 数据包类型(1 请求,2 回复)
- Sender MAC address: 发送者 MAC
- Sender IP address: 发送者 IP
- Target MAC address: 目标 MAC,全0表示在通过 ARP 协议获取未知的 MAC
- Target IP address: 目标 IP
特殊ARP分类
代理ARP(Proxy ARP)
当发起跨网段的 ARP 请求时,出口路由/网关设备将自身 MAC 地址回复该请求时,该过程称为代理 ARP
- 过程: 路由器在收到 ARP request 时,发现时跨网段的 ARP 请求,路由器发送 ARP Reply,回复自己的 MAC 地址。此 ARP Reply 称为代理ARP(路由器此时跨越了网段,且能访问其他网段的客户端。例如路由器的一个端口与一台PC连接且在一个网段,另一个端口与另一台PC连接且在另一个网段)
- 场景
- 没有路由功能的主机(网络终端)
- 网络设备有路由功能(路由器或网络终端),目的地指向本地出口(默认路由指向本地接口,而不是接收方的接口,
ip route 192.168.2.0 255.255.255.0 f0/0f0/0 接口 - 1.0网段的接口,可以连接到 2.0 网段)
免费ARP(Gratuitous ARP)
无故ARP。用于检测局域网内部IP地址冲突
- 场景: 获取 IP 后检查该 IP 是否已被其他设备使用
- IP 地址被修改
- DHCP 刚获取地址
- 过程:
- 在获取到 IP 后,发送 GARP Request(我的 IP 是 192.168.1.1,MAC 是 xxx,谁的 IP 是 192.168.1.1),询问当前的 IP 是否已被使用
- 已使用该 IP 的设备回复 GARP Reply(广播),我的 IP 是 192.168.1.1,MAC 是 xxx
- 发送 GARP Request 的设备收到代表冲突的免费 ARP Reply 后,也发送免费 ARP Reply(广播,我的 IP 是 192.168.1.1,MAC 是 xxx),表明冲突依然存在
- 双方互相发送 GARP Reply,表明冲突持续存在
逆向ARP(Inverse ARP)
中继网络(已淘汰)环境下,用于实现 IP 和 DLCI 地址映射
- 中继网络: 在该网络环境下,设备通过帧中继交换机连接。与以太网交换机不同,此处不使用 MAC 地址,而是使用 DLCI 号(标志符)来进行数据交换,接口与 DLCI 号映射表进行转发(不是接口与 MAC 地址映射表进行转发)
- IARP 是周期性的(每60秒),而不是指定场景触发性
- 过程: 每个网络终端都会发送 IARP request,该局域网的其他终端会回复 IARP Reply
- IARP request: 192.168.1.1 的 DLCI 号是 100(与 GARP 类型,发送自己的 IP 与 DLCI 号)
- IARP Reply: 192.168.1.2 的 DLCI 号是 200
翻转ARP(Reverse ARP)
原本 ARP 是已知对方 IP,获取对应的 MAC。RARP 是已知自身 MAC,获取自身 IP
- 场景: 无盘工作站通过 RARP 获取 IP,后续可通过 TFTP 引导加载系统。如网吧,机房等,客户端没有本地硬盘,而是需要无盘服务器提供的网络硬盘
- 过程
- 无盘工作站发送 RARP Request: 我的 MAC 是 xxx,IP 是多少?
- 无盘服务器发送 RARP Reply: xxx 对应的 IP 是 192.168.1.10
IP 协议
提供了数据的面向无连接和不可靠传输功能。为接入互联网的设备提供 IP 地址,保证设备的唯一性
封装

- Version 版本号,标识 IP 协议版本。目前 v4 版本的地址已经枯竭,V6 慢慢成为主流
- Header length 头部长度。默认20字节,最大60字节(扩展到60字节见 ICMP 中的 IP 记录路由、IP 源站路由等)
- Differentiated Services Field 服务区分符,表示服务类型(TOS),应用在 QoS 技术中。给数据包打标签,数据包的紧急程度
- Total length 总长度。标识 IP 头部加上上层协议的数据包大小(IP数据包的内容就是上层数据包),IP 包总长度最大 65535个字节(配合头部长度快速定位头部与数据的位置)
- Identification 标识符,标识分片进程。用于实现 IP 分片的重组,标识分片属于哪个进程,不同进程通过不同 ID 区分(与不同设备通信使用不同的ID)
- Flags 标志符。用于确认是否还有 IP 分片,实现 IP 分片重组(分片是否发完)
- Reserved bit
- Don't fragment: 0 - 允许分片,1 - 不允许分片
- More fragment: 后续是否还有数据包 0 - 没有,1 - 有
- Fragment offset 分片偏移量,用于标识 IP 分片未知,实现 IP 分片重组
- Time to live 生存时间,标识 IP 数据包还能生存多久。不同OS,TTL初始值不同。每经过一个三层设备(如路由器),TTL - 1,TTL 为 0 时数据包被丢弃
- Protocol 协议号。标识 IP 协议上层应用(类似 TCP/UDP 中的端口,此 IP 数据包的上一层协议的种类)。ICMP 为 1,TCP 为 6, UPD 为 17
- Header checksum 头部校验,用于检验 IP 数据包是否完整或被修改,存在问题时数据包被丢弃
- Source 源 IP 地址。32 bit
- Destination 目的 IP 地址,32 bit
头部的额外说明
最简单的头部 20 Bytes,可扩展到60字节(扩展内容见 ICMP 中的 IP 记录路由、IP 源站路由等)。没有分片时,分片相关头部字段值都是全0
头部变动: 经过一个路由器时,数据包的 IP 头部会产生变化
- TTL值会发送变动(逐跳改变)
- Header checksum 头部校验和 也会跟着变动
- IP 地址不变
- Ethernet 中的源与目的 MAC 会逐段改变(源/目的 MAC ,当前数据包所处位置两端的 MAC 地址。例如 PC1 连接路由器 A 口,PC2连接路由器 B 口,数据从 PC1 到 PC2。PC1 或 A 口处,源 MAC 是 PC1,目的 MAC 是 A 口,在 B 口或 PC2 处,源 MAC 是 B 口,目的 MAC 是 PC2)
IP 分片: 发送的数据过大时,需要将数据切分,通过多个 IP 数据包发送。且接收者(每一跳的设备)都会对分片进行重组
当网络中 MTU 低时(即单个数据包的装载内容变少),会产生更多的 IP 分片。此时若 IP 头部为通常值 20 Bytes,则数据内容长度为 MTU 值 - 20 Bytes
Identification 标识符补充。当两个设备同时有多个进程进行通信,为了使接收端收到数据包后得知该数据包是哪个通信进程的数据,需要使用 Identification 进行区分
Flags 标志符补充。告知接收端是否已经发完所有分片,可对数据包中的数据进行组装
- Reserved bit
- Don't fragment: 0 - 允许分片,1 - 不允许分片。数据发送方可设置该数据包内容能否进行分片。当后续设备收到此数据包时,若该包不能分片,且内容长度大于接收设备的 MTU,则丢弃这个过大的数据包,并不能分片的响应
- More fragment: 后续是否还有数据包 0 - 没有,1 - 有。最开始发送的数据包都为 1, 当收到 0 时,说明可以开始进行数据组装
Fragment offset (OF)分片偏移量。决定了分片数据在组装时的顺序。数据包的偏移应为数据内容长度,即 MTU 值 - IP头部(20) Bytes。当数据内容都按100切分时,第一个包的 offset 是 0,第二个是 100,以此类推
ICMP 协议
Internet Control Message Protocol 互联网控制信息协议。用于实现 IP 网络层与上层协议的连通性测试和差错报告。源发送者或上层协议根据返回的 ICMP 协议信息进行下一步操作
常用的 ping 与 tracert 程序便是基于 ICMP 协议进行开发。从功能来看,属于 IP 的附属协议,可当作网络层。从分组角度来看,由于在 IP 的上层,与 TCP/UDP 同层,可当作传输层
封装
0-7: Type 类型值 8 位,标识 ICMP 分组类型
8-15: Code 代码值 8 位,标识 ICMP 分组类型的某一种具体分组
16-31: Checksum 校验和 16 位,用于校验数据包是否完整或被修改
Identifier 标识符,标识进程号 16 位。与 IP 中的 id 类似,区分多个目的通信
Sequence Number 序列号 16 位,标识本地到目的地数据包序号,一般从 1 开始。当 ping -t 时,会从 0 开始,每次发包 +1
32-: 内容
说明: 不同类型下的不同代码值组合,说明了该数据包表达的信息
Type Code 描述 查询/差错 0-Echo响应 0 Echo响应报文(Ping 应答) 查询 3-目的不可达 0 目标网络不可达报文 差错 1 目标主机不可达报文 差错 2 目标协议不可达报文 差错 3 目标端口不可达报文 差错 4 要求分段并设置DF flag标志报文 差错 5 源路由失败报文 差错 6 未知的目标网络报文 差错 7 未知的目标主机报文 差错 8 源主机隔离报文 差错 9 禁止访问的网络报文 差错 10 禁止访问的主机报文 差错 11 对特定的TOS网络不可达报文 差错 12 对特定的TOS主机不可达报文 差错 13 由于过滤 网络流量被禁止报文 差错 14 主机越权报文 差错 15 优先权终止生效报文 差错 5-重定向 0 重定向网络报文 差错 1 重定向主机报文 差错 2 基于TOS的网络重定向报文 差错 3 基于TOS的主机重定向报文 差错 8-Echo请求 0 Echo请求报文(Ping请求) 查询 9-路由器通告 0 路由通告报文 查询 10-路由器请求 0 路由器的发现/选择/请求报文 查询 11-ICMP超时 0 TTL超时报文 差错 1 分片重组超时报文 差错 12-参数问题 0 IP报首部参数错误报文 差错 1 丢失必要选项报文 差错 2 不支持的长度报文 差错 13-时间戳请求 0 时间戳请求报文 查询 14-时间戳应答 0 时间戳应答报文 查询 15-信息请求 0 信息请求报文 查询 16-信息应答 0 信息应答报文 查询
ICMP 使用情形
Ping
- 发起 Ping 方(192.168.1.1)发送 Echo Request 请求回显(8|0): 192.168.1.2,是否在那里
- 连通状态: 被 Ping 方(192.168.1.2)发送 Echo Reply 回显应答(0|0): 192.168.1.1,我在这里
- 非连通状态: 没有回显应答
Traceroute
最常用的链路追踪工具,用于探测源目双方沿途设备的 IP 地址(windows 中该命令是 tracert,其他系统中通常是 traceroute)
- 向目的地址发送 TTL=1 ,高端口号(如55555,目的地址大概率没有提供此端口的服务)的 UDP 数据包(非 windows 系统使用 UDP,windows 使用 ICMP 的 Ping)
- 到达下一跳后,不是发给我,需要我转发,但 TTL 变成 0,告知发送方 此包已失效。返回给发送方 ICMP 协议的 TTL 超时报文(ICMP Type-Code 11|0,此时该报文的发送方是自己的 IP 地址,接收方是 traceroute IP。网络层与传输层 data 内容与 UDP 数据包一致)
- 此次追踪结束。TTL+1来追踪下一跳
- 直到该数据包到达接收地址。由于接收地址匹配,此时不会触发 TTL-1 并检查 TTL 有效性,而是检查此包访问的端口,而该端口不可用,发送 ICMP 协议的 端口不可达(3|3)报文,而不是 ICMP 协议的 TTL 超时报文
- 此时追踪完成
- 说明
- 每一跳会发送 3 个 TTL 值相同的包(可通过
probe参数控制数量)。每一跳能看到 3 个延时,最大、最小、平均 - 由于 windows 的
tracert采用 Ping 包进行追踪- 在真实环境中,一些设备的防火墙会开启防 Ping,因此不是每一跳都有响应数据包,从而出现结果列表中存在 请求超时 的点。
- 由于采用 Ping 包探测,到达终点后,返回的数据包是 ICMP Reply。而不是 ICMP 端口不可达
- 每一跳会发送 3 个 TTL 值相同的包(可通过
ICMP 不可达
根据分组列表,Type = 3 时,共 16 种不同的 Code。此处以最常见的三种为例
- 主机不可达(1): 网络设备收到了数据包,但本地没有到此数据包目的地的路径时,丢弃此包并发送 ICMP 协议的 主机不可达(3|1 host unreachabled)报文
- 端口不可达(3): 当目标系统收到 IP 数据包请求某个服务,且本地没有该服务,则向源头返回 ICMP 协议的 端口不可达(3|3)报文
- 请求 TFTP 服务(69 端口,UDP 协议报文),本地没有 69 端口服务,返回 ICMP 协议的 端口不可达(3|3 post unreachabled)报文,用于发送端终止 UDP 进程
- 请求 TCP 服务(80端口,TCP 协议报文),本地没有 80 端口服务,返回 TCP 协议 RST 重置位(RST 标志位 = 1)报文,重置发送方的 TCP 服务(此处端口不可达的情况下没有使用 ICMP ,而是 TCP 的重置位)
- 禁止不可达(13): IP 数据包沿途设备做了策略控制且不允许通过时,执行策略设备会丢弃数据包并返回 ICMP 禁止不可达 报文
- 网络设备做了防火墙或访问列表,禁止数据包进入,丢弃数据包,并返回 ICMP 协议的 administratively prohibited unreachable 3|13 报文
- 分片不可达 需要分片但 IP 头部设置不可分片的 DF Flag(4): 参考 IP 分片 中的流程
- 设备 A
ping 3.3.3.3 size 200 df-bit,通过路由器 B 连接到设备 C (3.3.3.3) - 路由器 B 设置了 MTU 为 100,因此需要分片,但数据包中设置了 DF 位
- 路由器 B 直接返回 ICMP 需要分片但有 DF 位的不可达(frag. needed and DF set unreachable 3|4)报文
- 设备 A
ICMP 重定向
(5|1)当本地收到的 IP 数据包目的地的下一跳 IP 地址与源发送者处于同一网段时,则告知源发送者将路由重定向到邻居设备
重定向包的示例
- 设备 A(12.0.0.1/24)与设备 B(12.0.0.2/24 / 2.2.2.2/32) 通过交换机连接到路由器(12.0.0.3/24)。设备 B
ip route add 12.0.0.2 255.255.255.0ip route add 2.2.2.2 255.255.255.255 - 当设备 A ping 2.2.2.2 时,由于该 IP 与设备 A 不在同一网段,因此该数据包到达网关(即路由器)
- 路由器发现该 IP 属于设备 B(路由器的路由表中存在静态路由 2.2.2.2 指向 12.0.0.2/24),而设备 B 与设备 A 属于同一局域网(直接连接设备 B 才是最优路线,次优路径的解决流程)
- 将该数据包转发给设备 B
- 同时发送 ICMP 重定向 包给设备 A 告知该 IP 的包可直接发送给设备 B
- 设备 A 收到重定向报文后,增加本地路由表信息,该 IP 指向设备 B 的局域网 IP
- 设备 A(12.0.0.1/24)与设备 B(12.0.0.2/24 / 2.2.2.2/32) 通过交换机连接到路由器(12.0.0.3/24)。设备 B
数据包流程
- 通过 ARP 数据包获取网关 12.0.0.3 的 MAC
- 设备 A 发送 ICMP 包,目的 MAC 是网关的 MAC
- 设备 A 收到 ICMP 协议的 Redirect for host (5|1,ICMP 的前2字节)报文,携带了 Gateway address: 12.0.0.2(ICMP 的5-8字节,3-4字节是校验和)。数据部分是 IP 报文 Src 12.0.0.1,Dst 2.2.2.2
- 设备 A 收到重定向报文后,路由表除了默认网关 12.0.0.3 外,增加了一条记录: Host 2.2.2.2 Gateway 12.0.0.2
- 后续的 Echo (ping) request 与 Echo (ping) Reply 均发生在设备 A 与 设备 B 之间
IP 记录路由
此处介绍的是 IP 协议中的 IP 头部扩展,需要借助 ping、traceroute 验证: 在 IP 协议的第 4-7 位表示的 4 位头部长度中,最大值 0b1111 时,即
- 概述
- 通过 IP 记录路由,可将沿途设备等出接口 IP 地址都记录下来,最多记录 9 个
- 由于记录选项受限于 IP 头部长度,没法对大型网络进行记录跟踪,后续才开发 Traceroute
- (IP 头部长度最多扩展到 60 字节,其中 20 字节是 IP 基础头部,只可用 40 字节的 IP Options 长度。
,即 40 字节最多只能放入 9 个 IP地址)
- 过程: 设备 A (12.1.1.1)连接路由器 B f0/0(12.1.1.2)接口,路由器 B f0/1(23.1.1.2)接口连接到 设备 C(23.1.1.3 / 3.3.3.3)
- 设备 A 使用扩展 ping,开启 Record 选项
- 数据包详情 Internet Protocol Version 4 栏内出现 Options: (40 bytes), Record Route. End of Options List (EOL)
- 数据包每经过一个出口,列表中增加一条 IP 记录
- 从 A 发出 Echo (ping) request,到达 B 前: 只有 12.1.1.1 (A 的出口 IP)
- 从 B 发出 Echo (ping) request,到达 C 前: 增加 23.1.1.2(B 的出口 IP)
- 从 C 发出 Echo (ping) Reply,到达 B 前: 增加 3.3.3.3(目的地址)23.1.1.3(C 的出口 IP)
- 从 B 发出 Echo (ping) Reply,到达 A 前: 增加 12.1.1.2(B 的出口 IP)
- 数据包 Options 中的完整记录
- 12.1.1.1 (设备 A 的出口 IP)
- 23.1.1.2 (路由器 B 发送时的出口 f0/1 的 IP)
- 3.3.3.3(目的地 IP)
- 23.1.1.3(目的地出口 IP)
- 12.1.1.2(路由器 B 返回时的出口 f0/0 的 IP)
- 12.1.1.1 <*> (返回发送人 IP,用星号标记已经到达目的地)
IP 源站路由
通过 IP 源站路由选项,可以在源发送者指定特定路径(将路径放入 IP 头部选项)来进行数据转发。即向目的地发送数据包时,指定数据包经过的IP
Options头部内容(Options 最大长度 40 bytes,最大使用长度 39 bytes): code(1) len(1) ptr(1) IP addr #1(4) ... IP addr #9(4)
实现方式: 网络设备收到数据包后,在 Options 中通过 Ptr 获取下一跳 IP 地址,将数据包发送过去,从而达到数据包在传输时会经过指定 IP 的效果
分类
- 严格源站路由: 发送端指明 IP 数据报 Datagram 所必须采用的确切路由。如果一个路由器发现源路由所指定的下一个路由器不在其连接的网络上,那么它就返回一个 源站路由失败 ICMP 差错报文(不依赖路由,即使中间设备关闭 OSPF 路由协议,依然可以发送)网络设备收到该数据包时不查询路由表,而是直接发送给 Options 中指定的下一跳 IP 地址
- 使用扩展 ping,开启 Strict 选项,填写多个路径 IP 地址,使用空格分隔(此处使用以下拓扑结构,示例路径地址为
14.1.1.4 24.1.1.2 25.1.1.15 35.1.1.3, 每个 IP 都是路径设备入口 IP,直至 Dst 设备入口 IP,R4 - R2 - R5 - R3)
- 数据包从 Src 发送到第一跳 R1 时 IP 报文头部

- IP 头部 Dst 的位置字段被解析成 Current Route,其值为列表中取出的当前跳的地址(第一个跳的入口 IP)。此时 列表长度 - 1
- 为了使 Ptr 指向下一个地址,
此时指向的 IP 是 - 。 即指向的是 0 - 3 ,列表中的第一个 IP 是输入的第二个 IP - 列表中除了被输入的多个路径 IP 被解析成 Source Route 外,还自动在 Option 列表末尾追加了 Dst 的 IP。此时 列表长度复原
- 第一跳设备使列表记录 后续的入口 IP + Dst。且当前位置通过 Current Route 记录(列表内容始终是先前地址+后续地址+dst,不包含当前地址)
- 数据包从第一个路径 IP 发出后,到达第二跳 R2

- IP 头部 Dst 依然被解析为 Current Route,其值为 Ptr 指向的地址,此时是列表的第一个地址(R2 入口 IP),作为当前跳地址。同时将列表中 Ptr 指向地址 替换为 上一跳的出口 IP。此时被解析成 Recorded Route
- Options 中的指针
用于指向下一跳地址(通过 IP 头部的 Ptr,使设备明确该数据包下一跳的 IP,来保证数据包始终按照给的路线) - 第二跳设备开始,使列表中记录了之前的出口 IP + 后续的入口 IP + Dst。当前位置通过 Current Route 记录(列表内容始终是先前地址+后续地址+dst,不包含当前地址)
- 依次类推,直至 Echo (ping) request 包到达目的地。由于列表被修改,最终包含所有途径出口 IP + Dst IP
- 目的地发送 Echo (ping) reply 包
- 由于收到的列表时途径的出口IP + Dst。构造 reply 包时会将该列表反转后放回 Option
3.3.3.3 35.1.1.5(R5出口) 25.1.1.2(R2出口) 14.1.1.4(R4出口),此时,之前的出口就作为回复包需要经过的入口。但第一项是 Dst。因此 Ptr 的初始值为 4
- 由于收到的列表时途径的出口IP + Dst。构造 reply 包时会将该列表反转后放回 Option
- 到达第一跳后

- IP 头部 Dst 的位置字段被解析成 Current Route。此时是从数组中取出所指向的 IP,即回复包第一跳设备的入口 IP
35.1.1.5。同时,列表第一项,变更为 Recored Route 记录回复包出口 IP35.1.1.3。最后一个出口被解析成 Dst ,此时指向的 IP 是 - 。即 4 - 7,列表中第二个 IP,R2发包的出口IP - 后续列表操作与发包时保持一致。(列表内容始终是先前地址+后续地址+最后一项被解析成dst,不包含当前地址)
- IP 头部 Dst 的位置字段被解析成 Current Route。此时是从数组中取出所指向的 IP,即回复包第一跳设备的入口 IP
- 依次类推,直到返回
- 使用扩展 ping,开启 Strict 选项,填写多个路径 IP 地址,使用空格分隔(此处使用以下拓扑结构,示例路径地址为
- 宽松源站路由: 发送端指明数据报经过的 IP 地址清单,但清单指明的任意两个地址之间可以经过其他路由器(还需要依据路由表进行)
- 使用扩展 ping,开启 Loose 选项,填写多个路径 IP 地址,使用空格分隔。例如经过
2.2.2.2只设置经过 R2 - 到达第一个设置的IP。此时变化与严格模式类似
- IP 头部 Dst 字段解析为 Current Route,其值为列表中取出的第一个地址(
2.2.2.2),列表默认再追加真实 Dst3.3.3.3 - 为了使 Ptr 指向下一个地址,
此时指向的 IP 是 - 。 即指向的是 0 - 3 ,列表中的第一个 IP,此时即为最终的 Dst
- IP 头部 Dst 字段解析为 Current Route,其值为列表中取出的第一个地址(
- 到达下一个列表中的目标,即目的地 Dst
- 取出
- 的值 3.3.3.3作为 Dst/Current Route,此时没有下一跳地址,解析成 Dst。同时将列表中当前指向的 IP 替换为 上一跳的出口 IP23.1.1.2。此时被解析成 Recorded Route
- 取出
- 目的地发送 Echo (ping) reply 包
- 设置初始值
- 根据 Ptr 指针,取出
- 的地址 23.1.1.2(2.2.2.2 的出口地址)作为 reply 的下一跳。列表长度 - 1 - 在 Options 中列表末尾追加 Dst IP,使列表长度不变
- 设置初始值
- 使用扩展 ping,开启 Loose 选项,填写多个路径 IP 地址,使用空格分隔。例如经过
- 严格源站路由: 发送端指明 IP 数据报 Datagram 所必须采用的确切路由。如果一个路由器发现源路由所指定的下一个路由器不在其连接的网络上,那么它就返回一个 源站路由失败 ICMP 差错报文(不依赖路由,即使中间设备关闭 OSPF 路由协议,依然可以发送)网络设备收到该数据包时不查询路由表,而是直接发送给 Options 中指定的下一跳 IP 地址
UDP 协议
User Datagram Protocol 用户数据报协议,提供面向无连接的不可靠传输服务,与 TCP 协议一起处于传输层
特征
- 不可靠(传输时丢包不会重传,不需要处理数据包时序问题,从而保证实时性)
- 无连接(不会与接收端协商,直接发送数据包,接收端无法得知数据包的大小)
- 不分片(上层给到的数据包,不对其进行分片,直接加上 UDP 头部封装成 UDP 包,交给下层的 IP 协议自行分片)
- 速度快
- 实时性(适合实时视频和语音流)
由于以上特性,交互过程快,包少,一般都基于 UDP 实现。常见的基于 UDP 协议的上层协议: DHCP、DNS、TFTP、OICQ
注意: 由于 UDP 不进行分片,因此如果 UDP 数据报被 IP 层分片了,那么只有第一个 IP 分片里包含 UDP 头部,后续的 IP 分片里是没有 UDP 头部的(只有 IP 头部 + 数据)。接收方通过 IP 头部来还原 UDP 数据报(数据报 Datagram: 可理解为 UDP/IP 协议的数据包,面向无连接。详见 TCP 协议内的说明)
- 标识符 (Identification / ID):
- 同一个原始 UDP 包切分出来的所有 IP 分片,都会拥有完全相同的 ID。
- 接收方看到 ID 相同的包,就知道它们属于同一个“祖先”。
- 标志位 (Flags - More Fragments, MF):
- 如果 MF = 1,说明后面还有分片。
- 如果 MF = 0,说明这是最后一个分片。
- 片偏移 (Fragment Offset):
- 告诉接收方,这块数据应该放在原始大包的哪个位置(是开头、中间还是结尾)。
封装
UDP头部 8 bytes
- 源端口 2 bytes
- 目的端口 2 bytes
- 长度 2 bytes UDP 头部与上层头部的长度和
- 校验和 2 bytes
基于 UDP 的 OICQ
QQ 使用的协议 数据包详情
- Frame ...(内容省略)
- Ethernet ...
- Internet Protocl Version 4 ...
- User Datagram Protocol
- Source Port: 4000
- Destination Port: 8000
- Length: 63 (UDP 报文长度)
- Checksum
- OICQ
- Flag: 0x02 (1 byte)
- Version: 0x3601(2 bytes)
- Command: (2 bytes)
- Sequence:(2 bytes)
- Data(OICQ Number,id sender is client): QQ号(4 bytes)
- Data: 数据内容(已加密)
DHCP 协议
Dynamic Host Configuration Protocol 动态主机配置协议,为网络中的设备提供动态IP地址信息,包括 IP 地址、网关、DNS 等等。DHCP 使整个网络等地址分片变得简单,减低了网络管理员的工作量。
基于 UPD 协议,使用 67(DHCP服务端) 与 68(DHCP客户端) 端口
过程
由于客户端在 DHCP 过程中没有 IP。客户端发包时使用的 Src 为 0.0.0.0。且 DHCP 获取 IP 的过程中的以下四个包都以广播形式,Dst 是广播地址 255.255.255.255
- 客户端发送 DHCP DISCOVER: 哪个服务器能分配 IP
- 服务端发送 DHCP OFFER: 你的 IP 是 x.x.x.x
- 客户端发送 DHCP REQUEST: 需要这个地址
- 服务端发送 DHCP ACK: 这个 IP 正式分配给你
封装
DHCP 共 8 种请求。下图是通用数据包格式,需配合具体种类分析 
案例
DHCP 地址请求
如过程中描述,涉及到 4 种 DHCP 请求。请求中最重要的部分是 DHCP 头部的 Option(Option 编号 1bytes + 内容长度 1 bytes + 内容值)
- 数据包详情
Discover: 客户端没有 IP,使用 0.0.0.0 作为 IP
- Internet Protocol Version 4, Src: 0.0.0.0 Dst: 255.255.255.255
- User Datagram Protocol, Src Port: 68, Dst Port 67(客户端68,发送给服务端67)
- Bootstrap Protocol (Discover) (DHCP 协议由 Bootstrap Protocol 发展而来,Wireshark使用协议名显示过滤时,需要使用
bootq而不是dhcp)- ...省略部分头部,以下仅展示部分字段
- Hardware type: Ethernet (0x01)
- Hardware address length: 6 (以太网 mac 地址的字节数)
- Hops: 0 (跳数。跨网段,需要中继时 + 1)
- Client IP address: 0.0.0.0 (客户 IP - 客户端使用字段)
- Your (client) IP address: 0.0.0.0 (服务端分配的客户 IP - 服务端使用字段)
- Next server IP address: 0.0.0.0 (服务器 IP)
- Relay agent IP address: 0.0.0.0 (代理 IP)
- Client MAC address: xx:xx:xx:xx:xx:xx (客户端 MAC 地址)
- Option: (53) DHCP Message Type (Discover) (标识该 DHCP 包的分组类型,原文: 35 01 01)
- Length: 1
- DHCP: Offer (1)
- Option: (57) Maximum DHCP Message Size (标识最大的 DHCP 分组长度)
- Option: (61) Client identifier (客户端标识符)
- Option: (12) Host Name (标识客户端的 Hostname 主机名)
- Option: (55) Parameter Request List (客户端请求参数 - 选项值1+长度1+内容8 共10字节)
- Length: 8 (参数列表长度)
- Parameter Request List Item: (1) Subnet Mask
- Parameter Request List Item: (6) Domain Name Server (DNS)
- Parameter Request List Item: (15) Domain Name
- Parameter Request List Item: (44) NetBIOS over TCP/IP Name Server
- Parameter Request List Item: (3) Router
- Parameter Request List Item: (33) Static Route
- Parameter Request List Item: (150) TFTP Server Address
- Parameter Request List Item: (43) Vendor-Specific Information
- Option: (255): End
Offer: 与 Discover 类似,Src 变更为 DHCP 服务端的 IP,UDP 端口互换。下面是 DHCP 头部的重点部分
- Client IP address: 0.0.0.0 (客户 IP)
- Your (client) IP address: 12.1.1.3 (服务端分配的 IP)
- Next server IP address: 0.0.0.0 (服务器 IP)
- Relay agent IP address: 0.0.0.0 (代理 IP)
- Client MAC address: xx... (将客户端的 MAC 原样传回)
- Option: (53) DHCP Message Type (Offer) (标识该 DHCP 包的分组类型,原文: 35 01 02)
- Length: 1
- DHCP: Offer (2)
- Option: (54) DHCP Server Identifier (服务器地址,discover中没有,用于告知客户端 DHCP 服务器的地址,原文 36 04 0c 01 01 02)
- Length: 4
- DHCP Server Identifier: 12.1.1.2
- Option: (51) IP Address Lease Time (IP 的租期 604800s 7天,原文 33 04 00 09 3a 80)
- Option: (58) Renewal Time Value (重绑时间 T1,302300s 3天12hr)
- Option: (59) Rebinding Time Value (重绑时间 T2)
- Option: (1) Subnet Mask (子网掩码)
- Option: (3) Router (网关地址)
- Option: (6) Domain Name Server (返回主副DNS地址,内容长度 8 ,原文 06 08 08 08 08 08 df 05 05 05)
- Option: (255): End
Request: 此时还没正式确定使用该 IP,客户端 IP 依然 0.0.0.0。最关键的 Option 是 Option 50
- Option: (53) DHCP Message Type (Request) (标识该 DHCP 包的分组类型,原文: 35 01 03)
- Option: (54) DHCP Server Identifier (标识接收该请求的 DHCP 服务器,在多 DHCP 服务器场景下,用于进行区分)
- Option: (50) Requested IP Address (标识客户端所请求的 IP 地址,原文: 32 04 0c 01 01)
- Length: 4
- Requested IP Address: 12.1.1.3
ACK: 与 Offer 包类似
重点
- 客户端没有 IP,如何发送 DHCP 包? 使用 0.0.0.0 全网地址
- 为什么都用广播包?
- DHCP 流程结束表明客户端分配到了 IP。在流程中,客户端没有 IP,因此服务器到客户端使用广播
- 当多 DHCP 服务端环境时,为了防止多个 DHCP 服务器进行响应,客户端使用广播包,服务端也使用广播包,使其他服务端得知已经有服务端处理 DHCP 请求,且分配到具体地址。防止后续其他服务器将该地址分配,导致地址冲突(免费 ARP 的效果)
- 为什么用 4 个包而不是 2 个? 多 DHCP 服务器环境中,客户端的请求会导致多个 DHCP 同时分配地址,此时客户端收到多个地址。没有确认过程时,服务端不知道哪个地址被使用,只能默认都被分配,造成地址浪费。通过确认包可以使服务器明确哪些地址未被使用,从而进行回收
DHCP 地址续租
客户端在地址租期到期之前,通过 DHCP Request 向 DHCP 服务器续约 IP。此时客户端已有 IP,因此只需 2 个包即可完成续约,且都是单播包
- 当客户端地址达到 50% 租用期(Renew Time, T1)时,客户端进入 RENEW 状态,使用 DHCP Request 报文续约。见之前的 Discover 请求头
- 当客户端地址达到 87.5% 租用期(Rebinding Time, T2)时,客户端进入 REBINDING 状态,使用 DHCP Request 报文续约。见之前的 Discover 请求头
- 数据包
- Request
- Client IP address: 12.1.1.3 (此时客户端在使用的 IP)
- Your (client) IP address: 0.0.0.0 (服务端分配的 IP)
- Next server IP address: 0.0.0.0 (服务器 IP)
- Relay agent IP address: 0.0.0.0 (代理 IP)
- ACK
- Client IP address: 12.1.1.3 (此时客户端在使用的 IP)
- Your (client) IP address: 12.1.1.3 (服务端分配的 IP)
- Next server IP address: 0.0.0.0 (服务器 IP)
- Relay agent IP address: 0.0.0.0 (代理 IP)
- Request
DHCP 地址释放
客户端通过 DHCP Release 向 DHCP 服务器释放其所用的地址。同续租,客户端已有 IP,因此数据包是单播包
- Release
- Client IP address: 12.1.1.3 (此时客户端在使用的 IP)
- Your (client) IP address: 0.0.0.0 (服务端分配的 IP)
- Next server IP address: 0.0.0.0 (服务器 IP)
- Relay agent IP address: 0.0.0.0 (代理 IP)
- Option: (53) DHCP Message Type (Release) (原文: 35 01 07)
DHCP 地址冲突
客户端通知服务器,其所分配的地址已经被使用,要求重新获取
- 避免静态 IP 冲突
- DHCP 服务器在收到 Discover 后,从地址池中选定 IP,在局域网内发送 ARP(同一局域网)/ICMP(DHCP中继时,使用ping,可见下一部分) 检测该地址是否被客户端使用(第一次检测,服务端检测)
- (ARP)Who has 12.1.1.1? Tell 12.1.1.0 : DHCP 服务器在局域网内对将分配的 IP 检测是否被使用
- 若有其他客户端通过静态 IP 方式使用了该地址,导致该地址在 DHCP 池中但已经被客户端使用。则在收到 ARP/ICMP 检测后回复 ARP/ICMP 该地址已使用
- (ARP)12.1.1.1 is at xx.:xx:xx:xx:xx:xx : 静态地址的客户端通过单播 ARP 包告知 DHCP 服务器已使用该 IP
- (ICMP)Echo (ping) request : DHCP 服务器通过 ping 确认该地址已使用
- DHCP 服务端重新选择 IP 并检查后,发送 DHCP Offer 包
- (ARP)Who has 12.1.1.2? Tell 12.1.1.0 : DHCP 服务器在局域网内将重新选择的 IP 检测是否被使用。没有回复,说明 OK
- (DHCP)DHCO Offer
- 客户端收到 Offer 包,走完 DHCP 流程后 。发送免费 ARP 检测该 IP 是否被使用(第二次检测,客户端检测)
- (DHCP)DHCP Request : 客户端请求获取该 IP
- (DHCP)DHCP ACK : 服务端确认
- (ARP)Gratuitous ARP for 12.1.1.2 : 客户端发送免费 IP 检测(双重保险)
- DHCP 服务器在收到 Discover 后,从地址池中选定 IP,在局域网内发送 ARP(同一局域网)/ICMP(DHCP中继时,使用ping,可见下一部分) 检测该地址是否被客户端使用(第一次检测,服务端检测)
- 双网卡冲突: 使用 DHCP Decline 包让服务端更换 IP
- 客户端拥有两个网卡,其中一个网卡设置了静态 IP(或通过回环口模拟双网卡
int lo1 + ip add 12.1.1.1 255.255.255.0) - 另一个网卡通过 DHCP 请求拿到 IP 后,发现与静态 IP 一致。不允许两网卡使用相同 IP
- DHCP Discover、Offer、Request、ACK : DHCP 流程拿到 IP
12.1.1.1
- DHCP Discover、Offer、Request、ACK : DHCP 流程拿到 IP
- 发送 DHCP Decline 包给服务端进行婉拒,告知服务器 IP 不可用。并重新发起 DHCP 流程
- (DHCP)DHCP DHCP Decline
- Option: (53) DHCP Message Type (Decline)
- Option: (50) Requested IP Address (指明当前分配给我的,但不可用的地址)
- (DHCP)DHCP Request
- (DHCP)DHCP DHCP Decline
- 服务端重新选择地址,ARP 检测通过后发起 Offer,完成 DHCP 流程
- 客户端拥有两个网卡,其中一个网卡设置了静态 IP(或通过回环口模拟双网卡
DHCP 中继代理(DHCP Relay)
当跨网段执行 DHCP 请求时,通过 DHCP 中继代理,将请求信息以单播方式转发给 DHCP 服务器
- 当
ip address dhcp跨网段时,中继设备(路由器/三层交换机)需要开启 RELAY 中继代理功能ip helper-address 23.1.1.3(DHCP 服务端 IP)。则客户端发送的 DHCP 包因为广播包无法跨网段,导致 DHCP 服务器无法收到 - 对客户端来说,是不关心上层情况的。即客户端与 DHCP Relay 间依旧收发广播包,DHCP Relay 与服务端都使用单播包进行收发
- 网络拓扑: 客户端在 12.1.1.0/24 连接中继路由器 12.1.1.2 的接口。中继路由器的 23.1.1.2 接口连接 DHCP 服务器 23.1.1.3 接口
- 客户端: 4 个 DHCP 包都是广播,其中收到的广播包地址是客户端所在网关 IP,即客户端所连接中继设备的接口地址
- DHCP Discover -> ICMP ping(服务端检测IP) -> Offer -> Request -> ACK -> ARP(客户端检测IP)
- DHCP Relay: 4 个 DHCP 单播包的 Src / Dst 是客户端所在网关接口 12.1.1.2 与 DHCP 服务端接口 23.1.1.3 间进行。不涉及中继路由连接服务端的 23.1.1.2 接口
- DHCP Discover Src 12.1.1.2 Dst 23.1.1.3
- ICMP ping Src 23.1.1.3 12.1.1.5 (服务端检查 .5 IP 是否被使用)
- DHCP Offer Src 23.1.1.3 Dst 12.1.1.2
- DHCP Request Src 12.1.1.2 Dst 23.1.1.3
- DHCP ACK Src 23.1.1.3 Dst 12.1.1.2
DNS 协议
Domain Name System 域名系统,用于互联网上提供域名到 IP 的解析服务。访问互联网时不用采用难记的 IP 地址,直接采用直观的域名即可
DNS 协议基于 UDP 和 TCP 协议,端口号 53。用户到服务器采用 UDP,DNS 服务器通信采用 TCP。大型的运营商、互联网机构等会向公众提供免费的 DNS 服务,如谷歌 8.8.8.8 与 8.8.4.4,阿里的 223.5.5.5 与 223.6.6.6
当 DNS 出现问题时,QQ 等不走 DNS 协议的应用可以正常使用,但域名解析都会出现问题
域名结构
如下图所示,域名以 . 作为层级分隔符,从后往前,层级依次下降。例如 news.baidu.com 中,com 是顶级域名,baidu 是二级域名,news 是三级域名。
通过域名分层,使域名解析实现分布式(不同层级的解析交由不同 DNS 服务器进行,如指定服务器只存储顶级域名是 com 的数据,从而可以继续进行拆分)。当解析指定域名,如顶级域名 .cn 的服务异常时,则该域名的 URL 都无法访问

注意: 顶级域名与二级域名中都常见 gov、edu 等,注意区分。严格按照 . 分隔后,从后往前的方式确定该 gov 或 edu 等域名的层级
域名查询与区域传送
DNS 服务中常见的请求与传输如下
- 正向查询: 查询指定域名的 IP
- 反向查询: 查询 IP 对应的域名
- 递归查询:(如同函数调用一样,DNS 服务区请求其他 DNS 服务)收到 DNS Query 的 DNS 服务不知道结果,再次发起 DNS Query 询问其他 DNS 服务。收到 DNS Response 结果后,再发送 DNS Reponse 告知 DNS Query 的发送方。对于请求发送方来说,只发送了一次请求,不知道上层是否发送递归查询。对末端的 DNS 服务端来说,收到的请求就是客户端发过来的(即使是中继 DNS 服务端发起的递归查询,其数据包与客户端无异)
- 迭代查询: DNS Query 发送方收到 DNS 服务提供其他 DNS 服务的地址,于是重新发送 DNS Query 请求其他 DNS 服务。由于递归查询的存在,DNS Query 发送方可能是客户端或上层 DNS 服务。对于请求发送方来说,发送了多次请求
- 递归 + 迭代:(真实环境常见情况)客户端发送请求给 DNS 服务1,DNS 服务1 递归查询发送给 DNS 服务2,DNS 服务2 无对应数据,将 DNS 服务3 的地址响应给 DNS 服务1。DNS 服务1 迭代查询 DNS 服务3,拿到 DNS Response 后进行递归响应给客户端
- 区域传送: 用于 DNS 服务主从备份,通过 TCP 传输域名映射关系,防止单点故障
封装
DNS 的封装分为 5 个部分: 头部部分,查询部分(查询包含有),回答部分(响应包含有),授权部分(响应包含有),额外信息(响应包含有)。如下图所示 
案例
DNS 查询应答
- 配置
- 客户端
- 开启域名解析。如果本地没有,则请求其他 DNS 服务
ip domain-lookup - 定义 DNS 服务器(有指定时DNS请求为单播,否则是广播)
ip name-server 192.168.1.2 - 访问域名,测试能否通过 DNS 正常解析为地址
ping www.xxx.com
- 开启域名解析。如果本地没有,则请求其他 DNS 服务
- 服务端
- 开启 DNS 服务
ip dns server - 建立 IP 与域名映射
ip host www.xxx.com 5.5.5.5 - 查看已建立的映射
show hosts
- 开启 DNS 服务
- 客户端
- 流程
- 客户端发起 Ping 操作来访问域名
>: ping www.xxx.com Translating "www.xxx.com"...domain server (192.168.1.2) [OK] - DNS 请求包 Src 192.168.1.1 | Dst 192.168.1.2 | DNS | Standard query 0x0001 A www.xxx.com (由于指定了 DNS 服务器,发送请求时是单播包)
- User Datagram Protocol, Src Port: xxxxx(发起 DNS 请求的应用运行的端口), Dst Port: 53 (DNS 基于 UDP,且 DNS 服务端口 53)
- Domain Name System (query) (DNS 包,以下是包内重点关注的内容)
- Transaction ID: 0x0001 (事务 ID 值,包信息中 DNS Standard query 后方的内容,DNS 请求序号,功能与 ICMP 中的标识符一致,用于应用区分是当前进程发送 DNS 的第几个包。DNS 头部的第一个字段)
- Flags: 0x0100 Standard query (标记该 DNS 请求的类型,是 query 还是 response)
- 0... .... .... .... = Response: Message is a query (0-请求包,1-响应包)
- .... ...1 .... .... = Recursion desired: Do query recursively (渴望递归请求,如果服务器没有对应数据,可自行发送递归请求获取数据后返回,通常为1。本包中仅该标志位是1)
- Questions: 1 (问题数,请求了几个域名)
- Additional RRs: 0(额外记录信息,DNS 头部的最后一个字段)
- Queries(查询部分,查询信息,多条记录)
- www.xxx.com: type A, class IN (记录: 域名+类型+类)
- Name: www.xxx.com
- [Name Length: 11] (域名的字节数。此字段是 Wireshark解析,不是数据包内的内容)
- [Label count: 3] (域名层级数。此字段是 Wireshark解析,不是数据包内的内容)
- Type: A (Host Address) (1) (DNS查询类型: A 类,域名查询 IPv4 地址。其他包含查询邮箱的地址、查询出 IPv6 地址等)
- www.xxx.com: type A, class IN (记录: 域名+类型+类)
- DNS 回复包: 服务端通过 53 端口返回给客户端发送端口的单播包
- Domain Name System (response)
- Transaction ID: 0x0001 (事务 ID 值,与请求包中的值保持一致)
- Flags: 0x8180 Standard query (标记该 DNS 请求的类型,是 query 还是 response)
- 1... .... .... .... = Response: Message is a response (标记为响应包 0-请求包,1-响应包)
- .000 0... .... .... = Opcode: Standard query (0)
- .... ..0. .... .... = Truncated: Message is not truncated
- .... ...1 .... .... = Recursion desired: Do query recursively
- .... .... 1... .... = Recursion available: Server can do recursive queries (告知客户端能做递归查询,与前一个标志位配合)
- .... .... .0.. .... = Z: reserved (0)
- .... .... ...0 .... = Non-authenticated data: Unacceptable
- Questions: 1 (问题数,请求了几个域名)
- Answer RRs: 0 (与上一个字段配合,表示当前包有一个问题的回复)
- Queries (回复包会将请求包中的该字段保持一致,记录问题的原本内容)
- Answers (回复部分,回复序列)
- www.xxx.com: type A, class IN, addr 5.5.5.5 (告知请求方域名对应的 IP)
- Flags: 0x8180 Standard query (标记该 DNS 请求的类型,是 query 还是 response)
- Transaction ID: 0x0001 (事务 ID 值,与请求包中的值保持一致)
- Domain Name System (response)
- 客户端发起 Ping 操作来访问域名
DNS 查询应答(多服务器)
当服务宕机,出现单点故障时。客户端请求 DNS 主服务出现问题,自动请求 DNS 从服务
- 客户端配置 DNS 时配置主从服务
ip name-server 192.168.1.2 192.168.1.3 - 流程
- 客户端请求向主服务发起 DNS 请求时,由于主服务
192.168.1.2关闭,收到服务端响应的 端口不可达 ICMP 差错报文- User Datagram Protocol, Src Port: xxxxx, Dst Port: 53 (服务器告知客户端,本机的 DNS 的 53 端口不可达)
- 客户端再次向从服务发起 DNS 请求包
- 从服务正常返回 DNS 响应包
- 客户端请求向主服务发起 DNS 请求时,由于主服务
DNS 递归查询
- 配置
- DNS-SERVER 1 (当服务端自身没有对应内容,发起递归查询请求其他 DNS 服务器时,与客户端类似。需要配置客户端所配置的内容)
- 定义请求的 DNS 服务器
ip name-server 192.168.1.3 - 开启域名解析。如果本地没有,则请求其他 DNS 服务
ip domian-lookup
- 定义请求的 DNS 服务器
- DNS-SERVER 3
- 开启 DNS 服务
ip dns server - 存储映射关系
ip host www.yyy.com 55.55.55.55
- 开启 DNS 服务
- DNS-SERVER 1 (当服务端自身没有对应内容,发起递归查询请求其他 DNS 服务器时,与客户端类似。需要配置客户端所配置的内容)
- 流程
- 客户端向 DNS-SERVER 1 发起 DNS 请求包
- IP Src 192.168.1.1 (客户端 IP) Dst 192.168.1.2(DNS-SERVER 1 IP)
- DNS-SERVER 1 作为下级客户端向 DNS-SERVER 2 发起递归请求
- IP Src 192.168.2.2 (DNS-SERVER 1 连接 SERVER 2 接口的 IP) Dst 192.168.2.3(DNS-SERVER 2 IP)
- DNS 内的 Transaction ID、Flags 等保持 DNS 服务端收到客户端时的值
- DNS-SERVER 2 发送 DNS Response 给 DNS SERVER 1
- DNS SERVER 1 将内容构造成自己的 DNS Response (将 DNS 内容原样取出后封装成自己的 DNS Response) 发送给客户端
- 客户端向 DNS-SERVER 1 发起 DNS 请求包
DNS 区域传送
- 配置: 保证主从相互连通,这里使用的方案是都配置了同一虚拟网卡 VMnet1
- 主服务: VMware 环境 WinServer2008
- VMware 网络配置: VMnet1(Host Only)
- 系统网盘配置: IP 1.1.1.1 DNS 1.1.1.1
- 开启 WinServer 的 DNS 服务功能
- 主服务开启 Wireshark 进行抓包
- 在 DNS 服务中的配置
- 创建映射文件(创建主域名): 新建区域向导 -> 主要区域(主服务的标志) -> 区域名称(当前 DNS 服务的主域名,填写到二级域名,如
xxx.com,即完成了该域名主服务的创建) - 在映射文件内增加 DNS 记录: 右键 -> 新建主机 (增加主域名下的子域名,如
www.xxx.comnews.xxx.com等) - 将映射记录传输到从服务: 映射文件 右键 -> 属性 -> 区域传送 -> 添加从服务地址(此时服务会开始和从服务进行沟通,并发送数据包)
- 创建映射文件(创建主域名): 新建区域向导 -> 主要区域(主服务的标志) -> 区域名称(当前 DNS 服务的主域名,填写到二级域名,如
- 从服务: VMware 环境 WinServer2008
- VMware 网络配置: VMnet1(Host Only)
- 系统网盘配置: IP 1.1.1.2 DNS 1.1.1.2
- 开启 WinServer 的 DNS 服务
- 在 DNS 服务中的配置
- 创建映射文件(创建主域名): 新建区域向导 -> 辅助区域(从服务的标志) -> 区域名称(保持主域名一致,表明当前是主域名的从服务) -> 填写主服务 IP(此时回响应主服务的沟通,完成数据传输)
- 主服务: VMware 环境 WinServer2008
- 过程
- 区域 SOA 查询 (DNS 协议包,从服务器向主服务器请求获取当前完整的 DNS 记录)
- 数据包: Src 1.1.1.2 | Dst 1.1.1.1 | DNS | Standard query 0xc46f SOA xxx.com
- DNS 内容: Transaction ID、Flags 等与普通请求一致,但 Queries 中存在不同
- Queries
- xxx.com: type SOA
- Name: xxx.com
- Type: SOA (Start of zone of authority) (此处标记出是区域认证的初始传送)
- xxx.com: type SOA
- Queries
- DNS 内容: Transaction ID、Flags 等与普通请求一致,但 Queries 中存在不同
- 数据包: Src 1.1.1.2 | Dst 1.1.1.1 | DNS | Standard query 0xc46f SOA xxx.com
- 区域 SOA 应答(DNS 协议包,主服务器对从服务器的请求进行回应)
- TCP 传输内容: 三个 TCP 包实现 TCP 三次握手
- 从服务器再次发起 DNS SOA 请求(握手已建立,此时数据包内含有 TCP 协议封装)
- 主服务器发起 DNS SOA 应答 (数据包体积较大,DNS 协议封装中的 Answers 内包含要传输的映射关系)
- Answers
- xxx.com: type SOA, class IN, mname win-xxxxx
- xxx.com: type NS, class IN, ns win-xxxxx
- test.xxx.com: type A, class IN, addr x.x.x.x
- www.xxx.com: type A, class IN, addr x.x.x.x
- Answers
- 区域 SOA 查询 (DNS 协议包,从服务器向主服务器请求获取当前完整的 DNS 记录)
TFTP 协议
Trivial File Transfer Protocol 简单文件传输协议,用于实现无盘系统引导、小文件传输、系统镜像升级引导等
基于 UDP 协议设计(69 端口),采用停止等待方式进行数据传输,超时重传机制进行重传(TFTP 本身的重传,而不是 UDP)。为了使服务端可以同时与多客户端进行传输,服务器仅确认包时 69,后续传输时使用其他接口防止端口占用
传输是将数据切割成 512 字节方式进行传输,当最后一块不是 512 字节,则表示传输完成(TFTP 自己进行分片,而 UDP 不分片)。支持 ASCII 码(文本模式)和 Octet(二进制模式)两种方式传输,可对文件进行读和写两种操作
过程
此处描述数据从服务端到客户端过程,如从服务器获取系统引导的场景
- 客户端发送读写请求
- 服务端回应
- 客户端发送 ACK 确认(此时沟通完成,三个数据包类似 TCP 三次握手)
- 服务端发送数据包
- 客户端对数据包回复 ACK 确认
- 服务端收到确认,发送下一分片的数据包,等待客户端确认。超时未收到客户端的确认,认定为数据包丢失,进行重传
- 直到客户端收到小于 512 字节的包,回复确认后传输完成
封装
通过操作码判断请求类型
- 读请求 RRQ。其中文件名是目标文件,使用 0 字节表示结束。模式即数据传输的模式(ascii 码传
netascii或octet),如文本信息使用文本模式,也使用 0 表示结束 - 写请求 WRQ
- 数据 DATA
- 块编码: 以读取为例,服务器发送块编码为 1 的数据分组,客户端收到后回复块编码为 1 的 ACK。服务器收到上一个块的确认后再发送块编码为 2 的数据,以此类推,直到数据传输完成(客户端写请求的服务器 ACK 包使用 0 ,客户端发送数据包再从 1 开始)
- 数据: 除最后一个分片不足 512,其余分片都是 512 字节的数据
- 确认 ACK
- 错误 ERR。用于传输错误编码信息。数字错误编码 与 文字段,如文件不存在,没有权限,传输中出现异常等情况
案例
- 环境搭建
- GNS3 内: 使用 Cloud 桥接电脑网卡,作为服务端。客户端连接服务端,并配置成与电脑同一网段的地址
- 电脑: 使用 tftpd32 软件构建 TFTP 服务端。网卡选择电脑当前的局域网 IP,文件夹选择目标文件所在文件
- 操作
- 读取: 客户端拉取目标文件
copy tftp: flash:,输入服务 IP(电脑 IP)和目标文件名 - 写入: 客户端向服务端进行文件备份
copy flash: tftp:,输入服务 IP(电脑 IP)和目标文件名
- 读取: 客户端拉取目标文件
- 数据包: 此处服务端 IP 1.1.1.1,客户端 IP 1.1.1.2
- 从服务端请求
- 读请求 Src 1.1.1.2 | Dst 1.1.1.1 | TFTP | Read Request, File: xxx.bin, Transfer type: octet
- UDP, Src Port: xxxxx, Dst Port: 69 (此处保持 TFTP 端口号 69)
- TFTP
- Opcode: Read Request (1) (操作代码,2字节)
- Source File: xxx,bin
- Type: octet
- 数据包 Src 1.1.1.1 | Dst 1.1.1.2 | TFTP | Data Packet, Block: 1(最后一个包会再此处的块编号后标注 last)
- UDP, Src Port: yyyyy, Dst Port: xxxxx (数据传输包使用端口不确定,为了防止端口占用,实现多客户端同时传输)
- TFTP
- Opcode: Data Packet (3)
- Block: 1
- Data (512 bytes)
- 确认包 Src 1.1.1.2 Dst 1.1.1.1 TFTP Acknowledgement, Block: 1
- UDP, Src Port: xxxxx, Dst Port: yyyyy (回复到数据传输包的端口)
- TFTP
- Opcode: Acknowledgment (4)
- Block: 1 (回复对应的块序号)
- 错误包: 客户端还未确认时发送 Src 1.1.1.2 | Dst 1.1.1.1 | TFTP | Error Code: Not defined, Message: Session terminated
- TFTP
- Opcode: Error Code (5)
- Error code: Not defined(0)
- Error message: Session terminated
- TFTP
- 读请求 Src 1.1.1.2 | Dst 1.1.1.1 | TFTP | Read Request, File: xxx.bin, Transfer type: octet
- 向服务端写入
- 写请求 Src 1.1.1.2 | Dst 1.1.1.1 | TFTP | Write Request, File: xxx.bin, Transfer type: octet
- TFTP
- Opcode: Write Request (4)
- Source File: xxx,bin
- Type: octet
- TFTP
- 服务端回复 ACK 给客户端(写请求确认包的块编号为 0) Src 1.1.1.1 | Dst 1.1.1.2 | TFTP | Acknowleagement, Block: 0
- 客户端向服务端发送块编号 1 的数据分组
- 服务端收到数据回复块编号 1 的 ACK 包
- 写请求 Src 1.1.1.2 | Dst 1.1.1.1 | TFTP | Write Request, File: xxx.bin, Transfer type: octet
- 从服务端请求
TCP 协议
Transmission Control Protocol 传输控制协议,提供面向连接的可靠传输服务,位于OSI七层模型的传输层 是整套 TCP/IP 协议栈中最核心、算法最多、知识最密集、学习难度最大的协议,是网络底层通信协议ID基石
面向字节流(byte stream service)的传输服务,通过MSS字段对数据进行分段传输,通过确认机制、重传机制等方式保证数据可靠传输,通过滑动窗口、拥塞避免等机制实现流量控制
TCP 需要先建立会话,再进行数据交互,结束时关闭会话。因此将围绕建立关闭,数据交互等方面进行介绍
- 数据报 vs 字节流
- UDP 面向数据报 Datagram。即 UDP 收到上层数据,不对数据进行分片,直接而是交由下层的 IP 进行处理
- TCP 面向字节流。TCP 收到上层数据,被 TCP 视为一串字节流,对其进行重组与分段
封装
TCP 头部共 20 字节。交给下层后,IP 协议会在其前方增加 20 字节的 IP 头部 
- Source port 源端口,客户端端口
- Destination port 目的端口,标识目标主机的服务端口
- Sequence number 的序列号,标识本底发送给通信接收方的第几个分组
- Acknowledgment number 确认号,标识本地接收到第几个分组(已确认分组的编号)。可靠情况下,确认号等于序列号加上数据包长度
- Header lengh 头部长度,标识 TCP 头部长度。与 IP 头部类似,默认 20 字节,最大 60
- Flags 标志位,标志数据包状态
- Reserved
- Nonce
- Congestion Window Reduced (CWR) 拥塞窗口
- ECN-Echo
- Urgent 紧急位
- Acknowledgment 确认位
- Push
- Reset 重置位
- Syn 同步位
- Fin 结束位
- Window size value 窗口大小,用于实现滑动窗口。用于使对方得知本地还能接受多少流量,TCP 缓存 10 M
- Checksnum 校验和,标识该数据报是否完整或被修改
连接建立与终止
三次握手
TCP 会话初始建立过程。主要涉及: SYN 同步位、ACK 确认位、SEQ Number 初始序列号 Init Sequence Number、ACK Number 确认号 Acknowledge
- 过程理解
- 客户端告知服务器 TCP SYN: 我需要访问你的 X 端口(SYN 表发起请求,同时还携带随机数 Seq)
- 服务器回复客户端 TCP SYN/ACK: 允许访问,我也需要访问你的 Y 端口(ACK 表同意,及 Ack 确认号 Seq + 1 表示确认了序列号。后续客户端发包需要以此为基准,即使用此处响应的 Ack 作为第一个包。并且服务端通过 SYN 发起请求,自己请求的随机数 Seq。TCP 中通常使用收到的 Ack 作为自己发包的 Seq)
- 客户端确认 TCP ACK: 允许访问 Y 端口(ACK 同意,及 确认号 Seq + 1 表示确认了当前对方的序列号,后续客户端发包需要以此为基准,即使用此处响应的 Ack 作为第一个包)
- 关于初始序列号
- 在之前的协议中,初始序列号从 0 开始,此处为何是随机值
- 防止在网络中被延迟的分组,在以后又被传送,而导致某个连接的一方对它有错误解释(当收到某些数据包的序列号与当前差距过大,会被认为是无效包,丢弃)
- 防止黑客进行 TCP 会话劫持、重放攻击等(如果需要作为中间人拦截并欺骗,需要准确的序列号,不然会被丢弃)
- 随机值的生成(不同操作系统 TCP 初始序列号生成机制有所差异)
- RFC 793 中 ISN 初始序列号为 32 位计数器,每 4ms + 1,到
时归零 (此时是线性增长,不安全) - RFC 1948 中,
ISN = M + F(localhost, localport, remoehostm remoteport),其中 M 是 32 位 4ms + 1 的计数器,F 是根据输入内容 Hash 计算出的随机数
- RFC 793 中 ISN 初始序列号为 32 位计数器,每 4ms + 1,到
- TCP 劫持工具 Juggernaut、Hunt(通过 ARP 欺骗等手段劫持当前 TCP 包,获取 ISN,进行数据注入)、Dsniffer
- 在之前的协议中,初始序列号从 0 开始,此处为何是随机值
- 案例: 运行一个基于 TCP 的协议,如
telnet ssh ftp http smtp ..- 环境: PC
12.1.1.1与 SERVER12.1.1.2建立连接- 配置 IP,保证连通。
conf tip add 12.1.1.1 255.255.255.0 - 配置 telnet/ssh/ftp.. 服务器。此处以 telnet 为例
- 服务端配置账号密码与级别
conf tusername admin privilege 15 password admin - 配置虚拟终端
line vty 0 15login localend - 配置写入
write
- 服务端配置账号密码与级别
- 客户端使用 telnet 触发 TCP 握手
telnet 12.1.1.2
- 配置 IP,保证连通。
- 数据包
- 客户端请求访问 23 端口 Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 33162->23 [SYN] Seq=0 Win=4128 Len=0 MSS=1460
- Sequence number: 0 (relative sequence number)(在 Wireshark 中,ISN 初始序列号被解析成 0,即此处的 Seq=0,此处 右键 -> Protocol Preferences -> 取消勾选 Relative sequence numbers 使其正常解析,修改后
1815716494) - Acknowledgment number: 0 (此处还不涉及,值就是 0)
- Flags: 0x002 (SYN)
- .... .... ..1. = Syn: Set
- Sequence number: 0 (relative sequence number)(在 Wireshark 中,ISN 初始序列号被解析成 0,即此处的 Seq=0,此处 右键 -> Protocol Preferences -> 取消勾选 Relative sequence numbers 使其正常解析,修改后
- 服务器同意,并请求访问 33162 端口 Src 12.1.1.2 | Dst 12.1.1.1 | TCP | 23->33162 [SYN, ACK] Seq=0 Ack=1 Win=4128 Len=0 MSS=1460
- Sequence number: 0 (relative sequence number) (服务端自己的 ISN)
- Acknowledgment number: 1815716495(收到的 Seq 加 1。服务端确认了客户端的该 ISN,建立连接后,客户端发送的第一个携带数据的包的 Seq 将使用此处服务端响应的 Ack ,后续包再进行累加)
- Flags: 0x012 (SYN, ACK)
- .... ...1 .... = Acknowledgment: Set
- .... .... ..1. = Syn: Set
- 客户端同意 Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 33162->23 [ACK] Seq=1 Ack=1 Win=4128 Len=0
- Sequence number: 1 (relative sequence number) (此处真实值为
1815716495即服务端确认号,表示建立连接后的第一个包将使用此序列号,再后续的包再进行累加 - 客户端下一个包将是正式包,也使用此作为 Seq) - Acknowledgment number: 1 (relative sequence number) (对服务器请求 ISN 进行响应)
- Flags: 0x010 (ACK)
- .... ...1 .... = Acknowledgment: Set
- Sequence number: 1 (relative sequence number) (此处真实值为
- 客户端请求访问 23 端口 Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 33162->23 [SYN] Seq=0 Win=4128 Len=0 MSS=1460
- 环境: PC
四次挥手
TCP 会话结束过程。主要涉及: FIN 结束位、ACK 确认位、SEQ Number 初始序列号 Sequence Number、ACK Number 确认号 Acknowledge
- 过程理解
- 客户端告知服务器 TCP FIN: 我已经使用好了你的 X 端口,没有数据给你这个端口了(此时 FIN 发送者 - 客户端 是主动关闭端)
- 服务端确认 TCP ACK: 好的,我将关闭你对这个端口的会话
- 服务器告知客户端 TCP FIN: 我已经使用好了你的 Y 端口,没有数据给你这个端口了
- 客户端确认 TCP ACK: 好的,我将关闭你对这个端口的会话
- 思考
- 为什么关闭过程不参考握手过程,FIN 请求接收端将 ACK 与 FIN 合并成一个包?
- 因为 TCP 是双向通信,当一方发送 FIN,说明自己没有数据给对方。对方仍可发送数据给自己(即半关闭状态,见后续内容)
- TCP 是双向传输机制
- 为什么关闭过程不参考握手过程,FIN 请求接收端将 ACK 与 FIN 合并成一个包?
- 数据包: 客户端
telnet连接后使用exit退出远程状态。telnet 较为特殊,断开连接的请求由服务端发起- 服务端发起关闭会话请求
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | 23->61428 [FIN, PSH, ACK] Seq=3073601829, Ack=4195708830 Win=4075
- Sequence number: 3073601829
- Acknowledgment number: 4195708830
- .... 0000 0001 1001 = Flags: 0x019 (FIN, PSH, ACK)
- .... ...1 .... = Acknowledgment: Set
- .... .... 1... = Push: Set (表示紧急关闭,此处可忽略,主要关注 FIN 与 ACK)
- .... .... ...1 = Fin: Set
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | 23->61428 [FIN, PSH, ACK] Seq=3073601829, Ack=4195708830 Win=4075
- 客户端确认
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 61428->23 [ACK] Seq=4195708830, Ack=3073601830 Win=4001
- Sequence number: 4195708830
- Acknowledgment number: 3073601830 (收到的 Seq+1)
- .... 0000 0001 0000 = Flags: 0x010 (ACK)
- .... ...1 .... = Acknowledgment: Set
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 61428->23 [ACK] Seq=4195708830, Ack=3073601830 Win=4001
- 客户端发起关闭请求
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 61428->23 [FIN, PSH, ACK] Seq=4195708830, Ack=3073601830 Win=4001
- Sequence number: 4195708830
- Acknowledgment number: 3073601830
- .... 0000 0001 1001 = Flags: 0x019 (FIN, PSH, ACK)
- .... ...1 .... = Acknowledgment: Set
- .... .... 1... = Push: Set
- .... .... ...1 = Fin: Set
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 61428->23 [FIN, PSH, ACK] Seq=4195708830, Ack=3073601830 Win=4001
- 服务端确认
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | 23->61428 [ACK] Seq=3073601830, Ack=4195708831 Win=4075
- Sequence number: 3073601830
- Acknowledgment number: 4195708831 (收到的 Seq +1)
- .... 0000 0001 0000 = Flags: 0x010 (ACK)
- .... ...1 .... = Acknowledgment: Set
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | 23->61428 [ACK] Seq=3073601830, Ack=4195708831 Win=4075
- 使用 Wireshark 数据流图 进行分析
- Menu Bar -> Statistics -> Flow Graph... 后,Choose flow type 选择 TCP flow
- 更方便的观察符号位与 Seq 序列号 与 Ack 确认号
- 服务端发起关闭会话请求
TCP 状态机
TCP 会话从建立到关闭,拥有不同的状态,整个过程使用 TCP 状态机维持。通过状态机,可深入了解 TCP 协议的交互
- 状态说明: 以一次 TCP 通信过程为例,说明 11 种状态。其中

- LISTEN 服务端监听请求(接收端端状态)
- SYN_SENT 客户端发送建立连接请求(发送握手1,发送端状态)
- SYN_RCVD 服务端收到请求(收到握手1,接收端状态)
- ESTABLISHED 客户端收到服务端的确认(收到握手确认,发送端状态)
- ESTABLISHED 服务端收到客户端的确认(收到握手确认,接收端状态)
- FIN_WAIT_1 客户端发送结束请求(发送挥手1,发送端状态)
- CLOSE_WAIT 服务端收到结束请求(收到挥手1,接收端状态)
- FIN_WAIT_2 客户端收到结束请求(收到挥手2,发送端状态)
- LAST_ACK 服务端发送结束请求(发送挥手3, 接收端状态)
- TIME_WAIT 客户端收到结束请求(收到挥手3,发送端状态。最多关注的状态,等待后进入 CLOSED 状态)
- CLOSED 服务端收到确认(收到挥手4,接收端状态)
- CLOSING (图外)双方同时尝试关闭,等待对方确认
- 查看当前设备的网络状态
netstat
连接建立超时
三次握手时出现数据包丢失,无法顺利建立的状态
- 分类
- SYN 重传: 发起建立连接请求 SYN 时,无法收到确认包 SYN/ACK(服务端异常)
- 配置: 暂停服务端
- 数据包
- 正常发送 SYN 包
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 48567->23 [SYN] Seq=0 Win=4129 Len=0 MSS=1460*
- 没收到到 SYN/ACK,发送 SYN 重传包
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | [TCP Retransmission] 48567->23 [SYN] Seq=0 Win=4128 Len=0*
- 正常发送 SYN 包
- SYN/ACK 重传: 在响应 SYN/ACK 后,无法收到 ACK(无法收到握手3,重发握手2)
- 配置: 客户端配置 ACL 防护列表丢弃指定协议指定 Flag 的包
- 配置模式
conf -t - ACL 配置模式
ip access-list extended TCP - 丢弃 tcp 协议包含 ack 的包
deny tcp any any ack permit ip any any- 接口配置模式
int f0/0(或:exit退出配置模式,config-if再进入配置接口模式) ip access-group TCP inend
- 配置模式
- 数据包
- 客户端发起请求
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 23842->23 [SYN] Seq=0 Win=4128 Len=0 MSS=1460
- 服务端确认并请求,此包会被 ACL 丢弃
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | 23->23842 [SYN, ACK] Seq=0 Ack=1 Win=4128 Len=0 MSS=1460
- 客户端因为丢弃没收到,发送 SYN 重传
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | [TCP Spurious Retransmission] 23842->23 [SYN] Seq=0 Win=4128*
- 服务端没收到握手3,发起 SYN/ACK 重传(此时重新生成一个 ISN,相关性解析无法与之前关联)
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | [TCP Retransmission] 23->23842 [SYN, ACK] Seq=2658717707 Ack=1
- 客户端发起请求
- 配置: 客户端配置 ACL 防护列表丢弃指定协议指定 Flag 的包
- SYN 重传: 发起建立连接请求 SYN 时,无法收到确认包 SYN/ACK(服务端异常)
连接关闭超时
四次挥手时,无法顺利关闭的状态。发起方发起关闭请求,接收方发送 ACK 响应后,发起方发送 FIN 无法得到响应,此时接收方会指数间隔重传
配置: 由于 telnet 的关闭请求由服务端发起,服务端配置 ACL 丢弃 TCP FIN 包(丢弃握手3)
- 服务端配置 ACL
- 配置模式
conf -t - ACL 配置模式
ip access-list extended TCP - 丢弃 tcp 协议包含 fin 的包
deny tcp any any fin permit ip any any- 接口配置模式
int f0/0 ip access-group TCP in- 关闭端口不可达
no ip unreachables(如果不关闭,服务端收不到握手3,会发送 ICMP 不可达,使客户端明白发生问题,自行关闭,无法触发重传) - 退出配置模式
end
- 配置模式
- 客户端删除 ACL
- 查看 ACL
ip access-lists - 配置模式
conf t - 删除 ACL
no ip access-list extended TCP - 退出配置模式
end
- 查看 ACL
- 服务端配置 ACL
数据包: 客户端使用
telnet连接进入后,使用exit退出- 服务端正常发送关闭请求
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | 23->51357 [FIN, PSH, ACK] Seq=155 Ack=60 Win=4069 Len=0
- 客户端正常响应
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 51357->23 [ACK] Seq=60 Ack=156 Win=3974 Len=0
- 客户端发送关闭请求。但此包会被服务端 ACL 丢弃
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 51357->23 [FIN, PSH, ACK] Seq=60 Ack=156 Win=3974 Len=0
- 客户端无法收到服务端的 ACK,发起握手3的重传(通过参考时间功能查看时间间隔指数增加 0.7s -> 5s -> 10.7s -> 22.2s -> 33.7s -> 56s -> 68s -> 79s ...)
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | [TCP Retransmission] 51357->23 [FIN, PSH, ACK] Seq=60 Ack=156
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | [TCP Retransmission] 51357->23 [FIN, PSH, ACK] Seq=60 Ack=156
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | [TCP Retransmission] 51357->23 [FIN, PSH, ACK] Seq=60 Ack=156
- ...
- TIME_WAIT 的说明: 当发送最后一个 ACK 后,TIME_WAIT 会持续2倍的 MSL(Maximum Segment Lifetime,报文最大生存时间)。当对方接收此 ACK 包出现异常时,会认为没有接收到 FIN 包(挥手3),触发此处介绍的连接关闭超时 FIN 重传。2MSL 保证了可以接收到此重传。如果强行将 TIME_WAIT 改的过小,虽然降低了连接关闭的耗时,但有两点问题:
- 如果某数据包延迟到达,由于 TIME_WAIT 改小,在设备已经进入下一个连接时依然存活并送达,会被认为是当前连接的数据包,导致数据错乱。TIME_WAIT 的 2MSL 可以使这种包到达时,该设备还没进入下一个连接,不污染下一个连接的数据(1 MSL 即可保证不污染的问题)
- 一旦出现对方接收 ACK 失败,触发了 FIN 重传,则收到 FIN 重传(ACK 发送与 FIN 重传)的最大时间是 2 MSL。TIME_WAIT 改小会出现已经进入下一个连接,却收到 FIN 重传,无法理解含义,回复一个 RST(重置),导致对方认为连接异常报错,而不是正常关闭
- 服务端正常发送关闭请求
TCP 半关闭: 当 TCP 一方发送 FIN位进行单方向会话关闭时,另一方依然可以发数据给对方,此状态称为半关闭
- 客户端没有数据,发送请求不再使用服务的此端口,服务回复确认。服务不发送握手3(请求不使用客户端端口),而是仍然传输数据。传输完成后再发送握手3,告知没有数据,请求关闭此端口。客户端回复确认
- 体现了 TCP 全双工
TCP 半打开: 如果一方已经关闭或异常终止连接而另一方却还不知道,这种 TCP 连接称为半打开 Half-Open
- 已经建立连接,其中一方突然 down 掉
TCP 同时打开: 两边同时发送 SYN 请求包,并都向对方回复 SYN/ACK 数据包
- 此时省略了 ACK 包,两边都正常进入 ESTABLISHED 状态
TCP 同时关闭: 两边同时发送 FIN 请求包,并都向对方回复 ACK 确认包
- 此时省略了挥手3&4,不再需要 FIN_WAIT_2 状态
- 两边收到 ACK 确认包后都进入 TIME_WAIT 状态
TCP 可靠传输
TCP 数据传输时,主要涉及到的两个部分,确认机制(实时反馈,是否正常送达)与重传机制
确认机制
通过序列号、确认号、分组长度等字段,结束接收方对发送方的数据进行实时反馈
过程理解
- 发送方发送数据包携带 Seq 序列号与 Len 长度。
- 根据之前的 TCP 封装介绍,Seq 标识当前发送的数据包编号(发送到第几个包)。Len 标识数据包长度
- 发送者实际已发送编号为 Seq + Len。例如: Seq = 10,Len = 10,则说明从 10 号开始,发送了 10。则已经发送到了编号 20。本地记录 20 这个数值,下次发包 Seq 为 20
- 接收方收到后回复确认包,Ack = Seq + Len。并也包含自己的 Seq 与 Len
- 根据之前的 TCP 封装介绍,此处 Ack 标识当前接收方已确认接收到的数据包编号
- 可理解为 Ack 表示收到的数据已经确认的长度。如上例 Seq = 10,Len = 10。收到此包并回复 Ack = 20。说明接收方已经确认一共收到了长度为 20 的数据。
- 如果发送方收到的确认包 Ack < Seq + Len,说明数据包丢失,需要进行重传
- 发送方的下一个数据包的 Seq 序列号则是基于之前发送的数据包长度(在一问一答中是接收方确认包内的 Ack 确认号,且 Ack 是接收方确认包的 Seq 与 Len)
- 以此类推,双方发送的 Ack 都是收到包的 Seq + Len
- 发送方发送数据包携带 Seq 序列号与 Len 长度。
数据包: 客户端通过
telnet连接服务端,运行show running-config等show命令生成数据传输过程,exit退出连接- 创建连接
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 61851->23 [SYN] Seq=0 Win=4128 Len=0 MSS=1460
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | 23->61851 [SYN, ACK] Seq=0 Ack=1 Win=4128 Len=0 MSS=1460
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 61851->23 [ACK] Seq=1 Ack=1 Win=4128 Len=0
- 传输过程: 按照数据包顺序
- 1 C->S 使用握手确认包的确认号
- Src 12.1.1.1 | Dst 12.1.1.12 | TELNET | Telnet Data ...
- TCP, Src Port: 61851, Dst Port: 23, Seq: 1, Ack: 1,Len: 9
- Src 12.1.1.1 | Dst 12.1.1.12 | TELNET | Telnet Data ...
- 2 S->C 服务端确认了 1 号包内容,使用接收到的包 (1 号包)的信息
- Src 12.1.1.1 | Dst 12.1.1.1 | TELNET | Telnet Data ...
- TCP, Src Port: 23, Dst 61851, Seq: 1, Ack: 10, Len: 12
,
- Src 12.1.1.1 | Dst 12.1.1.1 | TELNET | Telnet Data ...
- 3 S->C 客户端所有发送内容都已确认,使用上一个发出包(2 号包)的信息
- TCP, Seq: 13, Ack: 10, Len: 42
, 保持不变
- 4 C->S 确认服务端发送的第一个包(2 号包), 使用该包的信息
- TCP, Seq: 10, Ack: 13, Len: 3
,
- 5 C->S 目前只确认到 13,使用上一个发出包的信息
- TCP, Seq: 13, Ack: 13, Len: 3
, 保持不变
- 6 C->S 目前只确认到 13,使用上一个发出包的信息
- TCP, Seq: 16, Ack: 13, Len: 9
, 保持不变
- 7 S->C 目前服务端只确认到 10,即客户端发送的 1 号包
- Seq: 55, Ack: 10, Len: 3
- 8 S->C
- Seq: 58, Ack: 10, Len: 6
, 保持不变,因为目前服务端只确认到 10
- 9 S->C 服务端确认了 6 号包
- Seq: 64, Ack: 25, Len: 3
,
- 10 C->S 客户端确认到了 9 号包(从只确认了 2 到一口气确认了 7、8、9)
- Seq: 25, Ack: 67, Len: 0
- ...进入一问一答阶段...
- C->S Seq: 36, Ack: 84, Len: 1
- S->C Seq: 84, Ack: 37, Len: 1
- 满足了 Ack = Seq + Len, Seq = Ack
- C->S Seq: 37, Ack: 85, Len: 1
- 满足了 Ack = Seq + Len, Seq = Ack
- 1 C->S 使用握手确认包的确认号
- 创建连接
说明: 真实环境与理论略微不同。因为不是完全一问一答到形式,所以出现
不一定是丢包 - 真实环境中 TCP 不同于 TFTP,需要一个发送立刻收到一个确认,会存在单方面连续发包
- 接收端连续收到多个包时,先确认先收到的。例如以上案例中的 4 号包,客户端只确认了 S->C 的第一个包,即 2 号包
- Seq 是设备自己的计数器,ISN + 自己已发送的总长度内容。每次基于自己当前的计算值
- Ack 表示设备已确认的数据包长度。如上所示,如果还没确认完,则回复包中的 Ack 会保持已确认的长度
- 直到大流量数据传输完成,恢复一问一答后,仍可以满足 Ack = Seq + Len, Seq = Ack
重传机制
当 没有收到确认信息(超时重传),或 接收方发现丢包并回复 ACK 丢包(收到了 1、2、4、5、6 号,没有 3 号) 时,触发重传机制
第一种情况触发 超时重传,第二种情况触发 选择性重传 或 超时重传
超时重传
当发送方没有接收方返回的 ACK 确认时,则认为数据丢失,此时发送方依据 RTO 重传超时时间来对未确认数据包进行重传
Windows 系统最多重传 5 次,Linux 系统最多重传 15 次
- RTT: 往返时间(Round Trip TIme),记录数据从发出到收到确认所使用的时间。使用
ping发出 ICMP 包,显示结果中的 时间 与其类似,但此处是 TCP 协议的时间 - RTO: 重传超时时间(Retransmission Timeout),数据丢失后依据 RTO 值来进行重传。一般 RTO 依据 RTT 计算得出。以下是 RTO 的几种算法
在计算得到 RTO 后,若发送方发包后没有收到确认,每次使用指数退避的 RTO 将该包进行重传(1倍 RTO,2倍 RTO,4倍 RTO,8倍 RTO ...)
- 环境: 客户端 SSH 连接服务端后,服务端暂停(使用 telnet 时,在服务端暂停后,客户端会发送大量的 keep-live 包)
- 配置
- 服务端: SSH 服务
- 配置模式
conf t - 配置主机名
hostname SERVER - 配置域名
ip domain-name cisco.com - 生成密钥
crypto key generate rsa位数:1024 - 服务端配置账号密码与级别
username admin privilege 15 password admin - 配置虚拟终端
line vty 0 15login localend - 配置写入
write
- 配置模式
- 客户端使用 ssh 创建 TCP 会话
ssh -l admin 12.1.1.2 - 登陆后每输入一个字符,都会进行发包、返回、确认共三个包
- 客户端发包 Src 12.1.1.1 | Dst 12.1.1.2 | SSHv2 | Client: Encrypted packet (len=52)
- 服务端返回 Src 12.1.1.2 | Dst 12.1.1.1 | SSHv2 | Server: Encrypted packet (len=52)
- 客户端确认 Src 12.1.1.1 | Dst 12.1.1.2 | TCP | [ACK] Seq=1145 Ack=1705 Win=3920 Len=0
- 服务端暂停后,客户端输入字符
- 服务端: SSH 服务
- 数据包(通过显示过滤器来过滤重传包,有rto解析的包
tcp.analysis.rto- 在 TCP 头部的 RTO 字段 右键 -> Apply as Filter -> Selected)- 客户端发包
- Src 12.1.1.1 | Dst 12.1.1.2 | SSHv2 | Client: Encrypted packet (len=52)
- 重传
- Src 12.1.1.1 | Dst 12.1.1.2 | SSHv2 | Client: [TCP Retransmission] , Encrypted packet (len=52)
- [SEQ/ACK analysis]
- [iRTT: 0.030023000 seconds]
- [Bytes in flight: 52]
- [TCP Analysis Flags]
- [Expert Info (Note/Sequence): This frame is a (suspected) retransmission]
- [The RTO for this segment was: 0.632465000 seconds] (此包的 RTO 值)
- [RTO based on delta from frame: 75] (RTO 值依据 No.75 的包,列名处 右键 -> Displayed Columns -> 勾选 No. (Number))
- [SEQ/ACK analysis]
- Src 12.1.1.1 | Dst 12.1.1.2 | SSHv2 | Client: [TCP Retransmission] , Encrypted packet (len=52)
- 共 7 个重传包。每个包都依据 75 包(第一个重传包前,客户端正常发的 Client: Encrypted packet 包)计算出自己的 RTO 值
- RTO 0.632
- 第二个重传包距离上一个重传包的时间间隔 1.226
- 第三个重传包的时间间隔 2.488
- 第四个重传包的时间间隔 4.947
- 9.914
- 19.805
- 19.817
- 前几个包每个都是前一个的 2 倍,满足指数退避
- 客户端发包
经单重传
当服务端收到如 5 6 7 号数据包,但并未收到 1 2 3 4,认为这些数据包丢失。以丢失数据包最小的序号作为 Ack 进行回复(Dup-ACK: 重复 ACK 包)。客户端收到后从给定的序号重新开始传。如,此例子中从 1 开始传(TCP Retransmission: 重传包)。重传包的 Seq 会等于 重复 ACK 的 Ack
经典重传作为接收方触发的重传,相比其他两种接收方触发重传方式(快速重传、超时重传)来说,效率偏低,会传输一些已经收到的包。但 telnet、ssh 等使用经典重传
环境: 客户端通过 telnet 连接服务端后,服务端拦截客户端的请求,客户端进行操作,从而使服务端接收不到一些包。服务端再取消拦截,使服务端收到后续的包而丢失中间拦截掉的包,触发经单重传
配置:
- Telnet 服务: 同之前的 telnet 配置
- 在客户端登陆后每输入一个字符,都会进行发包、返回、确认共三个包(与 ssh 类似)
- Src 12.1.1.1 | Dst 12.1.1.2 | TELNET | Telnet Data ...
- Telnet (数据包详情的 Telnet 层)
- Data: s (输入的字符内容直接明文传输)
- Telnet (数据包详情的 Telnet 层)
- Src 12.1.1.2 | Dst 12.1.1.1 | TELNET | Telnet Data ...
- Telnet (数据包详情的 Telnet 层)
- Data: s (确认的字符内容明文传输)
- Telnet (数据包详情的 Telnet 层)
- Src 12.1.1.1 | Dst 12.1.1.2 | TCP | 15220->23 [ACK] Seq=63 Ack=359 Win=3704 Len=0
- Src 12.1.1.1 | Dst 12.1.1.2 | TELNET | Telnet Data ...
- 在连接建立后配置服务端拦截
- 进入配置模式
conf t - 进入 ACL 配置模式
ip access-list extended RETRANS - 拦截 telent 包
deny tcp any any eq telnetpermit ip any any - 退出 ACL 配置模式
exit - 进入接口配置模式
int f0/0 - 接口使用 ACL
ip access-group RETRANS in - 关闭服务端返回 ICMP 不可达
no ip unreachables
- 进入配置模式
- 客户端输入字符,此时这些包都无法收到。客户端没收到请求会触发客户端的超时重传
- 等待超时重传频率降低,服务端取消拦截
no ip access-group RETRANS in - 客户端在取消拦截后立即输入字符(使服务端立即收到产生后续的新包,从而发现丢失了中间的包)
数据包
- 拦截前服务端最后一个正常响应的 Ack 是 67
- 客户端没有收到确认,触发超时重传
- Src 12.1.1.1 | Dst 12.1.1.2 | TELNET | [TCP Retransmission] Telnet Data ...
- Telnet
- Data: show ip address (当多个包没有确认时,会组成一个大包进行重传。正常一个字符一个包)
- Telnet
- Src 12.1.1.1 | Dst 12.1.1.2 | TELNET | [TCP Retransmission] Telnet Data ...
- 客户端在取消拦截后立即输入字符。此时 Seq 已成长为 78
- Src 12.1.1.1 | Dst 12.1.1.2 | TELNET | Telnet Data ...
- TCP, Src Port: 15220, Dst Port: 23, Seq: 78, Ack: 363, Len: 2
- Src 12.1.1.1 | Dst 12.1.1.2 | TELNET | Telnet Data ...
- 服务端收到了 Seq 78,而上一个收到的包的 Seq 是 67 并返回了确认。而触发了服务端响应重复 ACK 包。此时的 129#1 标识第一个 Dup ACK,后续为 129#2 ...
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | [TCP Dup ACK 129#1] 23->15220 [ACK] Seq=363 Ack=67 (此处 Ack 返回丢失前最后一个正常响应)
- [SEQ/ACK Analysis]
- [TCP Analysis Flags]
- [This is a TCP duplicate ack]
- [Duplicate ACK #: 1]
- [Duplicate to the ACK in frame: 129] (告知客户端从第 129 个包开始重传,该包的 Ack 是 67)
- [TCP Analysis Flags]
- [SEQ/ACK Analysis]
- Src 12.1.1.2 | Dst 12.1.1.1 | TCP | [TCP Dup ACK 129#1] 23->15220 [ACK] Seq=363 Ack=67 (此处 Ack 返回丢失前最后一个正常响应)
- 客户端从服务端指定的 Seq=67 进行重传(将中间丢的内容重组成一个 telnet 包)
- Src 12.1.1.1 | Dst 12.1.1.2 | TELNET | [TCP Retransmission] Telnet Data ...
- TCP, Src Port: 15220, Dst Port 23, Seq: 67, Ack: 363, Len: 28 (按照服务器要求,客户端发起的重传包的 Seq=67)
- Telnet (将所有丢失包的内容统一放在这个包里进行传输)
- Data: xxx
- Data: xxx
- ...
- Src 12.1.1.1 | Dst 12.1.1.2 | TELNET | [TCP Retransmission] Telnet Data ...
快读重传
当接收方检测到有数据包没有收到时,则会向发送方返回 3 个重复 ACK(Dup-ACK),告知其某个序号的数据包需要重传 发送方从丢失序号开始重复发送之后的所有数据包(Retransmission)
在真实环境中,快速重传与快速选择性重传常常一起出现
快速选择性重传
当接收方检测到有数据包没有收到时,则会向发送方返回 3 个重复 ACK(Dup-ACK),告知其某个序号的数据包需要重传 发送方仅仅需要重新发送丢失序号的数据包,提高数据恢复的效率
封装
- SACK 选择性ACK: 接收方发送 Dup-TCP 前发送的 TCP 确认包的头部存在 Option 选项。TCP 头部原本默认 20 字节,此处多了 SACK 选项值,(SLE/SRE - 选择性 ACK 的窗口, Selective ACK Left/Right Edge)
- Src receiver | Dst deliver | TCP | 9057->80 [ACK] Seq=1204618440 Ack=3489599618 Win=258 Len=0 SLE=3489611138 SRE=3489614018
- Options: (20 bytes), No-Operation (NOP), No-Operation (NOP), SACK (TCP 的 Option 部分携带了选择性 ACK 信息,用于发起快速选择性重传)
- No-Operation (NOP)
- No-Operation (NOP)
- SACK: 3489611138-3489614018 3489601058-3489609698 (此处的 Seq 范围表示目前已收到的两个范围的数据包 01058-09698 和 11138-14018。说明 09698-11138 丢包了)
- Kind: SACK (5)
- Length: 18
- left edge = 3489611138
- right edge = 3489614018
- left edge = 3489601058
- right edge = 3489609698
- [TCP SACK Count: 2]
- Options: (20 bytes), No-Operation (NOP), No-Operation (NOP), SACK (TCP 的 Option 部分携带了选择性 ACK 信息,用于发起快速选择性重传)
- Src receiver | Dst deliver | TCP | 9057->80 [ACK] Seq=1204618440 Ack=3489599618 Win=258 Len=0 SLE=3489611138 SRE=3489614018
- Dup-ACK 重复 ACK。由于上一个 SACK 包的 SACK 数据已经能推导出 09698-11138 丢包,此包的 Ack 值也只能确认到9618(599618到第一个窗口的601058也丢包了)。让发送方从9618开始重传,并跳过 SACK 中记录的已收到的部分
- Src receiver | Dst deliver | TCP | [TCP Dup ACK 1#1] 9057->80 [ACK] Ack=3489599618
- 即 真实环境中,通过 SACK 告知接收方已收到的范围区间,Dup-ACK 告知重发的起始序号。快速选择性重传 与 快速重传 的一同出现
- 快速重传。服务端发送快速重传包给接收端。Seq 为重复 ACK 指定的数值,即 从重复 ACK 指定的位置开始重发。
。刚好是从 99618 发到 01058 第一段已接收窗口的开始位置。 - Src deliver | Dst recevier | TCP | [TCP Fast Retransmission] 80->9057 [ACK] Seq=3489599618, Ack=1204618440, Len=1440
- TCP, Src Port: 80, Dst Port: 9057, Seq: 3489599618, Ack: 1204618440, Len: 1440
- [SEQ/ACK analysis] (TCP 头部中的专家分析字段)
- [Bytes in flight: 18720]
- [TCP Analysis Flags]
- [Expert Info (Note/Sequence): This frame is a (suspected) fast retransmission] (此处标明了该包是快速重传包)
- [This frame is a (suspected) fast retransmission]
- [Severity level: Note]
- [Group: Sequence]
- [Expert Info (Note/Sequence): This frame is a (suspected) retransmission]
- [Expert Info (Note/Sequence): This frame is a (suspected) fast retransmission] (此处标明了该包是快速重传包)
- [SEQ/ACK analysis] (TCP 头部中的专家分析字段)
- TCP, Src Port: 80, Dst Port: 9057, Seq: 3489599618, Ack: 1204618440, Len: 1440
- Src deliver | Dst recevier | TCP | [TCP Fast Retransmission] 80->9057 [ACK] Seq=3489599618, Ack=1204618440, Len=1440
- SACK 选择性ACK: 接收方发送 Dup-TCP 前发送的 TCP 确认包的头部存在 Option 选项。TCP 头部原本默认 20 字节,此处多了 SACK 选项值,(SLE/SRE - 选择性 ACK 的窗口, Selective ACK Left/Right Edge)
案例
- 配置: 真实环境下,播放视频流会经常出现重传、乱序等情况。在播放视频时可以抓到快速重传包,再根据快速重传包定位到重复 Ack 包
- 数据包: 此处以封装为参考
- 重复 ACK: 声明接收方只确认到了 2331664644 的包,需要从此开始重传。内部携带选择 ACK 信息
- Src: receiver | Dst: deliver | TCP | [TCP Dup ACL] 4876->80 [ACK] Seq=3717379713 Ack=2331664644
- TCP
- Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), SACK
- No-Operation (NOP)
- No-Operation (NOP)
- SACK: 2331624324-2331625764
- Kind: SACK (5)
- Length: 10
- left edge = 2331624324
- right edge = 2331625764
- Options: (12 bytes), No-Operation (NOP), No-Operation (NOP), SACK
- TCP
- Src: receiver | Dst: deliver | TCP | [TCP Dup ACL] 4876->80 [ACK] Seq=3717379713 Ack=2331664644
- 快速重传包: 从指定的 2331664644 位置进行重传,Seq=2331664644
- Src: deliver | Dst: receiver | TCP | [TCP Fast Retransmission] [TCP segment of a reassembled PDU]
- TCP, Src Port: 80, Dst Port: 4876, Seq: 2331664644. Ack: 3717379713, Len: 1440
- Src: deliver | Dst: receiver | TCP | [TCP Fast Retransmission] [TCP segment of a reassembled PDU]
- 重复 ACK: 声明接收方只确认到了 2331664644 的包,需要从此开始重传。内部携带选择 ACK 信息
HTTP 协议
超文本传输协议(Hyper Text Transfor Protocol)用于实现 Web 服务器到客户端间的通信。HTTP 主要分为 0.9、1.0、1.1、2.0 四个版本,使用最广泛的是 1.1 版本。从0.9仅支持 GET 请求方式,到1.0支持 GET/POST/PUT/HEAD,再到1.1支持 GET/POST/PUT/HEAD/OPTIONS/DELETE/TRACE/CONNECT。1999年通过 RFC2616 定义了 HTTP1.1 ,2015年通过 RFC7540 定义了 HTTP2.0
HTTP 协议具备无连接无状态的特点(虽然基于 TCP 协议),每次请求响应在逻辑上都是独立的,服务器默认无法“记住”客户端(通过 cookie,session,token 解决这个问题)。虽然早期的 HTTP/1.0 是基于短连接的(每次请求新建和释放 TCP),但现代 HTTP(1.1及以后)默认采用长连接(Keep-Alive)和多路复用机制,多个 HTTP 请求会复用同一个底层 TCP 连接,以极大提升网络传输效率
抓包流程
- 打开 Wireshark。选择网卡
- 本地网卡用于抓取外部网络
- 127.0.0.1 等逻辑地址。在请求时通过 Loopback 接口不经过物理网卡直接调用本机的另一个应用
- Windows 需要 npcap 构建 Loopback 网卡
- Linux 等存在 lo0 用于回环,Wireshark 直接选择 lo0 即可
- 访问网站
- 过滤 HTTP 流程并追踪: 列表中的 HTTP 包 右键 -> Follow(追踪流)-> HTTP Stream(HTTP流)
结构
在 HTTP 中,根据 RFC 标准,使用 CRLF (\r\n) 进行换行
- 请求
- 请求行
- 请求方法。eg:
GET - 路径。eg
/ - 协议。eg:
HTTP/1.1 - 结束换行符号 CRLF
- 请求方法。eg:
- 请求头。每个请求头结尾都会有结束换行符号 CRLF
- 空行 - 一个结束换行符 CRLF。此时最后一个请求头的后方会有两个 CRLF
- 请求信息。表单内容
- 请求行
- 响应
- 响应行
- 协议
- 状态码
- 1xx: 信息性状态码,请求正在处理
- 2xx: 成功状态码
- 3xx: 重定向状态码(响应头中携带 Location 表明重定向目标,触发请求方发送GET请求)
- 302 Found(对应 404 Not Found)
- 4xx: 客户端错误状态码
- 5xx: 服务端错误状态码
- 状态描述
OK - 结束换行符号 CRLF
- 响应头。每个响应头结尾都会有结束换行符号 CRLF
- 空行 - 一个结束换行符 CRLF。此时最后一个响应头的后方会有两个 CRLF
- 响应信息
- 响应行
请求方法与跨域检测
GET: 用于请求指定页面信息,一般的浏览网页便是采用GET方法。请求访问的内容一般会显示在浏览器地址栏中
POST: 用于请求并提交内容到指定页面信息,提交表单/上传文件便是采用POST方法
HEAD: 跟GET类似,但返回的响应中没有具体内容,用于获取报头
- 扫描器做内容探测时常用 HEAD 方法,速度最快且不易被发现
- 与 ICMP 类似,但 ICMP 时传输层,用于检查目标端口的情况。HEAD 作为应用层请求用于检查目标服务是否可用
PUT: 从客户端向服务器传送数据取代指定的内容。常被黑客利用,可关闭此方法
DELETE: 请求服务器删除指定内容。常被黑客利用,可关闭此方法
CONNECT: HTTP/1.1 协议中与流给能够
OPTION: 允许客户端查看服务器性能 或 跨域检测
- 当发生跨域请求(请求的 HOST 与当前 HOST 不一致)时,如果出现以下情况
- 请求携带 JSON
- 请求头携带自定义字段(如 Authorization)
- PUT、DELETE 方法
- 浏览器自动发送 OPTIONS 预检请求,服务端是否接受指定方法,出现自定义字段,出现JSON
- 服务端响应允许的 Origin(跨域 HOST)、请求方法、自定义 Headers、Max-Age(预检结果缓存时间)
- 浏览器确认后再发送真实的请求
- 当发生跨域请求(请求的 HOST 与当前 HOST 不一致)时,如果出现以下情况
TRACE: 回显服务器收到的请求,主要用于测试与诊断
Headers
头部字段中,一部分仅出现在请求头中,一部分仅出现在响应头中,一部分在请求与响应内都可以出现
X开头的头部字段表示该字段没有在 RFC 规范中
请求字段
仅在请求头中出现的字段
- Host: 主机与端口,不写默认使用 80
- User-Agent
- Referer: 该请求是从哪个页面跳转来的(具体的URL)
- Origin: 与 Referer 相似。该请求从哪个HOST跳转来(不携带 query 参数)
- Cookie: 基于此机制构造 Session
- Range: HTTP 分块请求实体内容。如一个大文件,通过Range多线程下载(需要客户端支持)
- Range: bytes=0~100 (下载前100字节)
- Range: bytes=100~200(下载第二段100字节)
- x-forward-for: 客户端IP地址。一般称为 XXF 头
- 服务端通过该字段获取客户端的真实或代理IP(可能会记录每层代理的地址 - 该字段可内含多个地址)
- 攻击者通过这个头伪造源IP地址,欺骗服务端
- Accept: 客户端接收的文件类型(MIME)。以下是常见的 MIME 类型
- / - 任意格式
- text/plain - 文本
- text/html - HTML文档
- application/xml - XML文档
- text/css - CSS
- image/gif
- image/jpeg
- image/png
- application/pdf - PDF文档
- 多个格式逗号分隔,;表示修饰,q表示该项优先级 eg:
Accept: text/html,application/xhtml+xml;q=0.9.image/webp,*/*;q=0.8
- Accept-Charset: 客户端接受的字符集
- Accept-Encoding: 客户端接受的编码(压缩解压缩算法)eg:
Accept-Encoding: gzip, deflate - Accept-Language: 客户端接受的语言 eg:
zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7表示 zh-CN 优先级最高,zh 优先级0.8 - Authorization
- Expect
- From: 用户的 email
- if-Match: 比较实体标记
- if-Modified-Since: 比较资源更新时间
- if-Unmodified-Since: 比较资源更新时间
- if-None-Match: 比较实体标记
- if-Range
- Max-Fowards
- Proxy-Authorization
- TE
- DNT: Do Not Track, Chrome 安全功能,用于解决流量追踪。1-不想被第三方网站追踪,0-接受追踪,null-
- Upgrade-Insecure-Requests
- X-Requests-With: 标识请求信息
- 传统请求无该字段,Ajax 请求会携带此信息
- eg:
X-Requested-With: XMLHttpRequest
- X-CSRF-Token: 用于解决 CSRF 跨站请求伪造
响应字段
仅在响应头中出现的字段
- Server: Web 服务器信息。黑客可通过版本搜索漏洞,可关闭或修改。eg:
Server: nginx/1.4.6(Ubuntu)Server: Apache - X-Powered-By: 服务器程序版本。黑客可通过版本搜索漏洞,可关闭或修改。eg:
X-Powered-By: PHP/5.5.9-1unbuntu4.21 - Set-Cookie: 服务器通过此字段为客户端设置cookie信息,后湖客户端使用此cookie进行请求
- ed
Set-Cookie: info=xxxxxx;path=/;HttpOnly - httponly: JS 脚本不能获取 cookie。可全小写,用于解决 XSS 跨站请求脚本攻击
- ed
- Accept-Ranges: 是否接受字节范围请求,对应请求头中的
Range- 服务端响应
Accept-Range: bytes后,客户端请求Range
- 服务端响应
- Location: 服务端告知客户端去哪访问该资源,一般用于重定向302
- WWW-Authenticate: 服务器对客户端的认证信息
- 服务端在响应头携带此字段触发浏览器弹出小窗口进行登陆,实现 HTTPBasicAuth
- 客户端请求头携带
Authorization: Basic b64(usr:pwd)
- Proxy-Authenticate: 代理对客户端的认证信息
- 客户端请求头携带
Proxy-Authorization: Basic b64(usr:pwd)
- 客户端请求头携带
- Retry-After: 对再次发起请求的要求
- Refresh: 服务器告知客户端,定时刷新浏览器
- ETag: Entity Tags,表示资源的匹配信息
- 服务端对资源打标记,用于客户端区分数据流。与下层协议中区分时使用的标记类似
- eg
ETag: 5a2f5579-183
- Vary: 代理服务器缓存的管理信息
通用字段
可以存在请求和响应报文中
- Connection: 用于表示连接是否可持续
keep-alive或close - Cache-Control: 控制缓存信息
Cache-Control: no-cache: 防止缓存已过期的网页信息,要求获取最新缓存- 可以缓存,但不能直接使用缓存,必须验证
- 验证请求
If-None-MatchIf-Modified-Since - 得到响应
Not Modified便可直接使用缓存
Cache-Control: max-age=94608000告知客户端缓存信息的最长时间(秒)。max-age单独存在时,在指定时间段内直接使用缓存Cache-Control: no-cache,max-age=0,must-revalidate,no-storeno-cache: 如果 no-cache 存在,max-age 基本失去作用no-store: 不要进行缓存
- Transfer-Encoding: 表示输出的内容长度不能确定,需要分块处理
- 对于动态内容或在发送前不能判定长度的情况下,可使用分块的方法传送编码
- 对应静态内容或在发送前可以预判长度的情况下,可使用
content-length字段标识 - 即:二择,数据包长度确定时携带字段
content-length,不确定时携带字段Transfer-Encoding - eg:
Transfer-Encoding: chunked
- Upgrade: 升级为其他协议
- Via: 代理服务器相关信息
- Warning: 错误信息通告
- Date: 标识内容产生的时间(例如请求HTML时,响应使用该字段标识响应体的HTML文档的创建时间)
- Pragma: 报文指令 eg:
Pragma: no-cache - Trailer: 报文末段的首部一览
实体字段
可以存在请求与响应报文中,针对实体内部进行描述
HTTP报文信息部分(请求信息-请求体,响应信息-响应体,发送的内容信息)的描述
- Content-Type: 用于表示实体内容的介质类型(MIME 文件类型,接收方需要根据此类型对实体进行处理)
- Content-Encoding: 表示实体内容的编码方式
- Content-Length: 实体内容的长度
- Content-Language: 实体内容的语言
- Content-Location: 代替对应资源的URI
- Content-MD5: 实体内容的报文摘要
- Content-Range: 实体内容的位置范围
- Last-Modified: 实体内容的最后修改时间
- 与通用字段中的
Date异曲同工,一个是内容创建时间,一个是内容修改时间 - eg:
Last-Modified: Sat, 26 Feb 2021 02:33:38 GMT
- 与通用字段中的
- Expires: 实体内容的过期时间
- eg:
Expires: Sat, 26 Feb 2028 02:33:38 GMT - eg:
Expires: 0缓存立刻失效- 接收方可以缓存,但该缓存无效
- 效果等同于
Cache-Control: no-cache,可缓存,但该缓存不能使用。因为no-cache需要请求验证 - 效果等同于
Cache-Control: no-store,但no-store不会缓存
- eg: