在工作中,虽然已经了解了 OSI 七层协议和TCP/IP 四层协议。但是对于网络故障问题,有时还是不知从何处查起,甚至对各层对应的排查工具及用法很模糊。遇到深一点的问题,就卡壳了。
虽然TCP/IP 四层协议 和 三次握手和四次挥手属于面试八股文,必备的问题。我自己也是背过的,但是真正的理解却很少了。只是做到知其然,而又不知其所以然。
在介绍七层协议对应的排查工具之前,我们先了解下一些基本的概念。
报文、帧、分组、段、数据包,这些术语是同一个东西吗?
网络传输协议有HTTP ,这个表示不加密。在此之上吗,加入了TLS ,成为 HTTPS,表示加密传输协议。在七层中,第五层和第六层,分别代表TLS 的会话保持功能和数据加解密功能。如下图:
在一些技术文档,特别是 Wireshark 相关的文档中,“TCP 流”是一个很常见的词汇。它是什么意思呢?为什么叫“流”,难道跟水有关吗?
其实,这里的 TCP 流,就是英文的 TCP Stream。Stream 这个词有“流”的意思,也有“连续的事件”这样一个含义,所以它是有前后、有顺序的,这也正对应了 TCP 的特性
TCP 流,就是英文的 TCP Stream。Stream 这个词有“流”的意思,也有“连续的事件”这样一个含义,所以它是有前后、有顺序的,这也正对应了 TCP 的特性。
在具体的网络报文层面,一个 TCP 流,对应的就是一个五元组:传输协议类型、源 IP、源端口、目的 IP、目的端口。比如,今天你访问了极客时间网站,那么你这次的 TCP 流就可能就是这样一个五元组:
(TCP, your_ip, your_port, geekbang_ip, 443)
一个 IP 报文,包含了所有这五个元素,所以 Wireshark 在解析抓包文件时,自然就能通过五元组知道每个报文所属的 TCP 流了。这也是为什么我们可以在 Wireshark 里,用 Follow TCP Stream 的方法,找到报文所在的 TCP 流。
应用层排查工具也有很多,这里举例HTTP 应用的排查工具
在主流的浏览器是 Google 的 Chrome,它本身就内置了一个开发者工具。在 Chrome 界面里按下 F12,或者你是苹果系统的话,还可以按下组合键 option + command + I,启动开发者工具。
比如有用户报告死活访问不了你的网站,但是你很清楚这个网站的域名对应了很多 IP 地址,你怎么知道用户连的是哪个 IP 呢?
你可以这样做:让客户启用开发者工具,在 Network 页找到主页对象,在它的 Headers 部分,就能看到 Remote address,这里的 IP 就是当前连接的 IP,比如下面这样:
不过有句成语叫“刻舟求剑”,因为 DNS 解析的关系,你很可能下次重连就不是这个 IP 了,所以每次都应该重新确认一下这个信息。
这个技巧,在排查公网的访问问题的时候特别有用。要知道,现在流量大一点的网站都已经上了 CDN,那就必然在全国乃至全球各地,有少则数十个、多则数百个 CDN 终端节点,在给访问者提供就近的服务。如果有人说他访问不了某个站点了,那么请一定让他用开发者工具,找到他连的远程 IP,然后你再根据这个信息展开排查工作。
访问页面感觉很慢,那么可以借助开发者工具的时间统计功能,找到耗时较高的 HTTP 资源对象,再针对性排查。比如我觉得访问https://www.hao123.com/很慢,那么可以先打开开发者工具,然后访问站点,等全部加载完成后,到 Network 页查看这些 HTTP 对象的加载时间。
这个办法只能排查到是哪个资源对象耗时比较长,但更进一步的排查,比如“为什么这个对象的加载时间比别的对象长”这个问题,开发者工具就难以回答了。关于这个问题,我们会用到抓包分析这把“手术刀”,来根本性地排查这类问题。
有时候我们的 Cookie 过期了,导致无法正常登录站点,那么可以打开开发者工具,到 Application 页,找到 Storage -> Cookie,把对应的条目清除。这样下次你再访问这个站点,就已经“洗心革面”了。对站点来说,你就是一次新的访问,可以生成一次新的 Cookie 了。
当然也可以通过删除浏览器缓存的问题删除,但是会删除的是所有站点的cookie,这未必
是我们想要的。
前面有所提及,TLS 是属于这一层的。所以我们排查主要是围绕证书本身做检查
关于 TLS 握手、密钥交换、密文传输等方面的排查,还是需要用 tcpdump 和 Wireshark 来做
比如,我们可以直接看到 TLS 握手阶段里,双方协商过程中各自展示的 Cipher suite,而在开发者工具里,我们只能看到协商完成后的选择。
我们有 telnet、nc 这两个常规工具。比如 telnet,nc,wget:
root@juzi:~# telnet www.baidu.com 443
Trying 103.235.46.40...
Connected to www.wshifen.com.
Escape character is '^]'.
root@juzi:~# nc -w 2 -zv www.baidu.com 443
Connection to www.baidu.com (180.101.49.14) 443 port [tcp/https] succeeded!
# 这个适合内网之中没有工具的情况下使用,会下载网站的主页
root@juzi:~# wget www.baidu.com 443
--2022-11-26 19:21:40-- http://www.baidu.com/
Resolving www.baidu.com (www.baidu.com)... 180.101.49.13, 180.101.49.14
Connecting to www.baidu.com (www.baidu.com)|180.101.49.13|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2381 (2.3K) [text/html]
Saving to: ‘index.html’
index.html 100%[=============================================================================================>] 2.33K --.-KB/s in 0s
2022-11-26 19:21:41 (542 MB/s) - ‘index.html’ saved [2381/2381]
--2022-11-26 19:21:41-- http://443/
Resolving 443 (443)... 0.0.1.187
Connecting to 443 (443)|0.0.1.187|:80...
#命令安装
yum install -y net-tools
root@juzi:~# netstat -ant
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 127.0.0.1:42269 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN
tcp 0 96 192.168.221.130:22 192.168.221.1:52052 ESTABLISHED
tcp6 0 0 ::1:6010 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:631 :::* LISTEN
#命令安装
yum install -y epel-release && yum install -y iftop
iftop 参考链接
通过找出最费流量的 IP 和端口号这一具体实例,来演示 iftop 强大的功能。
$ iftop -i ens33 -nNB -m 10M
按下 L 参数直接显示进度条,方便人类阅读。
按下 T 显示总量
按下 3,根据最近 40s 统计排序
按下 t,发送和接受合成一行
多按几次 B,查看最近 2s、10s、40s 的统计
用 netstat 除了可以获取实时连接状况,还可以获取历史统计信息。比如,你怀疑一台机器的网络很不稳定,除了用 ping 做简单的测试,你还可以用 netstat -s 来获取更加详细的统计信息。比如,其中的 TCP 丢包和乱序计数值,就能帮助你判断传输层的状况。
watch --diff netstat -s
#这个命令会把发生变化的数值进行高亮,方便我们查看
netstat -s 各项参数意义
参数较多,如果仅仅查丢包率,建议使用ping
参考链接
在这一层,除了可以直接用 ping 这个非常简便的工具以外,你还应该掌握另外两个命令,它们能提供更为强大的排查能力,它们就是 traceroute 和 mtr。
#安装
apt-get install traceroute
traceroute 也有一个明显的不足:它不能对这个路径做连续多次的探测。
mtr 来获取更加全面和动态的链路状态信息了。
route 查看路由表
netstat -r 也可查看路由表
mtr www.baidu.com -r
第一列:HOST,显示IP地址或者主机名。
第二列:Loss%,这个结点的丢包率。
第三列:Snt,发送包的数量。
第四列:Last,最近一次的延时,单位是毫秒ms。
第五列:Avg,平均延时,单位是毫秒ms。
第六列:Best,最低延时,单位是毫秒ms。
第七列:Wrst,最高延时,单位是毫秒ms。
第八列:StDev,标准偏差。
MTR 报告,我们一般看两样东西:丢包率和延迟。从上图中可以看到,
有很多跳显示为 ??? (超时),并且丢包率为100%。这可能是家用
路由器或者运营商的路由器没有正确配置导致的,但是数据还是正常
传递的,看最后跳,丢包率为 0%,说明数据包全部到达目标主机。
导致丢包率主要有三个原因:
1、运营商限制 ICMP 速率。一些运营商会设置优先级,ICMP 包的优先级比较低,可能会被小部分丢弃。
2、ICMP 包在返回过程中出现丢包。数据包正常到达目的地,由于走不同的返程路由,在返回过程中出现
丢包。因此,在遇到问题时,最好在两个方向收集 MTR 报告。
3、其他的网络故障。
判断各区域是否存在异常,并根据各区域的情况分别处理。
区域 A:客户端本地网络,即本地局域网和本地网络提供商网络。针对该区域异常,客户端本地网络相关节点问题,请对本地网络进行排查分析;本地网络提供商网络相关节点问题,请向当地运营商反馈。
区域 B:运营商骨干网络。针对该区域异常,可根据异常节点 IP 查询归属运营商,然后直接或通过阿里云售后技术支持,向相应运营商反馈问题。
区域 C:目标服务器本地网络,即目标主机归属网络提供商网络。针对该区域异常,需要向目标主机归属网络提供商反馈问题
这一层离应用层已经很远了,一般来说是专职的网络团队在负责。如果这一层有问题,就会直接体现在网络层表现上面,比如 IP 会有丢包和延迟等现象,然后会引发传输层异常(如丢包、乱序、重传等)。所以,一个稳定的数据链路层乃至物理层,是网络可靠性的基石。
想查看这两层的状况,可以用 ethtool 这个工具
它的原理,是网卡驱动会到内核中注册 ethtool 回调函数,然后我们用 ethtool 命令就可以查看这些信息了。由于信息是由网卡驱动提供的,所以十分“接地气”。
如果你在传输层和网络层的排查工具上,已经看到明确的链路不稳定的信息,那就直接找网络团队去处理吧。
ethtool 参考链接
sudo apt install -y ethtool
root@juzi:~# ethtool -S ens33
NIC statistics:
rx_packets: 354294
tx_packets: 307035
rx_bytes: 306349191
tx_bytes: 90769830
rx_broadcast: 0
tx_broadcast: 0
rx_multicast: 0
tx_multicast: 0
rx_errors: 0
tx_errors: 0
tx_dropped: 0
multicast: 0
collisions: 0
rx_length_errors: 0
rx_over_errors: 0
rx_crc_errors: 0
rx_frame_errors: 0
rx_no_buffer_count: 0
rx_missed_errors: 0
tx_aborted_errors: 0
tx_carrier_errors: 0
tx_fifo_errors: 0
tx_heartbeat_errors: 0
tx_window_errors: 0
tx_abort_late_coll: 0
tx_deferred_ok: 0
tx_single_coll_ok: 0
tx_multi_coll_ok: 0
tx_timeout_count: 0
tx_restart_queue: 0
rx_long_length_errors: 0
rx_short_length_errors: 0
rx_align_errors: 0
tx_tcp_seg_good: 674
tx_tcp_seg_failed: 0
rx_flow_control_xon: 0
rx_flow_control_xoff: 0
tx_flow_control_xon: 0
tx_flow_control_xoff: 0
rx_long_byte_count: 306349191
rx_csum_offload_good: 353010
rx_csum_offload_errors: 0
alloc_rx_buff_failed: 0
tx_smbus: 0
rx_smbus: 0
dropped_smbus: 0
telnet 的握手SYN 包发出去,但对端一直没有回复SYN+ACK,导致telnet的connect()无法成功返回,造成挂起.
这个需要抓包去查看:
1. 客户端抓包中有发出SYN,但在服务端抓包中没收到SYN,一般是客户端去往服务端的问题,检查这个方向上的网络设备;虚拟化情况下,也可能是客户端或者服务端VM/pod所在的hypervisor上的virtual switch等环节做了“手脚”,这些也在广义的网络设备范畴内,也要查;
2. 在服务端抓包中,发现SYN包进来了,但服务端没回SYN+ACK,那就检查本地是否有拦截(比如iptables)、端口监听是否正常、系统资源是否正常、应用的网络部分的代码是否正常、应用层的network syscall的返回是否正常;
3. 在服务端抓包中,发现SYN包进来了,自己也回了SYN+ACK,但客户端抓包中没收到SYN+ACK,推进方法跟1类似。
在1的服务端的入方向,tcpdump抓包发生在iptables规则起作用之前,所以如果SYN/SYN+ACK已经来了,tcpdump就能看到SYN,但iptables接着做拦截的话,这个连接也还是无法建立(跟网络上丢失的效果一致),这时候虽然见着了SYN但协议栈没回复SYN+ACK,却并不是协议栈的问题(而是iptables拦截导致)。这个分析也适用于3的客户端入方向。
可能没有涵盖所有情况,先想到这些:) 防火墙的话,会引起1和3,当然防火墙也会引起别的问题,后面继续学习
“挂起”就是程序暂时没有响应,既不前进(成功)也不后退(报错失败)。telnet如果握手成功,是会进入正常提示符的,而挂起则不会,也意味着连接不成功(持续等待服务端的回应)
不太懂这个问题,有会的同学可以讨论下
下面是看别人的答案
traceroute UDP探测时,使用一个大于30000的端口号,服务器在收到这个数据包的时候会返回一个端口不可达的ICMP错误信息,客户端通过判断收到的错误信息是TTL超时,还是端口不可达来判断数据包是否到达目标主机。如收到超时则表示未得到对端主机应答,属于不通,收到端口不可达,则得到了对端主机的错误应答,属于通过
推荐链接