网络世界中的数据包一般都是不可见的,导致在学习计算机网络的时候会觉得其非常抽象,加大了学习的难度。
自从有了网络包分析工具:tcpdump 和 Wireshark,使不可见的数据包得以可视化。
问题:tcpdump 和 wireshark 有什么区别?
tcpdump 和 wireshark 是最常用的网络抓包和分析工具,更是分析网络性能必不可少的利器。
1)tcpdump 仅支持命令行格式使用,常在 Linux 服务器中抓取和分析网络包;
2)wireshark 除了可以抓包外,还提供了可视化分析网络包的图形页面(更加直观);
所以,这两者往往是搭配使用的,先用 tcpdump 命令在 Linux 服务器上抓包,接着把抓包的文件 copy 到 windows 电脑后,用 wireshark 进行可视化分析。
当然,如果你是在 windows 上抓包,只需要用 wireshark 工具就可以了。
问题:tcpdump 在 Linux 下如何抓包?
tcpdump 提供了大量的选项以及各式各样的过滤表达式,来帮助你抓取指定的数据包,不过不要担心,只需要掌握一些常用选项和过滤表达式,就可以满足大部分场景的需要了。
tcpdump 是一款用于截取网络分组,并输出分组内容的工具,可以用于网络故障排查、分析以及实现网络安全监控。它能够捕获网络上传输的数据包,并提供了强大的过滤功能,允许用户根据特定的需求进行定制化过滤,从而只捕获需要的网络流量。tcpdump 提供了灵活的截取策略,支持针对网络层、协议、主机、网络或端口的过滤,并提供 and、or、not 等逻辑语句来帮助用户去掉无用的信息。由于其强大的功能和灵活的截取策略,tcpdump 成为类 UNIX 系统下用于网络分析和问题排查的首选工具。
tcpdump [-adeflnNOpqStvx] [-c <数据包数目>] [-dd] [-ddd] [-F <表达文件>] [-i <网络界面>] [-r <数据包文件>] [-s <数据包大小>] [-tt] [-T <数据包类型>] [-vv] [-w <数据包文件>] [输出数据栏位]
参数说明:
1)[-adeflnNOpqStvx]
2)[-c ]:-c 收到指定的数据包数目后,就停止进行倾倒操作;
3)[-dd]:-dd 把编译过的数据包编码转换成C语言的格式,并倾倒到标准输出;
4)[-ddd]:-ddd 把编译过的数据包编码转换成十进制数字的格式,并倾倒到标准输出;
5)[-F ]:-F 指定内含表达方式的文件;
6)[-i ]:-i 使用指定的网络截面送出数据包;
7)[-r ]:-r 从指定的文件读取数据包数据;
8)[-s ]:-s 设置每个数据包的大小;
9)[-tt]:-tt 在每列倾倒资料上显示未经格式化的时间戳记;
10)[-T ]:-T 强制将表达方式所指定的数据包转译成设置的数据包类型;
11)[-vv]:-vv 更详细显示指令执行过程;
12)[-w ]:-w 把数据包数据写入指定的文件;
假设我们要抓取下面 ping 的数据包:
root@BZD25521:~# ping -I eth0 -c 3 baidu.com
PING baidu.com (110.242.68.66) from 172.27.142.199 eth0: 56(84) bytes of data.
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=1 ttl=52 time=30.0 ms
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=2 ttl=52 time=29.5 ms
64 bytes from 110.242.68.66 (110.242.68.66): icmp_seq=3 ttl=52 time=101 ms
--- baidu.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 29.545/53.613/101.329/33.740 ms
要抓取上面 ping 命令的数据包,首先我们要知道 ping 的数据包是 icmp 协议,接着在使用 tcpdump 抓包的时候,就可以指定只抓 icmp 协议的数据包,具体命令如下:
tcpdump -i eth0 icmp and host 172.27.142.199 -nn
问题:上述抓包命令参数含义是什么?
1)-i eth0 表示抓取 eth0 网口的数据包;
2)icmp 表示抓取 icmp 协议的数据包;
3)host 表示主机过滤,抓取对应 IP 的数据包;
4)-nn 表示不解释 IP 地址和端口号名称;
抓包具体操作流程如下:
1)在终端一中执行 tcpdump 抓包命令,等待抓包
tcpdump -i eth0 icmp and host 172.27.142.199 -nn
执行结果如下:
2)在终端二中执行 ping 命令
ping -I eth0 -c 3 baidu.com
执行结果如下:
3)在终端一中停止抓包
Ctrl + C
执行结果如下:
从 tcpdump 抓取的 icmp 数据包,可以清楚的看到 icmp echo 的交互过程,首先发送方发起了 ICMP echo request 请求报文,接收方收到后回了一个 ICMP echo reply 响应报文,之后 seq 是递增的。
当 tcpdump 抓取到 icmp 数据包后,其输出格式如下:
时间戳 协议 源地址.源端口 > 目的地址.目的端口 网络包详细信息
tcpdump 抓包常用命令参数如下表所示:
tcpdump 使用 —— 选项类 | ||
选项 | 示例 | 说明 |
-i | tcpdump -i eth0 | 指定网络接口,默认是 0 号接口(如 eth0),any 表示所有接口 |
-c | tcpdump -c 5 | 限制要抓取的网络包个数 |
-nn | tcpdump -nn | 不解析 IP地址和端口号名称 |
-w | tcpdump -w file.pcap | 把 tcpdump 抓取到的数据包保存到 file.pcap 文件中 |
接下来,我们再来看看常用的过滤包用法,在上面的 ping 例子中,我们用的是 icmp and host 172.27.142.199,表示抓取 icmp 协议的数据包,以及源地址或目标地址为 172.27.142.199 的包。其他常用的过滤选项如下表所示:
tcpdump 使用 —— 过滤表达式类 | ||
选项 | 示例 | 说明 |
host、src host、dst host | tcpdump -nn host 172.27.142.199 | 主机过滤 |
port、src port、dst port | tcpdump -nn port 80 | 端口过滤 |
ip、ip6、arp、tcp、udp、icmp | tcpdump -nn tcp | 协议过滤 |
and、or、not | tcpdump -nn host 172.27.142.199 and port 80 | 逻辑表达式过滤 |
tcp[tcoflages] | tcpdump -nn "tcp[tcoflages] & tcp-syn != 0" | 特定状态的 tcp 包 |
从上面的介绍可以发现,虽然 tcpdump 功能强大,但其输出格式并不直观。所以,在工作中 tcpdump 只是用来抓取数据包,不用来分析数据包,而是把 tcpdump 抓取的数据包保存成 .pcap 格式的文件,接着用 wireshark 工具进行数据包可视化分析。
wireshark 除了可以抓包外,还提供了可视化分析网络包的图形页面,同时,还内置了一系列的汇总分析工具。拿上面的 ping 例子来说,我们可以使用下面的命令,把抓取的数据包保存到 ping.pcap 文件,然后使用 wireshark 工具进行可视化分析。
具体操作流程如下:
1)在终端一中执行 tcpdump 抓包命令,等待抓包
tcpdump -i eth0 icmp and host 172.27.142.199 -w /root/ping.pcap
执行结果如下:
2)在终端二中执行 ping 命令
ping -I eth0 -c 3 baidu.com
执行结果如下:
3)在终端一中停止抓包
Ctrl + C
执行结果如下:
把 ping.pcap 文件 copy 到 windows 下,然后用 wireshark 打开它,将看到如下界面:
在 Wireshark 页面里,可以更加直观的分析数据包,不仅展示各个网络包的头部信息,还会用不同的颜色来区分不同的协议,由于这次抓包只有 ICMP 协议,所以只有紫色的条目。
接着,在网络包列表中选择某一个网络包后,在其下面的网络包详情中,可以更清楚的看到,这个网络包在协议栈各层的详细信息。以编号 1 的网络包为例:
1)在数据链路层,可以看到 MAC 包头信息,如:源 MAC 地址和目标 MAC 地址等字段;
2)在 IP 层,可以看到 IP 包头信息,如:源 IP 地址和目标 IP 地址、TTL、IP 包长度、协议等 IP 协议各个字段的数值和含义;
3)在 ICMP 层,可以看到 ICMP 包头信息,比如 Type、Code 等 ICMP 协议各个字段的数值和含义;
wireshark 用了分层的方式,展示了各个层的包头信息,把“不可见”的数据包进行了可视化。
从 ping 的例子中,我们可以看到网络分层就像有序的分工,每一层都有自己的责任范围和信息,上层协议完成工作后就交给下一层,最终形成一个完整的网络包。
注:使用 tcpdump 抓取 MySQL 协议包之前需要关闭 SSL 连接;否则因为 SSL 加密机制,无法抓取 MySQL 协议包!
1)在终端一启动抓包命令
tcpdump -i any tcp and host 127.0.0.1 and port 3306 -w /root/test.pcap
执行结果如下:
2)在终端二中执行如下命令
1) 启动 MySQL
2) 登陆 MySQL
3) 进行一些简单操作
4) 退出并关闭 MySQL
执行结果如下:
3)在终端一中停止 tcpdump 抓包
Ctrl + C
执行结果如下:
使用 wireshark 打开 test.pacp 文件,界面如下:
MySQL 是基于 TCP 协议进行传输的,MySQL 客户端与服务器端交互过程如下:
由 wireshark 抓包分析和 MySQL 客户端与服务器端交互过程可知:
1)最开始的 3 个包是 TCP 三次握手建立连接的包;
2)4~20 是 MySQL 握手认证阶段建立的包;
上述 4~20 数据包分别对应:
问题:握手认证阶段数据包各参数含义是什么?
1)Server Greeting proto=10 version=5.7.39-debug
- proto=10 是指协议版本号;
- version=5.7.39-debug 是指服务器版本号;
2)50156 → 3306 [ACK] Seq=1 Ack=85 Win=65536 Len=0 TSval=3380584881 TSecr=3380584881
- 50156 是 TCP 序列号,每个 TCP 段都有一个序列号,用于标识该段数据在字节流中的位置;
- → 表示数据是从哪个方向发送的;
- 3306 是 MySQL 端口号,表示接收该数据的应用程序端口;
- Ack 表示是一个确认段;
- Seq 序列号的缩写,表示该段数据的起始序列号,Seq=1 意味着这是从序列号为 0 的数据段之后发送的第一个数据段;
- Ack=85 表示接收端期望接收的下一个数据段的序列号,在这个例子中,它是 85,意味着接收端期望接收序列号为 85 的数据段;
- Win=65536 Win?是窗口大小的缩写,这个字段表示接收端为该连接分配的 TCP 接收窗口的大小;窗口大小是动态变化的,用于流量控制和拥塞控制。65536通常表示一个较大的窗口大小;
- 表示该段数据的长度(以字节为单位),在这个例子中,它是 0,意味着该段没有携带任何应用层数据;
- TSval=3380584881 是发送时间戳;
- TSecr=3380584881 是接收时间戳;这两个时间戳用于计算往返时间和重复数据包;
3)Login Request user=:当客户端与服务器尝试进行连接时,它会发送一个 Login Request 数据包,包中,user= 通常表示客户端正在尝试使用一个用户名进行身份验证;服务器会检查这个用户名,并与存储在服务器上的用户数据库进行比较,以确定客户端是否有权访问服务器上的资源;
4)Client Hello:客户端向服务器发送的第一条消息,用于握手认证,数据包中包含了客户端权能标志、最大消息长度、字符编码等(即登陆认证报文);
3)20~20021 是 MySQL 命令执行阶段建立的包;
4)20021~20024 是 TCP 四次挥手建立的包;
问题:为什么 tcpdump 抓包抓到的 TCP 挥手是三次,而不是四次?
当被动关闭方在 TCP 挥手过程中,「没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。
而通常情况下,服务器端收到客户端的 FIN 后,很可能还没发送完数据,所以就会先回复客户端一个 ACK 包,稍等一会儿,完成所有数据包的发送后,才会发送 FIN 包,这也就是四次挥手了。
此外,Wireshark 可以用时序图的方式显示数据包交互的过程,从菜单栏中,点击 统计 (Statistics) -> 流量图 (Flow Graph),然后,在弹出界面中的「流量类型」选择「TCP Flows」,你可以更清晰的看到,整个过程中 TCP 流的执行过程:
问题:为什么 TCP 三次握手过程中 Seq 是 0?
实际上是因为 Wireshark 工具帮我们做了优化,它默认显示的是序列号 Seq 是相对值,而不是真实值。
如果你想看到实际的序列号的值,可以右键菜单, 然后找到「协议首选项」、「Transmission Control Protocol」,接着找到「Relative Seq」后,把它给取消勾选(取消 Seq 相对值的显示方式),操作如下:
取消后,Seq 显示的就是真实值了,具体如下图所示:
可见,客户端和服务端的序列号实际上是不同的,序列号是一个随机值。
这其实跟我们书上看到的 TCP 三次握手和四次挥手很类似,作为对比,你通常看到的 TCP 三次握手和四次挥手的流程,基本是这样的:
说明:client_isn 是客户端初始化的序列号、server_isn 是服务端初始化的序列号;