关注CodingTechWork
tcpdump 简介
什么是 tcpdump
tcpdump 是 Linux/Unix 系统下最强大的网络抓包工具,它能够捕获和分析网络中传输的数据包。作为命令行版的 Wireshark,tcpdump 轻量、灵活、功能强大,是网络工程师、运维人员和开发人员排查网络问题的必备工具。
核心功能
| 功能 | 说明 |
|---|---|
| 数据包捕获 | 实时捕获经过网络接口的数据包 |
| 过滤规则 | 支持丰富的过滤表达式,精准定位目标包 |
| 协议解析 | 自动解析 TCP、UDP、ICMP、ARP 等协议 |
| 保存与读取 | 支持将捕获结果保存为 pcap 文件,离线分析 |
| 格式输出 | 支持多种输出格式,可与其他工具配合使用 |
工作原理

tcpdump 使用 libpcap 库,通过 BPF(Berkeley Packet Filter) 在内核态进行数据包过滤,只将匹配规则的数据包传递给用户态,极大地提高了效率。
安装与基础命令
安装方法
bash
# CentOS / RHEL
yum install -y tcpdump
# Ubuntu / Debian
apt-get install -y tcpdump
# Alpine Linux
apk add tcpdump
# macOS(已预装)
# 或使用 brew 升级
brew install tcpdump
权限要求
tcpdump 需要 root 权限才能抓包:
bash
# 使用 sudo
sudo tcpdump
# 或者切换到 root
su - root
tcpdump
基础命令格式
bash
tcpdump [选项] [过滤表达式]
命令行参数详解
核心参数一览
| 参数 | 说明 | 示例 |
|---|---|---|
-i |
指定网络接口 | -i eth0 |
-c |
捕获指定数量的包后退出 | -c 100 |
-n |
不解析主机名(显示 IP) | -n |
-nn |
不解析主机名和端口名 | -nn |
-s |
设置抓包长度(snaplen) | -s 1500 |
-v/-vv/-vvv |
详细输出级别 | -vv |
-w |
写入文件 | -w capture.pcap |
-r |
从文件读取 | -r capture.pcap |
-e |
显示链路层头部 | -e |
-X |
以十六进制和 ASCII 显示 | -X |
-A |
以 ASCII 显示 | -A |
-t |
不显示时间戳 | -t |
-tt |
显示 Unix 时间戳 | -tt |
-D |
列出可用网络接口 | -D |
-q |
安静模式(简洁输出) | -q |
-G |
按时间滚动文件 | -G 3600 |
-C |
按大小滚动文件 | -C 100 |
-W |
限制滚动文件数量 | -W 10 |
参数详细说明
接口选择 -i
bash
# 查看所有可用接口
tcpdump -D
# 输出示例:
# 1.eth0
# 2.lo
# 3.docker0
# 指定接口抓包
tcpdump -i eth0
# 抓取所有接口(需要特定支持)
tcpdump -i any
数量控制 -c
bash
# 只抓 100 个包
tcpdump -c 100 -i eth0
# 抓取 1000 个包后自动退出
tcpdump -c 1000 -w capture.pcap
名称解析 -n / -nn
bash
# 不解析主机名(显示 IP)
tcpdump -n
# 输出对比:
# 不加 -n: 192.168.1.100 -> server.example.com
# 加 -n: 192.168.1.100 -> 10.0.0.1
# 不解析主机名和端口名
tcpdump -nn
# 输出对比:
# 不加 -nn: 192.168.1.100.443 -> 10.0.0.1.8080
# 加 -nn: 192.168.1.100.80 -> 10.0.0.1.3306
抓包长度 -s
bash
# 默认抓取 262144 字节
# 设置抓取前 100 字节
tcpdump -s 100
# 抓取完整数据包
tcpdump -s 0
# 通常设置为 MTU 大小
tcpdump -s 1500
输出格式 -X / -A / -xx
bash
# 十六进制 + ASCII 输出
tcpdump -X -c 10
# 只输出 ASCII
tcpdump -A -c 10
# 十六进制输出(包含链路层)
tcpdump -xx -c 10
高级参数
bash
# 限制抓包时间
timeout 30 tcpdump -i eth0
# 环形缓冲区:每 100MB 滚动,保留 5 个文件
tcpdump -G 3600 -C 100 -W 5 -w capture_%Y%m%d_%H%M%S.pcap
# 缓冲区大小(减少丢包)
tcpdump -B 4096
# 不进入混杂模式
tcpdump -p
# 监听模式下不输出任何信息
tcpdump -l
过滤表达式详解
类型过滤
| 类型 | 关键字 | 示例 | 说明 |
|---|---|---|---|
| 主机 | host |
host 192.168.1.100 |
指定 IP 地址 |
| 网络 | net |
net 192.168.1.0/24 |
指定网段 |
| 端口 | port |
port 80 |
指定端口 |
| 端口范围 | portrange |
portrange 8000-9000 |
端口范围 |
方向过滤
| 方向 | 关键字 | 示例 | 说明 |
|---|---|---|---|
| 源地址 | src |
src host 192.168.1.100 |
源 IP |
| 目标地址 | dst |
dst host 192.168.1.100 |
目标 IP |
| 源端口 | src port |
src port 8080 |
源端口 |
| 目标端口 | dst port |
dst port 443 |
目标端口 |
协议过滤
| 协议 | 关键字 | 示例 |
|---|---|---|
| TCP | tcp |
tcp |
| UDP | udp |
udp |
| ICMP | icmp |
icmp |
| ARP | arp |
arp |
| HTTP | tcp port 80 |
tcp port 80 |
| HTTPS | tcp port 443 |
tcp port 443 |
| DNS | udp port 53 |
udp port 53 |
逻辑运算符
| 运算符 | 符号 | 英文 | 示例 |
|---|---|---|---|
| 与 | && |
and |
host 192.168.1.100 and port 80 |
| 或 | ` | ` | |
| 非 | ! |
not |
not port 22 |
TCP 标志位过滤
| 标志位 | 过滤表达式 | 说明 |
|---|---|---|
| SYN | tcp[tcpflags] & tcp-syn != 0 |
同步包 |
| ACK | tcp[tcpflags] & tcp-ack != 0 |
确认包 |
| FIN | tcp[tcpflags] & tcp-fin != 0 |
结束包 |
| RST | tcp[tcpflags] & tcp-rst != 0 |
重置包 |
| PSH | tcp[tcpflags] & tcp-push != 0 |
推送包 |
| SYN-ACK | `tcp[tcpflags] & (tcp-syn | tcp-ack) == (tcp-syn |
数据包内容过滤
bash
# 过滤包含特定字符串的包
tcpdump -A -s 0 | grep "GET /"
# 使用 BPF 过滤 HTTP GET 请求
tcpdump 'tcp[20:4] = 0x47455420'
# 过滤指定长度的包
tcpdump 'greater 1000'
# 过滤指定范围的数据包
tcpdump 'less 64'
输出格式详解
标准输出格式
12:34:56.789012 IP 192.168.1.100.54321 > 10.0.0.1.80: Flags [S], seq 123456789, win 65535, length 0
字段说明
| 字段 | 说明 |
|---|---|
| 12:34:56.789012 | 时间戳(时:分:秒.微秒) |
| IP | 网络层协议(IP / IPv6 / ARP) |
| 192.168.1.100.54321 | 源地址.源端口 |
| > | 方向标识 |
| 10.0.0.1.80 | 目标地址.目标端口 |
| Flags [S] | TCP 标志位(S=SYN, .=ACK, F=FIN, R=RST, P=PSH) |
| seq 123456789 | 序列号 |
| ack 987654321 | 确认号(有 ACK 标志时) |
| win 65535 | 窗口大小 |
| length 0 | 数据长度 |
TCP 标志位表示
| 标志 | 显示 | 含义 |
|---|---|---|
| SYN | [S] |
建立连接 |
| SYN-ACK | [S.] |
响应 SYN |
| ACK | [.] |
确认 |
| FIN | [F] |
关闭连接 |
| FIN-ACK | [F.] |
关闭确认 |
| RST | [R] |
连接重置 |
| PSH | [P] |
推送数据 |
不同协议的输出格式
TCP 包
12:34:56.789012 IP 192.168.1.100.54321 > 10.0.0.1.80: Flags [S], seq 123456789, win 65535, options [mss 1460], length 0
UDP 包
12:34:56.789012 IP 192.168.1.100.54321 > 10.0.0.1.53: UDP, length 48
ICMP 包(ping)
12:34:56.789012 IP 192.168.1.100 > 10.0.0.1: ICMP echo request, id 12345, seq 1, length 64
12:34:56.789123 IP 10.0.0.1 > 192.168.1.100: ICMP echo reply, id 12345, seq 1, length 64
ARP 包
12:34:56.789012 ARP, Request who-has 10.0.0.1 tell 192.168.1.100, length 28
文件大小控制详解
使用 -c 和 -s 精确控制文件大小
通过 -s 设置抓包长度,结合 -c 设置包数量,可以精确控制最终文件大小。
核心原理:
文件大小 ≈ 抓包长度(snaplen) × 包数量(c)
计算公式:
bash
# 目标文件大小 = snaplen × packet_count
# 实际文件会有少量额外开销(pcap文件头24字节、每个包头部16字节)
# 100MB = 100 × 1024 × 1024 = 104,857,600 字节
# 如果设置 snaplen = 1500 字节
# 需要的包数量 = 104857600 / 1500 ≈ 69905
# 如果设置 snaplen = 1000 字节
# 需要的包数量 = 104857600 / 1000 = 104857
基本命令:
bash
# 设置 snaplen=1500,抓取 70000 个包,文件约 100MB
tcpdump -i eth0 -s 1500 -c 70000 -w capture.pcap
# 设置 snaplen=1000,抓取 105000 个包,文件约 100MB
tcpdump -i eth0 -s 1000 -c 105000 -w capture.pcap
# 设置 snaplen=500,抓取 210000 个包,文件约 100MB
tcpdump -i eth0 -s 500 -c 210000 -w capture.pcap
不同 snaplen 的包数量参考表
| 目标大小 | snaplen | 包数量 | 实际大小(约) | 适用场景 |
|---|---|---|---|---|
| 100MB | 1500 | 69,900 | 100.6MB | 完整以太网帧 |
| 100MB | 1000 | 104,800 | 100.3MB | 一般应用数据 |
| 100MB | 800 | 131,000 | 100.2MB | 节省空间 |
| 100MB | 500 | 209,700 | 100.1MB | 只抓头部 |
| 100MB | 200 | 524,200 | 100.0MB | 最小抓包 |
| 100MB | 96 | 1,092,200 | 100.0MB | 只抓 IP/TCP 头 |
精确计算脚本
bash
#!/bin/bash
# tcpdump_sized.sh - 通过 -s 和 -c 精确控制文件大小
INTERFACE="eth0"
TARGET_SIZE_MB=100
SNAPLEN=1500
# 计算需要的包数量(留出头部开销空间)
TARGET_BYTES=$((TARGET_SIZE_MB * 1024 * 1024))
OVERHEAD=24
PACKET_OVERHEAD=16
AVAILABLE_BYTES=$((TARGET_BYTES - OVERHEAD))
PACKET_SIZE=$((SNAPLEN + PACKET_OVERHEAD))
NEEDED_PACKETS=$((AVAILABLE_BYTES / PACKET_SIZE))
echo "目标大小: ${TARGET_SIZE_MB}MB"
echo "抓包长度(snaplen): ${SNAPLEN} 字节"
echo "每包实际占用: ${PACKET_SIZE} 字节"
echo "预估包数量: ${NEEDED_PACKETS}"
OUTPUT_FILE="capture_$(date +%Y%m%d_%H%M%S).pcap"
tcpdump -i $INTERFACE -s $SNAPLEN -c $NEEDED_PACKETS -w $OUTPUT_FILE
ACTUAL_SIZE=$(stat -c%s "$OUTPUT_FILE")
ACTUAL_SIZE_MB=$((ACTUAL_SIZE / 1024 / 1024))
echo "实际文件大小: ${ACTUAL_SIZE_MB}MB"
动态检测实际包大小方案
bash
#!/bin/bash
# tcpdump_dynamic_sized.sh - 动态检测实际包大小
INTERFACE="eth0"
TARGET_MB=100
SAMPLE_COUNT=5000
# 先抓样本来计算平均包大小
SAMPLE_FILE="/tmp/sample.pcap"
tcpdump -i $INTERFACE -c $SAMPLE_COUNT -s 65535 -w $SAMPLE_FILE 2>/dev/null
if [ ! -f "$SAMPLE_FILE" ]; then
echo "无流量,使用默认值"
AVG_PACKET_SIZE=500
else
SAMPLE_SIZE=$(stat -c%s "$SAMPLE_FILE")
OVERHEAD=24
PACKET_OVERHEAD=16
DATA_SIZE=$((SAMPLE_SIZE - OVERHEAD - SAMPLE_COUNT * PACKET_OVERHEAD))
if [ $DATA_SIZE -lt 0 ]; then
DATA_SIZE=500
fi
AVG_PACKET_SIZE=$((DATA_SIZE / SAMPLE_COUNT))
echo "平均包数据大小: ${AVG_PACKET_SIZE} 字节"
fi
# 设置 snaplen 为平均包大小
SNAPLEN=$((AVG_PACKET_SIZE + 100))
[ $SNAPLEN -gt 1500 ] && SNAPLEN=1500
TARGET_BYTES=$((TARGET_MB * 1024 * 1024))
PACKET_OVERHEAD=16
FILE_HEADER=24
PACKET_TOTAL=$((SNAPLEN + PACKET_OVERHEAD))
NEEDED_PACKETS=$(((TARGET_BYTES - FILE_HEADER) / PACKET_TOTAL))
echo "目标大小: ${TARGET_MB}MB"
echo "使用 snaplen: ${SNAPLEN} 字节"
echo "需要包数量: ${NEEDED_PACKETS}"
OUTPUT_FILE="capture_$(date +%Y%m%d_%H%M%S).pcap"
tcpdump -i $INTERFACE -s $SNAPLEN -c $NEEDED_PACKETS -w $OUTPUT_FILE
关于 -C 和 -W 1 的说明
bash
# 这个命令会循环覆盖,不会退出
tcpdump -i eth0 -C 100 -W 1 -w capture.pcap
# 工作流程:
# 1. 文件达到 100MB 时,创建 capture.pcap 的新文件
# 2. 由于 -W 1,会覆盖原来的文件
# 3. 继续抓包,无限循环,不会退出
如果需要使用 -C 并自动退出,可以配合脚本:
bash
# 使用脚本监控并停止
tcpdump -i eth0 -C 100 -W 10 -w capture.pcap &
PID=$!
sleep 5
while true; do
if [ -f capture.pcap ]; then
SIZE=$(stat -c%s capture.pcap 2>/dev/null || echo 0)
if [ $SIZE -ge $((100 * 1024 * 1024)) ]; then
kill $PID
break
fi
fi
sleep 1
done
一行命令快速实现
bash
# 快速计算并抓包(snaplen=1500,目标100MB)
SNAPLEN=1500; TARGET_MB=100; PACKETS=$((TARGET_MB * 1024 * 1024 / (SNAPLEN + 16))); tcpdump -i eth0 -s $SNAPLEN -c $PACKETS -w capture.pcap
# 完整包抓取(snaplen=0,目标100MB,需要估算)
tcpdump -i eth0 -s 0 -c 60000 -w capture.pcap
# 只抓头部(snaplen=96,目标100MB)
tcpdump -i eth0 -s 96 -c 1092200 -w capture.pcap
文件大小控制方案对比
| 方案 | 精确度 | 是否退出 | 复杂度 |
|---|---|---|---|
-s + -c |
高 | 自动退出 | 低 |
-C + -W 1 |
精确 | 循环覆盖 | 低 |
-C + 监控脚本 |
精确 | 可退出 | 中 |
命令示例
基础抓包示例
bash
# 抓取所有包
tcpdump -i eth0
# 只抓 10 个包
tcpdump -i eth0 -c 10
# 抓取指定主机的包
tcpdump -i eth0 host 192.168.1.100
# 抓取指定端口
tcpdump -i eth0 port 80
# 抓取 HTTP 流量(不解析名称)
tcpdump -i eth0 -nn port 80
# 抓取 HTTPS 流量
tcpdump -i eth0 -nn port 443
# 抓取指定网段
tcpdump -i eth0 net 192.168.1.0/24
组合过滤示例
bash
# 抓取 192.168.1.100 的 HTTP 流量
tcpdump -i eth0 host 192.168.1.100 and port 80
# 抓取除 SSH 外的所有流量
tcpdump -i eth0 not port 22
# 抓取源或目标端口是 80 或 443
tcpdump -i eth0 port 80 or port 443
# 抓取源 IP 是 192.168.1.100 的包
tcpdump -i eth0 src host 192.168.1.100
# 抓取目标 IP 是 10.0.0.1 的包
tcpdump -i eth0 dst host 10.0.0.1
TCP 标志位过滤示例
bash
# 抓取 SYN 包(TCP 握手开始)
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'
# 抓取 SYN-ACK 包
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack != 0'
# 抓取 RST 包(连接重置)
tcpdump -i eth0 'tcp[tcpflags] & tcp-rst != 0'
# 抓取 FIN 包(连接关闭)
tcpdump -i eth0 'tcp[tcpflags] & tcp-fin != 0'
保存和读取文件
bash
# 保存到文件
tcpdump -i eth0 -w capture.pcap
# 保存到文件(不显示输出)
tcpdump -i eth0 -w capture.pcap -q
# 读取文件
tcpdump -r capture.pcap
# 读取文件并过滤
tcpdump -r capture.pcap port 80
# 保存为可读格式
tcpdump -i eth0 -A > capture.txt
# 文件滚动保存(每 100MB 一个新文件)
tcpdump -i eth0 -C 100 -W 10 -w capture.pcap
高级抓包示例
bash
# 抓取 HTTP GET 请求
tcpdump -i eth0 -s 0 -A 'tcp[20:4] = 0x47455420'
# 抓取 HTTP POST 请求
tcpdump -i eth0 -s 0 -A 'tcp[20:4] = 0x504f5354'
# 抓取 DNS 查询
tcpdump -i eth0 -nn port 53
# 抓取 ping 包
tcpdump -i eth0 icmp
# 抓取大于 1000 字节的包
tcpdump -i eth0 greater 1000
# 抓取 IPv6 流量
tcpdump -i eth0 ip6
时间控制示例
bash
# 按时间滚动文件(每小时)
tcpdump -G 3600 -w capture_%Y%m%d_%H%M%S.pcap
# 按大小滚动并限制文件数
tcpdump -C 100 -W 5 -G 3600 -w capture_%Y%m%d_%H%M%S.pcap
# 抓取 30 秒后自动停止
timeout 30 tcpdump -i eth0 -w capture.pcap
使用场景实战
场景一:排查网络连接问题
bash
# 抓取 SYN 包,查看 TCP 握手情况
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0'
# 抓取特定端口的完整握手过程
tcpdump -i eth0 port 8080 -nn
# 输出分析:
# 正常三次握手:
# 12:34:56.1 IP 10.0.0.1.12345 > 10.0.0.2.8080: Flags [S]
# 12:34:56.2 IP 10.0.0.2.8080 > 10.0.0.1.12345: Flags [S.]
# 12:34:56.2 IP 10.0.0.1.12345 > 10.0.0.2.8080: Flags [.]
#
# 异常情况:
# 12:34:56.1 IP 10.0.0.1.12345 > 10.0.0.2.8080: Flags [S]
# 12:34:56.1 IP 10.0.0.2.8080 > 10.0.0.1.12345: Flags [R] # 端口未监听
场景二:分析 HTTP 请求响应
bash
# 抓取 HTTP 请求并查看内容
tcpdump -i eth0 -A -s 0 port 80
# 输出示例:
# GET /api/users HTTP/1.1
# Host: example.com
# User-Agent: curl/7.68.0
# HTTP/1.1 200 OK
# Content-Type: application/json
# {"status":"ok"}
场景三:排查 DNS 解析问题
bash
# 抓取所有 DNS 查询
tcpdump -i eth0 -nn port 53
# 详细查看 DNS 响应
tcpdump -i eth0 -vv -s 0 port 53
# 抓取特定域名的 DNS 查询
tcpdump -i eth0 -nn 'udp port 53 and host 8.8.8.8'
场景四:排查 TCP 重传和丢包
bash
# 抓取 TCP 重传包
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack != 0'
# 抓取重传和重复 ACK
tcpdump -i eth0 'tcp[13] & 4 != 0'
# 分析窗口大小变化
tcpdump -i eth0 -vv 'tcp and host 10.0.0.1'
场景五:生产环境长时间抓包
bash
#!/bin/bash
# 生产环境安全抓包脚本
INTERFACE="eth0"
OUTPUT_DIR="/var/log/tcpdump"
MAX_SIZE_MB=100
MAX_FILES=10
SNAPLEN=1500
mkdir -p $OUTPUT_DIR
# 使用 -s 和 -c 精确控制文件大小
# 先计算需要的包数量
TARGET_BYTES=$((MAX_SIZE_MB * 1024 * 1024))
PACKET_OVERHEAD=16
FILE_HEADER=24
PACKET_SIZE=$((SNAPLEN + PACKET_OVERHEAD))
NEEDED_PACKETS=$(((TARGET_BYTES - FILE_HEADER) / PACKET_SIZE))
# 后台抓包,降低优先级
nice -n 10 tcpdump -i $INTERFACE \
-s $SNAPLEN \
-c $NEEDED_PACKETS \
-n \
-w $OUTPUT_DIR/capture_$(date +%Y%m%d_%H%M%S).pcap \
not port 22
常见问题与解决
抓不到包
bash
# 问题1:网卡不在混杂模式
tcpdump -i eth0 -p
# 问题2:BPF 过滤太严格
tcpdump -i eth0 -nn
# 问题3:权限不足
sudo tcpdump -i eth0
丢包严重
bash
# 增加内核缓冲区
tcpdump -B 4096
# 减少抓包长度
tcpdump -s 100
# 使用更精确的 BPF 过滤
tcpdump 'host 192.168.1.100 and port 80'
# 降低输出频率
tcpdump -q
# 调整系统参数
echo 4194304 > /proc/sys/net/core/rmem_max
文件太大
bash
# 限制抓包长度
tcpdump -s 100
# 使用 -c 限制包数量
tcpdump -c 10000 -w capture.pcap
# 使用 -s 和 -c 组合精确控制
tcpdump -s 1500 -c 70000 -w capture.pcap
# 压缩保存
tcpdump -i eth0 -w - | gzip > capture.pcap.gz
常用命令
bash
# 最常用命令
tcpdump -i eth0 -nn -s 0 -c 1000 -w capture.pcap
# 精确控制文件大小(100MB,snaplen=1500)
tcpdump -i eth0 -s 1500 -c 70000 -w capture.pcap
# 排查 HTTP 问题
tcpdump -i eth0 -A -s 0 port 80
# 排查网络连接
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-syn != 0'
# 排查 DNS
tcpdump -i eth0 -nn port 53
# 实时查看抓包
tcpdump -i eth0 -nn -c 100
# 分析 pcap 文件
tcpdump -r capture.pcap -nn | head -100