前言
Wireshark虽然好用,但服务器上通常没有图形界面。tcpdump是Linux下最常用的命令行抓包工具,排查网络问题、分析协议、定位连接异常都离不开它。
本文整理tcpdump的常用技巧,从基础语法到实际问题排查,配合真实场景案例。
1. 基础用法
1.1 最简单的抓包
# 抓取eth0网卡的所有流量
tcpdump -i eth0
# 抓取所有网卡
tcpdump -i any
# 指定抓取数量
tcpdump -i eth0 -c 100
1.2 常用参数
# -n:不解析主机名(速度快)
# -nn:不解析主机名和端口名
tcpdump -i eth0 -nn
# -v/-vv/-vvv:详细程度递增
tcpdump -i eth0 -vv
# -X:同时以十六进制和ASCII显示包内容
tcpdump -i eth0 -X
# -A:以ASCII显示包内容(适合HTTP)
tcpdump -i eth0 -A
# -s0:抓取完整数据包(默认只抓前96字节)
tcpdump -i eth0 -s0
# -w:保存到文件(可用Wireshark打开)
tcpdump -i eth0 -w capture.pcap
# -r:读取抓包文件
tcpdump -r capture.pcap
1.3 组合使用
# 常用组合:不解析、完整包、保存文件
tcpdump -i eth0 -nn -s0 -w /tmp/capture.pcap
# 边抓边看,同时保存
tcpdump -i eth0 -nn -U -w /tmp/capture.pcap &
tail -f /tmp/capture.pcap | tcpdump -r -
2. 过滤表达式
tcpdump强大之处在于过滤表达式,精准抓取需要的流量。
2.1 按主机过滤
# 抓取与192.168.1.100通信的所有流量
tcpdump -i eth0 host 192.168.1.100
# 抓取源地址为192.168.1.100
tcpdump -i eth0 src host 192.168.1.100
# 抓取目标地址为192.168.1.100
tcpdump -i eth0 dst host 192.168.1.100
# 抓取与多个主机通信
tcpdump -i eth0 host 192.168.1.100 or host 192.168.1.101
# 排除某个主机
tcpdump -i eth0 not host 192.168.1.100
2.2 按端口过滤
# 抓取80端口流量
tcpdump -i eth0 port 80
# 抓取源端口为80
tcpdump -i eth0 src port 80
# 抓取目标端口为80
tcpdump -i eth0 dst port 80
# 抓取端口范围
tcpdump -i eth0 portrange 8000-9000
# 抓取多个端口
tcpdump -i eth0 port 80 or port 443 or port 8080
2.3 按协议过滤
# 只抓TCP
tcpdump -i eth0 tcp
# 只抓UDP
tcpdump -i eth0 udp
# 只抓ICMP(ping)
tcpdump -i eth0 icmp
# 只抓ARP
tcpdump -i eth0 arp
2.4 按网段过滤
# 抓取192.168.1.0/24网段
tcpdump -i eth0 net 192.168.1.0/24
# 抓取源网段
tcpdump -i eth0 src net 10.0.0.0/8
2.5 按TCP标志位过滤
# 抓取SYN包(新连接)
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0'
# 抓取SYN+ACK包
tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) == (tcp-syn|tcp-ack)'
# 抓取FIN包(连接关闭)
tcpdump -i eth0 'tcp[tcpflags] & tcp-fin != 0'
# 抓取RST包(连接重置)
tcpdump -i eth0 'tcp[tcpflags] & tcp-rst != 0'
# 简写方式
tcpdump -i eth0 'tcp[13] & 2 != 0' # SYN
tcpdump -i eth0 'tcp[13] & 1 != 0' # FIN
tcpdump -i eth0 'tcp[13] & 4 != 0' # RST
2.6 组合过滤
# 抓取到MySQL的流量
tcpdump -i eth0 dst port 3306 and dst host 192.168.1.100
# 抓取HTTP请求(排除SSH)
tcpdump -i eth0 port 80 and not port 22
# 抓取指定IP的TCP SYN包
tcpdump -i eth0 'host 192.168.1.100 and tcp[tcpflags] & tcp-syn != 0'
# 复杂组合:抓取从web服务器到数据库的非SSH流量
tcpdump -i eth0 'src host 192.168.1.10 and dst host 192.168.1.20 and not port 22'
3. 输出解读
3.1 TCP包格式
14:22:31.123456 IP 192.168.1.10.45678 > 192.168.1.20.80: Flags [S], seq 123456, win 64240, length 0
各部分含义:
14:22:31.123456:时间戳192.168.1.10.45678:源IP.端口192.168.1.20.80:目标IP.端口Flags [S]:TCP标志位seq 123456:序列号win 64240:窗口大小length 0:数据长度
3.2 TCP标志位
| 标志 | 含义 |
|---|---|
| S | SYN,发起连接 |
| . | ACK,确认 |
| F | FIN,关闭连接 |
| R | RST,重置连接 |
| P | PSH,推送数据 |
| S. | SYN+ACK |
| F. | FIN+ACK |
3.3 三次握手示例
# 客户端发SYN
14:22:31.001 IP 10.0.0.1.45678 > 10.0.0.2.80: Flags [S], seq 100
# 服务端回SYN+ACK
14:22:31.002 IP 10.0.0.2.80 > 10.0.0.1.45678: Flags [S.], seq 200, ack 101
# 客户端发ACK
14:22:31.003 IP 10.0.0.1.45678 > 10.0.0.2.80: Flags [.], ack 201
4. 实战场景
4.1 排查连接超时
服务调用超时,先确认是不是网络层面的问题。
# 抓取到目标服务的SYN包
tcpdump -i eth0 -nn 'dst host 192.168.1.100 and dst port 8080 and tcp[tcpflags] & tcp-syn != 0'
如果看到大量SYN但没有SYN+ACK,说明:
- 对端服务没起来
- 防火墙拦截了
- 网络不通
# 同时看SYN和SYN+ACK
tcpdump -i eth0 -nn 'host 192.168.1.100 and port 8080 and (tcp[tcpflags] & tcp-syn != 0)'
4.2 排查连接被重置
如果连接频繁被RST,抓包分析原因。
# 抓取RST包
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'
常见原因:
- 端口没监听
- 连接数超限
- 防火墙策略
- 应用主动关闭
4.3 分析HTTP请求
# 抓取HTTP请求行
tcpdump -i eth0 -A -s0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
# 简单版本
tcpdump -i eth0 -A port 80 | grep -E 'GET|POST|HTTP'
4.4 抓取DNS查询
# 抓取所有DNS流量
tcpdump -i eth0 -nn port 53
# 只看DNS查询内容
tcpdump -i eth0 -nn -v port 53
输出示例:
14:22:31.123 IP 192.168.1.10.54321 > 8.8.8.8.53: 12345+ A? www.example.com. (33)
14:22:31.145 IP 8.8.8.8.53 > 192.168.1.10.54321: 12345 1/0/0 A 93.184.216.34 (49)
4.5 分析数据库连接
# MySQL连接问题排查
tcpdump -i eth0 -nn port 3306
# 看MySQL协议内容
tcpdump -i eth0 -X port 3306 | grep -A5 'Error'
# Redis连接
tcpdump -i eth0 -nn port 6379 -A
4.6 排查网络丢包
抓取重传包:
# 抓取TCP重传(通过序列号判断)
# 方法:保存抓包文件,用tshark分析
tcpdump -i eth0 -nn -w /tmp/capture.pcap
tshark -r /tmp/capture.pcap -Y "tcp.analysis.retransmission"
或者简单判断:
# 看有没有大量重复的seq
tcpdump -i eth0 -nn 'tcp' | awk '{print $1,$2,$3,$4,$5}' | sort | uniq -c | sort -rn | head
5. 高级技巧
5.1 抓包文件轮转
长时间抓包,防止文件过大:
# 每100MB切一个文件,最多保留10个
tcpdump -i eth0 -w /tmp/capture.pcap -C 100 -W 10
# 每小时切一个文件
tcpdump -i eth0 -w /tmp/capture_%Y%m%d_%H%M%S.pcap -G 3600
5.2 远程抓包本地分析
服务器上抓包,传到本地用Wireshark分析:
# 方法1:抓完传输
tcpdump -i eth0 -w /tmp/capture.pcap
scp server:/tmp/capture.pcap .
# 方法2:实时传输(需要网络稳定)
ssh user@server 'tcpdump -i eth0 -w - port 80' | wireshark -k -i -
# 方法3:通过管道
ssh user@server 'tcpdump -i eth0 -nn -U -w - port 80' > capture.pcap
5.3 多节点同时抓包
排查分布式系统问题,经常需要在多台机器同时抓包。
手动方式:
# 在多台机器上同时执行,用时间戳对齐
tcpdump -i eth0 -nn -tt -w /tmp/capture_$(hostname).pcap 'port 8080'
批量方式(用Ansible):
# capture.yml
- hosts: all
tasks:
- name: 开始抓包
shell: |
nohup tcpdump -i eth0 -nn -w /tmp/capture_{{ inventory_hostname }}.pcap \
'port 8080' &
async: 60
poll: 0
如果机器在不同网络环境,逐个SSH登录很麻烦。我一般会用组网工具(WireGuard、ZeroTier、星空组网这类)先把机器串到一个虚拟网络,然后Ansible批量执行。
5.4 抓取指定进程的流量
tcpdump本身不支持按进程过滤,但可以曲线救国:
# 1. 找到进程监听的端口
ss -tlnp | grep nginx
# 2. 按端口抓包
tcpdump -i eth0 port 80 or port 443
# 或者用strace跟踪进程的网络调用
strace -f -e trace=network -p $(pgrep nginx)
5.5 抓取特定内容的包
# 抓取包含"error"的HTTP响应
tcpdump -i eth0 -A port 80 | grep -i error
# 抓取包含特定字符串的包
tcpdump -i eth0 -s0 -A | grep -B5 -A5 'password'
# 使用ngrep(更专业的内容过滤)
ngrep -d eth0 'GET' port 80
6. 常用抓包命令速查
# 抓取到某服务的所有连接
tcpdump -i eth0 -nn host 192.168.1.100 and port 8080
# 抓取新建连接
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'
# 抓取连接关闭
tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-fin|tcp-rst) != 0'
# 抓取大于1000字节的包
tcpdump -i eth0 -nn 'greater 1000'
# 抓取HTTPS握手(Client Hello)
tcpdump -i eth0 -nn 'tcp port 443 and (tcp[((tcp[12] & 0xf0) >> 2)] = 0x16)'
# 抓取ICMP错误包
tcpdump -i eth0 -nn 'icmp[icmptype] != icmp-echo and icmp[icmptype] != icmp-echoreply'
# 排除干扰流量
tcpdump -i eth0 -nn 'not port 22 and not arp and not port 53'
7. 性能注意事项
7.1 避免影响生产
# 限制抓包速率
tcpdump -i eth0 -c 10000 # 只抓10000个包
# 只抓包头,不抓内容
tcpdump -i eth0 -s 96 # 默认值,够分析协议了
# 用BPF过滤减少内核到用户态的数据量
tcpdump -i eth0 'port 80' # 比抓全部再grep好
7.2 高流量场景
# 增加buffer防止丢包
tcpdump -i eth0 -B 4096 -w capture.pcap
# 用pfring等高性能抓包工具
# 或者直接用tc做流量镜像到分析机
总结
| 场景 | 命令 |
|---|---|
| 基础抓包 | tcpdump -i eth0 -nn |
| 抓特定主机 | tcpdump -i eth0 host 192.168.1.100 |
| 抓特定端口 | tcpdump -i eth0 port 80 |
| 抓TCP连接建立 | tcpdump 'tcp[tcpflags] & tcp-syn != 0' |
| 抓RST包 | tcpdump 'tcp[tcpflags] & tcp-rst != 0' |
| 保存到文件 | tcpdump -w capture.pcap |
| 看HTTP内容 | tcpdump -A port 80 |
| 抓DNS | tcpdump -nn port 53 |
排查思路:
- 先明确要抓什么流量,构造过滤表达式
- 抓包同时复现问题
- 用Wireshark或tshark分析抓包文件
- 对比正常和异常时的包,找出差异
tcpdump配合Wireshark,基本能搞定大部分网络问题排查。