剖析首部字段
序列号(sequence number)
IP数据包是无序的,在TCP层面用序列号来保证顺序
- TCP协议是面向字节流的,序列号是本报文段第一个字节的序号。
- 序列号是32位无符号整数,溢出后从0开始。
- 在 SYN 报文中,序列号用于交换彼此的初始序列号,在其它报文中,序列号用于保证包的顺序。
初始序列号(Initial Sequence Number, ISN)
其中第 2 步和第 3 步可以合并一起,这就是三次握手的过程
初始序列号是如何生成的
secure_tcp_sequence_number()
函数通过源地址、目标地址、源端口、目标端口和随机因子通过 MD5 进行进行计算。但这时的计算结果还可能是相同的。所以需要第二步seq_scale()
函数把上面计算的结果加入时间因子再次计算,保证不会重复
序列号绕回了怎么保证顺序
static inline bool before(__u32 seq1, __u32 seq2)
{
return (__s32)(seq1-seq2) < 0;
}
__u32
是无符号的,__s32
是有符号的。
如果seq1和seq2都没有绕回时,结果显然成立。当seq2绕回时,假设seq1=0xFFFFFFFF
,seq2=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:窗口缩放选项