IP 头里的服务类型字段到底是什么?DSCP 和 ECN 各占几位?标了 DSCP 的包到了公网上会怎样?这篇用 IP_TOS 把各种 DSCP/ECN 值发到线上,抓包验证,再沿着一条真实公网路径看每一跳收到的 TOS,以及 TCP 的 ECN 协商。
lo0:发了 12 个,抓到 12 个
CS0 设置 0x00 → 读回 0x00 → 线上 TOS 字节 0x00(DSCP 0,ECN 0) → 接收方 IP_RECVTOS 读到 0x00
CS1 设置 0x20 → 读回 0x20 → 线上 TOS 字节 0x20(DSCP 8,ECN 0) → 接收方 IP_RECVTOS 读到 0x20
AF11 设置 0x28 → 读回 0x28 → 线上 TOS 字节 0x28(DSCP 10,ECN 0) → 接收方 IP_RECVTOS 读到 0x28
AF21 设置 0x48 → 读回 0x48 → 线上 TOS 字节 0x48(DSCP 18,ECN 0) → 接收方 IP_RECVTOS 读到 0x48
AF31 设置 0x68 → 读回 0x68 → 线上 TOS 字节 0x68(DSCP 26,ECN 0) → 接收方 IP_RECVTOS 读到 0x68
AF41 设置 0x88 → 读回 0x88 → 线上 TOS 字节 0x88(DSCP 34,ECN 0) → 接收方 IP_RECVTOS 读到 0x88
EF 设置 0xb8 → 读回 0xb8 → 线上 TOS 字节 0xb8(DSCP 46,ECN 0) → 接收方 IP_RECVTOS 读到 0xb8
CS6 设置 0xc0 → 读回 0xc0 → 线上 TOS 字节 0xc0(DSCP 48,ECN 0) → 接收方 IP_RECVTOS 读到 0xc0
CS7 设置 0xe0 → 读回 0xe0 → 线上 TOS 字节 0xe0(DSCP 56,ECN 0) → 接收方 IP_RECVTOS 读到 0xe0
EF+ECT(0) 设置 0xba → 读回 0xba → 线上 TOS 字节 0xba(DSCP 46,ECN 2) → 接收方 IP_RECVTOS 读到 0xba
EF+ECT(1) 设置 0xb9 → 读回 0xb9 → 线上 TOS 字节 0xb9(DSCP 46,ECN 1) → 接收方 IP_RECVTOS 读到 0xb9
EF+CE 设置 0xbb → 读回 0xbb → 线上 TOS 字节 0xbb(DSCP 46,ECN 3) → 接收方 IP_RECVTOS 读到 0xbb
en0:发了 12 个,抓到 12 个
CS0 设置 0x00 → 读回 0x00 → 线上 TOS 字节 0x00(DSCP 0,ECN 0)
CS1 设置 0x20 → 读回 0x20 → 线上 TOS 字节 0x20(DSCP 8,ECN 0)
AF11 设置 0x28 → 读回 0x28 → 线上 TOS 字节 0x28(DSCP 10,ECN 0)
AF21 设置 0x48 → 读回 0x48 → 线上 TOS 字节 0x48(DSCP 18,ECN 0)
AF31 设置 0x68 → 读回 0x68 → 线上 TOS 字节 0x68(DSCP 26,ECN 0)
AF41 设置 0x88 → 读回 0x88 → 线上 TOS 字节 0x88(DSCP 34,ECN 0)
EF 设置 0xb8 → 读回 0xb8 → 线上 TOS 字节 0xb8(DSCP 46,ECN 0)
CS6 设置 0xc0 → 读回 0xc0 → 线上 TOS 字节 0xc0(DSCP 48,ECN 0)
CS7 设置 0xe0 → 读回 0xe0 → 线上 TOS 字节 0xe0(DSCP 56,ECN 0)
EF+ECT(0) 设置 0xba → 读回 0xba → 线上 TOS 字节 0xba(DSCP 46,ECN 2)
EF+ECT(1) 设置 0xb9 → 读回 0xb9 → 线上 TOS 字节 0xb9(DSCP 46,ECN 1)
EF+CE 设置 0xbb → 读回 0xbb → 线上 TOS 字节 0xbb(DSCP 46,ECN 3)高 6 位是 DSCP(服务等级,EF = 46、AF41 = 34 等),低 2 位是 ECN。IP_TOS 设的字节在线上原样出现,接收方可以用 IP_RECVTOS 读出来。
TTL(第几跳) 发 0xb8 发 0x88 发 0x28 发 0x00 发 0xba
1 -- -- -- 0x00 0xba
2 0x00 0x00 0x00 0x00 0x02
3 0x00 0x00 0x00 0x00 0x02
4 0x00 0x00 0x00 0x00 0x02
5 -- -- 0x00 0x00 0x02
6 0x00 -- -- -- 0x02
7 0x00 -- -- -- --
8 0x00 0x00 0x00 -- --
9 -- -- 0x00 -- 0x02
10 -- -- -- -- --
11 0x00 0x00 0x00 0x00 0x02
12 0x00 0x00 0x00 0x00 0x02
发出 0xb8:收到引用的 8 跳里,与原值不同的跳数 8;第一个不同出现在第 2 跳,之后各跳看到的值 [0]
发出 0x88:收到引用的 6 跳里,与原值不同的跳数 6;第一个不同出现在第 2 跳,之后各跳看到的值 [0]
发出 0x28:收到引用的 8 跳里,与原值不同的跳数 8;第一个不同出现在第 2 跳,之后各跳看到的值 [0]
发出 0x00:收到引用的 7 跳里,与原值不同的跳数 0;第一个不同出现在第 None 跳,所有跳看到的都与发出的相同
发出 0xba:收到引用的 9 跳里,与原值不同的跳数 8;第一个不同出现在第 2 跳,之后各跳看到的值 [2]
“--”表示该跳没有回超时报文。引用的是路由器收到这个包时的头部:第 N 跳看到的 TOS 已经是经过前 N−1 跳之后的值实验的这条路径上,DSCP 标记被清成 0 之后才到后面的路由器,说明 DSCP 通常只在自己管辖的网络内有意义;ECN 位没被清掉。
lo0:BK(后台) 读回 200 → 线上 TOS 0x00;BE(默认) 读回 0 → 线上 TOS 0x00;VI(视频) 读回 700 → 线上 TOS 0x00;VO(语音) 读回 800 → 线上 TOS 0x00;CTL(控制) 读回 900 → 线上 TOS 0x00 en0:BK(后台) 读回 200 → 线上 TOS 0x00;BE(默认) 读回 0 → 线上 TOS 0x00;VI(视频) 读回 700 → 线上 TOS 0x00;VO(语音) 读回 800 → 线上 TOS 0x00;CTL(控制) 读回 900 → 线上 TOS 0x00
SO_TRAFFIC_CLASS 告诉系统数据的类别,但不会替你写 DSCP;想让包带标记要自己设 IP_TOS。
net.inet.tcp.ecn: 1 主机 A:本机 SYN 标志 [SEW],SYN-ACK 标志 [S.E] → 接受 ECN;有数据的包:本机发出 1 个,IP ECN 位取值 [2];收到 2 个,ECN 位取值 [0](0 = 不支持,1 / 2 = ECT,3 = CE 已拥塞标记) 主机 B:本机 SYN 标志 [SEW],SYN-ACK 标志 [S.E] → 接受 ECN;有数据的包:本机发出 3 个,IP ECN 位取值 [0, 2];收到 2 个,ECN 位取值 [0](0 = 不支持,1 / 2 = ECT,3 = CE 已拥塞标记) 主机 C:本机 SYN 标志 [SEW],SYN-ACK 标志 [S.E] → 接受 ECN;有数据的包:本机发出 1 个,IP ECN 位取值 [2];收到 1 个,ECN 位取值 [0](0 = 不支持,1 / 2 = ECT,3 = CE 已拥塞标记) 主机 D:没有连通 主机 E:本机 SYN 标志 [SEW],SYN-ACK 标志 [S.E] → 接受 ECN;有数据的包:本机发出 1 个,IP ECN 位取值 [2];收到 1 个,ECN 位取值 [0](0 = 不支持,1 / 2 = ECT,3 = CE 已拥塞标记) ECN 位含义:00 不支持 ECN;10 = ECT(0) 和 01 = ECT(1) 表示发送方支持;11 = CE 表示路径上的路由器在队列拥塞时打了标记,而没有丢包
TCP 主动连接时 SYN 带 ECE + CWR,对方回只带 ECE 的 SYN-ACK 表示接受;之后双方的数据包 IP 头里设 ECT(0),路由器拥塞时可以改成 CE 代替丢包。这个实验看到了协商成功和 ECT(0),没有看到 CE。
客户端(IP_TOS=0xb8) 的 FIN:1 个包,TOS 字节取值 ['0xb8'] 客户端(IP_TOS=0xb8) 的 SYN:1 个包,TOS 字节取值 ['0xb8'] 客户端(IP_TOS=0xb8) 的 带数据/ACK:4 个包,TOS 字节取值 ['0xb8'] 服务端(没设 TOS) 的 FIN:1 个包,TOS 字节取值 ['0x00'] 服务端(没设 TOS) 的 SYN:1 个包,TOS 字节取值 ['0x00'] 服务端(没设 TOS) 的 带数据/ACK:4 个包,TOS 字节取值 ['0x00']
IP_TOS 按套接字设置,连接的另一端不会继承;TCP 的 SYN、数据、FIN 都带这个值。