主要原因有两个方面:
- 很大程度上避免历史报文被下一个相同四元组的 TCP 连接接收问题(主要方面)
- 防止黑客伪造相同序列号的 TCP 报文被接收
接下来,详细说说第一点
假设每次建立 TCP 连接时,客户端和服务端的初始序列号都是从 0 开始,这种话情况下,很容易出现历史报文被下一个相同四元组的 TCP 连接接收问题
假设每次建立 TCP 连接时,客户端和服务端的初始序列号都不一样,这种情况下,大概率因为历史报文的序列号不在对方接收窗口范围内,进而很大程度上避免了历史报文被下一个相同四元组的 TCP 连接接收问题
注意是很大程度上,并不是完全避免了,因为序列号存在回绕问题,那如何完全避免呢?需要额外引入「时间戳」机制
Linux 内核参数 tcp_timestamps(/proc/sys/net/ipv4/tcp_timestamps 默认值为 1) 可以设置是否开启「时间戳」机制
时间戳也存在回绕问题,但历史报文等不到时间戳回绕就已经在网络中消失了
static inline int tcp_paws_check(const struct tcp_options_received *rx_opt,
int paws_win)
{
/* s32:32 位有符号整数类型,主要用于解决 32 位无符号整数(如时间戳、序列号)的回绕问题,
* 例如,(s32)(5(时间戳回绕) - 4294967290(接近 32 位无符号整数最大值)) == 11,
* 满足条件 <= 0,正确反映出 5 > 4294967290 的逻辑关系
*
* rx_opt->ts_recent:TSval_new
* rx_opt->rcv_tsval:TSval_new
* paws_win = 0 or 1
*/
if ((s32)(rx_opt->ts_recent - rx_opt->rcv_tsval) <= paws_win)
return 1;
...
}