📌 本文亮点:覆盖 tcpdump 全部核心知识点------常用选项速查、BPF 过滤语法从入门到字节偏移高级用法、TCP 标志位精准抓包、生产环境抓包策略与性能优化,附速查表一键收藏!
前言
网络排障时,你是否遇到过这些问题:服务明明收到了请求却返回 RST?DNS 解析偶尔超时却不知道查询是否发出?线上疑似 SYN 洪水却无从确认?tcpdump 是解决这些问题的第一利器,但很多人只会 tcpdump -i eth0,面对高流量环境手足无措,过滤表达式写不对、抓包丢包、pcap 文件撑爆磁盘。
本文从零基础 出发,系统梳理 tcpdump 的完整知识体系,重点攻克 BPF 过滤语法这一核心技能,最终给出生产环境可直接复用的命令模板。
一、基础概念与安装
1.1 tcpdump 是什么
tcpdump 是基于 libpcap 的命令行网络数据包抓取工具,能够捕获流经网络接口的原始数据包,打印到终端或写入 .pcap 文件,是网络诊断、安全分析和协议调试的必备工具。
💡 核心优势 :BPF 过滤器在内核层面生效,只有匹配的数据包才会到达用户空间,高流量环境下过滤极为高效。
1.2 安装方式
bash
# Debian/Ubuntu
sudo apt install tcpdump
# RHEL/CentOS
sudo yum install tcpdump
# macOS(通常已预装)
brew install tcpdump
# Alpine
apk add tcpdump
二、基本语法与常用选项
2.1 语法结构
bash
tcpdump [选项] [BPF 过滤表达式]
2.2 接口选择
接口相关选项:
| 选项 | 说明 | 示例 |
|---|---|---|
-i <接口> |
指定监听接口 | tcpdump -i eth0 |
-i any |
监听所有接口 | tcpdump -i any |
-D |
列出可用接口 | tcpdump -D |
⚠️
-i any是 Linux 的伪接口,不支持混杂模式。如需看到所有流量(不仅是单播到本机的),请指定具体物理接口。
2.3 抓包控制
抓包行为控制选项:
| 选项 | 说明 | 示例 |
|---|---|---|
-c <数量> |
抓取 N 个包后停止 | tcpdump -c 100 |
-s <字节数> |
每包抓取长度(snaplen) | tcpdump -s 0(全量) |
-w <文件> |
写入 pcap 文件 | tcpdump -w capture.pcap |
-r <文件> |
读取 pcap 文件 | tcpdump -r capture.pcap |
-C <MB> |
文件大小限制,自动轮转 | tcpdump -C 100 -w capture.pcap |
-W <数量> |
轮转文件最大数量 | tcpdump -C 100 -W 10 -w capture.pcap |
-G <秒> |
按时间轮转文件 | tcpdump -G 3600 -w cap_%Y%m%d.pcap |
2.4 输出格式
输出显示控制选项:
| 选项 | 说明 | 示例 |
|---|---|---|
-n |
不解析主机名 | tcpdump -n |
-nn |
不解析主机名和端口名 | tcpdump -nn |
-v / -vv / -vvv |
递增详细度 | tcpdump -vvv |
-q |
简洁输出 | tcpdump -q |
-t |
不打印时间戳 | tcpdump -t |
-tttt |
可读日期格式 | tcpdump -tttt |
-e |
显示以太网头(MAC) | tcpdump -e |
-X |
十六进制 + ASCII | tcpdump -X |
-XX |
包含以太网头的十六进制 | tcpdump -XX |
-A |
ASCII 输出(适合 HTTP) | tcpdump -A |
-S |
绝对 TCP 序列号 | tcpdump -S |
-l |
行缓冲(可管道) | tcpdump -l |
-U |
包缓冲(实时刷新) | tcpdump -U |
💡 黄金法则 :生产环境永远加
-nn,禁止 DNS 反解,避免卡顿和额外流量。
三、BPF 过滤语法(核心技能)
3.1 过滤原语一览
BPF(Berkeley Packet Filter)是 tcpdump 过滤的引擎,由原语组合而成:
| 原语 | 说明 | 示例 |
|---|---|---|
host |
按主机 IP | host 192.168.1.1 |
src host |
源主机 | src host 10.0.0.5 |
dst host |
目的主机 | dst host 10.0.0.5 |
net |
按网段 | net 192.168.0.0/24 |
port |
按端口 | port 80 |
src port |
源端口 | src port 443 |
dst port |
目的端口 | dst port 53 |
portrange |
端口范围 | portrange 8000-8100 |
ether host |
按 MAC 地址 | ether host aa:bb:cc:dd:ee:ff |
3.2 协议快捷过滤
bash
tcpdump tcp # 所有 TCP 流量
tcpdump udp # 所有 UDP 流量
tcpdump icmp # 所有 ICMP(ping)
tcpdump arp # 所有 ARP
tcpdump ip6 # 所有 IPv6
3.3 逻辑组合(and / or / not)
bash
# AND ------ 两个条件同时匹配
tcpdump 'host 192.168.1.1 and port 80'
tcpdump 'tcp and src port 443 and dst host 10.0.0.1'
# OR ------ 任一条件匹配
tcpdump 'port 80 or port 443'
tcpdump 'host 192.168.1.1 or host 192.168.1.2'
# NOT ------ 排除
tcpdump 'not port 22'
tcpdump 'not arp and not icmp'
# 复合表达式(用引号包裹)
tcpdump 'tcp port 5432 and not host 10.0.0.99'
tcpdump 'port 53 and (udp or tcp)'
⚠️ 含空格或特殊字符的过滤表达式必须用单引号 包裹,防止 shell 解析
&、|、()等符号。
四、TCP 标志位过滤
TCP 标志位于 TCP 头部第 14 字节(偏移 13),是诊断连接问题的利器。
4.1 使用命名标志(推荐)
bash
# SYN 包(连接建立请求)
tcpdump 'tcp[tcpflags] & tcp-syn != 0'
# ACK 包
tcpdump 'tcp[tcpflags] & tcp-ack != 0'
# RST 包(连接重置/拒绝)
tcpdump 'tcp[tcpflags] & tcp-rst != 0'
# FIN 包(连接关闭)
tcpdump 'tcp[tcpflags] & tcp-fin != 0'
# PSH 包(推送数据)
tcpdump 'tcp[tcpflags] & tcp-push != 0'
# 纯 SYN(SYN=1, ACK=0)------ 新连接请求
tcpdump 'tcp[tcpflags] & tcp-syn != 0 and not tcp[tcpflags] & tcp-ack != 0'
# SYN-ACK(服务端响应)
tcpdump 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0'
# 连接状态变化包(SYN + FIN + RST)
tcpdump 'tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0'
4.2 使用位掩码(底层方式)
TCP 标志位掩码对照:
| 标志 | 位值 | 说明 |
|---|---|---|
| FIN | 0x01 |
连接关闭 |
| SYN | 0x02 |
连接建立 |
| RST | 0x04 |
连接重置 |
| PSH | 0x08 |
推送数据 |
| ACK | 0x10 |
确认 |
| URG | 0x20 |
紧急 |
bash
tcpdump 'tcp[13] & 2 != 0' # SYN
tcpdump 'tcp[13] & 16 != 0' # ACK
tcpdump 'tcp[13] & 18 != 0' # SYN-ACK
tcpdump 'tcp[13] & 1 != 0' # FIN
tcpdump 'tcp[13] & 4 != 0' # RST
tcpdump 'tcp[13] & 8 != 0' # PSH
五、BPF 字节偏移高级过滤
5.1 通用语法
proto[offset:size] operator value
- proto :
ip、ip6、tcp、udp、icmp、arp、ether - offset:协议头部内的字节偏移
- size :
1(字节)、2(短整型)、4(整型) - operator :
=、!=、<、<=、>、>=、&
5.2 IP 头部过滤
bash
# TTL 小于 10(经过很多跳)
tcpdump 'ip[8] < 10'
# IP 协议号过滤
tcpdump 'ip[9] = 6' # TCP(协议号 6)
tcpdump 'ip[9] = 17' # UDP(协议号 17)
tcpdump 'ip[9] = 1' # ICMP(协议号 1)
tcpdump 'ip[9] = 50' # ESP/IPsec(协议号 50)
# IP 包总长度
tcpdump 'ip[2:2] > 1400' # 大于 1400 字节
tcpdump 'ip[2:2] < 64' # 小于 64 字节
# 带 IP 选项的包(IHL > 5)
tcpdump 'ip[0] & 0x0f > 5'
# DSCP EF(Expedited Forwarding)
tcpdump 'ip[1] >> 2 = 46'
5.3 ICMP 类型过滤
bash
tcpdump 'icmp[0] == 8' # Echo Request(ping 请求)
tcpdump 'icmp[0] == 0' # Echo Reply(ping 响应)
tcpdump 'icmp[0] == 3' # Destination Unreachable(目标不可达)
tcpdump 'icmp[0] == 11' # Time Exceeded(路由超时/Traceroute)
5.4 包大小过滤
bash
tcpdump 'greater 1400' # 包长度 >= 1400
tcpdump 'less 64' # 包长度 <= 64
tcpdump 'len > 1000' # 包长度 > 1000
5.5 HTTP 载荷匹配
bash
# 抓取 HTTP GET 请求("GET " = 0x47455420)
tcpdump -A 'tcp dst port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'
# 抓取 HTTP POST 请求("POST" = 0x504f5354)
tcpdump -A 'tcp dst port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354'
# 抓取 HTTP 响应("HTTP" = 0x48545420)
tcpdump -A 'tcp src port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x48545420'
六、VLAN 与隧道过滤
6.1 VLAN 过滤
bash
# 任意 VLAN 标签帧
tcpdump 'vlan'
# 指定 VLAN ID
tcpdump 'vlan 10'
# QinQ(VLAN 200 嵌套在 VLAN 100 内)
tcpdump 'vlan 100 and vlan 200'
# VLAN 内的 IPv4 流量
tcpdump 'vlan and ip'
⚠️ BPF 中
vlan关键字会改变解码偏移 ,表达式中vlan应放在前面。
6.2 隧道封装过滤
bash
# VXLAN
tcpdump 'vxlan 0x7' # VNI 7
# Geneve
tcpdump 'geneve 0xb' # VNI 0xb
# MPLS
tcpdump 'mpls 100000' # MPLS 标签 100000
tcpdump 'mpls 100000 and mpls 1024' # 两层 MPLS 标签栈
# PPPoE
tcpdump 'pppoes' # 任意 PPPoE 会话
tcpdump 'pppoes 0x27 and ip' # PPPoE 会话 0x27 + IPv4
七、实战场景示例
7.1 Web 流量分析
bash
# 抓取所有 HTTP/HTTPS 流量
tcpdump -nn -i eth0 'tcp port 80 or tcp port 443'
# 实时查看 HTTP 请求内容(ASCII)
tcpdump -nn -A -i eth0 'tcp port 80'
# 只抓 HTTP 数据包(排除纯 SYN/FIN/ACK)
tcpdump -nn -i eth0 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
# 保存到文件供 Wireshark 分析
tcpdump -nn -s 0 -w http.pcap -i eth0 'tcp port 80 or tcp port 443'
7.2 DNS 排障
bash
# 所有 DNS 查询和响应
tcpdump -nn -i any 'udp port 53'
# 指定主机的 DNS 查询
tcpdump -nn -i eth0 'udp port 53 and src host 192.168.1.50'
# TCP DNS(区域传送、大响应)
tcpdump -nn -i eth0 'tcp port 53'
# DNS 到指定服务器
tcpdump -nn -i eth0 'udp port 53 and host 8.8.8.8'
7.3 TCP 连接诊断
bash
# 新连接请求(纯 SYN)
tcpdump -nn 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'
# 连接重置(RST)
tcpdump -nn 'tcp[tcpflags] & tcp-rst != 0'
# 服务端拒绝的连接(RST 来自服务端口)
tcpdump -nn 'tcp[tcpflags] & tcp-rst != 0 and src port 443'
# 连接关闭(FIN)
tcpdump -nn 'tcp[tcpflags] & tcp-fin != 0'
# 完整连接生命周期
tcpdump -nn 'tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) != 0'
7.4 DHCP 分析
bash
# DHCP 流量
tcpdump -nn -i eth0 'udp port 67 or udp port 68'
# DHCP 详细输出
tcpdump -nn -v -i eth0 'udp port 67 or udp port 68'
7.5 ARP 排障
bash
# 所有 ARP 流量
tcpdump -nn -e -i eth0 'arp'
# ARP 请求(who-has)
tcpdump -nn -e -i eth0 'arp[6:2] = 0x0001'
# ARP 响应
tcpdump -nn -e -i eth0 'arp[6:2] = 0x0002'
7.6 安全与事件响应
bash
# 排除自身 SSH 会话,减少噪声
tcpdump -nn -i eth0 'not port 22'
# SYN 洪水检测
tcpdump -nn -i eth0 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'
# 异常 ICMP(大包可能为 ICMP 隧道)
tcpdump -nn -i eth0 'icmp and greater 100'
# ARP 欺骗检测
tcpdump -nn -e -i eth0 'arp'
八、文件保存与读取
8.1 保存到 pcap 文件
bash
# 基本保存
tcpdump -nn -i eth0 -w capture.pcap 'port 80'
# 按文件大小轮转(每个 100MB)
tcpdump -nn -i eth0 -C 100 -w capture.pcap 'port 443'
# 按时间轮转(每小时一个文件)
tcpdump -nn -i eth0 -G 3600 -w 'capture_%H:%M:%S.pcap'
# 环形缓冲:保留最近 10 个 100MB 文件
tcpdump -nn -i eth0 -C 100 -W 10 -w capture.pcap
# 轮转后自动压缩
tcpdump -nn -i eth0 -C 100 -w capture.pcap -z gzip
8.2 读取 pcap 文件
bash
# 基本读取
tcpdump -nn -r capture.pcap
# 读取时应用过滤
tcpdump -nn -r capture.pcap 'host 192.168.1.10 and port 443'
# 读取并查看 HTTP 内容
tcpdump -nn -A -r capture.pcap 'tcp port 80'
# 转为文本供 grep 搜索
tcpdump -nn -A -r capture.pcap > capture.txt
# 统计源 IP 分布
tcpdump -nn -r capture.pcap | awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn
8.3 双路输出(终端 + 文件)
bash
# 同时写文件和输出到终端
tcpdump -nn -U -l -i eth0 -w - | tee capture.pcap | tcpdump -r -
九、时间戳格式控制
bash
# 默认:hh:mm:ss.fraction
tcpdump -nn -i eth0
# 完整日期 + 时间
tcpdump -nn -tttt -i eth0
# Unix 时间戳
tcpdump -nn -tt -i eth0
# 与上一个包的时间差
tcpdump -nn -ttt -i eth0
# 与第一个包的时间差
tcpdump -nn -ttttt -i eth0
# 不显示时间戳
tcpdump -nn -t -i eth0
# 显示包序号
tcpdump -nn --number -i eth0
十、生产环境最佳实践
10.1 黄金法则
生产抓包四步法:
1. 指定接口(-i any 如果不确定)
2. 加 -nn 禁止名字解析(避免卡顿)
3. 用 BPF 过滤收窄到关心的主机/端口/协议
4. 要么实时看,要么 -w 写 pcap 后用 Wireshark 分析
10.2 性能优化
性能问题与解决方案:
| 问题 | 解决方案 |
|---|---|
| 丢包 | 使用更精确的 BPF 过滤;-B 增大内核缓冲区;写入更快的磁盘 |
| pcap 文件过大 | 更精确过滤;-s 限制抓包长度;-C 按大小轮转 |
| CPU 占用高 | 避免 -A/-X 实时输出;用 -w 写文件不打印;内核层 BPF 过滤 |
| 磁盘写满 | 用 -C + -W 环形缓冲;监控磁盘空间 |
bash
# 增大内核缓冲区到 4096 KiB(减少丢包)
tcpdump -B 4096 -nn -i eth0 -w capture.pcap
# 打开设备后降权到 nobody(安全实践)
tcpdump -Z nobody -nn -i eth0 -w capture.pcap
# timeout 配合 tcpdump,防止忘关
timeout 60 tcpdump -nn -i eth0 -w /tmp/capture.pcap
10.3 抓包策略模板
bash
# ===== 快速排查(实时终端)=====
tcpdump -nn -c 100 -i eth0 'host 10.0.0.50 and port 443'
# ===== 事件响应(完整抓包)=====
tcpdump -nn -s 0 -i eth0 -w incident_$(date +%Y%m%d_%H%M%S).pcap \
'host 10.0.0.50 and (port 80 or port 443)'
# ===== 长期监控(轮转 + 压缩)=====
tcpdump -nn -s 0 -i eth0 -G 3600 -W 24 \
-w '/var/captures/hourly_%H.pcap' \
-z gzip \
'net 10.0.0.0/8'
# ===== 高流量抓包(大缓冲 + 写快速磁盘)=====
tcpdump -nn -s 0 -i eth0 -B 4096 -w highspeed.pcap 'tcp port 443'
# ===== 容器流量(Docker)=====
tcpdump -nn -i docker0
tcpdump -nn -i br-XXXXXXXXXXXX
10.4 常见踩坑
| 踩坑点 | 解决方案 |
|---|---|
tcpdump 无输出 |
用 -D 检查接口名;确认有 root 权限 |
-i any 看不到 VLAN 标签 |
改用具体物理接口抓包 |
-w 后终端无输出 |
这是设计行为,需要双路输出用管道方案 |
Shell 解析了 &、` |
、()` |
| 旧版默认 snaplen 太小 | 始终加 -s 0 确保全量抓取 |
| DNS 解析导致卡顿 | 始终加 -nn |
十一、学习路线图
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 入门 │───▶│ 进阶 │───▶│ 高级 │───▶│ 实战 │───▶│ 专家 │
│ │ │ │ │ │ │ │ │ │
│ 基本抓包 │ │ BPF组合 │ │ 字节偏移 │ │ 生产排障 │ │ 自动化脚本 │
│ host/port│ │ and/or/not│ │ proto[] │ │ pcap分析 │ │ 定时+告警 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
各阶段目标与练习内容:
| 阶段 | 目标 | 练习内容 |
|---|---|---|
| 入门 | 掌握基本抓包 | tcpdump -i any -nn,加 host/port 过滤 |
| 进阶 | BPF 逻辑组合 | and/or/not 组合,TCP 标志位 |
| 高级 | 协议头部字段 | proto[offset:size] 字节偏移过滤 |
| 实战 | 生产排障 | 保存 pcap + Wireshark 分析,轮转抓包 |
| 专家 | 自动化脚本 | tcpdump 配合 shell 脚本,定时抓包告警 |
十二、速查表
bash
# ===== 基础 =====
tcpdump -D # 列出接口
tcpdump -i any -nn # 监听所有接口,不解析
tcpdump -i eth0 -nn -c 100 # 抓 100 个包
# ===== 过滤 =====
tcpdump -nn host 10.0.0.1 # 指定主机
tcpdump -nn port 80 # 指定端口
tcpdump -nn tcp # 指定协议
tcpdump -nn 'host 10.0.0.1 and port 443' # 组合
# ===== TCP 标志 =====
tcpdump -nn 'tcp[tcpflags] & tcp-syn != 0' # SYN
tcpdump -nn 'tcp[tcpflags] & tcp-rst != 0' # RST
tcpdump -nn 'tcp[tcpflags] & tcp-fin != 0' # FIN
# ===== 保存/读取 =====
tcpdump -nn -w file.pcap # 保存
tcpdump -nn -r file.pcap # 读取
tcpdump -nn -r file.pcap 'port 80' # 读取并过滤
# ===== 输出格式 =====
tcpdump -nn -A 'port 80' # ASCII(HTTP)
tcpdump -nn -X # 十六进制+ASCII
tcpdump -nn -vvv # 最详细
tcpdump -nn -e # 含 MAC 地址
总结
本文核心要点:
-nn是第一原则 :生产环境永远加-nn,避免 DNS 反解导致卡顿- BPF 过滤在内核生效:先过滤再抓包,而不是抓全量再过滤,高流量下差异巨大
- TCP 标志位是排障利器 :
tcp[tcpflags]语法精准抓取 SYN/RST/FIN,快速定位连接问题 - 字节偏移解锁高级场景 :
proto[offset:size]语法可匹配 HTTP 载荷、IP 头字段、ICMP 类型等 - 生产抓包写文件,离线分析 :
-w保存 pcap,用 Wireshark 或tcpdump -r事后分析
📌 官方文档:
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。