
1. 为什么网络排查场景里大家第一时间还是会想起tcpdump我是从装图形化抓包工具开始接触网络分析这行的Wireshark用了很久界面清晰、过滤顺手、还能直接看流。但凡是干过几年运维或者后端开发的人都会有共同体验线上环境出了问题手上只有一个没有图形界面的最小化服务器Wireshark根本装不上或者装上去了也没法把图形界面转发到本地。这种时候tcpdump就是大家最常想起的那个命令行抓包工具。tcpdump本质上是Linux环境下最经典的基于命令行的数据包分析工具它做的事情很简单——把网卡上流经的数据包按指定的规则捕获下来要么直接输出到终端要么写入文件用于后续分析。之所以这个工具至今没有被更“现代”的替代品淘汰核心原因有三第一它几乎存在于所有Linux发行版的软件源里安装极其简单第二它运行时的系统开销很小在性能紧张的服务器上也能用第三它配合-w参数保存的pcap文件可以被Wireshark直接打开等于说命令行采集、图形化分析两头都能干。这篇文章不是简单把man手册翻译一遍。我会按照自己从零开始接触tcpdump的真实路径来讲从下载安装开始到基本命令使用再到高级过滤表达式最后结合几个曾经让我排查到凌晨的实际网络故障场景把那些“文档上没写、踩了坑才知道”的经验一并说出来。如果你现在属于看到Linux命令就头大的阶段这篇文章能帮你把tcpdump的骨架搭起来如果你已经用过一段时间中间关于性能开销、buffer大小、抓包点选择的几个小节大概率能解答一些你此前没想明白的问题。需要说明的是我下面的所有操作示例都是基于CentOS 7.9和Ubuntu 20.04两个环境验证过的但命令本身在所有主流Linux发行版上通用只是包管理器略有差异。2. 安装tcpdump的完整路径在线、离线、源码编译三种方式我都走了一遍2.1 在线安装两行命令解决但需要先确认系统发行版tcpdump的在线安装没什么技术含量但对刚入门的朋友来说最大的坑反而是“不知道自己装的是什么系统的哪个版本”。很多人照着网上的教程执行yum install tcpdump结果系统是Ubuntu包管理器叫apt完全不匹配。所以第一步永远不是执行安装命令而是确认发行版类型。cat /etc/os-release这个命令会输出系统的名称和版本号。看到IDcentos或者IDrhel就走Yum路线看到IDubuntu或者IDdebian就走Apt路线。CentOS / RHEL系列执行:sudo yum install -y tcpdumpUbuntu / Debian系列执行:sudo apt update sudo apt install -y tcpdump安装完成后可以用tcpdump --version验证是否成功能看到类似下面的输出就说明装好了tcpdump version 4.9.3 libpcap version 1.9.1 OpenSSL 1.1.1k FIPS 26 Mar 2021这里要留意libpcap这个版本号。tcpdump本身只是一个前端解析工具真正干“从网卡抓数据包”这个脏活累活的是底层的libpcap库。后面讲离线安装的时候这个依赖关系会变得非常关键。2.2 离线安装服务器上不了外网时这套手动流程必须掌握生产环境里有一类机器是物理隔离的内网机器不给开外网权限。这时候yum install或者apt install会直接失败很多人到这里就卡住了。离线安装的核心思路是找一台能上外网的同架构、同版本系统的机器把rpm或者deb包下载下来再拷贝到目标机器上安装。这个方法的关键是解决依赖。tcpdump依赖libpcap所以至少需要两个安装包。在能上网的机器上执行# CentOS / RHEL系列使用yumdownloader工具下载rpm包 # 先安装yum-utils提供yumdownloader命令 sudo yum install -y yum-utils mkdir -p /tmp/tcpdump-rpms cd /tmp/tcpdump-rpms yumdownloader --resolve tcpdump执行完以后目录下会出现两个或者多个rpm文件一个是tcpdump-4.x.x-x.el7.x86_64.rpm另一个是libpcap-1.x.x-x.el7.x86_64.rpm依赖关系清晰的显示出来了。然后把这两个文件拷贝到目标离线机器上执行sudo rpm -ivh libpcap-*.rpm tcpdump-*.rpm注意安装顺序先libpcap后tcpdump因为tcpdump安装时要求libpcap已经存在。如果顺序反了rpm会报依赖缺失。有人会用rpm -Uvh来装参数-U是升级的意思在第一次安装时-ivh和-Uvh效果一样但-ivh语义上更准确。Ubuntu/Debian的离线安装流程类似只是包格式变成deb。在一台能上网的Ubuntu机器上执行mkdir -p /tmp/tcpdump-debs cd /tmp/tcpdump-debs apt download tcpdump libpcap0.8然后把deb文件拷贝到离线机器上sudo dpkg -i libpcap0.8*.deb tcpdump*.deb2.3 源码编译安装为什么我要专门提这条老路源码编译安装今天已经不常用了但有两类人还是会遇到一类是没有对应发行版软件包的环境另一类是必须要用比发行版自带更新版本的tcpdump来做某些协议解析的场景。比如CentOS 7自带的tcpdump是4.9.2这个版本对HTTP/2的解析支持不够好如果你需要抓取HTTP/2流量做分析就得从源码编译高版本。源码编译需要提前解决libpcap依赖因为tcpdump只是壳libpcap才是真正和网卡驱动打交道的库。# 第一步从官网下载源码 # tcpdump官网https://www.tcpdump.org/ wget https://www.tcpdump.org/release/tcpdump-4.99.4.tar.gz wget https://www.tcpdump.org/release/libpcap-1.10.4.tar.gz # 第二步先编译安装libpcap tar -zxvf libpcap-1.10.4.tar.gz cd libpcap-1.10.4 ./configure --prefix/usr/local make -j4 sudo make install # 第三步再编译安装tcpdump cd .. tar -zxvf tcpdump-4.99.4.tar.gz cd tcpdump-4.99.4 ./configure --prefix/usr/local make -j4 sudo make install源码编译本身不复杂但有几个前置条件容易漏。./configure的时候如果报错找不到lex或者yacc是因为系统缺少flex和bison两个构建工具CentOS上执行sudo yum install -y flex bisonUbuntu上执行sudo apt install -y flex bison解决。编译libpcap还需要内核头文件CentOS上执行sudo yum install -y kernel-develUbuntu上执行sudo apt install -y linux-headers-$(uname -r)。编译安装完成后tcpdump默认装到了/usr/local/sbin/tcpdump但系统自带的旧版本可能还在/usr/sbin/tcpdump执行tcpdump命令时到底用的是哪个版本取决于PATH环境变量里谁在前面。直接执行which tcpdump查看路径如果想明确指定用新版本就执行/usr/local/sbin/tcpdump --version验证。2.4 安装过程中的常见报错与解决对照报错信息原因解决办法Neither found/lex is required缺少flex或bison安装flex和bison后重新configurelibpcap not foundlibpcap未安装或路径未识别rpm方式先装libpcap源码方式加--with-libpcap/usr/localPermission denied当前用户无抓包权限使用sudo或将用户加入wheel/tcpdump组You dont have permission to capture on that device非root用户执行抓包需要root权限或CAP_NET_RAW能力/usr/sbin/tcpdump: error while loading shared libraries: libpcap.so.1动态链接库路径不对执行sudo ldconfig或配置/etc/ld.so.conf.d/libpcap.confPermission denied这个问题在源码编译安装后特别常见。tcpdump在新版本内核下抓包需要CAP_NET_RAW这个capability从源码编译时如果没设置setcap普通用户就会抓包失败。临时方案是加sudo彻底方案是把需要用到的账号加入tcpdump组sudo groupadd tcpdump sudo usermod -a -G tcpdump 你的用户名 # 重新登录后生效3. 先用最简单的命令跑通抓包理解tcpdump的四个核心要素3.1 全网卡抓包一条命令看清tcpdump的工作方式安装完成后第一件事先别急着看复杂参数用一条最简单的命令感受一下sudo tcpdump不跟任何参数时它会监听第一个非loopback网卡并持续抓包输出到终端。你会看到类似这样的输出12:33:45.123456 IP 192.168.1.100.56789 192.168.1.1.443: Flags [P.], seq 1:517, ack 1, win 501, options [nop,nop,TS val 981823121 ecr 981823121], length 516第一次看到这种输出大概率是懵的。但先不用急只需要记住这条信息包含的几个要素时间戳、协议类型这里是IP、源地址和端口、目标地址和端口、TCP标志位、序列号、长度。到了后面解读抓包结果的章节这一行的含义会逐步展开。这里插入一个很重要的提示在没有明确目标的情况下不建议直接在繁忙的生产服务器上执行不带过滤条件的tcpdump。一个千兆网卡在正常业务流量下有几千PPS每秒数据包数是很常见的你会看到终端疯狂刷屏然后陷入按CtrlC的循环。正确做法永远是先想清楚“我要分析什么场景”再决定过滤条件。3.2 网卡接口tcpdump抓包的第一个决策点网卡接口interface决定了数据包的采集来源。执行tcpdump -D可以列出系统上所有可用的网卡1.eth0 [Up, Running] 2.eth1 [Up, Running] 3.lo [Up, Running, Loopback] 4.any [Pseudo-device that captures on all interfaces]用-i参数指定网卡sudo tcpdump -i eth0 sudo tcpdump -i any sudo tcpdump -i lo # 抓本机回环流量三个选择各自的含义-i eth0只抓物理网卡eth0上的流量-i lo抓本机进程间通过loopback地址通信的流量-i any则是同时监听所有网卡。刚入门时最容易犯的错误是本机测试一个服务明明确实有请求发出去但tcpdump什么都抓不到最后发现自己没加-i lo默认监听的是第一个物理网卡而loopback流量根本不经过它。any这个伪接口很实用但要注意一点在any接口上抓包时-e参数显示链路层头不会工作VLAN ID也无法正确显示这是any接口机制上的限制。需要分析VLAN标签时必须指定具体的物理网卡。3.3 名称解析和宽输出两个“反直觉”的默认行为tcpdump默认会做两件“多余”的事一是把IP地址反向解析为主机名DNS PTR查询二是把端口号映射成服务名比如443显示成https。听起来很贴心实际跑起来会让人抓狂DNS解析慢的话抓包会卡顿服务名显示让输出变得冗长难读。处理方式用-n关闭主机名解析-nn同时关闭主机名和端口号解析。这是tcpdump使用频率最高的参数组合我在实际工作中几乎永远是sudo tcpdump -nn -i eth0这么用。sudo tcpdump -nn -i eth0对比一下加不加-nn的输出差异不加12:33:45.123456 IP 192.168.1.100.56789 server.example.com.https: Flags [P.], ...加了12:33:45.123456 IP 192.168.1.100.56789 192.168.1.1.443: Flags [P.], ...第二种格式一眼就能看出目标IP和端口做问题排查时信息密度高得多。另一个高频参数是-v、-vv、-vvv用于调整输出详细程度。-v显示TTL、总长度等基础信息-vv额外显示TCP选项-vvv把应用层协议的解码也带出来。日常排查基础网络问题时用-v就够排查TCP参数问题时才需要-vv以上。3.4 抓包中断条件-c、-n和超时控制的正确组合生产环境抓包经常需要“抓够一定数量就自动停止”这时候-c参数派上用场。sudo tcpdump -nn -i eth0 -c 100表示抓满100个包后自动停止不用一直盯着终端手动CtrlC。这在自动化脚本里尤其好用比如计划任务里定时抓包分析抓完自动退出。还有一个容易忽略的参数是--time-stamp-precision默认微秒精度micro如果对时延分析有更高精度要求可以用--time-stamp-precisionnano把时间戳精度提到纳秒级。这个参数需要网卡和驱动支持老网卡可能不支持纳秒时间戳执行时会直接报错。关于“抓包多久算够”这个没有标准答案的问题。我的经验是分析连接建立问题抓100到200个包足够分析慢请求至少要抓满一个完整请求从发起到结束的全过程分析周期性异常建议抓10到15分钟长时段数据再做聚合统计。-c控制包数量真正控制时间的是timeout命令配合比如# 抓10秒后自动停止 sudo timeout 10 tcpdump -nn -i eth04. 过滤表达式才是tcpdump的灵魂从基础语法到组合实战4.1 按IP和端口过滤业务排查中最常用的三板斧如果tcpdump只是能把网卡流量都打出来那它和cat /dev/random没什么区别。它的强大之处在于支持伯克利包过滤BPF语法可以把海量流量瞬间收敛到需要关注的那一小撮。最基本的过滤是“我要看某台机器跟我之间的流量”。比如业务方反馈说“这台机器连不上我的数据库”我需要确认请求是否真的达到了数据库服务器执行sudo tcpdump -nn -i eth0 host 192.168.1.50host关键字后面跟IP过滤条件是源地址或目标地址任意一方匹配就算。如果只想看“从这台机器发出来的”或“发给这台机器的”用src host和dst host来限定方向# 只看源地址是192.168.1.50发出的流量 sudo tcpdump -nn -i eth0 src host 192.168.1.50 # 只看目标地址是192.168.1.50的流量 sudo tcpdump -nn -i eth0 dst host 192.168.1.50端口过滤是另一个基础操作特别是排查服务端口问题时sudo tcpdump -nn -i eth0 port 8080 sudo tcpdump -nn -i eth0 src port 12345 sudo tcpdump -nn -i eth0 dst port 443与host一样不加方向时port表示源端口或目标端口任意一方匹配。一个常见场景Nginx监听80端口配了443的SSL证书导致握手失败用port 443过滤能看到客户端在发TLS ClientHello但Nginx完全没有响应问题就定位到了Nginx配置上。4.2 组合条件与逻辑运算and、or、not的实际使用规则单个条件只能解决“简单问诊”真实场景往往需要复合条件。比如“看看192.168.1.50访问数据库服务器3306端口的流量”单个host会把所有端口流量都捞出来单个port 3306又会把其他机器访问3306的流量混进来需要写sudo tcpdump -nn -i eth0 host 192.168.1.50 and port 3306逻辑运算有三个关键字and且、or或、not非也可以用符号、||、!但建议都用英文单词可读性更好。组合条件多了以后要加括号区分优先级但注意括号在shell里有特殊含义必须转义或用引号包裹# 看从MySQL服务器发出的且源端口为3306的流量 sudo tcpdump -nn -i eth0 src host 10.0.0.1 and src port 3306 # 看SSH和HTTP流量排除HTTPS sudo tcpdump -nn -i eth0 port 22 or port 80 and not port 443第二个命令里的and not这种写法说明逻辑运算支持嵌套使用可以组合出很复杂的采集条件。不过实践中有个原则过滤条件不是写得越复杂越好能过滤到“精确的流量集合”就够了。太复杂的表达式一是容易写错二是tcpdump的BPF编译器会进行复杂的指令集编译过滤条件本身消耗CPU资源。能用两层逻辑解决的问题不用三层。4.3 按协议和方向过滤TCP、UDP、ICMP等实际判断逻辑tcp、udp、icmp这几个关键字直接按协议类型过滤# 只看TCP流量 sudo tcpdump -nn -i eth0 tcp # 只看UDP流量DNS查询、NTP同步等 sudo tcpdump -nn -i eth0 udp # 只看ICMP流量ping sudo tcpdump -nn -i eth0 icmp协议端口组合的场景很常见。比如排查DNS解析问题DNS默认走UDP 53端口抓包命令应该写成sudo tcpdump -nn -i eth0 udp port 53只写port 53不限定udp也能抓到但会把TCP 53的DNS over TCP流量区域传送、大响应场景会用到也包含进来。明确协议类型让抓到的数据包集合更纯净。有个容易混淆的地方是TCP端口和UDP端口是独立的命名空间——TCP 80和UDP 80是完全不同的流量如果只写port 80两种都会被抓到。需要精确区分时按上面示例同时写明协议类型和端口。icmp关键字不需要指定端口因为ICMP协议本身没有端口概念。ping命令的echo request和echo reply在Wireshark里会显示为Echo (ping) request和Echo (ping) reply在tcpdump输出里则是ICMP echo request和ICMP echo reply。4.4 抓取完整的TCP三次握手tcpdump的经典用法场景TCP握手分析是我用tcpdump最多的场景之一。怀疑某个服务端没有正常响应客户端的连接请求时只需要抓带SYN标志的包就能看出问题所在。三次握手中客户端首先发一个SYN包服务端回SYNACK客户端最后再回ACK。抓包命令sudo tcpdump -nn -i eth0 tcp[tcpflags] (tcp-syn|tcp-ack) ! 0 -c 10这个表达式的含义检查TCP标志位字段只要SYN或ACK任一标志位被置位就抓取。实际排查时根本不需要看完三个握手包——第一个SYN包如果服务端不回应说明服务端根本没收到或者防火墙直接丢弃了如果服务端回了SYNACK但客户端不发ACK说明客户端丢弃了服务端的数据包问题可能出在客户端方向的中间设备上。三次握手的抓包分析是最直观的网络连通性诊断方式没有之一。另一个高频场景是判断“连接被谁关闭了”。TCP四次挥手也是通过标志位判断比如# 查看带FIN标志的包确认连接关闭是否正常 sudo tcpdump -nn -i eth0 tcp[tcpflags] tcp-fin ! 0输出里Flags [F.]表示FINACKFlags [R.]表示RST。大量RST出现往往意味着程序异常关闭连接进一步排查的方向就是“谁在发RST”。4.5 基于端口范围的过滤-nn和端口段写法有些服务端口不是固定一个而是一个范围比如很多游戏服务器用udp 27000-27050这样一段端口。tcpdump的端口过滤支持范围写法# 抓取TCP 8000到9000之间的所有流量 sudo tcpdump -nn -i eth0 tcp portrange 8000-9000 # 抓取某台机器访问这段端口的流量 sudo tcpdump -nn -i eth0 host 192.168.1.100 and tcp portrange 8000-9000portrange关键字就是为这种场景准备的。需要注意BPF语法里portrange不能和port混用在同一过滤条件里比如port 80 or portrange 8000-9000这种写法会报错。需要这种情况就拆成两次抓包。5. 看懂抓包结果TCP标志位、序列号和状态流转的解读经验5.1 TCP Flags字段拆解S、P、F、R、A这些字母分别代表什么tcpdump输出里每个TCP包都带一个Flag标记格式如Flags [S]、Flags [P.]、Flags [F.]、Flags [R.]。这里的字母含义是字母全称含义SSYN同步序列号连接建立时使用FFIN发送方完成数据发送请求断开连接RRST重置连接通常代表异常中断PPSH接收方应立即把数据交给应用层AACK确认收到对方数据点号(.)—只包含ACK标志没有其他标志一个有趣的细节是Flags [P.]里的点号。这个包同时设置了PSH和ACK两个标志tcpdump用P.来表示点号代表ACK。Flags [S.]是SYNACK——这是三次握手的第二个包服务端回应客户端的SYN并带上确认信息。刚开始学的人会把它误认为“SYN和点号”其实点号就是ACK。这个显示规则在tcpdump的很多输出里都存在理解这一点后看抓包结果会顺畅很多。5.2 seq、ack和win一段抓包输出背后的TCP状态逻辑一个标准输出行里除了五元组协议、源地址、源端口、目标地址、目标端口和Flags还有三个数字关键信息seq、ack和win。比如12:34:56.789012 IP 192.168.1.100.5000 192.168.1.1.8080: Flags [P.], seq 1:517, ack 1, win 501, length 516seq 1:517表示这个TCP包的序列号范围从1开始到517结束长度516字节。这里的序列号是tcpdump做了相对序列号显示的结果——实际上TCP序列号是一个随机的超大数字ISN初始序列号tcpdump取首个包的序列号作为基准值1后续包都用相对值。如果不想要这种相对值显示加-S参数显示绝对序列号。ack 1表示确认号是1含义是“我已经收到对方序列号1之前的所有数据下一个期望收到的字节是1”。win 501是接收窗口大小单位是字节表示发送方愿意接收的数据量。窗口值如果持续变小说明接收方TCP缓冲区有压力可能出现了应用层读取数据不及时的问题。分析TCP性能问题的时候我会重点看三样东西第一是seq的连续性如果出现跳跃意味着中间有重传或丢包第二是ack是否持续增长说明数据在正常流动还是停滞不动说明对端卡住了第三是win的变化趋势持续下降往往预示着拥塞控制或缓冲压力。5.3 抓包结果保存与读取-w、-r、-v的组合用法终端输出只适合快速看两眼真正做深度分析必须把抓包结果保存下来。保存用-w参数sudo tcpdump -nn -i eth0 host 192.168.1.100 and port 8080 -w /tmp/http_traffic.pcap-w写入的是pcap格式这是Wireshark的标准格式。保存期间终端不会显示任何包信息因为数据直接写文件了这是正常现象不是卡住了。想边保存边看可以同时加-v参数这样把包信息输出到标准错误同时保存文件sudo tcpdump -nn -i eth0 host 192.168.1.100 and port 8080 -w /tmp/http_traffic.pcap -v读取pcap文件用-r参数配合-nn和-v按需显示sudo tcpdump -nn -r /tmp/http_traffic.pcap -v-r有个很有用的特性读取文件时也可以继续叠加过滤条件。比如抓包时没指定端口录了全量流量事后只想看80端口的流量不需要重新抓包直接过滤sudo tcpdump -nn -r /tmp/http_traffic.pcap port 80这种“先全量存储、后按需过滤”的工作方式是我最推荐的生产环境抓包策略。因为在上线前很难预料到故障现场需要分析哪个端口的流量全量抓完再本地筛选比现场反复试过滤条件要稳妥得多。配合-Z root参数指定用户权限可以避免保存的文件权限混乱。pcap文件会被持续写入默认不会因为磁盘空间不够自动停止而是直接写满磁盘。生产环境抓包一定不能忽略磁盘空间管理。解决方式是用-C参数指定单个文件大小超过后自动切换新文件# 每个文件最大100MB写满后自动切换新文件 sudo tcpdump -nn -i eth0 -w /tmp/capture.pcap -C 1005.4-vvv、-e、-A这些参数什么时候值得用-vvv是三重详细模式配合-X能同时看到十六进制和ASCII码输出。需要分析应用层协议内容时比如确认HTTP请求头是否携带了特定Cookie用-A只显示ASCII文本就够了# 查看HTTP请求的明文内容适合HTTP不是HTTPS sudo tcpdump -nn -i eth0 port 80 -A-e显示数据链路层信息包括源MAC地址和目标MAC地址。排查交换机端口、ARP问题、VLAN配置时需要。比如两台服务器间通了IP却连不上TCP用-e能看到ARP请求是否正常从而判断是哪一跳的MAC地址问题sudo tcpdump -nn -i eth0 -e arp-X和-XX用于十六进制数据包内容展示。-X在输出行显示十六进制和ASCII-XX额外包含以太网头数据链路层的十六进制。做协议逆向或深度包分析时配合-vvv使用平时用不上。有一个参数组合我特别提醒-w和-v并不冲突但-w和-A是冲突的因为-w写入的是二进制pcap-A要求文本输出两者互斥。想边存文件边看内容用tee命令间接实现sudo tcpdump -nn -i eth0 port 80 | tee /tmp/http.log6. 从故障出发的tcpdump实战三个曾经让我排查到深夜的真实场景6.1 场景一内网服务偶发超时如何用tcpdump快速定位丢包点一次线上接口偶发超时监控图显示服务端响应99%都在50ms以内但每过几分钟就会出现一次500ms以上的耗时。开发团队怀疑是数据库慢查询但数据库DBA说慢查询日志里没有对应的记录。当时我需要证明“客户端到服务端的网络链路本身有丢包”。抓包策略是这样设计的同时在这台报障机器上开两个tcpdump一个抓客户端发出去到服务端的流量一个抓服务端返回的流量。实际上更实用的做法是在单机上用-i any同时抓进出两个方向因为接口返回本身就不需要区分物理网卡。sudo tcpdump -nn -i any host 192.168.1.100 and port 8080 -w /tmp/client_side.pcap抓了20分钟拿到pcap后本地用-r读取并过滤重传包。TCP重传有一个标志特征seq号比前一个包小或者seq一样但len更大。sudo tcpdump -nn -r /tmp/client_side.pcap tcp[13] 4 ! 0这里tcp[13]是TCP标志位的字节偏移 4是检查RST标志位。实际上更直接的办法是过滤tcp-analysis-flagsWireshark里才有tcpdump命令行层面没有直接按重传过滤的语法。我当时的做法是把pcap拉回本地用Wireshark打开Wireshark的tcp.analysis.retransmission过滤条件可以直接标出重传包。结果清晰显示客户端收到服务端第一个ACK后过了200ms才触发重传而正常情况是1ms内收到。这说明第一个ACK大概率在路上丢失了问题指向中间链路而不在数据库。这里有个很重要的实战经验分享抓包点必须尽量靠近怀疑对象。如果怀疑网络设备丢包只在一端抓包只能看到重传结果看不到真正的丢包现场。两端同时抓包才能还原完整链路。中间是交换机还是专线、跑了什么路由协议都会影响判断但tcpdump至少能把问题边界从“全链路的黑盒”缩小到“哪一段出了问题”。6.2 场景二SSH登录卡顿如何区分是认证问题还是网络往返问题有一次新上线的机器SSH登录输入密码后要等5秒才出现shell公司内部论坛已经有人抱怨“这个机房的新机器有问题”。排查思路SSH登录卡顿可能是网络延迟高每个TCP握手或加密协商包网络往返慢也可能是服务端sshd在尝试反向DNS解析导致超时。用tcpdump直接看SSH端口连接建立的情况。sudo tcpdump -nn -i eth0 port 22抓到登录全过程的包观察从SYN到ACK的间隔。如果间隔在毫秒级说明网络本身没问题如果间隔在秒级先检查路由和防火墙。当时抓包结果显示三次握手在毫秒级完成问题被排除在网络链路之外。继续看后续的SSH协议包发现服务端在收到客户端SSH版本信息后过了2秒才发下一个包——典型的服务端内网做了一次失败的反向DNS解析超时。原因确认后在/etc/ssh/sshd_config里加了一行UseDNS no并重启sshd服务问题消失。这个案例的价值在于很多网络“卡顿”其实不是网络问题而是应用层行为导致的延迟。tcpdump可以帮你快速界分“包在网络上的耗时”和“包到达对端后经过的耗时”这一步判断做对了后面的排查方向才不至于跑偏。6.3 场景三抓不到HTTPS流量内容的应对方法有同事拿着tcpdump来问“为什么我抓了443端口的HTTPS流量用-A看到的是乱码全被加密了。”这是对tcpdump能力边界的误解。HTTPS的SSL/TLS层把HTTP内容加密了tcpdump作为抓包工具拿到的是加密后的密文看不到明文HTTP头。这不代表tcpdump无用而是需要配合其他机制第一抓TLS握手阶段的包看证书交换和密钥协商过程能确认TLS版本、证书链是否完整、是否走了SNI等信息。这不是没有价值——很多HTTPS故障证书链不完整、TLS版本不匹配、SNI配置错误在握手阶段就暴露了。sudo tcpdump -nn -i eth0 port 443 -v输出里Certificate字段后会有证书信息的摘要可以在不破解加密的前提下判断证书链是否完整。第二真正需要看HTTPS流量明文内容的时候正确姿势是配置SSLKEYLOGFILE环境变量让浏览器或应用导出TLS会话密钥然后Wireshark导入密钥文件解密。但这个方案只在应用层支持SSLKEYLOGFILE时才有效服务端程序很多不支持。第三如果目标服务是自己的后端服务最直接的办法是在服务端卸载TLS后再抓包即抓取Nginx反代到后端应用服务器的HTTP流量这个流量默认不带TLS加密直接用-A就能看到明文。实战里最推荐的方案是第三种。很多后端性能排查场景根本不需要解密客户端到Nginx之间的TLS流量——分析Nginx到应用服务器之间的明文HTTP已经足够判断响应慢是发生在哪个环节。6.4 tcpdump看Nginx和后端通信一条命令定位响应慢的环节一个典型的Java应用接口响应慢外部表现为“用户点击后等了3秒”开发怀疑是Java应用处理慢但Java应用侧监控显示CPU使用率很低、线程栈也看不出阻塞。这时候用tcpdump看Nginx到应用服务器的HTTP交互过程。sudo tcpdump -nn -i eth0 src host 应用服务器IP and tcp port 8080 -w /tmp/nginx_to_app.pcap抓包后打开pcap看时间戳差值。Nginx在T1时刻发出HTTP GET请求Flags [P.], seq ... length应用服务器在T2时刻返回HTTP响应Flags [P.], seq ... length用T2减去T1得到应用服务器的整体处理时间。如果这个时间很短比如20ms说明应用没问题慢在网络如果这个时间是2.8秒问题就在应用侧。这个方法的最大好处是把问题边界清晰分割。Web架构里请求要经过浏览器、CDN、Nginx、应用服务器、数据库等很多环节tcpdump在各个环节分别抓包测时间差就能精准定位慢在哪个环节。这是任何监控系统都难以替代的“现场取证”能力。7. tcpdump的高阶参数和生产环境避坑指南7.1 -s参数决定抓包完整性默认的snaplen可能会丢掉应用层数据snaplen规定每个数据包从开头截取多少字节。tcpdump默认值是262144字节老版本默认65535字节对绝大多数场景都够用。但如果你明确知道某个协议包很小可以缩小snaplen值加快捕获速度和高并发场景下的性能反过来如果是Jumbo Frame巨型帧环境默认snaplen可能会截断某些超大包。# 只抓每个包的前96字节对于TCP头少量载荷足够 sudo tcpdump -nn -i eth0 -s 96 # 恢复为完整默认值 sudo tcpdump -nn -i eth0 -s 0注意一个历史坑老版本的tcpdump默认snaplen就是65535如果你在和老版本输出的pcap文件打交道遇到“有长度但内容不全”的情况先怀疑是不是snaplen截断了。-s 0这个特殊值表示“按系统最大值完整抓取”不是“不抓数据”。7.2 buffer_size和性能问题高流量服务器上抓包的注意事项生产环境在高峰期抓包最大的风险不是抓不到包而是tcpdump本身性能开销太大把服务器拖垮。tcpdump在内核态通过libpcal的BPF机制拷贝数据包到用户态流量越大CPU开销越高。几个降低开销的实际手段第一用-c尽快结束抓包不要无脑一直抓。第二抓包前明确过滤条件只抓需要分析的流量“先全量抓再慢慢分析”在高峰期是危险策略。第三使用-B参数加大内核缓冲区。# 设置内核抓包缓冲区为4096KB默认值通常较小高流量下容易丢包 sudo tcpdump -nn -i eth0 -B 4096 -w /tmp/capture.pcap-B的值单位是KB默认值在多数系统上是2048KB。缓冲区太小时高流量场景下内核会丢弃来不及拷贝到用户态的数据包pcap文件里会出现捕获长度小于原始长度的情况丢包现象可以在tcpdump输出的最后一行统计信息里看到192 packets captured 192 packets received by filter 0 packets dropped by kernelpackets dropped by kernel表示有多少包被内核丢弃了。这个数字在生产环境里要特别关注出现大量drop意味着抓包结果不完整后续分析没有意义。调大-B通常能缓解如果drop仍然严重说明流量超出了tcpdump单实例的处理能力需要用分流器或交换机镜像口。7.3 权限管理不推荐直接root跑tcpdump但也别指望普通用户直接跑执行tcpdump需要root权限或者CAP_NET_RAW能力。很多人图方便直接sudo tcpdump这个我在小流量环境不反对但生产环境建议做权限收口。一个比较好的实践是用setcap给tcpdump二进制添加capability让指定用户能执行抓包命令sudo setcap cap_net_raw,cap_net_admineip /usr/sbin/tcpdump设置后普通用户可以直接执行tcpdump不再需要sudo。注意setcap只对二进制文件生效如果tcpdump是符号链接要把capability设置到最终链接指向的真实文件上。也需要确认文件系统支持file capabilityext4、xfs都支持。另一种方式是把用户加入tcpdump组。部分发行版安装tcpdump时会自动创建这个组并赋予组内用户使用tcpdump的权限。不太推荐纯普通用户跑一个是权限模型不清晰另一个是第三方使用者在服务器上没有人会给你sudo权限用setcap加上用户组的方式相对可控。7.4 tcpdump在容器环境下的使用限制Docker容器里用tcpdump有几类典型的坑。第一个是容器默认只有eth0且通常没装tcpdump需要apt install tcpdump或yum install tcpdump先装。第二个是容器内抓到的网络命名空间流量并不等于宿主机上的全部流量容器内抓包只能看到本容器所属的网络命名空间的流量。对于host网络模式--networkhost的容器抓包行为和普通进程一致对bridge网络的容器宿主机的tcpdump抓的是完整veth流量需要指定具体veth对。生产环境推荐这样抓容器流量宿主机上执行tcpdump按容器映射的端口或目标IP过滤能看到宿主机视角的完整进出流量。容器内抓包更适合排查“容器本身是否有奇怪的出站流量”这种场景。在Kubernetes环境里想抓某个Pod的流量通常的做法是在宿主机上找到对应的veth网卡接口再用-i vethxxx抓取。7.5 常见误判抓不到包不代表没有流量抓不到包是最容易让人怀疑人生的现象。三个高频原因一是放错了抓包位置。tcpdump只能看到它所在主机网卡上经过的流量如果流量在别的机器上比如负载均衡器上在这台机器上当然抓不到。二是过滤条件写法有问题比如目标端口写错、协议类型写错。三是内核缓冲区太小导致丢包丢包率不是100%但关键包恰好被丢了。遇到“抓不到”的第一反应应该是逐项排除先-D确认网卡列表和接口状态再用-i any不加过滤条件全量抓3秒验证网卡是否真的有流量最后才怀疑过滤条件。这个顺序能帮你快速从“抓不到”的情绪里走出来。这里还有一个容易忽略的工具限制tcpdump默认不会抓取发给自身的数据包吗不是的它会抓取所有经过接口的包包括本机自己发出和接收的包。所以测试本机服务-i lo加上明确端口过滤是可以直接看到本机到本机流量的并不会因为“target是本机”就跳过。8. 从tcpdump到Wireshark的工作流当命令行分析不够用时tcpdump终端输出可以做快速判断但做深度协议分析我的工作流永远是“tcpdump采集文件Wireshark图形化分析”。这不是说tcpdump能力不行而是Wireshark在GUI里提供的可视化、协议树状结构、状态统计图、专家信息提示这些能力确实比命令行高效太多。一个比较流畅的工作流如下第一步服务器上用tcpdump把可疑流量抓到pcap文件sudo tcpdump -nn -i eth0 -s 0 -w /tmp/issue.pcap host 192.168.1.100 and port 8080第二步把pcap文件从服务器下载到本地scp userserver:/tmp/issue.pcap ~/Desktop/第三步Wireshark打开pcap文件先用tcp.analysis.flags看专家信息提示这些是Wireshark自动标记的异常包再用http、dns等协议过滤条件缩小范围最后用tcp.stream eq 0按TCP流重组观察完整会话。如果本地没有Wireshark又想快速统计信息tcpdump配合Linux命令行工具也能做基础分析。比如统计pcap里每个IP的流量大小# 从pcap提取五元组信息 sudo tcpdump -nn -r /tmp/issue.pcap -q | awk {print $3} | sort | uniq -c | sort -nr这里的awk提取的是每行输出的第三个字段源IP和端口按计数排序能快速得出哪些源IP发了最多包。再高级一点的统计可以用tsharkWireshark的命令行版本但它需要单独安装不属于tcpdump的范畴了。9. 新手最容易踩的五个坑对照表坑位现象原因解决忘加-nn抓包结果里有主机名和服务名解析慢默认开启反向DNS和服务名解析加-nn关闭过滤条件写错顺序括号在shell里被解释命令报错没对条件加引号整个过滤条件用单引号包裹监听接口选错-i eth0抓不到本机回环流量loopback流量不经过eth0改用-i lo或-i any内核丢包严重统计信息里packets dropped by kernel不为0缓冲区太小流量太大加-B 4096单位KB打开pcap文件乱码-w保存的文件直接cat或lesspcap是二进制格式用-r读取或用Wireshark打开五个坑里前三个在入门阶段几乎每个人都踩过后两个更多出现在生产环境操作里。初学者可以把这个表存下来抓包之前先看一眼。我自己在实际操作中还有一个建议开始抓包前先在终端执行tcpdump -h看一眼当前版本支持的参数不同小版本的tcpdump在参数细节上有些细微差异。比如有些老版本不支持-C的文件大小限制参数配置了以后直接报错。版本确认这一步虽然不起眼但能避免很多“为什么我的命令和你不一样”的困惑。tcpdump这个工具的价值不在于它有多复杂而在于它总能稳定地出现在任何一台Linux服务器上在关键时刻帮你还原网络现场。把基础命令练熟把过滤表达式用顺再配合Wireshark做深度分析这套组合已经能覆盖绝大多数网络故障排查场景。