土法炼钢兴趣小组的算法知识备份

【网络工程】tcpdump 实战精通:BPF 过滤与捕获策略

文章导航

分类入口
network
标签入口
#tcpdump#bpf#packet-capture#diagnostics#linux

目录

tcpdump 是 Linux 上最基础也最强大的抓包工具。但大多数工程师只会 tcpdump -i eth0——然后被洪水般的输出淹没。

真正的抓包不是”打开水龙头”,而是精确过滤。在每秒数十万包的线上环境中,写好 BPF 过滤表达式决定了你能不能抓到关键包而不打爆磁盘。捕获策略——滚动文件、大小限制、时间窗口——决定了你能不能在事后找到那几个有问题的包。

一、tcpdump 基础与输出解读

1.1 基本命令

# 最基本的抓包(不推荐在生产环境直接执行)
tcpdump -i eth0

# 推荐的基本用法
tcpdump -i eth0 -n -c 100

# 参数说明:
#   -i eth0     指定网卡
#   -n          不做 DNS 反解(避免额外 DNS 查询)
#   -c 100      抓 100 个包后自动停止
#   -nn         不解析端口号(如 80 显示为 80 而非 http)
#   -v / -vv    增加输出详细度
#   -X          同时显示 hex + ASCII
#   -XX         同上,含链路层头部
#   -S          显示绝对序列号(不显示相对值)
#   -t / -tt    不显示时间 / 显示 Unix 时间戳
#   -ttt        显示与上一个包的时间差(极有用)

1.2 输出格式解读

tcpdump 输出示例:

14:32:01.123456 IP 10.0.1.5.43210 > 10.0.1.10.80: Flags [S], seq 1234567890, win 65535, options [mss 1460,sackOK,TS val 1000 ecr 0,nop,wscale 7], length 0
│               │  │              │               │        │  │            │    │                                                                         │
│               │  源 IP.端口     │               │        │  序列号       │    TCP 选项                                                                  Payload 长度
│               │                目标 IP.端口     │        窗口大小
│               协议                             TCP 标志
时间戳

TCP 标志含义:
  [S]     SYN
  [S.]    SYN-ACK
  [.]     ACK
  [P.]    PSH-ACK(有数据推送)
  [F.]    FIN-ACK
  [R]     RST
  [R.]    RST-ACK

1.3 常见场景速查

# 抓 HTTP 流量
tcpdump -i eth0 -nn 'tcp port 80'

# 抓 HTTPS 流量(只能看到 TLS 握手,看不到内容)
tcpdump -i eth0 -nn 'tcp port 443'

# 抓 DNS 查询
tcpdump -i eth0 -nn 'udp port 53'

# 抓 SYN 包(分析新连接)
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-syn != 0'

# 抓 RST 包(分析连接异常)
tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'

# 抓指定主机之间的流量
tcpdump -i eth0 -nn 'host 10.0.1.5 and host 10.0.1.10'

# 抓指定子网的流量
tcpdump -i eth0 -nn 'net 10.0.1.0/24'

# 保存到文件(后续用 Wireshark 分析)
tcpdump -i eth0 -nn -w capture.pcap 'tcp port 80'

# 读取 pcap 文件
tcpdump -r capture.pcap -nn

二、BPF 过滤语法完整指南

BPF(Berkeley Packet Filter)是 tcpdump 的过滤引擎。过滤表达式在内核层面执行,只有匹配的包才会拷贝到用户空间——这意味着好的过滤表达式能大幅降低 CPU 和内存开销。

2.1 基础元语

BPF 过滤表达式由 "类型 + 方向 + 协议" 三部分组成:

类型(Type):
  host 10.0.1.5       主机
  net 10.0.1.0/24     网段
  port 80             端口
  portrange 8000-8100 端口范围

方向(Direction):
  src                 源
  dst                 目的
  src or dst          源或目的(默认)
  src and dst         源且目的

协议(Protocol):
  ip                  IPv4
  ip6                 IPv6
  tcp                 TCP
  udp                 UDP
  icmp                ICMP
  arp                 ARP
  ether               以太网

组合示例:
  src host 10.0.1.5                # 源地址是 10.0.1.5
  dst port 80                      # 目标端口是 80
  tcp src port 443                 # TCP 源端口 443
  src net 172.16.0.0/12            # 源地址在 172.16.0.0/12 网段

2.2 逻辑运算

AND(与):
  tcpdump 'src host 10.0.1.5 and dst port 80'
  tcpdump 'tcp and port 443'

OR(或):
  tcpdump 'port 80 or port 443'
  tcpdump 'host 10.0.1.5 or host 10.0.1.10'

NOT(非):
  tcpdump 'not port 22'           # 排除 SSH
  tcpdump 'not host 10.0.1.1'    # 排除网关

括号(注意 Shell 转义):
  tcpdump '(port 80 or port 443) and host 10.0.1.5'
  tcpdump 'not (port 22 or port 53)'  # 排除 SSH 和 DNS

复杂组合:
  # 抓 10.0.1.5 到 10.0.1.10 的 HTTP/HTTPS 流量,排除 ACK-only
  tcpdump -i eth0 -nn \
    'src host 10.0.1.5 and dst host 10.0.1.10 and \
     (tcp port 80 or tcp port 443) and \
     (tcp[tcpflags] & tcp-push != 0)'

2.3 TCP 标志位过滤

TCP 标志位过滤是诊断 TCP 问题的利器:

TCP 标志位定义:
  tcp-fin = 0x01    FIN
  tcp-syn = 0x02    SYN
  tcp-rst = 0x04    RST
  tcp-push = 0x08   PSH
  tcp-ack = 0x10    ACK
  tcp-urg = 0x20    URG

使用方法:
  tcpdump 'tcp[tcpflags] & tcp-syn != 0'     # 含 SYN 的包
  tcpdump 'tcp[tcpflags] & tcp-rst != 0'     # 含 RST 的包
  tcpdump 'tcp[tcpflags] & tcp-fin != 0'     # 含 FIN 的包

精确匹配:
  tcpdump 'tcp[tcpflags] == tcp-syn'         # 仅 SYN(不含 ACK)
  tcpdump 'tcp[tcpflags] == tcp-syn|tcp-ack' # SYN-ACK

常用诊断场景:
  # 只看三次握手的 SYN
  tcpdump -i eth0 -nn 'tcp[tcpflags] == tcp-syn'

  # 只看 RST(连接被拒绝/异常关闭)
  tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-rst != 0'

  # 只看 FIN(正常关闭)
  tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-fin != 0'

  # 看有数据的包(排除纯 ACK)
  tcpdump -i eth0 -nn 'tcp[tcpflags] & tcp-push != 0'

  # 看重传(SYN 重传,连接建立失败的征兆)
  # 需要配合 -ttt 观察时间间隔
  tcpdump -i eth0 -nn -ttt 'tcp[tcpflags] == tcp-syn and dst port 80'

2.4 字节偏移过滤

BPF 允许访问包的任意字节位置,这是高级过滤的核心:

语法: proto[offset:length]
  proto: ip, tcp, udp, icmp, ether 等
  offset: 从该层头部起始的字节偏移
  length: 读取的字节数(1, 2, 或 4)

IP 头部偏移:
  ip[0]  = Version + IHL
  ip[1]  = ToS / DSCP
  ip[2:2] = Total Length
  ip[6:2] = Flags + Fragment Offset
  ip[8]  = TTL
  ip[9]  = Protocol (6=TCP, 17=UDP, 1=ICMP)
  ip[12:4] = Source IP
  ip[16:4] = Destination IP

TCP 头部偏移:
  tcp[0:2] = Source Port
  tcp[2:2] = Destination Port
  tcp[4:4] = Sequence Number
  tcp[8:4] = Acknowledgment Number
  tcp[12]  = Data Offset + Reserved
  tcp[13]  = TCP Flags

实际案例:

  # TTL 为 1 的包(traceroute 发出的包)
  tcpdump -i eth0 -nn 'ip[8] == 1'

  # IP 分片包(Fragment Offset > 0 或 MF 标志)
  tcpdump -i eth0 -nn 'ip[6:2] & 0x3fff != 0'

  # DSCP 值为 46 的包(EF/快速转发流量)
  tcpdump -i eth0 -nn 'ip[1] >> 2 == 46'

  # TCP 窗口为 0 的包(零窗口,流控问题)
  tcpdump -i eth0 -nn 'tcp[14:2] == 0'

2.5 应用层过滤

HTTP 请求方法过滤(明文 HTTP):

  # GET 请求(TCP payload 前 3 字节是 "GET")
  tcpdump -i eth0 -nn -A \
    'tcp dst port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420'

  解释:
    tcp[12:1] & 0xf0 >> 2  = TCP 数据偏移(头部长度)
    从 TCP payload 起始位置开始匹配
    0x47455420 = "GET " (ASCII)

  # POST 请求
  tcpdump -i eth0 -nn -A \
    'tcp dst port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x504f5354'

  # HTTP 响应(以 "HTTP" 开头)
  tcpdump -i eth0 -nn -A \
    'tcp src port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x48545450'

DNS 查询过滤:
  # DNS 查询(QR=0)
  tcpdump -i eth0 -nn 'udp port 53 and udp[10] & 0x80 == 0'

  # DNS 响应(QR=1)
  tcpdump -i eth0 -nn 'udp port 53 and udp[10] & 0x80 != 0'

  # DNS NXDOMAIN 响应(RCODE=3)
  tcpdump -i eth0 -nn 'udp port 53 and udp[11] & 0x0f == 3'

三、捕获策略

3.1 滚动文件捕获

线上抓包最重要的原则:不要让 pcap 文件无限增长

# 滚动捕获: 每个文件 100MB,最多保留 10 个文件
tcpdump -i eth0 -nn -w /tmp/capture.pcap \
    -C 100 -W 10 \
    'tcp port 80 or tcp port 443'

# 参数说明:
#   -C 100   每个文件最大 100MB(单位: 百万字节)
#   -W 10    最多保留 10 个文件(覆盖最旧的)
#
# 文件命名: capture.pcap0, capture.pcap1, ..., capture.pcap9
# 总磁盘占用上限: 100MB × 10 = 1GB

# 按时间分割: 每 3600 秒(1 小时)一个文件
tcpdump -i eth0 -nn -w /tmp/capture.pcap \
    -G 3600 -W 24 \
    'tcp port 80'

# 参数说明:
#   -G 3600  每 3600 秒轮转一次
#   -W 24    最多保留 24 个文件(24 小时的数据)
#
# 配合 -w 的 strftime 格式:
tcpdump -i eth0 -nn -w '/tmp/cap_%Y%m%d_%H%M%S.pcap' \
    -G 3600 -W 24 \
    'tcp port 80'
# 文件名: cap_20250803_143200.pcap

3.2 只捕获头部

大多数网络诊断只需要协议头部,不需要完整的 Payload:

# 只捕获前 96 字节(默认 262144)
tcpdump -i eth0 -nn -s 96 -w capture.pcap 'tcp port 80'

# 只捕获前 68 字节(IP + TCP 头部,无 Payload)
tcpdump -i eth0 -nn -s 68 -w capture.pcap

# 捕获长度选择:
#   -s 0      捕获完整包(需要看 Payload 内容时)
#   -s 68     只有 IP + TCP 头(连接分析)
#   -s 96     含部分 TCP 选项(RTT 分析)
#   -s 256    含 HTTP 请求行/响应行
#   -s 1500   标准 MTU 完整包

# 意义:
#   线上环境每秒可能有 100K+ 的包
#   -s 68 比 -s 0 减少 95% 的磁盘写入
#   对 CPU 和 I/O 的影响显著降低

3.3 后台抓包

# 后台抓包(nohup + 限制条件)
nohup tcpdump -i eth0 -nn -w /tmp/debug.pcap \
    -C 50 -W 20 -s 256 \
    'host 10.0.1.10 and tcp port 8080' \
    > /dev/null 2>&1 &

echo $! > /tmp/tcpdump.pid

# 停止抓包
kill $(cat /tmp/tcpdump.pid)

# 定时抓包(抓 5 分钟)
timeout 300 tcpdump -i eth0 -nn -w /tmp/debug.pcap \
    'tcp port 80'

# 抓到特定数量后停止
tcpdump -i eth0 -nn -c 10000 -w /tmp/debug.pcap \
    'tcp port 80'

3.4 时间戳精度

# 高精度时间戳(纳秒级)
tcpdump -i eth0 -nn --time-stamp-precision=nano -w capture.pcap

# 时间戳显示格式:
#   -t       不显示时间戳
#   -tt      Unix 时间戳(秒.微秒)
#   -ttt     与上一包的时间差(分析延迟间隔)
#   -tttt    日期 + 时间
#   -ttttt   与第一个包的时间差

# 延迟分析推荐使用 -ttt:
tcpdump -i eth0 -nn -ttt 'host 10.0.1.10 and tcp port 80' -c 50

# 输出示例:
#   00:00:00.000000 IP 10.0.1.5.43210 > 10.0.1.10.80: Flags [S] ...
#   00:00:00.000342 IP 10.0.1.10.80 > 10.0.1.5.43210: Flags [S.] ...
#   00:00:00.000012 IP 10.0.1.5.43210 > 10.0.1.10.80: Flags [.] ...
#   00:00:00.000089 IP 10.0.1.5.43210 > 10.0.1.10.80: Flags [P.] ...
#
# 从时间差可以看出:
#   SYN → SYN-ACK: 342μs(网络 RTT)
#   SYN-ACK → ACK: 12μs(本地处理)
#   ACK → Data: 89μs(应用层准备数据)

四、容器与 Kubernetes 环境抓包

4.1 Docker 容器抓包

# 方法 1: 进入容器网络命名空间
# 获取容器 PID
CONTAINER_PID=$(docker inspect -f '{{.State.Pid}}' my-container)

# 在容器的网络命名空间中抓包
nsenter -t $CONTAINER_PID -n tcpdump -i eth0 -nn -c 100

# 方法 2: 使用 docker exec(容器需要有 tcpdump)
docker exec my-container tcpdump -i eth0 -nn -c 100

# 方法 3: 启动一个调试容器共享网络
docker run --rm -it --net=container:my-container \
    nicolaka/netshoot tcpdump -i eth0 -nn -c 100

# 方法 4: 在宿主机上抓 veth pair
# 找到容器对应的 veth
VETH=$(ip link | grep -A1 "$(docker exec my-container cat /sys/class/net/eth0/iflink)" | head -1 | awk -F: '{print $2}' | tr -d ' ')
tcpdump -i $VETH -nn -c 100

4.2 Kubernetes Pod 抓包

# 方法 1: kubectl debug(推荐,K8s 1.25+)
kubectl debug -it my-pod --image=nicolaka/netshoot \
    --target=my-container -- \
    tcpdump -i eth0 -nn -c 100 -w /tmp/capture.pcap

# 方法 2: 在 Node 上抓包
# 找到 Pod 所在的 Node
NODE=$(kubectl get pod my-pod -o jsonpath='{.spec.nodeName}')

# SSH 到 Node,找到 Pod 的网络命名空间
# Containerd 环境:
POD_ID=$(crictl pods --name my-pod -q)
CONTAINER_ID=$(crictl ps --pod $POD_ID -q)
CONTAINER_PID=$(crictl inspect $CONTAINER_ID | jq .info.pid)
nsenter -t $CONTAINER_PID -n tcpdump -i eth0 -nn -c 100

# 方法 3: 使用 ksniff(kubectl 插件)
kubectl sniff my-pod -n default -o capture.pcap

# 方法 4: 抓 CNI 接口
# Calico 环境: 接口名通常是 cali*
tcpdump -i cali1234abcd -nn -c 100

# Cilium 环境: 接口名通常是 lxc*
tcpdump -i lxc1234abcd -nn -c 100

4.3 Service Mesh 环境

在 Istio/Linkerd 等 Service Mesh 环境中,
请求会经过 Sidecar Proxy (Envoy),抓包需要注意分层:

应用容器 (port 8080)
    ↓ (iptables 重定向)
Envoy Sidecar (port 15001/15006)
    ↓
网络 (eth0)

抓包位置选择:
  1. eth0: 看到的是 Envoy 发出的流量(mTLS 加密后)
  2. lo (127.0.0.1): 看到 Envoy → 应用 的流量(明文)
  3. 应用端口: 看到应用实际收到的请求

# 抓 Envoy → 应用的流量(明文)
tcpdump -i lo -nn 'tcp port 8080'

# 抓 Envoy 对外的 mTLS 流量
tcpdump -i eth0 -nn 'tcp port 15001 or tcp port 15006'

# 对比入站和出站延迟
# 入站: Envoy 收到请求
tcpdump -i eth0 -nn -ttt 'tcp port 15006 and tcp[tcpflags] & tcp-push != 0' -c 10
# 出站: Envoy 转发给应用
tcpdump -i lo -nn -ttt 'tcp port 8080 and tcp[tcpflags] & tcp-push != 0' -c 10

五、高级用法

5.1 组合诊断场景

# 场景 1: 诊断 TCP 连接超时
# 抓 SYN 但没有 SYN-ACK 的情况
tcpdump -i eth0 -nn -ttt \
    'tcp[tcpflags] == tcp-syn and dst host 10.0.1.10 and dst port 3306' \
    -c 20

# 如果 SYN 多次出现但间隔 ~1s/2s/4s/8s → 重传,对端不响应
# 如果立即收到 RST → 端口未监听或被防火墙拒绝

# 场景 2: 诊断 HTTP 5xx 错误
# 抓 HTTP 响应,grep 状态码
tcpdump -i eth0 -nn -A -s 256 \
    'tcp src port 80 and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x48545450' \
    | grep -E 'HTTP/1\.[01] [45][0-9][0-9]'

# 场景 3: 诊断 DNS 解析慢
# 抓 DNS 查询和响应,用 -ttt 看响应时间
tcpdump -i eth0 -nn -ttt 'udp port 53' -c 50

# 查询到响应的时间差如果 >100ms → DNS 服务器慢或网络延迟
# 多次查询同一域名 → 缓存未生效

# 场景 4: 抓取特定 VLAN 的流量
tcpdump -i eth0 -nn 'vlan 100'

# 场景 5: 检测 ARP 风暴
tcpdump -i eth0 -nn 'arp' -c 100 | \
    awk '{print $NF}' | sort | uniq -c | sort -rn | head

# 场景 6: 诊断 MTU 问题(ICMP Fragmentation Needed)
tcpdump -i eth0 -nn 'icmp[0] == 3 and icmp[1] == 4'

5.2 与其他工具组合

# tcpdump + wireshark: 抓包后分析
tcpdump -i eth0 -nn -w capture.pcap -c 10000 'tcp port 80'
# 然后用 Wireshark 打开 capture.pcap 做流分析

# tcpdump + tshark: 命令行分析
tcpdump -i eth0 -nn -w - 'tcp port 80' | \
    tshark -r - -T fields -e ip.src -e ip.dst -e tcp.analysis.retransmission

# tcpdump + awk: 实时统计
# 统计每秒包数
tcpdump -i eth0 -nn -tt 'tcp port 80' 2>/dev/null | \
    awk '{split($1,a,"."); print a[1]}' | uniq -c

# tcpdump + grep: 过滤 HTTP Host
tcpdump -i eth0 -nn -A -s 512 'tcp port 80' 2>/dev/null | \
    grep -oP 'Host: \K[^\r\n]+'

# tcpdump 输出统计(远程抓包)
# 在远程服务器抓包,通过 SSH 管道传到本地
ssh server 'tcpdump -i eth0 -nn -w - tcp port 80' > remote.pcap

# 或直接在本地 Wireshark 中打开
ssh server 'tcpdump -i eth0 -nn -w - tcp port 80' | \
    wireshark -k -i -

5.3 多网卡与特殊接口

# 抓所有接口
tcpdump -i any -nn 'tcp port 80'

# 注意: -i any 时看到的是 Linux cooked capture (SLL) 格式
# 不包含以太网帧头,而是 Linux 特有的伪头部

# 列出所有可抓包接口
tcpdump -D
# 输出:
# 1.eth0 [Up, Running]
# 2.lo [Up, Running, Loopback]
# 3.docker0 [Up, Running]
# 4.veth1234 [Up, Running]
# 5.any (Pseudo-device that captures on all interfaces)

# 抓 loopback 流量(本机内部通信)
tcpdump -i lo -nn 'tcp port 6379'  # 抓 Redis 通信

# 抓 bridge 接口(Docker 网络)
tcpdump -i docker0 -nn

# 抓 bond 接口
tcpdump -i bond0 -nn

六、性能优化与注意事项

6.1 tcpdump 的性能影响

tcpdump 的工作原理:

  1. 内核通过 BPF 过滤包
  2. 匹配的包从内核态拷贝到用户态
  3. tcpdump 格式化并输出/写文件

性能影响因素:
  1. 过滤表达式: BPF 在内核态执行,开销极低
     但匹配的包越多 → 内核到用户态的拷贝越多
  2. -s 参数: 捕获长度越短 → 拷贝的数据越少
  3. 输出方式: -w 写文件比终端输出快得多
  4. 包速率: >100K pps 时需要注意丢包

线上环境最佳实践:

  # 推荐: 精确过滤 + 限制捕获长度 + 写文件 + 限制数量/时间
  tcpdump -i eth0 -nn -s 96 -w /tmp/cap.pcap \
      -C 100 -W 5 \
      'host 10.0.1.10 and tcp port 8080'

  # 不推荐: 无过滤 + 完整捕获 + 终端输出
  tcpdump -i eth0   # ← 这会让你后悔的

6.2 丢包检测

# tcpdump 退出时会报告丢包统计
tcpdump -i eth0 -nn -c 10000 'tcp port 80'
# 结束后输出:
# 10000 packets captured
# 10234 packets received by filter
# 234 packets dropped by kernel    ← 关注这个数字

# 如果 kernel drop > 0:
# 1. 加大缓冲区
tcpdump -i eth0 -nn -B 4096 'tcp port 80'
#   -B 4096  设置内核缓冲区为 4MB(默认 ~2MB)

# 2. 减少捕获长度
tcpdump -i eth0 -nn -s 68 'tcp port 80'

# 3. 写文件而不是终端输出
tcpdump -i eth0 -nn -w capture.pcap 'tcp port 80'

# 4. 缩小过滤范围
tcpdump -i eth0 -nn 'src host 10.0.1.5 and dst port 80'

# 5. 检查系统缓冲区设置
sysctl net.core.rmem_max
sysctl net.core.rmem_default
# 可以增大:
# sysctl -w net.core.rmem_max=8388608

6.3 安全注意事项

1. 权限要求
   tcpdump 需要 root 权限或 CAP_NET_RAW capability
   # 给非 root 用户抓包能力
   setcap cap_net_raw=eip /usr/sbin/tcpdump

2. 捕获文件的安全
   pcap 文件可能包含敏感数据(密码、Token、Cookie)
   ✅ 只捕获头部(-s 68)用于连接分析
   ✅ 及时删除 pcap 文件
   ✅ 不要把 pcap 文件放在可公开访问的路径
   ❌ 不要用 -s 0 抓包后分享 pcap 文件

3. 法律合规
   在某些环境中,抓包需要合规审批
   企业环境: 遵循公司的安全审计政策
   生产环境: 记录抓包的时间、原因、操作人

4. 性能安全
   不做限制的 tcpdump 可能影响线上性能
   ✅ 始终使用 -c 或 -W 限制
   ✅ 使用 timeout 命令做时间限制
   ✅ 先在测试环境验证 BPF 表达式

七、实战案例

7.1 案例:连接建立超时排查

# 现象: 应用报 "connection timeout" 连接 10.0.1.10:3306
# 排查步骤:

# 1. 抓 SYN 包
tcpdump -i eth0 -nn -ttt \
    'tcp[tcpflags] == tcp-syn and dst host 10.0.1.10 and dst port 3306' \
    -c 20

# 观察结果 A: SYN 重传
#   00:00:00.000000 IP 10.0.1.5.43210 > 10.0.1.10.3306: Flags [S] ...
#   00:00:01.003421 IP 10.0.1.5.43210 > 10.0.1.10.3306: Flags [S] ...
#   00:00:02.007892 IP 10.0.1.5.43210 > 10.0.1.10.3306: Flags [S] ...
#   → 1秒、2秒间隔 = SYN 重传
#   → 对端不响应(防火墙丢弃 / 服务未启动 / Backlog 满)

# 观察结果 B: 立即收到 RST
#   00:00:00.000000 IP 10.0.1.5.43210 > 10.0.1.10.3306: Flags [S] ...
#   00:00:00.000453 IP 10.0.1.10.3306 > 10.0.1.5.43210: Flags [R.] ...
#   → 端口未监听或被防火墙主动拒绝

# 2. 在对端同时抓包确认
ssh 10.0.1.10 'tcpdump -i eth0 -nn -ttt \
    "tcp port 3306 and host 10.0.1.5" -c 20'

# 如果对端看到 SYN 但没有 SYN-ACK → 服务端问题
# 如果对端看不到 SYN → 中间网络丢弃了

7.2 案例:TLS 握手失败

# 现象: curl -k https://10.0.1.10 超时或报 SSL 错误
# 排查:

tcpdump -i eth0 -nn -ttt -v \
    'host 10.0.1.10 and tcp port 443' -c 50

# 正常的 TLS 握手:
#   → [S]        TCP SYN
#   ← [S.]       TCP SYN-ACK
#   → [.]        TCP ACK
#   → [P.]       ClientHello
#   ← [P.]       ServerHello, Certificate, ServerHelloDone
#   → [P.]       ClientKeyExchange, ChangeCipherSpec, Finished
#   ← [P.]       ChangeCipherSpec, Finished
#   → [P.]       Application Data (HTTP 请求)

# 异常情况:
# 1. ClientHello 后收到 RST → 对端不支持 TLS 或端口配置错
# 2. ClientHello 后无响应 → 对端处理卡住或防火墙
# 3. ServerHello 后断开 → 密码套件不匹配
# 4. Certificate 后客户端发 Alert → 证书验证失败

# 查看 TLS Alert 消息
tcpdump -i eth0 -nn -X \
    'host 10.0.1.10 and tcp port 443' -c 50 | \
    grep -A2 "Alert"

7.3 案例:间歇性丢包

# 现象: 服务偶尔出现 502/504 错误
# 策略: 长时间后台抓包,事后分析

# 1. 启动滚动抓包(保留 2 小时的数据)
nohup tcpdump -i eth0 -nn -s 96 \
    -w '/tmp/cap_%Y%m%d_%H%M%S.pcap' \
    -G 600 -W 12 \
    'host 10.0.1.10 and tcp port 8080' \
    > /dev/null 2>&1 &

# 2. 等故障复现后,分析 pcap 文件
# 用 tshark 找出重传包
tshark -r /tmp/cap_*.pcap \
    -Y 'tcp.analysis.retransmission' \
    -T fields -e frame.time -e ip.src -e ip.dst \
    -e tcp.srcport -e tcp.dstport

# 用 tshark 统计重传率
tshark -r /tmp/cap_*.pcap -z io,stat,1,tcp.analysis.retransmission

# 用 tshark 找 RST 包
tshark -r /tmp/cap_*.pcap -Y 'tcp.flags.reset == 1'

# 3. 分析时间分布
# 如果重传集中在某个时间段 → 对端或网络问题
# 如果均匀分布 → 持续性的网络质量问题

八、BPF 过滤表达式速查表

┌────────────────────────────────────────────────────────────────┐
│ 场景                    │ BPF 过滤表达式                       │
├────────────────────────┼──────────────────────────────────────┤
│ 指定主机               │ host 10.0.1.5                       │
│ 指定网段               │ net 10.0.1.0/24                     │
│ 指定端口               │ port 80                             │
│ TCP SYN                │ tcp[tcpflags] == tcp-syn            │
│ TCP SYN-ACK            │ tcp[tcpflags] == tcp-syn|tcp-ack    │
│ TCP RST                │ tcp[tcpflags] & tcp-rst != 0        │
│ TCP FIN                │ tcp[tcpflags] & tcp-fin != 0        │
│ 有数据的包             │ tcp[tcpflags] & tcp-push != 0       │
│ 零窗口                 │ tcp[14:2] == 0                      │
│ DNS 查询               │ udp port 53 and udp[10] & 0x80 == 0│
│ DNS NXDOMAIN           │ udp port 53 and udp[11] & 0x0f == 3│
│ ICMP 不可达            │ icmp[0] == 3                        │
│ TTL=1                  │ ip[8] == 1                          │
│ IP 分片                │ ip[6:2] & 0x3fff != 0              │
│ 排除 SSH               │ not port 22                         │
│ 排除 SSH 和 DNS        │ not (port 22 or port 53)           │
│ VLAN 100               │ vlan 100                           │
│ ARP                    │ arp                                 │
│ 指定 MAC 地址          │ ether host aa:bb:cc:dd:ee:ff       │
│ 广播包                 │ ether broadcast                    │
│ 包长度 > 1000          │ greater 1000                       │
│ 包长度 < 100           │ less 100                           │
└────────────────────────┴──────────────────────────────────────┘

九、总结

tcpdump 的精髓不在命令本身,而在于过滤策略分析思路

  1. BPF 过滤在内核执行。好的过滤表达式不是可选的优化——在高流量环境中它决定了你能不能成功抓到包。写 BPF 表达式要像写数据库查询一样精确。

  2. 线上抓包必须有保护机制-C -W(滚动文件限制)、-s(捕获长度限制)、-ctimeout(数量/时间限制)——这三个至少用一个,否则一个 tcpdump 就能打爆你的磁盘。

  3. -ttt 是最被低估的参数。它显示每个包与上一个包的时间差,让你立即看出”SYN 重传间隔”“DNS 查询到响应的耗时”“数据发送到 ACK 的延迟”。大多数延迟问题用 -ttt 三秒就能看出来。

  4. 容器环境抓包要找对接口kubectl debug + nsenter 是最可靠的方法。理解 veth pair、CNI 接口、Sidecar 拦截的分层,才能在正确的位置抓到正确的包。

  5. tcpdump 抓包,Wireshark 分析。tcpdump 的强项是在线过滤和捕获,Wireshark 的强项是图形化分析。用 -w 保存 pcap,然后在 Wireshark 中做流分析、RTT 测量、统计图表。


参考文献


上一篇:WebTransport 与 WebCodecs:下一代浏览器传输

下一篇:Wireshark 深度分析:流图、专家信息与协议解析

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2025-08-15 · network

【网络工程】BPF 网络诊断:bpftrace 与 bcc 工具实战

系统讲解 eBPF 在网络诊断中的工程应用:bcc 工具集(tcplife/tcpretrans/tcpdrop)的使用场景、bpftrace 自定义网络探针编写、XDP 丢包分析、内核协议栈延迟追踪,建立基于 eBPF 的系统化网络诊断方法。

2025-08-05 · network

【网络工程】eBPF 可编程网络:从包过滤到流量工程

eBPF 正在重新定义网络工程——从传统的 iptables/netfilter 规则堆砌,到可编程、可观测、高性能的网络数据平面。本文系统讲解 eBPF 网络程序类型(XDP/TC/Socket)、Map 数据结构、Cilium 的 eBPF 数据平面实现,以及 eBPF 在负载均衡、可观测性和网络安全中的工程实践。


By .