Tcpdump

tcpdump 是一个强大的命令行工具,用于捕获和分析网络流量。它是网络管理员、安全专业人员和开发人员在 Linux/Unix-like 系统上进行网络故障排除、监控、安全审计和协议分析的必备工具。

你可以把 tcpdump 想象成是给你的网卡安装了一双“透视眼”

它让你实时看到数据包是怎么在网络中穿梭的,从 MAC 层到 IP 层再到应用层,全都一览无遗。你不仅能“看到”别人说了什么(数据内容),还能知道他们从哪里来、去哪儿、用了哪种语言(协议)、走了哪条路(路由)

一、Tcpdump 核心概念:捕获与过滤

tcpdump 的核心功能:

  1. 实时数据包捕获tcpdump 可以实时地从网络接口捕获数据包,并将其内容打印到标准输出或保存到文件。
  2. 丰富的过滤表达式:它支持使用 BPF (Berkeley Packet Filter) 语法创建高度定制的过滤规则,只捕获你感兴趣的流量。
  3. 协议解析tcpdump 可以解析多种网络协议的头部信息,包括 Ethernet, IP, TCP, UDP, ICMP, ARP, DNS, HTTP 等,并以可读的格式显示。
  4. 远程捕获:由于是命令行工具,tcpdump 非常适合在没有图形界面的远程服务器上运行。
  5. 离线分析:可以将捕获的数据包保存为 .pcap 文件,供后续使用 tcpdump 本身或 Wireshark 等图形工具进行离线分析

二、Tcpdump 基本用法与常用选项

  • 基本语法sudo tcpdump [选项] [过滤表达式]

由于 tcpdump 需要访问网络接口的原始数据,因此通常需要 root 权限运行(使用 sudo)。

核心命令行选项:

  • -i <interface>:指定要监听的网络接口。
    • 示例:sudo tcpdump -i eth0 (监听 eth0 接口)
    • 示例:sudo tcpdump -i any (监听所有可用接口,但不使用混杂模式)
    • 可以通过 tcpdump -Dtcpdump --list-interfaces 查看所有可用接口。
  • -n:不解析主机名(显示 IP 地址)。这可以加快输出速度,并避免 DNS 解析带来的额外网络流量。
  • -nn:不解析主机名和端口号(显示 IP 地址和端口号)。
  • -v/-vv/-vvv :显示更详细的包信息。
  • -c <count>:指定捕获数据包的数量,达到数量后自动停止。
    • 示例:sudo tcpdump -c 10 -i eth0 (捕获 eth0 上的前10个数据包)
  • -w <file>:将捕获的数据包写入文件,文件格式为 .pcap
    • 示例:sudo tcpdump -w capture.pcap -i eth0 (将捕获的数据保存到 capture.pcap)
  • -r <file>:从指定的 .pcap 文件中读取数据包进行分析。
    • 示例:sudo tcpdump -r capture.pcap (读取 capture.pcap 并显示内容)
  • -v, -vv, -vvv:提高输出的详细程度(verbosity)。v 越多,显示的信息越多,包括 TTL、ID、报文长度等。
  • -A:以 ASCII 格式打印每个数据包的内容(用于查看 HTTP 等文本协议的数据)。
  • -X:以十六进制和 ASCII 格式打印每个数据包的内容(包括链路层头部)。
  • -XX:同 -X,但包含链路层头部。
  • -s <snaplen>:设置捕获的数据包的快照长度(snap length),即每个数据包捕获的字节数。默认通常很大 (e.g., 65535),这意味着捕获整个数据包。设置为 0 则表示捕获整个数据包。
    • 示例:sudo tcpdump -s 100 -i eth0 (只捕获每个数据包的前100字节)
  • -e:在输出中显示链路层头部信息(如 MAC 地址)。
  • -l:使输出行缓冲。这在将 tcpdump 的输出通过管道传递给其他命令(如 grep)时很有用。

三、精通过滤表达式 (BPF 语法)

过滤表达式是 tcpdump 最强大的功能之一。你可以通过它们来精确地指定你想要捕获的流量。过滤器可以由一个或多个“原语”(primitives)组成,并通过逻辑运算符 and (&&), or (||), not (!) 进行组合。

BPF(Berkeley Packet Filter) 是一种用于过滤网络数据包的表达式语言,最初用于 Unix 系统的 tcpdumplibpcap,现在广泛用于:

  • tcpdump
  • iptables/nftables
  • Wireshark
  • eBPF(扩展 BPF,用于内核监控、安全、可观测性)

BPF 表达式基本结构

BPF 表达式由若干部分构成,一般顺序如下:

1
[协议] [方向] [匹配类型] [操作符] [值]

这几项不是必须全部出现,可以按需组合。

  1. 类型(Type)限定符:指定要匹配的“类型”。

    • host <hostname/ip>:匹配特定主机。

      • tcpdump host 192.168.1.1 (捕获与 192.168.1.1 相关的所有流量)
    • net <network/cidr>:匹配特定网络。

      • tcpdump net 192.168.1.0/24 (捕获 192.168.1.0/24 网络的所有流量)
    • port <port_number>:匹配特定端口。

      • tcpdump port 80 (捕获所有端口 80 的流量)
    • portrange <port1-port2>:匹配端口范围。

      • tcpdump portrange 1024-65535
  2. 方向(Direction)限定符:指定流量方向。

    • src:源地址。
    • dst:目的地址。
    • src or dst: (默认)任意方向。
    • src and dst:必须同时匹配。
      • tcpdump src host 192.168.1.10 (捕获源自 192.168.1.10 的流量)
      • tcpdump dst port 22 (捕获目标端口为 22 的流量)
  3. 协议(Protocol)限定符:指定协议类型。

    • ether (以太网帧)
    • ip (IPv4)
    • ip6 (IPv6)
    • arp (ARP)
    • rarp (RARP)
    • tcp (TCP)
    • udp (UDP)
    • icmp (ICMP)
    • vlan (VLAN)
      • tcpdump tcp (捕获所有 TCP 流量)
      • tcpdump udp port 53 (捕获 UDP 协议的 DNS 流量)

逻辑运算符:

  • and (&&):与

  • or (||):或

  • not (!):非

  • = / !=

  • > / <

  • &(按位与)

  • 组合示例tcp and host 192.168.1.10 and port 80 (注意括号转义 \( \) 在 shell 中使用时的必要性)

组合过滤表达式:

使用 and, or, not (或它们的符号 &&, ||, !) 来组合过滤条件。

当使用逻辑运算符时,建议使用括号 () 来明确优先级,但需要注意在 shell 中使用括号时需要进行转义 ( 和 )。

  • tcpdump host 192.168.1.1 and port 22
  • tcpdump src host 192.168.1.10 and not dst port 22
  • tcpdump 'tcp port 80 or tcp port 443' (注意引号,避免 shell 解析 |)
  • tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) == tcp-syn' (捕获 SYN 包)
  • tcpdump icmp[icmptype] == icmp-echo (捕获 ping 请求)

字节偏移访问(BPF 原生命令)

BPF 支持通过偏移访问协议头部的特定字段,这种方式功能强大,能直接检查如 TTL、flags、窗口大小等底层内容。

格式如下:

1
2
proto[offset[:len]] op value
[协议名][偏移位置[:长度]] 操作符 值

例如:

1
2
3
4
5
6
tcp[13] & 0x02 != 0        # SYN
tcp[13] & 0x12 = 0x12 # SYN+ACK
tcp[13] & 0x04 != 0 # RST
tcp[13] = 0x02 # 仅 SYN
tcp[14:2] = 4096 # 窗口大小
ip[8] < 64 # TTL 小于 64

ip[8] 是 IPv4 报文中的 TTL 字段

tcp[13] 是 TCP 报文中的 flags 字节

tcp[14:2] 是 TCP 窗口大小字段(两个字节)

TCP Flags参考表(TCP/IP 报文结构):

Flag16进制描述
FIN0x01连接终止
SYN0x02发起连接
RST0x04连接复位
PSH0x08推送数据
ACK0x10应答确认
URG0x20紧急数据

组合示例:

1
2
tcpdump 'ip[9] = 6 and tcp[13] & 0x12 = 0x12 and port 80'
# 抓 TCP 包(协议号 = 6)且 同时带 SYN+ACK 标志 且 端口为 80(即服务端回应)

四、tcpdump 输出

tcpdump 的输出格式可能看起来很复杂,但它遵循一定的模式。一般信息包括:

1
时间戳 协议 源IP.源端口 > 目的IP.目的端口 标志: [数据] 序列号 确认号 窗口大小 选项 [数据长度]

例子:

1
14:30:00.123456 IP 192.168.1.10.54321 > 192.168.1.1.80: Flags [S], seq 1234567, win 29200, options [mss 1460,sackOK,TS val 123456 ecr 0], length 0
  • 14:30:00.123456:数据包捕获的时间戳。
  • IP:网络层协议(IPv4)。
  • 192.168.1.10.54321:源 IP 地址和源端口。
  • >:方向指示符。
  • 192.168.1.1.80:目的 IP 地址和目的端口。
  • Flags [S]:TCP 标志。S 表示 SYN (同步)。常见标志有 S (SYN), . (ACK), P (PSH), F (FIN), R (RST)。
  • seq 1234567:TCP 序列号。
  • win 29200:TCP 窗口大小。
  • options [...]:TCP 选项,如 MSS (最大段大小), SACK (选择性确认) 等。
  • length 0:应用层数据长度(不包括头部)。

五、Tcpdump 实践:常见网络问题诊断

核心理念:通过观察数据包的进出、序列号、标志位和时间戳来判断问题。

常用诊断用例与命令示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 1. 检查特定主机的所有流量:快速判断主机网络活动。
sudo tcpdump -n -i eth0 host 203.0.113.5

# 2. 检查特定端口的流量:验证服务是否接收到请求或发出响应。
sudo tcpdump -n -i eth0 port 80
sudo tcpdump -n -i eth0 dst port 80 # 仅入站
sudo tcpdump -n -i eth0 src port 80 # 仅出站

# 3. 检查 TCP 连接的三次握手:诊断连接建立失败原因。
sudo tcpdump -n -i eth0 tcp and host 192.168.1.100 and port 22

# 4. 捕获 TCP RST (连接重置) 或 FIN (连接结束) 包:识别异常断开。
sudo tcpdump -n -i eth0 'tcp[tcpflags] & (tcp-rst|tcp-fin) != 0'

# 5. 检查 DNS 查询流量:排查域名解析问题。
sudo tcpdump -n -i eth0 udp port 53

# 6. 捕获 HTTP 流量 (显示 ASCII 内容):简单查看 Web 请求/响应。
sudo tcpdump -n -i eth0 port 80 -A

# 7. 分析网络延迟和丢包的用例:
# 延迟:通过 SYN/SYN-ACK 时间戳判断往返时间。
# 丢包:观察 TCP 重传、序列号跳跃、ICMP 不可达消息。
sudo tcpdump -n -i eth0 host <IP> and port <PORT> -vvv # 用于详细分析时间戳和序列号
sudo tcpdump -n -i eth0 icmp # 捕获ICMP错误消息

# 8. 验证防火墙规则是否按预期工作:
# 允许规则:看请求和响应是否都出现。
# 拒绝/丢弃规则:看请求是否到达但无响应 (DROP) 或收到 RST/ICMP (REJECT)。
sudo tcpdump -n -i eth0 tcp dst port 22 # 看是否收到SYN-ACK或RST

# 9. 捕获特定主机和端口相关的 RST 包
# 示例:监听 eth0 接口上与 203.0.113.5 和 8080 端口相关的 RST 包。
sudo tcpdump -n -i eth0 'tcp[tcpflags] & tcp-rst != 0' and host 203.0.113.5 and port 8080
# 10. 将捕获的数据保存到 .pcap 文件以供离线分析
# 示例:监听 eth0 接口上所有流量,并将数据保存到 my_capture.pcap 文件中。
# 按 Ctrl+C 停止捕获。
sudo tcpdump -n -i eth0 -w my_capture.pcap

# 11. 读取并分析已保存的 .pcap 文件
# 示例:读取并显示 my_capture.pcap 文件的内容。

sudo tcpdump -r my_capture.pcap
# 12. 将捕获的数据保存到 .pcap 文件以供离线分析

# 示例:监听 eth0 接口上所有流量,并将数据保存到 my_capture.pcap 文件中。
# 按 Ctrl+C 停止捕获。
sudo tcpdump -n -i eth0 -w my_capture.pcap

六、优化 Tcpdump 使用体验与进阶技巧

  • 屏幕输出控制:
    • Ctrl+S (暂停) / Ctrl+Q (恢复):最简单的实时控制。
    • 通过管道传输到 lesssudo tcpdump -l -i eth0 | less (分页查看)。
  • 实时保存与文件轮换:
    • -w <filename.pcap>:保存到文件。
    • -C <size> / -W <count> / -G <seconds>:实现文件自动轮换,管理存储空间。
    • 示例:sudo tcpdump -n -i eth0 -w /var/log/tcpdump/capture_%Y%m%d_%H%M.pcap -G 3600 -W 24
  • 与 Wireshark 结合:
    • 强调 tcpdump 捕获 .pcap 文件后,使用 Wireshark 进行可视化和深度分析是最佳实践。
  • 远程抓包:
    • 通过 SSH 结合 tcpdump 进行远程服务器上的抓包。
    • 示例:ssh user@host "sudo tcpdump -i eth0 -w - 'not port 22'" | wireshark -k -S -i -

图解

tcpdump 在 Linux 内核中的视角模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
                     [ 物理网卡 (NIC) ]

(驱动层/Ring Buffer 拷贝)

┌────────────────────────┴────────────────────────┐
│ ▼
[ 路径 A: AF_PACKET Socket ] [ 路径 B: 正常网络栈 ]
│ │
▼ ▼
[ 内核 BPF 过滤引擎 ] [ Netfilter / iptables ]
│ (仅符合规则的报文) │
▼ ▼
[ libpcap 缓冲区 ] [ 路由决策 & TCP/UDP ]
│ (用户态/mmap 共享内存) │
▼ ▼
[ tcpdump 命令行输出/落盘 ] [ 应用层服务 (Nginx/SSH) ]

为什么 iptables DROP 掉了包,tcpdump 还能看到?

  • 因为 tcpdump 挂接在 AF_PACKET 原始套接字上,捕获点位于驱动接收层,早于 Netfilter / iptables 的 PREROUTING 链

为什么抓包会导致 CPU 或磁盘 I/O 飙升?

  • 默认情况下,内核需要将每一个报文从内核态拷贝到用户态(Memory Copy)。如果开启二进制落盘且磁盘 I/O 较慢(如机械硬盘),会导致内核环形缓冲区溢出并引发丢包(dropped by kernel)。

按功能划分为三大组,每组 3 个核心参数:

1
2
3
4
5
6
7
8
9
10
11
                    ┌── -i (Interface / 监听网卡)
┌─ 1. 控制抓取 ┼── -s (Snaplen / 截取长度)
│ └── -c (Count / 抓取数量)

│ ┌── -n / -nn (No Domain/Port Resolution / 禁用解析)
tcpdump ┼─ 2. 控制显示 ┼── -v / -vv / -vvv (Verbose / 详细程度)
参数矩阵 │ └── -e / -X (Ethernet / Hex Display / 链路头与16进制)

│ ┌── -w (Write / 写文件)
└─ 3. 控制落盘 ┼── -r (Read / 读文件)
└── -C / -W (Cut & Window / 文件切分与轮转)

BPF 过滤语法切片

1
2
3
4
                ┌── Type   : host, net, port, portrange
├── Dir : src, dst, src or dst, src and dst
修饰词三要素 ──┼── Proto : ip, ipv6, arp, tcp, udp, icmp
└── Logic : and (&&), or (||), not (!)

规则组合逻辑树

1
2
3
4
5
6
7
                         ┌── src host 192.168.1.10
┌── 针对主机 ┴── dst host 10.0.0.1

├── 针对端口 ─── portrange 8000-8080
BPF 常用模式 ┼── 针对网段 ─── net 172.16.0.0/16

└── 综合复合 ─── (host A and port B) and not port 22

语法书写铁律: 复合逻辑带括号时,必须使用单引号 '...' 或双引号 "..." 包裹,防止被 Linux Shell 误识别为子 Shell 执行。

当普通原语不够用时(如查找特定 TCP 标志位),需要使用字节偏移量(Offset)位运算

1
proto [ offset : size ]
  • proto:协议名称(如 tcp, ip, udp
  • offset:相对于该协议头的起始字节偏移量
  • size:抓取的字节数(1、2 或 4 字节,默认 1)

拆解:TCP 头部标志位(Flags)偏移分析

TCP 头的第 13 个字节(从 0 开始计数)包含了控制标志位:

1
2
3
4
5
6
Byte 12      Byte 13 (TCP Flags)
┌───────────┬───┬───┬───┬───┬───┬───┬───┬───┐
│ Data Offset│CWR│ECE│URG│ACK│PSH│RST│SYN│FIN│
└───────────┴───┴───┴───┴───┴───┴───┴───┴───┘
Bit 7 6 5 4 3 2 1 0
Value 128 64 32 16 8 4 2 1

抓取 SYN 包(Bit 1 为 1):

  • 值匹配:tcp[13] & 2 != 0 或直接使用常量 tcp[tcpflags] & tcp-syn != 0

抓取 RST 包(Bit 2 为 1):

  • 值匹配:tcp[13] & 4 != 0tcp[tcpflags] & tcp-rst != 0

抓取 SYN-ACK 包(SYN=1, ACK=1 ➔ 2 + 16 = 18):

  • 值匹配:tcp[13] == 18

排障演练:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# ==============================================================================
# 场景 1:基础排查 —— 抓取 DNS 域名解析报文
# 适用场景:排查域名无法解析、DNS 延迟高或 DNS 劫持问题
# ==============================================================================
tcpdump -i any -nn -s 0 -v "udp port 53"


# ==============================================================================
# 场景 2:生产级高吞吐抓包 —— 低 I/O 安全落盘
# 适用场景:在高并发服务器上抓包,只保留头部,分段轮转写入内存盘,防止磁盘 I/O 爆表
# ==============================================================================
tcpdump -i eth0 -nn -s 96 -w /dev/shm/app_dump.pcap -C 50 -W 4 "tcp port 8080"


# ==============================================================================
# 场景 3:协议栈调优 —— 捕获异常连接重传与连接拒绝(RST)
# 适用场景:服务端无响应、TCP 握手失败、防火墙拦截排查
# ==============================================================================
tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-rst|tcp-syn) != 0'


# ==============================================================================
# 场景 4:二层与虚拟化网络排查 —— 捕获 ICMP 且显示 MAC 地址与 VLAN Tag
# 适用场景:PVE / OpenWrt 虚拟网桥排查,确定报文到达的是物理网卡还是 Tap/veth 设备
# ==============================================================================
tcpdump -i eth0 -e -nn "icmp or vlan"


# ==============================================================================
# 场景 5:应用层 Payload 分析 —— 抓取 HTTP GET / POST 明文请求
# 适用场景:在没有 SSL/TLS 加密的情况下,检查 API 请求体或 HTTP 状态码
# BPF 逻辑:0x47455420 即 ASCII 码的 "GET "
# ==============================================================================
tcpdump -i eth0 -nn -s 0 -A 'tcp port 80 and (tcp[12:4] == 0x47455420 or tcp[12:4] == 0x504f5354)'


# ==============================================================================
# 场景 6:极客无缝调试 —— Linux 抓包直接实时管道推送到本地 Wireshark 界面
# 适用场景:服务器上没有 GUI 界面,直接利用 SSH 管道将流式数据传递给本地 Mac 显示
# 注意:此命令在本地终端(如 Mac Terminal)上运行
# ==============================================================================
ssh root@192.168.1.200 "tcpdump -i eth0 -U -s 0 -w - 'not port 22'" | wireshark -k -i -

排障思考路径决策树

1
2
3
4
5
6
7
8
9
10
11
12
13
遇到网络故障时:

├─ 1. 是否存在网卡/二层转发问题?
│ └─► tcpdump -i eth0 -e -nn

├─ 2. 报文吞吐量极大,担心写爆磁盘?
│ └─► tcpdump -i eth0 -nn -s 96 -w /dev/shm/out.pcap -C 100 -W 3

├─ 3. 怀疑 TCP 建连超时或被异常中断?
│ └─► tcpdump -i eth0 -nn 'tcp[tcpflags] & (tcp-syn|tcp-rst) != 0'

└─ 4. 需要深度分析报文内容(HTTP/TLS 握手/DNS)?
└─► tcpdump -i eth0 -w dump.pcap ➔ 下载到本地用 Wireshark 打开