网络问题排查笔记
{Back to Index}
Table of Contents
1 TCP 队列溢出
1.1 原理
Figure 1: syns/accept 队列
上图中,有两个队列:
- syns queue
- 半连接队列
- accept queue
- 全连接队列
三次握手阶段,当 server 收到 client 的 syn 后,把消息放到 syns queue 中,并回复 syn+ack 给 client ,
当 server 收到 client 的 ack 时,如果这时 accept queue 没满,那就从 syns queue 中 pop 出暂存的信息放入 accept queue 中,
否则按 net.ipv4.tcp_abort_on_overflow
设置的值执行:
net.ipv4.tcp_abort_on_overflow = 0
表示如果三次握手第三步的时候 accept queue 满了,则 server 丢弃 client 发过来的 ack 。net.ipv4.tcp_abort_on_overflow = 1
表示第三步的时候如果全连接队列满了,server 发送一个 rst 包给 client ,表示拒绝这个握手过程和这个连接
1.2 查看队列溢出信息
# netstat -s | grep -i listen 1 times the listen queue of a socket queue overflowed -> 全连接队列溢出的次数 1 SYNs to LISTEN sockets dropped -> 半连接队列溢出的次数
# ss -lnt State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:80 *:* --- |-> 全连接队列 (backlog) 最大为 128 LISTEN 0 5 *:48080 *:* - |-> 全连接队列当前使用了多少 LISTEN 0 128 *:4369 *:* LISTEN 0 128
1.3 设置队列参数
全连接队列的大小取决于 min(backlog, net.core.somaxconn)
。
其中 backlog 是在 socket 创建的时候传入的,somaxconn 是一个 OS 级别的系统参数。
半连接队列的大小取决于 max(64, net.ipv4.tcp_max_syn_backlog)
。
# cat /proc/sys/net/core/somaxconn 128 cat /proc/sys/net/ipv4/tcp_max_syn_backlog 128
2 TIME_WAIT
TIME_WAIT 对系统是有帮助的,不是多余的。
Figure 2: TCP 状态机
2.1 统计 TCP 状态
# ss -ant | awk '{print $1}' | sort | uniq -c 20 CLOSE-WAIT 233 ESTAB 26 LISTEN 1 State -> 这行是 header ,可以忽略 14 TIME-WAIT
2.2 作用
Figure 3: 上一个连接延迟的数据包被接收后,影响到新的连接
Figure 4: 最后一个 ACK 包丢失,导致对端停留在 LAST-ACK 状态,影响新连接的建立
2.3 相关参数
2.3.1 net.ipv4.tcp_timestamps
tcp_tw_reuse 和 tcp_tw_recycle 默认都是关闭的,这两个参数必须在 timestamps 打开的前提下才能生效使用,timestamps 默认都是打开的。
2.3.1.1 查看因 timestamps 而被拒绝的数据包
~$ netstat -s | grep timestamp timestamp requests: 8 282 packets rejects in established connections because of timestamp -> 282 个包被丢弃
2.3.2 net.ipv4.tcp_tw_reuse
允许将一个处于 TIME-WAIT 状态的端口重新用于新的 TCP 连接,默认为 0 ,表示关闭,其防止重复报文的原理是基于时间戳。
当复用连接后,连接的时间被更新为当前的时间,当延迟的数据达到,延迟数据的时间是小于新连接的时间, 所以,内核可以通过时间判断出,延迟的数据可以安全的丢弃掉了。
2.3.3 net.ipv4.tcp_tw_recycle
不要开启
这个配置,主要影响到了 inbound 的连接即 做为服务端角色 ,客户端连进来,服务端主动关闭了连接,TIME_WAIT 状态的 socket 处于服务端,服务端快速的回收该状态的连接。
当开启了这个配置后,内核会快速(3.5*RTO
内)回收处于 TIME_WAIT 状态的 socket 连接,
这里不再是复用了,而是之前处于 TIME_WAIT 状态的连接已经被 destroy 掉了, 新的连接,刚好是和某一个被 destroy 掉的连接使用了相同的五元组而已。
在启用该配置,当一个连接进入 TIME_WAIT 状态后,内核里会记录包括该连接对应的五元组中的对方 IP 等在内的一些统计数据, 包括从对方 IP 所接收到的最近的一次数据包时间。 当有新的数据包到达,只要时间晚于内核记录的这个时间,数据包都会被统统的丢掉。 因此,同一源 IP 主机的连接请求中的 timestamp 必须是递增的。
但是同一个源 IP 可能会是 NAT 后很多机器,这些机器 timestamp 递增性无可保证,服务器会拒绝非递增请求连接,直接导致不能三次握手。
2.3.4 net.ipv4.tcp_max_tw_buckets
调整 tcp_tw_reuse 和 tcp_tw_recycle 其实是违反 TCP 协议规定的,在服务器资源允许,负载不大的条件下,尽量不要打开,尽量选择调大该参数。
这个参数的意义是:同时保持 TIME_WAIT 套接字的最大个数。
超过这个数字那么该 TIME_WAIT 套接字将立刻被释放并在 /var/log/message
日志中打印警告信息(TCP: time wait bucket table overflow)。
3 抓取 HTTPS 数据
假设使用跳板机,且目标地址为 webexapis.com
。
跳板机上生成自签名证书
openssl genrsa 2048 >ca.key.pem ## 生成 2048 位私钥 openssl req -new -x509 -key ca.key.pem -out ca.cert.pem -days 365
跳板机上监听加密端口
LOCAL_PLAIN_PORT=48080 socat openssl-listen:443,cert=ca.cert.pem,key=ca.key.pem,verify=0,reuseaddr,fork tcp:127.0.0.1:$LOCAL_PLAIN_PORT
跳板机上监听明文端口
LOCAL_PLAIN_PORT=48080 TARGET=webapis.com socat tcp-listen:$LOCAL_PLAIN_PORT,reuseaddr,fork openssl-connect:$TARGET:443,verify=0
修改本机 /etc/hosts
<jumper_ip> webexapis.com