38-案例篇:怎么使用tcpdump和Wireshark分析网络流量?





前言

ping是一个最常用的测试服务延迟的工具
很多情况下ping可以帮我们定位出延迟问题
不过有时候ping本身也会出现意想不到的问题
这时,就需要抓取ping命令执行时收发的网络包,然后分析这些网络包,进而找出问题根源

tcpdump和Wireshark就是最常用的网络抓包和分析工具,更是分析网络性能必不可少的利器

  1. tcpdump仅支持命令行格式使用,常用在服务器中抓取和分析网络包
  2. Wireshark除了可以抓包外,还提供了强大的图形界面和汇总分析工具
    在分析复杂的网络情景时,尤为简单和实用

因而在实际分析网络性能时,先用tcpdump抓包,后用Wireshark分析,也是一种常用的方法




案例1

  1. 环境准备

    Ubuntu 18.04
    机器配置:2 CPU,4GB内存
    
    #预先安装tcpdump等工具
    # Ubuntu
    apt-get install tcpdump 
    # CentOS
    yum install -y tcpdump 
    
    ##
    windows或者Mac安装wireshark抓包分析工具
    
    
  2. ping工具本身也可能出现异常,比如运行缓慢,但实际网络延迟却并不大的情况

    ## ping geektime.org 3次
    root@alnk:~# ping -c3 geektime.org
    PING geektime.org (35.190.27.188) 56(84) bytes of data.
    64 bytes from 188.27.190.35.bc.googleusercontent.com (35.190.27.188): icmp_seq=1 ttl=111 time=45.6 ms
    64 bytes from 188.27.190.35.bc.googleusercontent.com (35.190.27.188): icmp_seq=2 ttl=111 time=45.6 ms
    64 bytes from 188.27.190.35.bc.googleusercontent.com (35.190.27.188): icmp_seq=3 ttl=111 time=45.6 ms
    
    --- geektime.org ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2003ms
    rtt min/avg/max/mdev = 45.660/45.668/45.680/0.008 ms
    
    
    ##
    假如运行时发现ping很快就结束了,那就执行下面的命令,再重试一下
    # 禁止接收从DNS服务器发送过来并包含googleusercontent的包
    root@alnk:~# iptables -I INPUT -p udp --sport 53 -m string --string googleusercontent --algo bm -j DROP
    
    
    ##
    root@alnk:~# ping -c3 geektime.org
    PING geektime.org (35.190.27.188) 56(84) bytes of data.
    64 bytes from 35.190.27.188: icmp_seq=1 ttl=111 time=45.7 ms
    64 bytes from 35.190.27.188: icmp_seq=2 ttl=111 time=45.7 ms
    64 bytes from 35.190.27.188: icmp_seq=3 ttl=111 time=45.6 ms
    
    --- geektime.org ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 11057ms
    rtt min/avg/max/mdev = 45.694/45.711/45.734/0.247 ms
    ##
    根据ping的输出可以发现,geektime.org解析后的IP地址是35.190.27.188
    而后三次ping请求都得到了响应,延迟都是45ms多一点
    ##
    但汇总的地方就有点儿意思了
    3次发送,收到3次响应,没有丢包,但三次发送和接受的总时间居然超过了11s(11057ms)
    
    
  3. 可能是DNS解析缓慢的问题,但到底是不是呢?

    root@alnk:~# time nslookup geektime.org
    Server:		114.114.114.114
    Address:	114.114.114.114#53
    
    Non-authoritative answer:
    Name:	geektime.org
    Address: 35.190.27.188
    
    real	0m0.070s
    user	0m0.004s
    sys	0m0.004s
    ##
    可以看到域名解析还是很快的,只需要70ms,显然比11s短了很多
    
    
  4. 到这里再往后该怎么分析呢?其实,这时候就可以用tcpdump抓包,查看ping在收发哪些网络包

    # 在终端1抓包
    # -nn表示不解析抓包中的域名(即不反向解析)、协议以及端口号
    # udp port 53表示只显示UDP协议的端口号(包括源端口和目的端口)为53的包
    # host 35.190.27.188表示只显示IP地址(包括源地址和目的地址)为35.190.27.188的包
    # 这两个过滤条件中间的or,表示或的关系,也就是说,只要满足上面两个条件中的任一个,就可以展示出来
    root@alnk:~# tcpdump -nn udp port 53 or host 35.190.27.188
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
    
    
    # 终端2,执行相同的ping命令
    root@alnk:~# ping -c3 geektime.org
    
    
    # 回到终端1,查看抓包情况
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
    14:02:31.100564 IP 172.16.3.4.56669 > 114.114.114.114.53: 36909+ A? geektime.org. (30)
    14:02:31.507699 IP 114.114.114.114.53 > 172.16.3.4.56669: 36909 1/0/0 A 35.190.27.188 (46)
    14:02:31.508164 IP 172.16.3.4 > 35.190.27.188: ICMP echo request, id 4356, seq 1, length 64
    14:02:31.539667 IP 35.190.27.188 > 172.16.3.4: ICMP echo reply, id 4356, seq 1, length 64
    14:02:31.539995 IP 172.16.3.4.60254 > 114.114.114.114.53: 49932+ PTR? 188.27.190.35.in-addr.arpa. (44)
    14:02:36.545104 IP 172.16.3.4.60254 > 114.114.114.114.53: 49932+ PTR? 188.27.190.35.in-addr.arpa. (44)
    14:02:41.551284 IP 172.16.3.4 > 35.190.27.188: ICMP echo request, id 4356, seq 2, length 64
    14:02:41.582363 IP 35.190.27.188 > 172.16.3.4: ICMP echo reply, id 4356, seq 2, length 64
    14:02:42.552506 IP 172.16.3.4 > 35.190.27.188: ICMP echo request, id 4356, seq 3, length 64
    14:02:42.583646 IP 35.190.27.188 > 172.16.3.4: ICMP echo reply, id 4356, seq 3, length 64
    ##
    1. 前两行表示tcpdump的选项以及接口的基本信息
    2. 从第三行开始,就是抓取到的网络包的输出
    3. 这些输出的格式都是时间戳 协议 源地址.源端口 > 目的地址.目的端口 网络包详细信息
    ## 
    举例
    第一条就表示从本地IP发送到114.114.114.114的A记录查询请求
    它的报文格式记录在RFC1035中,在这个tcpdump的输出中
    1. 36909+表示查询标识值,它也会出现在响应中,加号表示启用递归查询
    2. A?表示查询A记录
    3. geektime.org.表示待查询的域名
    4. 30表示报文长度
    ##
    接下来的一条,则是从114.114.114.114发送回来的DNS响应,域名geektime.org.的A记录值为35.190.27.188
    ##
    第三条和第四条,是ICMP echo request和ICMP echo reply
    响应包的时间戳14:02:31.539667,减去请求包的时间戳14:02:31.508164
    就可以得到这次ICMP所用时间为30ms,这看起来并没有问题
    ##
    随后的两条反向地址解析PTR请求,就比较可疑了
    只看到了请求包,却没有应答包
    仔细观察它们的时间发现,这两条记录都是发出后5s才出现下一个网络包,两条PTR记录就消耗了10s
    ##
    再往下看最后的四个包,则是两次正常的ICMP请求和响应,根据时间戳计算其延迟,也是30ms
    ##
    到这里,其实就找到了ping缓慢的根源
    正是两次PTR请求没有得到响应而超时导致的
    PTR反向地址解析的目的,是从IP地址反查出域名
    但事实上,并非所有IP地址都会定义PTR记录,所以PTR查询很可能会失败
    所以在ping时,如果发现结果中的延迟并不大,而ping命令本身却很慢,有可能是背后的PTR在搞鬼
    
    
  5. 禁止PTR

    # -n 选项禁止名称解析
    root@alnk:~# ping -n -c3 geektime.org
    PING geektime.org (35.190.27.188) 56(84) bytes of data.
    64 bytes from 35.190.27.188: icmp_seq=1 ttl=43 time=33.5 ms
    64 bytes from 35.190.27.188: icmp_seq=2 ttl=43 time=39.0 ms
    64 bytes from 35.190.27.188: icmp_seq=3 ttl=43 time=32.8 ms
    
    --- geektime.org ping statistics ---
    3 packets transmitted, 3 received, 0% packet loss, time 2002ms
    rtt min/avg/max/mdev = 32.879/35.160/39.030/2.755 ms
    ##
    现在只需要2s就可以结束,比刚才的11s可是快多了
    
    
  6. 删掉执行的iptables命令

    root@alnk:~# iptables -D INPUT -p udp --sport 53 -m string --string googleusercontent --algo bm -j DROP
    
    
  7. 明明案例跟Google没啥关系,为什么要根据googleusercontent ,这个毫不相关的字符串来过滤包呢?

    # 实际上如果换一个DNS服务器,就可以用PTR反查到35.190.27.188所对应的域名
    root@alnk:~# nslookup -type=PTR 35.190.27.188 8.8.8.8
    Server:		8.8.8.8
    Address:	8.8.8.8#53
    
    Non-authoritative answer:
    188.27.190.35.in-addr.arpa	name = 188.27.190.35.bc.googleusercontent.com.
    
    Authoritative answers can be found from:
    ##
    虽然查到了PTR记录,但结果并非geekbang.org,而是188.27.190.35.bc.googleusercontent.com
    其实这也是为什么,案例开始时将包含googleusercontent的丢弃后,ping就慢了
    因为iptables ,实际上是把PTR响应给丢了,所以会导致PTR请求超时
    
    



tcpdump

tcpdump是最常用的一个网络分析工具
它基于libpcap ,利用内核中的AF_PACKET套接字,抓取网络接口中传输的网络包
并提供了强大的过滤规则,从大量的网络包中,挑出最想关注的信息

tcpdump展示了每个网络包的详细细节,这就要求,在使用前必须要对网络协议有基本了解
而要了解网络协议的详细设计和实现细节, RFC当然是最权威的资料

不过RFC的内容,对初学者来说可能并不友好
如果对网络协议还不太了解,推荐去学《TCP/IP 详解》,特别是第一卷的TCP/IP协议族
这是每个程序员都要掌握的核心基础知识

再回到tcpdump工具本身,它的基本使用方法,还是比较简单的,也就是tcpdump[选项] [过滤表达式]
当然,选项和表达式的外面都加了中括号,表明它们都是可选的

提示:在Linux工具中,如果在文档中看到,选项放在中括号里,就说明这是一个可选选项
这时候就要留意一下,这些选项是不是有默认值

查看tcpdump的手册 ,以及pcap-filter的手册
发现tcpdump提供了大量的选项以及各式各样的过滤表达式
不过不要担心,只需要掌握一些常用选项和过滤表达式,就可以满足大部分场景的需要了

image-20211229113252393

image-20211229113721648




Wireshark

Wireshark也是最流行的一个网络分析工具,它最大的好处就是提供了跨平台的图形界面
跟tcpdump类似,Wireshark也提供了强大的过滤规则表达式,同时还内置了一系列的汇总分析工具

  1. 拿刚刚的案例来说,可以执行下面的命令,把抓取的网络包保存到ping.pcap文件中

    root@alnk:~# tcpdump -nn udp port 53 or host 35.190.27.188 -w ping.pcap
    tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
    ^C8 packets captured
    8 packets received by filter
    0 packets dropped by kernel
    
    root@alnk:~# ls -l
    total 10760
    -rw-r--r-- 1 root root      964 Dec 29 11:49 ping.pcap
    
    
  2. 把文件保存到本机,以便于分析

    root@alnk:~# sz ping.pcap
    
    
  3. 用Wireshark打开它

    image-20211229115434173

    从Wireshark的界面里,可以发现它不仅以更规整的格式,展示了各个网络包的头部信息
    还用了不同颜色,展示DNS和ICMP这两种不同的协议
    可以一眼看出,中间的两条PTR查询并没有响应包

    接着在网络包列表中选择某一个网络包后,在其下方的网络包详情中
    还可以看到,这个包在协议栈各层的详细信息
    比如以编号为5的PTR包为例
    image-20211229115616699

    可以看到IP层(Internet Protocol)的源地址和目的地址、
    传输层的UDP协议(User Datagram Protocol)、应用层的DNS协议(Domain Name System)的概要信息

    继续点击每层左边的箭头,就可以看到该层协议头的所有信息
    比如点击DNS后,就可以看到Transaction ID、Flags、Queries等DNS协议各个字段的数值以及含义



案例2

  1. 案例说明

    看一个HTTP的例子,并理解TCP三次握手和四次挥手的工作原理
    
    这个案例将要访问的是http://example.com/ 
    进入终端一,执行下面的命令,首先查出example.com的IP
    然后,执行tcpdump命令,过滤得到的IP地址,并将结果保存到web.pcap中
    
    
  2. 终端一,查出example.com的IP

    root@alnk:~# dig +short example.com
    93.184.216.34
    
    
  3. 终端一,执行tcpdump命令

    root@alnk:~# tcpdump -nn host 93.184.216.34 -w web.pcap
    
    
  4. 终端二,执行curl命令

    root@alnk:~# curl http://example.com
    
    
  5. 回到终端一,按下 Ctrl+C 停止 tcpdump,并把得到的 web.pcap 拷贝出来

    root@alnk:~# sz web.pcap
    
    
  6. 使用Wireshark打开web.pcap后,可以在Wireshark看到如下的界面

    image-20211229140357092

    由于HTTP基于TCP ,所以最先看到的三个包,分别是 TCP 三次握手的包
    接下来,中间的才是HTTP请求和响应包,而最后的四个包,则是 TCP 连接断开时的四次挥手包

    从菜单栏中点击 Statistics -> Flow Graph,
    然后在弹出的界面中的Flow type选择TCP Flows,可以更清晰的看到整个过程中 TCP 流的执行过程
    image-20211229140727188

    这其实跟各种教程上讲到的,TCP三次握手和四次挥手很类似,作为对比, 通常看到的TCP三次握手和四次挥手的流程,基本是这样的
    image-20211229140829397


posted @ 2021-12-29 14:11  李成果  阅读(1213)  评论(0编辑  收藏  举报