兄弟们,干运维和网络这行的,甭管你平时吹得多天花乱坠,最后都得落到排查问题这根钢丝绳上。你跟我说你网络架构玩得花,我信,但你说你链路抖动、应用卡顿、用户那边嗷嗷叫的时候,你连个包都没抓明白,那我只能说兄弟你还得练。排查网络问题,咱们不靠猜,全靠看证据。而抓包这个动作,tcpdump就是那个最趁手、最不起眼、但最关键的工具。
今天这篇不整虚的,就聊聊tcpdump最普通的玩法。不是那种把man手册给你抄一遍的教程,而是从一个老运维的实际工作角度,把它当成一把螺丝刀,告诉你什么时候拧哪颗螺丝,怎么拧不滑丝,拧完之后怎么判断螺丝到底紧没紧。看完这篇,你至少能对付掉工作里八成以上的网络排查场景,不用再遇到问题就先重启或者对着屏幕发呆。
1. 为什么偏偏是它:tcpdump的核心思路拆解
先把心态摆正。tcpdump不是万能的,它就是一个命令行下的抓包分析器,原理简单得吓人——把网卡上跑过的数据包按你的规则截获一份副本,然后要么打印到屏幕上,要么存成文件给你慢慢分析。它的优势从来不是分析得有多深,而是贴近底层、轻巧、灵活,在服务器上一条命令就能跑起来,不依赖任何花哨的图形界面。
很多人一上来就被各种各样的参数吓住了,其实你只需要抓住三个核心点:抓什么、抓多久、抓完怎么办。
- 抓什么:这就是BPF过滤规则,也就是伯克利包过滤语法。它是tcpdump的灵魂,决定了你是把所有流量都捞上来还是只捞你关心的那部分。
- 抓多久:抓包不是录监控视频,不能无限录。你需要明确时间窗口和包数量上限,否则生成的文件能把你的磁盘撑爆。
- 抓完怎么办:是直接在屏幕上实时滚动看,还是落盘保存成
.pcap文件,然后用Wireshark等工具去细看。这是两种完全不同的工作模式,一个适合临时瞄一眼,一个适合深挖慢查。
这三个问题想清楚了,你已经比百分之八十的初级使用者强。剩下的就是语法熟练度的问题。记住,我们是在有目的地取证,而不是在海里捞针。
2. 上场之前:先拿最基本的几个命令热热身
上手别整那些花活,先保证你能看到东西。
2.1 抓包前的三件事,先想明白
第一,你当前要监控哪块网卡。用ip addr或者老的ifconfig看一眼,找到你关心流量的那块物理网卡名称,常见的比如eth0、ens192、em1。你要是连网卡都搞错了,抓半天抓了个寂寞,流量根本不过这块卡,纯属浪费时间。
第二,你有没有权限。tcpdump需要root权限才能抓包,所以别老想着用普通用户去执行然后报错说找不到设备,先sudo -i或者检查一下你的用户是否在sudo组里。在有的生产环境里,甚至得看看你的系统是不是SELinux或者AppArmor拦着你,到时候报Operation not permitted都不知道是哪儿出了问题。
第三,确定你要不要开混杂模式。网卡默认只把发给自己MAC地址的包交给内核处理,但抓包工具通常会让网卡进入混杂模式,把经过这块网卡的所有包都接收一份。所以我们经常看到tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes这行提示,其中promiscuous mode开没开就说明这件事。默认情况下,像在交换机镜像口这种场景,我们必须依赖混杂模式才能抓到别人的流量,这个开关非常关键。
2.2 从一个最朴素的抓包命令说起
比如说我现在怀疑我这台服务器访问某个数据库有问题,最简单的,我就想先看看连接能不能建立,TCP握手能不能完成。直接在机器上敲:
tcpdump -i eth0 tcp port 3306这条命令的意思是,在eth0这块网卡上,抓取TCP协议且目标或源端口是3306的包。屏幕上开始一行行刷数据,你会看到类似下面这样的东西:
05:03:02.123456 IP 10.0.1.10.54321 > 10.0.1.20.3306: Flags [S], seq 123456789, win 64240, options [mss 1460,sackOK,TS val 123 ecr 0,nop,wscale 7], length 0 05:03:02.124002 IP 10.0.1.20.3306 > 10.0.1.10.54321: Flags [S.], seq 987654321, ack 123456790, win 65160, options [mss 1460,sackOK,TS val 456 ecr 123,nop,wscale 7], length 0 05:03:02.125001 IP 10.0.1.10.54321 > 10.0.1.20.3306: Flags [.], ack 987654322, win 512, length 0看到这个,你就知道TCP三次握手是正常完成的。S是SYN包,S.是SYN-ACK包,.就是纯ACK包。如果后面还有一串P开头的包,那就是携带了应用层数据的PUSH包。这里有个小技巧,Flags字母如果出现[F.]那就是正常四次挥手,[R.]就是连接被重置了,这往往是排查问题的重要信号。
如果我就想把这个过程实时看完,那直接-n参数把域名和端口翻译关掉,让输出更快更干净:
tcpdump -n -i eth0 tcp port 3306加了-n之后,屏幕上就不会去做IP到域名的反解析,也不会把3306显示成mysql,输出速度更快,也更纯粹。特别是当你抓的是公网流量,反查DNS这个过程非常拖慢速度,甚至会因为DNS解析超时导致输出卡顿,所以-n是我在任何抓包场景里几乎必带的一个参数。
2.3 不只会抓,还要会看基本输出
很多新手看到tcpdump输出的原始行就像看天书一样,其实拆开看很简单。我也顺手给你解读一下标准输出格式的几段信息:
时间戳是第一个字段,精确到微秒级,是判断延迟的重要依据。然后是协议层头信息,比如IP表示这个包是裸的IP包,如果是ARP那就会显式写出来。再往后是源地址源端口 > 目的地址目的端口,这个指向关系可以让你快速判断数据流向。
然后是很关键的Flags,也就是TCP控制位。S建连、F断开、R重置、P推送数据、E显式拥塞,混合标志位用点号分隔。seq和ack则是TCP序号和确认号,专门用来分析重传和乱序。最后面的win是接收窗口大小,可用来辨别接收方缓存压力。
把这些字段拼在一起,你就能在屏幕世界里看见两台机器之间到底发生了什么。熟练之后,很多网络故障从表面现象到根因方向,在你脑子里一瞬间就能拉出一条线来。
3. 进阶玩法:像筛金子一样用过滤表达式
基础命令会了,接下来得学会“挑食”。电脑网络上的数据包就像嘈杂集市里的人群,你得学会只盯着你想找的那个目标,而不是把所有人都喊到你面前来一个一个看。
3.1 过滤主机:只关心源或目的IP
排查问题时最常用的就是限定主机。比如我怀疑后端服务器10.0.2.5返回数据有问题,那我只抓跟它相关的流量:
tcpdump -nn -i eth0 host 10.0.2.5这里用了两个n,既不做域名反解,也不翻译端口号,纯纯的数字化输出,速度最极致。如果你还想精确到是我本机发给它还是它发给我,那就再加方向限定词:
tcpdump -nn -i eth0 src host 10.0.2.5 tcpdump -nn -i eth0 dst host 10.0.2.5src和dst这两个词非常直观,组合使用能精准锁定单向流量。还有更灵活的src or dst组合,比如我要看所有从10.0.2.5来或者去往10.0.2.6的流量:
tcpdump -nn -i eth0 'src host 10.0.2.5 or dst host 10.0.2.6'单引号包起来很重要,因为or、and这些关键字会被shell解释成管道或者逻辑符号,别看小看这层单引号,少了它很多命令执行结果会直接崩掉或者行为大变。
3.2 过滤端口:直击服务的咽喉
端口过滤是跟具体应用排查绑定的。比如排查web服务,直接抓80或443端口:
tcpdump -nn -i eth0 tcp port 443但我个人更推荐你直接写全五元组逻辑,源端口+目的端口分开写,因为有时候你想区分是外部请求进来还是内部向外请求。比如我只想看本机eth0上对外发起的连接,那目的端口一定是远程的443:
tcpdump -nn -i eth0 dst port 443反过来像收集内部服务日志或观测出网流量,那就看源端口:
tcpdump -nn -i eth0 src port 53这里的逻辑还可以用portrange来抓端口段。比如某个游戏服务器开了一大片UDP端口,从20000到30000,写起来就是:
tcpdump -nn -i eth0 udp portrange 20000-30000这种连续端口的匹配在排查P2P或者RPC类服务的时候特别给力,不然一条条端口写进去,手都能给你写酸。
3.3 协议与多条件组合:把网撒得刚刚好
最后来点真正有实战感的组合。比如你要定位是不是有人在你内网里搞ARP欺骗,那就直接只抓ARP协议:
tcpdump -nn -i eth0 arp比如你要排查DNS解析慢的问题,又不想抓太多无关流量,可以限定UDP端口53:
tcpdump -nn -i eth0 udp port 53遇到那种既有TCP又有UDP的服务,比如某些基于QUIC/HTTP3的应用,想一把梭全抓下来:
tcpdump -nn -i eth0 '(tcp or udp) and port 8443'这行表达式里的括号也同样需要单引号保护。另外有个更实在的建议:你真的特别明确就要某一种类型,比如排除掉SSH的22端口干扰,那你可以直接:
tcpdump -nn -i eth0 port not 22 and host 10.0.2.5活了。这一套组合拳打下来,你要的流量就像从沙子里淘出来的金子,其余无关数据全都安静地躺在内核缓冲区之外。这里多插一句经验:过滤器并不是越复杂越好,规则写得越宽,内核拷贝到用户态的数据越多,丢包概率越大。所以先宽后窄,先抓到方向、再细抠颗粒度,是实战里效率最高的节奏。
4. 落盘为王:生产环境必会的抓包姿势
实时滚动看几眼适合临时验证,但遇到那种老大看了半天才说“好像有点不对劲”的疑难杂症,我们就得把包保存下来,拿回去慢慢分析。
4.1 写出文件与循环切割
保存成.pcap文件的命令很常规:
tcpdump -i eth0 -w /tmp/capture.pcap tcp port 8080这里有个极其重要的点:写文件模式下,屏幕上不会打印每一行抓包信息,因为你告诉它把原始包写到文件里,它就不做实时翻译了。很多人第一次用半天没输出,以为卡死了,其实它是默默在后台干活。如果需要一边抓一边确保文件数量可控,可以用-C参数按大小分割文件,单位是MB:
tcpdump -i eth0 -w /tmp/capture.pcap -C 100 tcp port 8080这行命令表示单个文件超过100MB就自动新建下一个文件,后面会跟着生成capture.pcap、capture.pcap1、capture.pcap2这样的序列文件。也可以按时间切片,-G 60表示每60秒切一个文件,适合长时间无人值守。这种循环切文件的方式,在生产排查里就是神器,既能防止单个文件过大拖垮后期分析,也能让回传文件时间点更精确。
4.2 控制抓包时长和包数量
抓包不能无限期抓下去,要给个极限约束。比如我就是想抓3分钟看看情况:
timeout 180 tcpdump -i eth0 -w /tmp/capture.pcap -n host 10.0.2.5用timeout命令来限时,这是一个很可靠的做法,比后台挂PID然后手动杀要优雅得多。另外也可以用-c数量控制,抓到一定包数自动退出:
tcpdump -i eth0 -c 2000 -w /tmp/capture.pcap这里-c 2000意思就是抓满2000个包就自己停了,适合你只想快速采样几个包验证协议特征时用。要知道,有时候你并不需要全程录屏,前几秒的特征足以说明问题。结合-c加上-w这个组合是典型的快速取证姿势,比如你接到报警说某台机器疯狂发包,抓2000个包看看包长跟目标,往往几秒就能锁到具体进程的特征。
4.3 落盘抓包的缓冲区大小调节
生产环境流量稍微大一点,你会碰到一个很头疼的坑:抓包丢包。这个时候就需要调整内核抓包缓冲区大小,参数是-B,单位是KB。默认值往往只有几MB,高并发下瞬间就被打满,新到的包只能丢弃。常见做法是拉高到几十甚至几百MB:
tcpdump -i eth0 -B 4096 -w /tmp/huge_capture.pcap-B 4096意思是缓冲区设为4MB(注意这个单位,默认KB)。这能显著降低高流量场景下的丢包率。虽然tcpdump在退出时会打印一条包含dropped kernel的统计信息,但你要是看完包再发现丢包严重,再回头重新抓可就麻烦多了。所以流量很大的场景,开局就先把缓冲区拉高,别省着用。
还有一个很有用的参数是-s,控制每个包抓取的长度,也就是snaplen。默认是262144字节(256KB),但绝大多数常见协议我们只看头部和部分载荷就行了。为了减小文件体积,可以设成:
tcpdump -i eth0 -s 128 -w /tmp/small.pcap这样每个包就只保留前128字节,足以看到TCP/UDP头以及部分应用层信息。这在无线网卡或者流量大的环境里非常节省空间,代价是你可能看不到完整的应用载荷,需要根据实际需求权衡。有一点提醒:有些协议比如TLS证书、某些文件传输,要看全证书或全载荷,这时候-s 0才代表完整抓取整个包,别到时候抓完发现里面只有一截数据,悔到肠子都青了。
4.4 直取出可读的十六进制和ASCII内容
有时候你不想开Wireshark,就是想快速看看包里的实际内容。这时候可以用-X或者-A。-A把包以纯ASCII形式打印出来,适合看HTTP明文请求和响应;-X则同时输出十六进制和ASCII,适合检查二进制协议细节或加密流量的特征。
tcpdump -nn -i eth0 -A -s 0 tcp port 80这条命令会把80端口的所有TCP载荷以可读字符形式刷出来,排查HTTP接口调试时特别直观。你会直接看到GET / HTTP/1.1、Host:之类的请求行,限流了、乱码了、参数丢了,一眼就能判断。
5. 真实排障演练:一次假性超时的血泪实录
理论讲了一大堆,不发点实战总觉得心里不踏实。跟大家分享一个我亲身经历过的排查案例,让我们把前面这些参数串起来用一遍。
某个周五下午,业务方那边反馈说调用一个内部接口经常超时,但又不是每次必现,大概两三次里有一次要等好几秒。监控看CPU、内存都没啥压力,数据库负载也不高。这种间歇性超时最折磨人,因为无从下手。
我决定在应用服务器网卡上落盘抓包。先确认网卡名是eth0,然后用了这么一条命令:
tcpdump -nn -i eth0 -B 2048 -s 128 -w /tmp/app_timeout.pcap 'tcp port 8080 and host 10.10.20.15'抓了大概十分钟,等到业务方又反馈一次超时之后,我把包拿了回来。直接用Wireshark打开,先看TCP流。结果发现一个很有意思的现象:客户端发出的请求包(带P标志,数据段)已经到达服务器IP,但服务器这边迟迟没回ACK,然后客户端就开始疯狂重传。重传了好几轮,服务器终于是回了一个S.?不对,是回了一个R,也就是连接重置。看起来就像是业务层把连接关了。
顺着这个方向去查,发现后端Tomcat的线程池配置有问题,活跃线程数上限设得太低,请求一多连接就被pending,最终触发超时重置。客户端看到的是几十秒超时,实际上瓶颈根本不在网络链路,而在于服务端应用线程池满。这个结论可以说完全是依靠抓包锁定到端口的:如果不是看到TCP层面的重传和RST时间点,我们可能还在傻乎乎查中间交换机丢包和防火墙策略。
这里的经验总结就是:抓到包后,别急着看乱七八糟的几百兆数据,先按照时间顺序过滤出几个关键节点,找TCP重传(TCP Retransmission)、找RST、找零窗口(Zero Window),这三个信号基本能覆盖大部分“网络慢”的根因方向。
6. 那些年我们踩过的坑:tcpdump使用常见问题速查
玩的时间久了,总会有些小坑反反复复出现。我把它们整理成表格,方便你对着查。
| 问题现象 | 大概率原因 | 解决/排查方向 |
|---|---|---|
| 屏幕上什么都看不到 | 过滤规则写错、网卡选错、流量确实没走这块网卡 | 先用-i any抓所有网卡,拿掉过滤条件试试;确认抓包机器是否在流量路径上 |
提示Permission denied | 非root用户执行 | sudo执行,或者给普通用户配置CAP_NET_RAW能力 |
| 抓包过程中丢包严重 | 缓冲区太小或CPU不够 | 用-B调大缓冲区;减少无关抓包范围;用-s限制抓包长度 |
| 文件写入特别巨大 | 抓包范围太宽,或snaplen太大 | 严格限定host/port组合;使用-s 128限制载荷;用-C/-G自动切割文件 |
tcpdump: syntax error | 过滤表达式引号没加或写错 | 检查是否用了单引号;检查语法中and、or的优先级;用括号包裹条件时务必加引号 |
| 抓到包但Wireshark打不开 | 文件未正常结束,或抓包过程中被强杀 | 用-w落盘务必用timeout或者-c限制;别用kill -9强杀进程 |
| 只看到请求没看到ACK | 负载均衡器做了代理,或者本机防火墙拦了 | 确认流量路径上是否有四层负载设备;检查本机iptables规则 |
| 想抓但机器带宽已被打满 | 抓包本身成为最后一根稻草 | 改用硬件镜像旁观抓包,或先做流量限速;及时止损,别硬抓 |
最后再给大家一个杀手锏级别的提醒:如果你要在生产环境长期蹲点,千万别忘了-Z root参数。默认tcpdump在打开抓包设备后,会把自己降权到tcpdump用户来执行,以防止安全问题。但有些环境下这个降权反而导致无法写文件。如果你遇到这种诡异问题,加个-Z root操作就能解决。当然这是个双刃剑,自己评估风险再上,别乱用。
再分享一个我自己的习惯:抓完包之后,不管有没有发现问题,我都会写一行小字记录在案的命令行。这样一个月后看到这个文件还能回忆起当初抓包时的范围和意图,而不是对着几百兆的二进制文件发愁,我已经忘了自己在验证什么。
如果你从头看到这里,恭喜你,至少已经从“会用tcpdump”进阶到“知道为什么这么用tcpdump”的阶段了。工具永远是死的,能灵活组合思路去抽丝剥茧,才是这行真正值钱的地方。下次线上出故障,别慌,先深吸一口气,把上面这些招数从头到尾过一遍,多半就能摸到一点门道。