Skip to content

实验 12:端口镜像与流量采样 ​

交换机只把包发给目的端口,别的端口看不到。但排查问题时你经常需要旁听别人的流量。

OVS 提供三种手段,用途完全不同:

手段复制什么送给谁适合
Mirror完整报文本机的某个端口排障、入侵检测、合规抓包
sFlow按比例抽样的报文头远端采集器长期流量画像、容量规划
NetFlow/IPFIX流级别的统计(不含报文)远端采集器计费、审计、异常检测

起环境 ​

bash
cd labs/learn-ovs/10-mirror
./start.sh
text
   h1 .1 ─┐
   h2 .2 ─┼── ovs1 br0
   h3    ─┘   ← 抓包机,不配 IP

h3 接在交换机上但不配地址,它只负责接收镜像来的帧。

先确认现在抓不到 ​

一个终端抓包:

bash
docker exec clab-mirror-h3 timeout 6 tcpdump -i eth1 -n -l icmp

另一个终端打流量:

bash
docker exec clab-mirror-h1 ping -c 3 192.168.10.2
text
0 packets captured

什么都没有。 交换机把包发给了 eth2(h2),不会发给 eth3。这是交换机该有的行为。

这就是交换机和 hub 的区别

早年的 hub 会把包广播给所有端口,抓包很容易。交换机按 MAC 表定向转发,所以想旁听必须显式配置镜像。

配端口镜像 ​

bash
N="docker exec clab-mirror-ovs1"

$N ovs-vsctl \
  -- --id=@eth1 get Port eth1 \
  -- --id=@eth3 get Port eth3 \
  -- --id=@m create Mirror name=mirror0 \
       select-src-port=@eth1 select-dst-port=@eth1 output-port=@eth3 \
  -- set Bridge br0 mirrors=@m

这条命令值得拆开讲,它是 ovs-vsctl 里最容易写错的一类。

@ 符号是什么 ​

Mirror 是 OVSDB 里的一张独立的表,通过 Bridge.mirrors 引用(实验 2 讲的 UUID 引用链)。

问题是:新建的 Mirror 记录的 UUID 要等创建之后才知道,但同一个事务里就要把它挂到 Bridge 上。ovs-vsctl 用 @名字 解决这个问题 —— 它是个事务内的临时变量。

text
--id=@eth1 get Port eth1     把 Port eth1 的 UUID 存进 @eth1
--id=@eth3 get Port eth3     把 Port eth3 的 UUID 存进 @eth3
--id=@m create Mirror ...    新建 Mirror,它的 UUID 存进 @m
                             (创建时引用 @eth1 和 @eth3)
set Bridge br0 mirrors=@m    把 @m 挂到 br0

@ 符号必须先定义后使用

把 create Mirror 写在 get Port 前面会报:

text
ovs-vsctl: row id "@eth1" specified on "get" command
           was used before it was defined

先 get 拿 UUID,再 create 去引用。 顺序错了不会静默失败,会直接报这个错。

三个 select 字段 ​

字段含义
select-src-port镜像从这些端口进来的流量
select-dst-port镜像发往这些端口的流量
select-vlan镜像某个 VLAN 的全部流量
select_all=true镜像整个网桥的流量

select-src-port 和 select-dst-port 都设成 eth1,就是双向镜像 eth1 的流量。

只想看单方向就只设一个。

验证 ​

bash
docker exec -d clab-mirror-h3 \
  sh -c 'timeout 8 tcpdump -i eth1 -n -l icmp > /tmp/cap.txt 2>&1'
sleep 1
docker exec clab-mirror-h1 ping -c 4 -i 0.3 192.168.10.2
sleep 8
docker exec clab-mirror-h3 cat /tmp/cap.txt
text
IP 192.168.10.1 > 192.168.10.2: ICMP echo request, id 77, seq 1, length 64
IP 192.168.10.2 > 192.168.10.1: ICMP echo reply,   id 77, seq 1, length 64
IP 192.168.10.1 > 192.168.10.2: ICMP echo request, id 77, seq 2, length 64
IP 192.168.10.2 > 192.168.10.1: ICMP echo reply,   id 77, seq 2, length 64
...
8 packets captured

往返两个方向都抓到了。 h3 完全不参与通信,却看到了全部流量。

tcpdump 写文件要加 -l

默认情况下 tcpdump 输出到文件是块缓冲的,被 timeout 杀掉时缓冲区内容直接丢失,文件里只剩开头两行提示 —— 看起来像"什么都没抓到"。

-l 改成行缓冲,每抓到一个包就落盘。脚本里抓包一律加 -l。

看镜像的统计 ​

bash
$N ovs-vsctl list Mirror
text
name             : mirror0
output_port      : c440e5e9-e7c4-4520-8c31-45f8f9b3f4c9
select_all       : false
select_dst_port  : [c37811f5-cc9e-4f8d-b187-3cedb4602b02]
select_src_port  : [c37811f5-cc9e-4f8d-b187-3cedb4602b02]
snaplen          : []
statistics       : {tx_bytes=1372, tx_packets=14}

statistics 是排查镜像问题的第一站:

  • tx_packets 在涨 → 镜像在工作,问题在抓包侧
  • tx_packets 是 0 → 镜像没生效,检查 select-* 配置

两个实用选项 ​

select_all:镜像整个网桥 ​

bash
$N ovs-vsctl set Mirror mirror0 select_all=true \
  select-src-port=[] select-dst-port=[]
text
select_all      : true
select_dst_port : []
select_src_port : []
statistics      : {tx_bytes=4158, tx_packets=49}

select_all 会翻倍流量

网桥上所有流量都复制一份到输出口。输出口的带宽必须撑得住整个网桥的总流量,否则会丢包 —— 而且丢的可能是业务包。

生产环境慎用,优先用 select-src-port 精确指定。

snaplen:只镜像报文头 ​

bash
$N ovs-vsctl set Mirror mirror0 snaplen=64

只复制每个包的前 64 字节。协议头够用了,载荷不要 —— 大幅降低镜像开销,也避免把敏感数据复制出去。

做入侵检测或协议分析时通常只需要头部,这个选项很实用。

镜像到 VLAN ​

除了输出到端口,还能输出到一个 VLAN:

bash
ovs-vsctl set Mirror mirror0 output-vlan=999 output-port=[]

镜像的帧打上 VLAN 999 标签后在网桥里泛洪。远端的分析设备只要接在这个 VLAN 里就能收到 —— 这就是物理交换机上的 RSPAN。

output-port 和 output-vlan 只能设一个。

sFlow:抽样采集 ​

Mirror 是本机的、全量的。要把流量信息送到远端采集器做长期分析,用 sFlow。

bash
$N ovs-vsctl -- --id=@s create sFlow \
     agent=br0 target=\"127.0.0.1:6343\" \
     header=128 sampling=64 polling=10 \
  -- set Bridge br0 sflow=@s
text
agent    : br0
header   : 128
polling  : 10
sampling : 64
targets  : ["127.0.0.1:6343"]
字段含义
target采集器地址,sFlow 标准端口 6343
sampling每 N 个包抽 1 个。64 表示 1/64
header每个抽样包发送前多少字节
polling每隔几秒上报一次接口计数
agent用哪个接口的地址作为上报者身份

sampling 是性能和精度的权衡。 值越小越精确但开销越大。生产环境通常 512 到 4096;万兆链路上用 64 会明显吃 CPU。

常用采集端:sflowtool、ntopng、Prometheus 的 sflow exporter。

NetFlow / IPFIX:流级统计 ​

sFlow 送的是抽样的报文,NetFlow 送的是聚合后的流记录(五元组 + 包数 + 字节数 + 时间戳),不含报文内容。

bash
$N ovs-vsctl -- --id=@n create NetFlow \
     targets=\"127.0.0.1:2055\" active-timeout=30 \
  -- set Bridge br0 netflow=@n
text
active_timeout : 30
targets        : ["127.0.0.1:2055"]

active-timeout=30 表示长连接每 30 秒上报一次中间统计,不用等连接结束。

IPFIX 是 NetFlow v10 的标准化版本,配置类似(create IPFIX),支持更灵活的字段。

三者怎么选 ​

text
   想看到完整报文内容,本机分析          → Mirror
   想长期画流量趋势,能接受抽样          → sFlow
   想按连接计费/审计,不需要报文内容      → NetFlow / IPFIX

清理 ​

bash
$N ovs-vsctl clear Bridge br0 mirrors
$N ovs-vsctl clear Bridge br0 sflow
$N ovs-vsctl clear Bridge br0 netflow

只清 Bridge 的引用会留下孤儿记录

clear Bridge br0 mirrors 只是解除引用。Mirror 记录本身会因为没有引用而被 OVSDB 自动回收(它是弱引用表),但保险起见可以确认:

bash
ovs-vsctl list Mirror      # 应该是空的

自动检查 ​

bash
./verify.sh
text
  [通过] br0 上挂了 Mirror
  [通过] 业务流量仍然正常
  [通过] h3 抓到了 h1→h2 的 ICMP(镜像生效)
  [通过] h3 也抓到了回程方向

自测清单 ​

  1. 为什么默认情况下 h3 抓不到 h1 和 h2 之间的流量?
  2. ovs-vsctl 里的 @名字 是什么?为什么必须先定义后使用?
  3. select-src-port 和 select-dst-port 都设成同一个口,效果是什么?
  4. 排查"镜像不生效"该先看哪个字段?
  5. select_all=true 有什么风险?
  6. tcpdump 写文件时为什么要加 -l?
  7. Mirror、sFlow、NetFlow 分别适合什么场景?

常见问题 ​

抓不到镜像流量 ​

先看镜像本身有没有工作:

bash
ovs-vsctl list Mirror | grep statistics
  • tx_packets 在涨 → 镜像正常,问题在抓包侧。检查抓包的接口对不对、有没有加 -l、过滤条件是否太严
  • tx_packets 是 0 → 检查 select-src-port/select-dst-port 指的是不是你想监控的端口,以及 Bridge.mirrors 有没有挂上

配置镜像后业务丢包 ​

输出口带宽不够。改用 snaplen 只镜像报文头,或者缩小 select-* 的范围。

报错 row id "@xxx" was used before it was defined ​

--id=@xxx get ... 必须写在引用它的命令之前。

镜像的输出口能同时跑业务吗 ​

技术上可以,但不建议。镜像流量会和业务流量争带宽,而且抓包时会看到一堆无关的业务包。专门留一个口给抓包机。

sFlow 配了但采集器收不到 ​

bash
ovs-vsctl list sFlow                 # 确认 targets 正确
ovs-vsctl get Bridge br0 sflow       # 确认挂到了网桥上

再检查 OVS 所在主机到采集器的 UDP 6343 是否可达。sFlow 是 UDP,丢了不会重传也不会报错。

小结 ​

  • 交换机定向转发,想旁听流量必须显式配置镜像
  • Mirror 是 OVSDB 的独立表,用 @名字 在一个事务里完成"建记录 + 挂到 Bridge"
  • @ 符号必须先定义后使用,顺序错了会直接报错
  • select-src-port + select-dst-port 设成同一个口 = 双向镜像
  • Mirror.statistics.tx_packets 是排查镜像问题的第一站
  • select_all=true 会把网桥总流量复制一份,注意输出口带宽
  • snaplen 只镜像报文头,大幅降低开销
  • 脚本里 tcpdump 写文件一定加 -l,否则缓冲区内容会丢
  • Mirror 看报文(本机)、sFlow 看抽样趋势(远端)、NetFlow 看流统计(远端)

第 3 部分到此结束。下一部分进入 Overlay 网络:为什么 4094 个 VLAN 不够用,以及 VXLAN 怎么解决它。