常用linux命令定位程序性能问题——网络命令与基础(一)

一、linux命令概览图

1.1 网络分层结构图

1.2 网络通信状态图

我们用wireshark抓包的时候可能会发现不是按照这个图来的,例如挥手的第一个FIN报文可以合并(Piggybacking)在 POST 报文里。Wireshark 的主界面还有个特点,就是当它的 Information 列展示的是应用层信息时,这个报文的 TCP 层面的控制信息就不显示了。这样会让我们以为缺少了某些标志位,可以去TCP详情部分查看。

TLS协议的通信状态图

TLS 是一种密码学协议,保证了两个端点之间的会话安全,一种最好的学习方法是使用抓包工具,捕获网络数据包,基于这些真实的数据包能够有一些直观的感受,例如:Wireshark,它可以捕获 HTTP、TCP、TLS 等各种网络协议数据包,是我们学习的好工具。

TLS 定义了四个核心子协议:握手协议 (handshake protocol)、密钥规格变更协议 (change cipher spec protocol)、应用数据协议 (application data protocol) 和警报协议 (alert protocol),这里最主要、最复杂是"握手协议",协商对称密码就是在该协议中完成的。

握手过程图示

本文是抓取的 www.imooc.com 网站数据包,基于 TLS v1.2 协议未对数据包做解密处理。

下图展示了 HTTPS 链接建立、TLS 握手协议里参数传递、证书验证、协商对称密钥的过程,更详细的内容,下文会介绍。

1.3 tcp状态

在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.

其中,对于我们日常的分析有用的就是前面的五个字段。

它们的含义是:

URG:Urget pointer is valid (紧急指针字段值有效)

SYN: 表示建立连接

FIN: 表示关闭连接

ACK: 表示响应

PSH: 表示有 DATA数据传输

RST: 表示连接重置。

1.4 应用程序如何跟内核的TCP协议栈交互

客户端发起连接,依次调用的是这几个系统调用:

  • socket()
  • connect()

服务端监听端口并提供服务,那么要依次调用的就是以下几个系统调用:

  • socket()
  • bind()
  • listen()
  • accept()

服务端的用户空间程序要使用 TCP 连接来接收请求,首先要获得上面最后一个接口,也就是 accept() 调用的返回。而 accept() 调用能成功返回的前提呢,是正常完成三次握手。

你看,这次客户端在握手中的第三个包不是 ACK,而是 RST(或者 RST+ACK),握手不是失败了吗?那么自然地,这次失败的握手,也不会转化为一次有效的连接了,所以 Nginx 都不知道还存在过这么一次失败的握手。

当然,在客户端日志里,是可以记录到这次握手失败的。这是因为,客户端是 TCP 连接的发起方,它调用 connect(),而 connect() 失败的话,其 ECONNRESET 返回码,还是可以通知给应用程序的。

二、TcpDump

2.1 TcpDump工作原理

TcpDump:我们先来认识大名鼎鼎的 tcpdump。1988 年,劳伦斯伯克利国家实验室的四位工程师编写出了 tcpdump 这个殿堂级的工具。这个实验室呢,也很值得我们尊敬。这里涌现过13 位诺贝尔奖获得者,其中包括 1997 年获得物理学奖的华人巨匠朱棣文,可见这是多么耀眼的一块科学圣地。这个地方能做出开创性的技术,确实一点都不令人意外。tcpdump 可以工作在各种 Unix 类的操作系统上,包括 Linux、FreeBSD、macOS、Solaris 等,也是目前使用最为广泛的抓包工具之一。但是 tcpdump 要过滤报文的话,还要依赖一个底层能力:BPF

BPF 全称是 Berkeley Packet Filter(也叫 BSD Packet Filter),它是 tcpdump 等抓包工具的底层基础。在 BPF 出现之前,虽然各家操作系统都有自己的抓包工具,但也都有这样或那样的不足。比如,有些系统把所有网络报文一股脑儿全都塞给用户空间程序,开销非常大;而有些系统虽然有报文过滤功能,但是工作很不稳定。为了解决这些问题,1992 年,也还是在劳伦斯伯克利国家实验室,当初 tcpdump 的两个作者史蒂文·麦克凯恩(Steven McCanne)和范·雅各布森(Van Jacobson)发表了关于 BPF 的论文,它以一种新的基于寄存器的虚拟机方式,实现了高效稳定的报文过滤功能。从此以后,抓包技术这棵大树有了一个甚为强大的根基,而构建在 BPF 之上的 libpcap、tcpdump 等不断枝繁叶茂,进一步使得抓包工作变得方便、稳定,我们这些凡夫俗子才好在这棵大树底下,下棋乘凉。

libpcap:BPF 实现了抓包虚拟机,但它是如何被用户空间程序使用的呢?于是,libpcap 出现了,它提供了 API 给用户空间程序(包括 tcpdump、Wireshark 等),使得后者能方便地调用 BPF 实现抓包过滤等功能。也就是说,libpcap 是 BPF 的一层 API 封装。

2.2 抓包文件类型

libpcap:这个是 libpcap 的格式,也是 tcpdump 和 Wireshark 等工具默认支持的文件格式。pcap 格式的文件中除了报文数据以外,也包含了抓包文件的元信息,比如版本号、抓包时间、每个报文被抓取的最大长度,等等。

cap:cap 文件可能含有一些 libpcap 标准之外的数据格式,它是由一些 tcpdump 以外的抓包程序生成的。比如 Citrix 公司的 netscaler 负载均衡器,它的 nstrace 命令生成的抓包文件,就是以.cap 为扩展名的。这种文件除了包含 pcap 标准定义的信息以外,还包含了 LB 的前端连接和后端连接之间的 mapping 信息。Wireshark 是可以读取这些.cap 文件的,只要在正确的版本上。

pcapng:pcap 格式虽然满足了大部分需求,但是它也有一些不足。比如,现在多网口的情况已经越来越常见了,我们也经常需要从多个网络接口去抓取报文,那么在抓包文件里,如果不把这些报文跟所属的网口信息展示清楚,那我们的分析,岂不是要张冠李戴了吗?为了弥补 pcap 格式的不足,人们需要有一种新的文件格式,pcapng 就出现了。有了它,单个抓包文件就可以包含多个网络接口上,抓取到的报文了。

我们可以看到,上图中右边的 pcapng 格式是包含报文的网络接口信息的,而左边的 pcap 就没有。

当然,pcapng 还有很多别的特性,比如更细粒度的报文时间戳、允许对报文添加注释、更灵活的元数据,等等。如果你是用版本比较新的 Wireshark 和 tshark 做抓包,默认生成的抓包文件就已经是 pcapng 格式了。

2.2 TcpDump命令

arduino 复制代码
我们可以用 host {对端 IP} 作为抓包过滤条件
tcpdump host 10.10.10.10

抓取某个端口的流量,比如,我们想抓取 SSH 的流量,那么可以这样
tcpdump port 22

抓取tcp里面的具体内容
tcpdump -i any -X port 80

抓取与47.94.129.219 ip通信的任意包
tcpdump -i any host 47.94.129.219

抓取与47.94.129.219 ip通信的80端口的包
tcpdump -i any host 47.94.129.219 and port 80

参数表:

  • -w 文件名,可以把报文保存到文件;
  • -c 数量,可以抓取固定数量的报文,这在流量较高时,可以避免一不小心抓取过多报文;
  • -s 长度,可以只抓取每个报文的一定长度,后面我会介绍相关的使用场景;
  • -n,不做地址转换(比如 IP 地址转换为主机名,port 80 转换为 http);
  • -v/-vv/-vvv,可以打印更加详细的报文信息;
  • -e,可以打印二层信息,特别是 MAC 地址;
  • -p,关闭混杂模式。所谓混杂模式,也就是嗅探(Sniffing),就是把目的地址不是本机地址的网络报文也抓取下来。

2.2.1 过滤报文

最近我们有个实际的需求,要统计我们某个 HTTPS VIP 的访问流量里,TLS 版本(现在主要是 TLS1.0、1.1、1.2、1.3)的分布。为了控制抓包文件的大小,我们又不想什么 TLS 报文都抓,只想抓取 TLS 版本信息。这该如何做到呢?要知道,针对这个需求,tcpdump 本身没有一个现成的过滤器。

其实,BPF 本身是基于偏移量来做报文解析的,所以我们也可以在 tcpdump 中使用这种偏移量技术,实现我们的需求。下面这个命令,就可以抓取到 TLS 握手阶段的 Client Hello 报文:

css 复制代码
tcpdump -w file.pcap 'dst port 443 && tcp[20]==22 && tcp[25]==1'

我给你解释一下上面的三个过滤条件。

  • dst port 443:这个最简单,就是抓取从客户端发过来的访问 HTTPS 的报文。
  • tcp[20]==22:这是提取了 TCP 的第 21 个字节(因为初始序号是从 0 开始的),由于 TCP 头部占 20 字节,TLS 又是 TCP 的载荷,那么 TLS 的第 1 个字节就是 TCP 的第 21 个字节,也就是 TCP[20],这个位置的值如果是 22(十进制),那么就表明这个是 TLS 握手报文。
  • tcp[25]==1:同理,这是 TCP 头部的第 26 个字节,如果它等于 1,那么就表明这个是 Client Hello 类型的 TLS 握手报文。

下面是抓包文件里的样子:

这里你可能会有疑问:上面的图里,TCP[20]的位置的值不是 16 吗?其实,这个是十六进制的 16,换算成十进制,就是 22 了。

报文偏移量及其含义

过滤出 TCP RST 报文

tcpdump 也预定义了一些相对方便的过滤器。比如我们想要过滤出 TCP RST 报文,那么可以用下面这种写法,相对来说比用数字做偏移量的写法,要更加容易理解和记忆:

scss 复制代码
tcpdump -w file.pcap 'tcp[tcpflags]&(tcp-rst) != 0'

如果是用偏移量的写法,会是下面这样:

arduino 复制代码
tcpdump -w file.pcap 'tcp[13]&4 != 0'

抓包时显示报文内容

tcpdump port 80 -X

如何读取抓包文件

这个比较简单,tcpdump 加上 -r 参数和文件名称,就可以读取这个文件了,而且也可以加上过滤条件。比如:

scss 复制代码
tcpdump -r file.pcap 'tcp[tcpflags] & (tcp-rst) != 0'

如何过滤后转存?

有时候,我们想从抓包文件中过滤出想要的报文,并转存到另一个文件中。比如想从一个抓包文件中找到 TCP RST 报文,并把这些 RST 报文保存到新文件。那么就可以这么做:

scss 复制代码
tcpdump -r file.pcap 'tcp[tcpflags] & (tcp-rst) != 0' -w rst.pcap

如何让抓包时间尽量长一点?

前面我提到过 -s 这个长度参数,它的使用场景其实就包括了延长抓包时间。我们给 tcpdump 加上 -s 参数,指定抓取的每个报文的最大长度,就能节省抓包文件的大小,也就延长了抓包时间

一般来说,帧头是 14 字节,IP 头是 20 字节,TCP 头是 20~40 字节。如果你明确地知道这次抓包的重点是传输层,那么理论上,对于每一个报文,你只要抓取到传输层头部即可,也就是前 14+20+40 字节(即前 74 字节):

tcpdump -s 74 -w file.pcap

而如果是默认抓取 1500 字节,那么生成的抓包文件将是上面这个抓包文件的 20 倍。反过来说,使用同样的磁盘空间,上面这种方式,其抓包时间可以长达默认的 20 倍!

但是,如果你还想看 TLS 甚至更上层的应用层的信息,这么做就不行了。比如下面这个抓包,我就是用了 74 字节作为每个报文的抓取长度,所以第五层开始的信息,就看不到或者看不全了。

显然,TLS 只分到了 8 个字节,信息不完整,所以 Wireshark 也无能为力,没法告诉我们这个 TLS 头部里的信息了。

那么,如果你怀疑 TLS 或者更上面的应用层也跟问题有关,我建议你就去掉 size 的限制,还是抓取全部数据为好。

这是因为,应用层头部的长度跟二到四层的情况非常不同,应用层头部可能非常大,甚至可以超过 TCP 的 MSS。比如 HTTP 头部,小的话可能只有几十个字节,大的话可能要几十个 KB,也就是好多个 TCP segment 才放得下。这种时候,要是我们还在纠结如何节省抓取的报文长度,却放过了可能真正对排查有关键价值的数据,就得不偿失了。

三、Tcptrace

不过,有时候我们并不方便用 Wireshark 打开抓包文件做分析,比如抓包的机器不允许向外传文件,也就是可能只能在这台机器上做分析。我们可以用 tcpdump -r 的方式,打开原始抓包文件看看:

bash 复制代码
tcpdump -r test.pcap | head -10

报文都是按时间线原样展示的,缺乏逻辑关系,是不是难以组织起有效的分析?比如,要搞清楚里面有几条 TCP 连接都不太容易。这时候怎么办呢?

其实,还有一个工具能帮上忙,它就是 tcptrace。它不能用来抓包,但是可以用来分析。知道这个工具的人不算很多,但其实 tcptrace 是一个挺"古老"的工具了。在 Wireshark 工具集(Wireshark 图形界面和 tshark 等命令行工具)还没一统江湖的时候,tcptrace 也有其独到的价值,因为它不仅可以读取 pcap 格式的抓包文件,也可以读取 snoop 等其他格式的抓包文件。

比如下面这样,tcptrace 告诉我们,这个抓包文件里有 2 个 TCP 连接,并且是以 RST 结束的:

css 复制代码
tcptrace -b test.pcap

三、Wireshark

3.1 怎么知道抓包文件是在哪一端抓取的?

这是一个有趣的问题。尽管我们做抓包的时候很清楚是在哪端做了抓包,但是把文件传给别人后,对方就未必知道这一点,甚至我们自己过段时间也迷糊了:我上次这个抓包文件到底是在客户端上,还是服务端上抓取的?

要搞清楚这一点也很简单,我们可以利用 IP 的 TTL 属性。显然,无论是哪一端,它的报文在发出时,其 TTL 就是原始值,也就是 64、128、255 中的某一个。而对端报文的 TTL,因为会经过若干个网络跳数,所以一般都会比 64、128、255 这几个数值要小一些。

所以,我们只要看一下抓包文件中任何一个客户端报文(也就是源端口为高端口)的 TTL,如果它是 64、128 或 255,那说明这个抓包文件就是在客户端做的。反之,就是在服务端做的。

3.2 如何定位到应用层的请求和返回的报文?

在众多报文中找到应用层请求和对应的响应报文,这个任务用人工去做的话是很繁琐的。还好,用 Wireshark 就很方便。在 Wireshark 界面中,我们很容易找到请求和返回的报文。比如这样:

我们只要选中请求报文,Wireshark 就会自动帮我们匹配到对应的响应报文,反过来也一样。从图上看,应用层请求(这里是 HTTP 请求)是一个向右的箭头,表示数据是进来的方向;应用层响应是一个向左的箭头,表示数据是出去的方向。

3.3 只截到报文的一部分,这个问题有什么影响吗?

造成这个报错的原因是,当时 tcpdump 程序并不是用 Ctrl+C 等正常方式终止的,而是可能用了操作系统的 SIGKILL 信号,比如用了 kill -9 命令。这样的话,tcpdump 就被强行终止了,导致一部分被抓取的报文还在内存中,还没来得及由 tcpdump 程序正常写入到 pcap 文件里。

要避免这个报错,我们可以在停止 tcpdump 时,用正常退出的方式,比如 Ctrl+C,或者 timeout 命令,或者 kill 但不要用 -9 作为参数。

3.4 乱序一定会引起问题吗?

乱序(Out-of-Order),也是我们时常能在 Wireshark 里看到的一类现象。那么,乱序一定会引起问题吗?有一句话叫"脱离了剂量谈毒性就是耍流氓"。其实,乱序是否是问题,也取决于乱序的严重程度。

这个问题,跟实际场景的具体情况也有很大的关系,不同的操作系统以及 TCP 实现细节,都可能有挺大的差异。不过,我还是想正面回答一下这个问题。我的经验是,如果乱序报文达到 10% 以上,就是严重的传输质量问题了,甚至可能导致传输失败,或者应用层的各种卡顿、报错等症状。所以,你可以统计一下乱序包的占比,如果它超过了 10%,就要重视了。

wireshark通过时间和flag过滤特定时间内非握手和挥手阶段的RST

yaml 复制代码
# 过滤ip是10.255.252.31以及非握手阶段的RST TCP包
ip.addr eq 10.255.252.31 and tcp.flags.reset eq 1 and !(tcp.seq eq 1 or tcp.ack eq 1)

# 过滤在时间范围内ip是10.255.252.31以及非握手阶段的RST TCP包
frame.time >="dec 01, 2015 15:49:48" and frame.time <="dec 01, 2015 15:49:49" and ip.addr eq 10.255.252.31 and tcp.flags.reset eq 1 and !(tcp.seq eq 1 or tcp.ack eq 1)
ip.addr eq my_ip:过滤出源IP或者目的IP为my_ip的报文
ip.src eq my_ip:过滤出源IP为my_ip的报文
ip.dst eq my_ip:过滤出目的IP为my_ip的报文

3.5 TTL

linux类系统的TTL初始值一般是64

windows系统的TTL初始值一般是128

在外网TTL的数值可能会变化,因为你每天回家的路线可能会变。但是在内网这种情况就会少很多。

wireshark如何把某个属性加到默认展示列

你也能很清楚地看到,同样的服务端,在三次握手中(SYN+ACK 报文)的 TTL 是 59,在 导致连接中断的 RST 包里却变成了 64!显然,这个 RST 包并不是跟我们握手的那个服务 端发出的,否则 TTL 值就不会变化

3.6 如何屏蔽防火墙对客户端与服务端连接的屏蔽

我个人看法是,可以到网络层(IP 层)去寻找机会。利用 IPSec(比如 IPv6 默认启用了 IPsec),我们就获得了在第三层加密的能力。因为就连 IP 报文本身都是加密的,那么即 使防火墙要插入报文,因为它不具备密钥,所以这个报文会被接收端认为非法而被丢弃。 这样就有希望真正摆脱防火墙对传输层(TCP/UDP)的这种控制

相关推荐
憨子周1 小时前
2M的带宽怎么怎么设置tcp滑动窗口以及连接池
java·网络·网络协议·tcp/ip
ZachOn1y7 小时前
计算机网络:运输层 —— 运输层端口号
网络协议·tcp/ip·计算机网络·udp·tcp·端口号
qq_254674418 小时前
在C2M(Customer-to-Manufacturer)柔性制造模式下,算法
网络协议
橘色的喵8 小时前
工业通信协议对比:OPC-UA、Modbus、MQTT、HTTP
mqtt·网络协议·http·modbus·opc-ua·工业协议
迷途小码农零零发8 小时前
http的发展史
网络·网络协议·http
cocapop9 小时前
IP地址 与 DNS
网络协议·tcp/ip·智能路由器
CP-DD9 小时前
TCP编程API
网络·网络协议·tcp/ip
张某布响丸辣10 小时前
HTTP状态码详解
java·网络·python·网络协议·http·c#
小李学软件10 小时前
SSL 证书申请以及配置流程
网络·网络协议·ssl
企鹅chi月饼12 小时前
TCP可靠连接的建立和释放,TCP报文段的格式,UDP简单介绍
网络·网络协议·tcp/ip