Skip to content

剖析首部字段

TCP首部字段

序列号(sequence number)

IP数据包是无序的,在TCP层面用序列号来保证顺序

  • TCP协议是面向字节流的,序列号是本报文段第一个字节的序号。
  • 序列号是32位无符号整数,溢出后从0开始。
  • 在 SYN 报文中,序列号用于交换彼此的初始序列号,在其它报文中,序列号用于保证包的顺序。

初始序列号(Initial Sequence Number, ISN)

三次握手

其中第 2 步和第 3 步可以合并一起,这就是三次握手的过程

初始序列号是如何生成的

  1. secure_tcp_sequence_number() 函数通过源地址、目标地址、源端口、目标端口和随机因子通过 MD5 进行进行计算。但这时的计算结果还可能是相同的。所以需要第二步
  2. seq_scale()函数把上面计算的结果加入时间因子再次计算,保证不会重复

序列号绕回了怎么保证顺序

static inline bool before(__u32 seq1, __u32 seq2)
{
        return (__s32)(seq1-seq2) < 0;
}

__u32是无符号的,__s32是有符号的。

如果seq1和seq2都没有绕回时,结果显然成立。当seq2绕回时,假设seq1=0xFFFFFFFFseq2=0x02

seq1 - seq2 = 0xFFFFFFFF - 0x02 = 0xFFFFFFFD

转换成有符号数之后,0xFFFFFFFD是-3。这样seq1 before seq2也成立

确认号(Acknowledgment number, ACK)

告知对方下一个期望接收的序列号,小于此确认号的所有字节都已经收到。

关于确认号有几个注意点:

  • 不是所有的包都需要确认的
  • 不是收到了数据包就立马需要确认的,可以延迟一会再确认
  • ACK 包本身不需要被确认,否则就会无穷无尽死循环了
  • 确认号永远是表示小于此确认号的字节都已经收到

TCP Flags

一共8个,常用的有几个(当然这些标志位可以组合起来使用):

  • SYN:同步包
  • ACK:确认包
  • RST(reset):用来强制断开连接,通常是之前建立的连接已经不在了、包不合法、或者实在无能为力处理
  • FIN:通知对方我发完了所有数据,准备断开连接,后面我不会再发数据包给你了。
  • PSH(push):告知对方这些数据包收到以后应该马上交给上层应用,不能缓存起来

窗口大小

窗口大小占16位,也就是说最大的窗口是65535 字节(64KB)。为了扩大窗口,引入了缩放因子。

例如,窗口大小缩放前为 1050,缩放因子为 7,则真正的窗口大小为 1050 * 128(2^7) = 134400

可选项

可选项的格式为:

  • Kind: 1byte
  • Length: 1byte
  • value

以 MSS 为例,kind=2,length=4,value=1460

常用的可选项:

  • MSS:最大段大小选项,是 TCP 允许的从对方接收的最大报文段
  • Window Scale:窗口缩放选项