tcpdump 抓包命令详解:参数、过滤表达式与排障用法
tcpdump 是 Linux 服务器上的命令行抓包工具,最常用的写法是 tcpdump -i eth0 -nn -c 100 'tcp and host 198.51.100.7 and port 443':-i 选网卡,-nn 不做名称解析,-c 抓够就停,引号里是过滤条件。要细看就加 -w 存成 pcap 文件,下载到电脑上用 Wireshark 打开。
tcpdump 抓包的基本格式是 tcpdump -i 网卡 参数 '过滤表达式'。比如在服务器上只看 198.51.100.7 访问本机 443 端口的 TCP 包:tcpdump -i eth0 -nn -c 50 'tcp and host 198.51.100.7 and port 443'。-i 指定网卡,-nn 不把地址和端口换成名字,-c 50 抓够 50 个包自动停止,引号里是 BPF 过滤表达式。需要事后细看时,加 -w 文件名.pcap 把原始报文存下来,下载到电脑上用 Wireshark 分析。本文示例地址均取自文档专用网段:服务器 203.0.113.10,客户端 198.51.100.7。
tcpdump 是什么软件:安装、权限和选网卡
tcpdump 是一款开源的命令行抓包工具,由 Tcpdump Group 维护,底层用 libpcap 库收包,能在 Linux、BSD、macOS 上运行。它把经过网卡的报文逐个解析成一行文字,也可以把原始报文写成 pcap 文件。它的过滤语法(BPF)和 Wireshark 的捕获过滤器是同一套,在 tcpdump 里写熟的条件可以直接拿到 Wireshark 里用。
apt install tcpdump # Debian、Ubuntu
dnf install tcpdump # RHEL、Rocky、AlmaLinux 8 及以后,老版本用 yum
tcpdump --version # 查看 tcpdump 和 libpcap 的版本
tcpdump -D # 列出可以抓包的网卡
抓包需要 root 权限,或者由管理员单独授予抓包能力。最容易出错的一步是选网卡,抓错了网卡,过滤条件写得再对也是一片空白:
| 要看的流量 | 在哪块网卡上抓 | 说明 |
|---|---|---|
| 服务器的公网业务 | 配着公网 IP 的业务网卡,如 eth0、eno1 | 用 ip -br addr 查哪块网卡上有这个地址 |
| 不确定从哪块网卡进出 | -i any |
所有网卡一起抓,但不开混杂模式;较新的版本会在每行前标出网卡名和 In、Out 方向;经过 bond、网桥或 VLAN 子接口的包可能出现两次 |
| 本机访问本机 | -i lo |
访问 127.0.0.1 或本机自己的 IP,包都不经过物理网卡 |
| 链路聚合 | 整体看 bond0,看具体走哪条链路就抓成员口 | cat /proc/net/bonding/bond0 查成员网卡 |
| KVM 虚拟机 | 宿主机上对应的 vnet 网卡 | virsh domiflist 虚拟机名 查虚拟机和 vnet 的对应关系 |
| Docker 容器 | 容器的网络命名空间 | 用下面的 nsenter 命令,容器里不用装 tcpdump |
# 进入名为 web 的容器的网络命名空间,用宿主机上的 tcpdump 抓它的 eth0
nsenter -t "$(docker inspect -f '{{.State.Pid}}' web)" -n tcpdump -i eth0 -nn -c 20
tcpdump 参数详解:常用的 15 个
| 参数 | 作用 | 用法与注意 |
|---|---|---|
-i eth0 |
指定网卡 | 不写时自动选一块已启用的非回环网卡,容易选错,建议每次都写上 |
-n、-nn |
不做名称解析 | -n 不把 IP 反查成主机名,-nn 连端口和协议也显示数字。不同版本对单个 -n 是否转换端口名处理不一,习惯写 -nn 最稳妥。不加时每个地址都要做 DNS 反查,输出变慢,还会产生额外的 DNS 流量 |
-c 100 |
抓够 N 个包后退出 | 防止忘了停止;和 -w 一起用时,写够 N 个包就退出 |
-w 文件.pcap |
把原始报文写入文件 | 屏幕上不再逐行打印;tcpdump 4.99 起可以加 --print 边存边看,老版本没有这个选项 |
-r 文件.pcap |
读取抓包文件 | 可以再加过滤条件,从大文件里筛出一部分 |
-s 128 |
每个包只截取前 N 字节 | -s 0 表示完整长度,较新的版本默认就抓完整的包(262144 字节);只看报头时截短,文件小,也不留业务内容 |
-v、-vv |
显示更多报头字段 | TTL、IP ID、总长度、TCP 选项等,排查 TTL 和分片问题时用 |
-e |
显示二层帧头 | 打印源、目的 MAC 和 VLAN 标签,用来核对网关 MAC、查地址冲突 |
-A、-X |
显示报文内容 | -A 按 ASCII 显示,-X 按十六进制加 ASCII 显示,用来看明文 HTTP 请求头、Redis 命令 |
-tttt、-ttt |
时间戳格式 | -tttt 带日期,方便和日志对时间;-ttt 显示与上一个包的间隔 |
-l |
行缓冲输出 | 输出接管道交给 grep、tee 时加上,否则结果要攒一大块才出来 |
-Q in、-Q out |
只抓一个方向 | Linux 上可用,部分平台不支持 |
-p |
不把网卡设为混杂模式 | 只收本机该收的包:发给本机的单播,以及广播、组播 |
-B 8192 |
内核抓包缓冲区大小,单位 KiB | 大流量下统计里出现 dropped by kernel 时调大 |
-C、-G、-W |
按大小或时间轮转文件 | 见后文“用 -w 保存”一节 |
几个常用组合:
tcpdump -i eth0 -nn -c 20 icmp # 看 ping 的请求和应答
tcpdump -i eth0 -nn -tttt -l 'udp port 53' | tee /tmp/dns.txt # 带日期时间,同时存一份文本
tcpdump -i eth0 -nn -A -s 0 'tcp dst port 80' # 看明文 HTTP 请求头
tcpdump -i eth0 -nn -e -c 10 arp # 带 MAC 地址看 ARP 请求和应答
过滤表达式:抓取指定 IP 和端口的 TCP 包
过滤表达式由三类关键字组合而成:对象(host、net、port、portrange)、方向(src、dst,不写表示两个方向都算)、协议(tcp、udp、icmp、arp、ip6),再用 and、or、not 连起来。常见需求的写法:
| 需求 | 表达式 |
|---|---|
| 与某个 IP 有关的所有包,双向 | host 198.51.100.7 |
| 只看这个 IP 发来的包 | src host 198.51.100.7 |
| 只看发往本机 22 端口的包 | dst port 22 |
| 指定 IP 和端口的 TCP 包 | tcp and host 198.51.100.7 and port 3306 |
| 某个网段 | net 198.51.100.0/24 |
| 一段连续端口 | portrange 30000-30100 |
| 这个 IP 的 80 或 443 端口 | host 198.51.100.7 and (port 80 or port 443) |
| DNS 查询 | udp port 53 |
| 只看新建连接的 SYN | tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn |
| 只看 RST | tcp[tcpflags] & tcp-rst != 0 |
| ICMP 不可达 | icmp[icmptype] == icmp-unreach |
| IPv6 地址 | ip6 and host 2001:db8::10 |
| 排除自己的 SSH 会话 | not (host 192.0.2.5 and port 22) |
写的时候注意四点:
- 整个表达式用单引号括起来。括号、
!、&在 shell 里有特殊含义,不加引号会被 shell 先解释掉,报语法错误。 - and 和 or 的优先级相同,从左到右结合。
host 198.51.100.7 and port 80 or port 443实际含义是“(这个 IP 且 80 端口) 或 所有人的 443 端口”,会抓进一大堆无关的包。想表达“这个 IP 的 80 或 443”,必须加括号。 - port 不分 TCP 和 UDP。
port 443同时匹配 TCP 443 和 UDP 443(HTTP/3 走 UDP 443),只要 TCP 就写成tcp port 443或tcp and port 443。 - 按抓包点看到的地址写条件。客户端在 NAT 后面时,服务器上看到的是它出口的公网 IP,而不是它的内网地址;拿不准就先只按端口过滤,看清地址后再收紧。
在所有网卡上找某个 IP 的流量,是排查多网卡服务器时的常用写法:
tcpdump -i any -nn host 198.51.100.7
看懂输出:一行 TCP 包里有什么
一次 HTTPS 连接的开头,在服务器上抓到的输出是这样的(示例):
10:15:02.381245 IP 198.51.100.7.51544 > 203.0.113.10.443: Flags [S], seq 2814473922, win 64240, options [mss 1460,sackOK,TS val 1520637 ecr 0,nop,wscale 7], length 0
10:15:02.381290 IP 203.0.113.10.443 > 198.51.100.7.51544: Flags [S.], seq 90233411, ack 2814473923, win 65160, options [mss 1460,sackOK,TS val 88123 ecr 1520637,nop,wscale 7], length 0
10:15:02.412870 IP 198.51.100.7.51544 > 203.0.113.10.443: Flags [.], ack 1, win 502, options [nop,nop,TS val 1520668 ecr 88123], length 0
10:15:02.413511 IP 198.51.100.7.51544 > 203.0.113.10.443: Flags [P.], seq 1:518, ack 1, win 502, options [nop,nop,TS val 1520669 ecr 88123], length 517
从左到右依次是:时间戳;IP 表示 IPv4(IPv6 显示 IP6);源地址.源端口 > 目的地址.目的端口,最后一段数字是端口;Flags 是 TCP 标志;seq 和 ack 是序号和确认号,握手之后 tcpdump 默认改用相对值,加 -S 显示原始值;win 是接收窗口;length 是这个包携带的数据字节数,不含报头。
标志位里 S 是 SYN(发起连接),点号是 ACK,P 是 PSH(携带数据),F 是 FIN(正常关闭),R 是 RST(强制复位)。所以 [S] 是连接请求,[S.] 是服务器同意建连,[R.] 是拒绝或强制断开。
ICMP 和 UDP 的输出更简单,ping 的序号和端口不可达都直接写在行里:
10:20:11.004512 IP 198.51.100.7 > 203.0.113.10: ICMP echo request, id 3021, seq 17, length 64
10:20:11.004560 IP 203.0.113.10 > 198.51.100.7: ICMP echo reply, id 3021, seq 17, length 64
10:21:40.770031 IP 203.0.113.10 > 198.51.100.7: ICMP 203.0.113.10 udp port 9999 unreachable, length 36
用 -w 保存到文件:-c、-C、-G 控制大小
屏幕输出适合边抓边看,复杂问题要存下来,交给 Wireshark 看重传、时序和协议细节:
# 写够 5000 个包自动退出,文件名带上日期时间
tcpdump -i eth0 -nn -c 5000 -w /tmp/api-$(date +%m%d-%H%M).pcap 'tcp port 8080'
# 每 300 秒换一个文件,写满 12 个(共 1 小时)后自动退出
tcpdump -i eth0 -nn -G 300 -W 12 -w '/tmp/cap-%Y%m%d-%H%M%S.pcap' 'host 198.51.100.7'
# 从已有的文件里只挑出 RST 包,另存一个小文件
tcpdump -nn -r /tmp/cap-20261222-101500.pcap -w /tmp/rst.pcap 'tcp[tcpflags] & tcp-rst != 0'
# 直接按文本查看文件内容
tcpdump -nn -r /tmp/rst.pcap | head -20
几个容易踩的坑:
- -w 写的是二进制 pcap,不是文本。把屏幕输出重定向成
tcpdump ... > a.pcap,得到的是文字,Wireshark 打不开;反过来,-w 的文件用 cat 看是乱码,要用tcpdump -r或 Wireshark 读。 - 用 -G 时文件名里要带时间格式。不带
%H%M%S这类占位符,每个新文件都会覆盖上一个。按大小轮转用 -C,单位是百万字节,配合 -W 限制文件个数,写满后从第一个开始覆盖。 - 先估算文件有多大。1 Gbps 的流量跑满时每秒 125 MB,完整抓包一分钟就是约 7.5 GB。只需要看连接建立、重传、时序这些报头信息时,加
-s 128截短,一般够装下以太网、IP 和 TCP 报头,文件能小一个数量级,也不会把业务内容留在磁盘上。 - 看清停止时的统计。按 Ctrl+C 后,tcpdump 会打印抓到、过滤通过和被内核丢弃的包数。dropped by kernel 不为 0,是 tcpdump 自己没来得及收,属于漏抓,不能当成网络丢包;处理办法是收紧过滤条件、改成写文件而不是打印、用 -B 加大缓冲区。
端口不通、丢包:两端同时抓包定位
单点抓包只能说明“这里看到了什么”,要回答“包丢在哪”,最有效的办法是两端同时抓,用同一组条件对照。
端口不通:服务器和客户端各抓一份
# 服务器 203.0.113.10 上:-i any 能看出包从哪块网卡进出
tcpdump -i any -nn 'host 198.51.100.7 and tcp port 8080'
# 客户端 198.51.100.7 上:同时开始抓,再从另一个窗口发起连接
tcpdump -i eth0 -nn 'host 203.0.113.10 and tcp port 8080'
nc -zv -w 3 203.0.113.10 8080
判断依据有一个前提:在 Linux 上,tcpdump 看到的入方向包发生在本机防火墙处理之前,出方向包则是已经通过了本机防火墙的。对照两端的输出:
| 客户端看到 | 服务器看到 | 结论 | 下一步 |
|---|---|---|---|
| SYN 反复重发(Linux 上间隔约 1、2、4 秒) | 没有任何包 | 包在路上被丢:安全组、机房 ACL、上游防火墙或路由 | 从服务器往外逐段排查 |
| SYN 重发 | 收到 SYN,没有回应 | 多半是本机防火墙把 SYN 丢了 | 查 iptables、nftables 或 firewalld 的入方向规则 |
收到 [R.] |
收到 SYN,回了 [R.] |
端口上没有程序监听 | 用 ss -lntp 'sport = :8080' 确认服务是否在运行、监听在哪个地址 |
| SYN 重发 | 收到 SYN,也发出了 [S.] |
回程出了问题:回包从别的网卡或别的源 IP 发出、路由不对称被沿途的状态防火墙丢弃,或交换机的源地址校验丢掉了源 IP 不属于该端口的包 | 用 ip route get 198.51.100.7 看回包从哪块网卡、用哪个源地址发出 |
| 收到 ICMP 不可达 | 回了 ICMP,或者服务器上什么也没看到 | 被 REJECT 规则拒绝,或路由不可达 | 看 ICMP 包的源地址,就知道是哪台设备拒绝的 |
第四行最容易被忽略:服务器明明回了包,客户端却收不到,问题不在应用,也不在入方向的防火墙,而在回程。多网卡、多 IP 的服务器尤其要查这一项。
丢包:对照 ping 序号判断去程还是回程
ping 的每个请求都带递增的序号,两端一对就知道丢在哪个方向:
# 1. 服务器上开始抓,同时存一份文本
tcpdump -i eth0 -nn -l 'icmp and host 198.51.100.7' | tee /tmp/icmp.txt
# 2. 客户端上发 200 个 ping,记下最后统计的收到数
ping -c 200 -i 0.2 203.0.113.10
# 3. 服务器上按 Ctrl+C 停止,统计收到的请求数、发出的应答数,并找出缺了哪些序号
grep -c 'echo request' /tmp/icmp.txt
grep -c 'echo reply' /tmp/icmp.txt
awk '/echo request/{for(i=1;i<=NF;i++) if($i=="seq"){s=$(i+1)+0; if(p && s!=p+1) print "缺少 " p+1 " 到 " s-1; p=s}}' /tmp/icmp.txt
假设客户端显示 200 个只收到 188 个,对照服务器上的计数:
| 服务器收到请求 | 服务器发出应答 | 客户端收到 | 结论 |
|---|---|---|---|
| 200 | 200 | 188 | 丢在回程,即服务器到客户端方向 |
| 188 | 188 | 188 | 丢在去程,即客户端到服务器方向 |
| 200 | 188 | 188 | 服务器自己没回:有 ICMP 限速规则或者系统过忙 |
确定方向之后,把抓包点往中间移动,例如客户端的出口网关、机房边界设备,或者在交换机上做端口镜像,范围就能逐步缩小。还要注意,不少网络设备会对 ICMP 限速或低优先级处理,ping 丢包不一定代表业务丢包;业务是 TCP 的,用 -w 抓一段业务流量,在 Wireshark 里看重传比例,结论更可靠。
在管理系统里怎么落地
抓包之前,先要知道该在哪台机器、哪个端口上抓。Toplink DCIM 的实时流量图带异常检测,网络 TOP 排行能快速定位高占用的节点;服务器列表里直接显示它接在哪台交换机的哪个端口、端口速率和 VLAN,需要在交换机侧查计数或配镜像时,可以在交换机管理里打开浏览器终端,会话全程录像。
上面“服务器回了 SYN-ACK、客户端却收不到”的情况,在开启了源地址校验的机房里有一个常见原因:服务器私自加了一个没有分配给它的 IP,交换机只放行源 IP 属于该端口的流量,用这个地址回的包被直接丢弃。到 IP 地址管理里核对这台服务器名下分配了哪些地址,比在服务器上反复抓包更快。如果网络已经断到 SSH 登不上,就通过 IPMI 远程管理打开 VNC 控制台,在服务器本地执行 tcpdump 并写入文件,等网络恢复后再下载分析。
常见问题
tcpdump 抓包会影响服务器性能吗?
加了过滤条件、写入文件时开销很小。在大流量网卡上不加过滤、把每个包都打印到终端,会明显占用 CPU,经 SSH 回传的输出还会制造更多流量。生产环境抓包要加过滤,用 -c 或 -G、-W 限定范围,只看报头时用 -s 截短。
tcpdump 能抓到 HTTPS 的内容吗?
能抓到包,但内容是加密的:看得到双方 IP、端口、握手时序,通常还能从 TLS 握手里看到访问的域名(SNI),看不到 URL 和正文。要分析明文,得在 TLS 终止之后的环节抓,例如反向代理到后端的明文段,或者把会话密钥导出给 Wireshark 解密。
Windows 上能用 tcpdump 吗?
tcpdump 主要用在 Linux、BSD、macOS 上,Windows 上一般不用它,而是装 Wireshark。Wireshark 自带命令行版 tshark,用 -i 选网卡、-f 写捕获过滤条件、-w 保存文件,捕获过滤语法和 tcpdump 相同。