使用Wireshark研究网络协议 - TCP/IP 协议问题
还是从TCP/IP协议说起
首先这篇文章并不是一篇完全的科普文章,所以这里并不打算回顾协议原理的部分(三次握手、四次挥手的协议细节就不打算啰嗦了)和协议的历史沿革(明明TCP和IP属于不同的网络层- 传输层和网络层又为什么放在一起讲?)。
下面的部分会从常见的几个网络问题开始,逐步根据现象找本质。
常见的网络问题
小包问题也叫愚笨窗口综合症(Silly Window Syndrome)
常常会有这样的情况,应用层传递给TCP传输层的数据很小,要比TCP(20Bytes)+ IP(20Byte)头部的大小还要小,这样其实是一种浪费,我们把这样的问题叫愚笨窗口综合症,也叫小包问题。
延迟确认(Delayed ACK) 与 Nagle 算法
延迟确认是一种常见的TCP交互式场景策略,常见的例子(愚笨窗口综合症)就是SSH与远程服务器建立连接之后在命令行输入的每一个字母在发送方打开延迟确认的TCP选项之后,发送方发给接收方的网络包中如果除了确认收到报文之外没有什么信息需要回复给发送方,那么接收方就会等待一个超时的时间(TCP协议规定这个值应当小于500ms rfc1122,不同的操作系统中这个值有所不同)之后在回复确认报文。本质上是通过在网络信道中减少网络包的发送来优化网络。
Nagle算法是通过在发送方维护一个“buffer”,将发送包体大小积累到一个MSS之后发送网络包。
1 | if there is new data to send then |
本质上也是通过在信道中减少网络包的发送数量来优化网络传输。
听起来两种机制都是没有什么问题的,都是在解决特定环境下的特定问题。但是如果发送方开启了Nagle算法与此同时接收方也开启了延迟确认,在发送方出现write-write-read这样的报文组合的时候就有可能出现等待到一个延迟确认超时时间后的网络等待。
如何解决延迟确认与Nagle算法冲突的问题
两种思路:
- 在应用层,避免出现 write-wirte-read这样的网络调用
- 在传输协议层,在发送方关闭Nagle算法机制或者在接收方关闭DelayACK
对于关闭DelayedACK:
- 可以使用
TCP_QUICKACK
- 如果是RHRL,可以使用下面的命令修改
tcp_delack_min
的值(默认值为 40ms)
1 | # echo 1 > /proc/sys/net/ipv4/tcp_delack_min |
RHEL - realtime tuning - Reduce TCP Delayed ack timeout
对于关闭 Nagle 算法:
在TCP连接选项中选择 TCP_NO_DELAY
网络包重传
协议中的重传场景:
超时重传
出现拥塞,当发送方的原包发出后经过一个RTO的超时等待时间后就会重传这个网络包。
超时重传之后,拥塞窗口的大小也需要调整,协议中建议 ssthresh = max ( FlightSize / 2, 2MSS)。
快速重传
如果接收方收到一组乱序的网络包,按照协议它应当连续发送3个重复ack,当发送方收到这样的ACK后需要启动快速重传机制。
具体细节详见下面wireshark抓包分析截图:
网络包乱序
接收方收到乱序包需要根据协议发送重复的3个ACK确认包,随后发送方会有快速重传的机制将接收方的ACK包重传,而无视RTO的超时限制。
细节参见上方快速重传Wireshark抓包分析截图。
网络传输慢速
慢启动与网络拥塞点
计划使用FTP协议传输大文件,进行网络抓包分析,得出类似下面的 time/sequence graph(steven):
// TODO
TCP发送窗口
LRO(Large Recieve offload)
大量处于TIME_WAIT状态的连接
现象
远程登录服务器之后使用 netstat -nlp | head -n 11
命令查看TCP协议中有大量处于TIME_WAIT状态的连接
为什么会有TIME_WAIT这个状态
根据RFC-1122协议中关于TCP关闭连接的规定(RFC-1122 Closing a Connection)
消息发送方在主动关闭连接,收到消息接收方的ACK和FIN消息之后就会进入这个TIME_WAIT
状态,同时会等待2 * MSL
时间timeout之后会关闭连接,进入ClOSED
的状态。
这个MSL
在协议中的约定是2分钟(2 minutes),不同的实现下是不同的值。
以CentOS为例,利用cat /proc/sys/net/ipv4/tcp_fin_timeout
查看得知2 * MSL
,该值的默认值为60
s
如何解决
大量处于 TIME_WAIT 状态的连接,它们会占用大量内存和端口资源。这时,我们可以优化与 TIME_WAIT 状态相关的内核选项,比如采取下面几种措施。(注: 一定确定该修改项在请求发起方-“客户端”进行):
- 增大处于 TIME_WAIT 状态的连接数量
net.ipv4.tcp_max_tw_buckets
,并增大连接跟踪表的大小net.netfilter.nf_conntrack_max
。
内核选项 | 默认值 | 参考值 |
---|---|---|
net.ipv4.tcp_max_tw_buckets | 5000 | 1048576 |
net.netfilter.nf_conntrack_max | 1048576 |
- 减小
net.ipv4.tcp_fin_timeout
和net.netfilter.nf_conntrack_tcp_timeout_time_wait
,让系统尽快释放它们所占用的资源。
内核选项 | 默认值 | 参考值 |
---|---|---|
net.ipv4.tcp_fin_timeout | 60 | 15 |
net.netfilter.nf_conntrack_tcp_timeout_time_wait | 30 |
- 开启端口复用
net.ipv4.tcp_tw_reuse
。这样,被 TIME_WAIT 状态占用的端口,还能用到新建的连接中。
内核选项 | 默认值 | 参考值 |
---|---|---|
net.ipv4.tcp_tw_reuse | 0 | 1 |
- 增大本地端口的范围
net.ipv4.ip_local_port_range
。这样就可以支持更多连接,提高整体的并发能力。
内核选项 | 默认值 | 参考值 |
---|---|---|
net.ipv4.ip_local_port_range | 32768 60999 | 10000 65000 |
- 增加最大文件描述符的数量。你可以使用
fs.nr_open
和fs.file-max
,分别增大进程和系统的最大文件描述符数;或在应用程序的 systemd 配置文件中,配置 LimitNOFILE ,设置应用程序的最大文件描述符数。
内核选项 | 默认值 | 参考值 |
---|---|---|
fs.nr_open | 1048576 |
SYN-flood
什么是 SYN FLOOD
我们都知道,TCP 建立连接的三次握手机制,如果客户端是别有用心的黑客,在发送第一个SYN之后就不再回复ACK,这样会造成大量的连接处于半连接状态,如果一直持续不断的SYN不回复ACK,会造成大量的资源消耗、占满整个syn队列让正常的业务流量进不来,从而达到搞垮服务的目的。
如何解决 SYN FlOOD 问题
其实SYN FLOOD是协议栈的设计漏洞,我们可以通过下面这些措施进行优化:
- 增大 TCP 半连接的最大数量
net.ipv4.tcp_max_syn_backlog
,或者开启 TCP SYN Cookiesnet.ipv4.tcp_syncookies
,来绕开半连接数量限制的问题(注意,这两个选项不可同时使用)。
内核选项 | 默认值 | 参考值 |
---|---|---|
net.ipv4.tcp_max_syn_backlog | 1024 | 16384 |
net.ipv4.tcp_syncookies | 1 | 1 |
- 减少 SYN_RECV 状态的连接重传 SYN+ACK 包的次数
net.ipv4.tcp_synack_retries
。
内核选项 | 默认值 | 参考值 |
---|---|---|
net.ipv4.tcp_synack_retries | 2 | 1 |
WireShark 如何解决
查看超时重传的方法
Analysis -> Expert information
Note 类信息中Summary 部分为 “this frame is a (suspected) retransmission “
使用Wireshark自动分析
- 查看分析的摘要信息
Analysis -> Expert information
- 选定需要查看的TCP连接,查看TCP stream Graph 等统计信息
Statistics -> TCP Stream Graph
Time-Sequence Graph(steven)
- 查看TCP 流图
Statistics -> Flow Graph
Flow type 中选择: TCP Flows
有用的几个WireShark配置
- WireShark中列出超过200ms的ack报文(查询疑似延迟确认报文)
1 | tcp.analysis.ack_rtt>0.1 and tcp.len==0 |
常用的几个网络命令
- 使用ICMP发送指定大小的网络包(macOS)
1 | ping <ip_address> -s <packa_size_of_IP_Package_content> -D -c <repeat_time> |
其中 -D 参数表示 在 IP 包中flag设置中 DF(Don’t Fragment) 标志
常见名词解释
MSS(Maximum Segment Size)
TCP的3次握手会将双方的MSS值告诉对方,将MSS值加上TCP头和IP头的长度就知道了下面的MTU的大小。
MTU(Maximum Trasmission Unit)
MTU = MSS + TCP Head length(一般是20Bytes) + IP Head leagth(20Bytes)
通常的大多数网络中MTU的大小在 1500 Bytes,而开启了巨帧(Jumbo Frame)的网络MTU的大小在 9000 Bytes。
LSO(Large Segmentation Offload)
LRO(Large Reciever Offload)
RTO(Retransmission TimeOut)
发送端从丢包到重传包之间经过的超时时间,这个数值在一些OS中提供方法进行设置,RTO设置数值该为多少有在协议中由Karn 算法以及 Jacobson 算法 rfc1122 - Retransmission Timeout Calculation计算决定。
SACK(Selective Acknowlegement)
在Sliding-Windows ARQ机制中 有一种纠错的机制是Selective Repeate ARQ (也叫 Selective Rejection),是在接收方打开Selective Acknowledge 选项后的一种旨在提升纠错效率的机制。
更多参考
网上链接
Wireshark 官方wiki
Wireshark 抓包样例
酷壳 - TCP 的那些事儿(上)
酷壳 - TCP 的那些事儿(下)
书籍
Wireshark网络分析就这么简单
wireshark网络分析的艺术
TCP/IP详解 卷1:协议
TCP/IP详解 卷2:实现