在上一篇文章里我们看到,GENEVE 可以在两台主机之间建立隧道,用 VNI 区分租户。但谁来决定"哪个包走哪条隧道"、“哪个虚拟机能和哪个虚拟机通信”?这就是 OVS 和 OVN 要解决的问题。
OVS(Open vSwitch)是一套可编程的虚拟交换机实现,提供数据平面;在 Linux 上最常见的快速路径是内核 datapath,也支持纯 userspace datapath。OVN(Open Virtual Network)是构建在 OVS 之上的网络虚拟化系统,提供逻辑交换机、逻辑路由器、ACL 等高层抽象,把"网络意图"翻译成 OVS 能执行的 OpenFlow 规则。
1. OVS:可编程的虚拟交换机
架构
OVS 由三个组件构成:
flowchart TB
subgraph User["用户态"]
VSCTL["ovs-vsctl\n配置管理 CLI"]
OFCTL["ovs-ofctl\nOpenFlow CLI"]
OVSDB["ovsdb-server\n配置数据库"]
VSWITCHD["ovs-vswitchd\n主守护进程"]
VSCTL --> OVSDB
OFCTL --> VSWITCHD
OVSDB <--> VSWITCHD
end
subgraph Kernel["内核态"]
DP["openvswitch.ko\n快速转发路径(datapath)"]
end
VSWITCHD <-->|"upcall / flow install"| DP
style OVSDB fill:#4a90d9,color:#fff
style VSWITCHD fill:#4a90d9,color:#fff
style DP fill:#27ae60,color:#fff
ovsdb-server:存储 OVS 的配置(网桥、端口、接口),使用 OVSDB 协议,ovs-vsctl 通过它读写配置。
ovs-vswitchd:核心守护进程,读取 OVSDB 配置,维护 OpenFlow 流表,并将可缓存的转发结果同步到 datapath;当 datapath 无法匹配时,也负责处理 upcall。
openvswitch.ko:Linux 内核 datapath 模块,实现最常见的快速路径。已知流的包直接在 datapath 中转发,未知流的包上报给 ovs-vswitchd 处理。
核心概念
| 概念 |
说明 |
| Bridge |
虚拟交换机实例,类似物理交换机。所有端口都挂在 bridge 下 |
| Port |
Bridge 上的一个逻辑端口,可以是物理网卡、veth、隧道接口等 |
| Interface |
Port 下的实际网络接口;最常见是一对一,但 bond 等场景下一个 Port 也可以包含多个 Interface |
| Flow Table |
OpenFlow 流表,定义"匹配条件 → 动作"规则,是 OVS 的核心 |
| Controller |
外部 SDN 控制器(如 OVN),通过 OpenFlow 协议向 OVS 下发流规则 |
常用命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# 查看所有 bridge 及其端口
ovs-vsctl show
# 创建一个 bridge
ovs-vsctl add-br br0
# 添加物理网卡到 bridge
ovs-vsctl add-port br0 eth0
# 查看流表(所有 table)
ovs-ofctl dump-flows br-int
# 查看某张流表
ovs-ofctl dump-flows br-int table=0
# 查看 datapath 中已缓存的流(内核快速路径,可能包含多个 bridge)
ovs-dpctl dump-flows
# 按 bridge 查看 datapath 流
ovs-appctl dpif/dump-flows br-int
# 查看 OVS 版本信息
ovs-vsctl --version
ovs-appctl version
|
2. OVN:网络虚拟化的逻辑层
直接用 OVS 管理虚拟网络有明显局限:OpenFlow 规则是"如何转发"的底层指令,而不是"我要一个能互通的二层网络"这样的意图表达。有几十台主机、几百个虚拟机时,手写和维护 OpenFlow 规则极其复杂。
OVN 在 OVS 之上增加了一层抽象:用户只需声明逻辑拓扑(哪些端口在同一个逻辑交换机上、路由器怎么连接),OVN 自动将其翻译成每台 hypervisor 上的 OpenFlow 规则,并通过 GENEVE 隧道实现跨主机通信。
架构
flowchart TB
subgraph NB["Northbound(用户意图层)"]
NBDB["OVN NB DB\n逻辑网络配置\n(LS、LR、ACL、LB)"]
NBCTL["ovn-nbctl\n配置 CLI"]
NBCTL --> NBDB
end
NORTHD["ovn-northd\n翻译引擎:NB → SB"]
subgraph SB["Southbound(实现层)"]
SBDB["OVN SB DB\n物理绑定 + 逻辑流表\n(Chassis、Port_Binding、Logical_Flow)"]
end
subgraph HV1["Hypervisor 1"]
CTRL1["ovn-controller"]
OVS1["OVS + br-int"]
CTRL1 -->|"OpenFlow 规则"| OVS1
end
subgraph HV2["Hypervisor 2"]
CTRL2["ovn-controller"]
OVS2["OVS + br-int"]
CTRL2 -->|"OpenFlow 规则"| OVS2
end
NBDB --> NORTHD
NORTHD --> SBDB
SBDB --> CTRL1
SBDB --> CTRL2
OVS1 <-->|"GENEVE 隧道"| OVS2
style NBDB fill:#4a90d9,color:#fff
style SBDB fill:#e67e22,color:#fff
style NORTHD fill:#8e44ad,color:#fff
style CTRL1 fill:#27ae60,color:#fff
style CTRL2 fill:#27ae60,color:#fff
数据流方向:
- 用户通过
ovn-nbctl 向 NB DB 写入逻辑网络配置。
ovn-northd 监听 NB DB 变化,将逻辑网络翻译成 Logical Flow(逻辑流表)写入 SB DB。
- 每台 hypervisor 上的
ovn-controller 监听 SB DB,读取 Logical Flow 和 Port Binding,转换为实际的 OpenFlow 规则下发给本机 OVS。
- hypervisor 间流量通过 GENEVE 隧道传输;VNI 承载 logical datapath ID,Geneve option 继续携带逻辑 ingress/egress port 等元数据。
3. OVN 核心概念
Logical Switch(逻辑交换机)
对应一个二层广播域,类似 VLAN 或 VPC 子网。同一个 Logical Switch 上的端口可以二层互通,不同 Logical Switch 上的端口需要通过 Logical Router 路由。
1
2
3
4
5
|
# 创建逻辑交换机
ovn-nbctl ls-add ls-web
# 查看所有逻辑交换机
ovn-nbctl ls-list
|
Logical Switch Port(逻辑交换机端口)
逻辑交换机上的一个端口,对应一个虚拟机或容器的网卡。每个端口有固定的 MAC 地址和 IP 地址,OVN 用这些信息生成 ARP 响应规则(ARP responder),避免广播。
1
2
3
4
5
6
|
# 添加逻辑端口,绑定 MAC + IP
ovn-nbctl lsp-add ls-web lsp-vm1
ovn-nbctl lsp-set-addresses lsp-vm1 "02:ac:10:ff:01:01 10.0.1.1"
# 查看端口详情
ovn-nbctl lsp-list ls-web
|
常见端口类型:
| 类型 |
说明 |
"" (默认) |
普通虚拟机端口 |
router |
连接 Logical Router 的端口,用于网关 |
localnet |
连接本地可达的物理二层网络,常用于 provider network |
localport |
本地端口,流量不走隧道,每台主机独立一份(如 metadata 服务) |
vtep |
连接硬件 VTEP 设备 |
Logical Router(逻辑路由器)
提供 L3 路由,连接多个 Logical Switch。OVN 支持两种路由模式:
- 分布式路由(Distributed):东西向流量(同数据中心内)直接在源 hypervisor 上路由,不需要经过集中节点,延迟低。
- 集中式网关路由(Gateway Router):南北向流量(访问外部网络)通过指定的 Gateway Chassis 转发,便于统一做 NAT。
1
2
3
4
5
6
7
8
9
10
11
|
# 创建逻辑路由器
ovn-nbctl lr-add lr-main
# 添加路由器端口(连接 ls-web,MAC + IP 作为网关地址)
ovn-nbctl lrp-add lr-main lrp-to-web 02:ac:10:ff:01:ff 10.0.1.254/24
# 在 ls-web 上创建连接路由器的端口
ovn-nbctl lsp-add ls-web ls-web-to-lr
ovn-nbctl lsp-set-type ls-web-to-lr router
ovn-nbctl lsp-set-addresses ls-web-to-lr router
ovn-nbctl lsp-set-options ls-web-to-lr router-port=lrp-to-web
|
Chassis 与 Port Binding
Chassis 是 OVN SB DB 中对一台 hypervisor 的抽象,每台运行 ovn-controller 的机器会自动注册为一个 Chassis。
Port Binding 记录某个逻辑端口当前绑定在哪台 Chassis 上,由 ovn-controller 在检测到对应 OVS port 时自动完成绑定。绑定信息写入 SB DB 后,其他 hypervisor 上的 ovn-controller 就知道"这个逻辑端口在那台机器上",从而建立 GENEVE 隧道路径。
1
2
3
4
5
|
# 查看所有 Chassis(hypervisor)
ovn-sbctl show
# 查看端口绑定状态
ovn-sbctl list Port_Binding
|
ACL(访问控制列表)
OVN ACL 是基于连接跟踪(conntrack)的分布式防火墙规则,作用在 Logical Switch 或 Port Group 上。常见动作有 allow-related、allow、allow-stateless、drop、reject、pass。其中 allow-related 是最常用的有状态放行动作,匹配到的新连接会建立 conntrack 状态,回程流量会被自动放行。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 放行 ARP(二层必须,否则无法解析 MAC)
ovn-nbctl acl-add ls-web to-lport 1100 "arp" allow-stateless
# 放行到 lsp-vm1 的 HTTP 连接;回程流量会被自动放行
ovn-nbctl acl-add ls-web to-lport 1000 \
"outport == \"lsp-vm1\" && ip4 && tcp.dst == 80" allow-related
# 其他发往 lsp-vm1 的 IP 流量默认拒绝
ovn-nbctl acl-add ls-web to-lport 900 \
"outport == \"lsp-vm1\" && ip" drop
# 查看 ACL 列表
ovn-nbctl acl-list ls-web
|
Load Balancer
OVN 原生支持 L4 负载均衡,将 VIP 流量分发到后端池,底层通过 conntrack 的 DNAT 实现,无需额外组件。
1
2
3
4
5
6
7
8
|
# 创建 LB:VIP 10.0.0.100:80 → 后端 10.0.1.1:80, 10.0.1.2:80
ovn-nbctl lb-add lb-web 10.0.0.100:80 "10.0.1.1:80,10.0.1.2:80" tcp
# 将 LB 绑定到逻辑交换机
ovn-nbctl ls-lb-add ls-web lb-web
# 查看 LB
ovn-nbctl lb-list
|
由于最小单机 namespace 实验更适合稳定验证逻辑交换机、逻辑路由器、ACL 和 Port Security,本文不再提供负载均衡的实操实验,避免把 gateway router、localnet、distributed gateway port 等更复杂前提混入入门文章。
4. 实验:单台物理机搭建 OVN 测试环境
下面的实验不再共享一套环境,而是拆成 4 个彼此独立的实验。每个实验都有:
- 独立拓扑
- 独立创建步骤
- 独立验证步骤
- 独立清理步骤
这样做有两个好处:
- 读者可以从任意一个实验开始,不依赖前面已经执行过什么;
- 即使某个实验失败,也不会污染后面的实验环境。
本文包含 4 个实验:
- 逻辑交换机(二层通信)
- 逻辑路由器(跨网段通信)
- ACL 过滤
- Port Security 防 IP/MAC 伪造
环境准备
1
2
3
4
5
|
# 安装 OVS 和 OVN(Debian/Ubuntu)
apt install -y openvswitch-switch ovn-central ovn-host
# 或 RHEL/CentOS
yum install -y openvswitch ovn-central ovn-host
|
启动服务
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
|
# Debian/Ubuntu 上的 OVS systemd unit 名是 openvswitch-switch
systemctl start openvswitch-switch
# RHEL/CentOS 上通常是 openvswitch
# systemctl start openvswitch
# 启动 OVN 北向/南向数据库和 ovn-northd
systemctl start ovn-central
# 将本机 OVS 接入 OVN(告知 OVN 本机的 underlay IP 和隧道类型)
ovs-vsctl set open . \
external-ids:ovn-remote=unix:/var/run/ovn/ovnsb_db.sock \
external-ids:ovn-encap-type=geneve \
external-ids:ovn-encap-ip=127.0.0.1 # 单机实验用 127.0.0.1
# 启动 ovn-controller(在每台 hypervisor 上运行)
systemctl start ovn-host
# 某些发行版上,修改 external-ids 后重启 ovn-host 更稳妥
systemctl restart ovn-host
# 先看服务状态。Debian 上 ovn-central / ovn-host 显示 active (exited) 是正常的,
# 它们是 oneshot 包装服务,真正常驻的进程是 ovn-northd / ovn-controller / ovsdb-server。
systemctl status openvswitch-switch --no-pager
systemctl status ovn-central --no-pager
systemctl status ovn-host --no-pager
# 再看关键进程是否存在
ps aux | grep -E 'ovs-vswitchd|ovn-controller|ovn-northd|ovsdb-server' | grep -v grep
# 查看 OVS 当前配置
ovs-vsctl show
# 确认 OVN 相关 external-ids 已写入成功
ovs-vsctl get open . external-ids
# 查看 OVN Southbound 中是否已注册本机 Chassis
ovn-sbctl show
|
预期现象:
1
2
3
4
5
6
7
8
9
10
11
|
- `openvswitch-switch` 处于 active 状态;
- `ovn-central` 和 `ovn-host` 可能显示为 `active (exited)`,这在 Debian 上是正常的;
- `ps` 能看到 `ovs-vswitchd`、`ovn-controller`、`ovn-northd`、`ovsdb-server`;
- `ovs-vsctl show` 至少能看到 `br-int`;
- `ovs-vsctl get open . external-ids` 中应包含 `ovn-remote`、`ovn-encap-type`、`ovn-encap-ip`;
- `ovn-sbctl show` 在 Chassis 注册成功后会出现类似下面的输出:
Chassis <uuid>
hostname: <hostname>
Encap geneve
ip: "127.0.0.1"
|
补充说明:
- 单机场景里
br-int 显示 state DOWN 不一定是错误。只要 bridge 已创建、后续端口能正常加入,实验仍然可以继续。
- 如果
ovn-sbctl show 为空,通常说明本机还没有成功注册为 Chassis。优先检查:
ovs-vsctl get open . external-ids 输出中是否真的包含 ovn-remote、ovn-encap-type、ovn-encap-ip;
ovn-controller 是否正在运行;
- 写入
external-ids 后是否已重启 ovn-host。
ovn-controller.log 在启动初期短暂出现 br-int.mgmt: connection failed (No such file or directory) 不一定是故障;如果随后很快出现 connected,通常说明只是 br-int 管理 socket 尚未就绪、后续已自动重连成功。
常用排障命令:
1
2
3
4
5
6
7
8
|
# 查看 external-ids 是否完整
ovs-vsctl get open . external-ids
# 查看 ovn-controller 最近日志
tail -n 20 /var/log/ovn/ovn-controller.log
# 关注 chassis 是否已注册成功
ovn-sbctl show
|
实验一:逻辑交换机(二层通信)
flowchart TB
subgraph LS["Logical Switch ls-l2-1 (10.0.1.0/24)"]
VM1["vm1 / lsp-l2-1\n10.0.1.1"]
VM2["vm2 / lsp-l2-2\n10.0.1.2"]
end
VM1 --- VM2
style LS fill:#4a90d9,color:#fff
创建环境:
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
|
ovn-nbctl ls-add ls-l2-1
ovn-nbctl lsp-add ls-l2-1 lsp-l2-1
ovn-nbctl lsp-set-addresses lsp-l2-1 "02:ac:10:ff:01:01 10.0.1.1"
ovn-nbctl lsp-add ls-l2-1 lsp-l2-2
ovn-nbctl lsp-set-addresses lsp-l2-2 "02:ac:10:ff:01:02 10.0.1.2"
ip netns add vm1
ip link add veth-vm1 type veth peer name ovs-vm1
ip link set veth-vm1 netns vm1
ip netns exec vm1 ip link set veth-vm1 address 02:ac:10:ff:01:01
ip netns exec vm1 ip addr replace 10.0.1.1/24 dev veth-vm1
ip netns exec vm1 ip link set veth-vm1 up
ip netns exec vm1 ip link set lo up
ip link set ovs-vm1 up
ovs-vsctl add-port br-int ovs-vm1
ovs-vsctl set interface ovs-vm1 external_ids:iface-id=lsp-l2-1
ip netns add vm2
ip link add veth-vm2 type veth peer name ovs-vm2
ip link set veth-vm2 netns vm2
ip netns exec vm2 ip link set veth-vm2 address 02:ac:10:ff:01:02
ip netns exec vm2 ip addr replace 10.0.1.2/24 dev veth-vm2
ip netns exec vm2 ip link set veth-vm2 up
ip netns exec vm2 ip link set lo up
ip link set ovs-vm2 up
ovs-vsctl add-port br-int ovs-vm2
ovs-vsctl set interface ovs-vm2 external_ids:iface-id=lsp-l2-2
ovn-sbctl show
|
验证:
1
|
ip netns exec vm1 ping -c3 10.0.1.2
|
清理环境:
1
2
3
4
5
|
ovs-vsctl --if-exists del-port br-int ovs-vm1
ovs-vsctl --if-exists del-port br-int ovs-vm2
ip netns del vm1 2>/dev/null || true
ip netns del vm2 2>/dev/null || true
ovn-nbctl --if-exists ls-del ls-l2-1
|
实验二:逻辑路由器(跨网段通信)
flowchart LR
subgraph LS1["Logical Switch ls-l3-1 (10.0.1.0/24)"]
VM1["vm1 / lsp-l3-1\n10.0.1.1"]
end
subgraph LR1["Logical Router lr-l3"]
GW1["10.0.1.254/24"]
GW2["10.0.2.254/24"]
end
subgraph LS2["Logical Switch ls-l3-2 (10.0.2.0/24)"]
VM2["vm3 / lsp-l3-2\n10.0.2.1"]
end
VM1 --- GW1
GW1 --- GW2
GW2 --- VM2
style LR1 fill:#27ae60,color:#fff
创建环境:
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
|
ovn-nbctl ls-add ls-l3-1
ovn-nbctl ls-add ls-l3-2
ovn-nbctl lr-add lr-l3
ovn-nbctl lsp-add ls-l3-1 lsp-l3-1
ovn-nbctl lsp-set-addresses lsp-l3-1 "02:ac:10:ff:01:01 10.0.1.1"
ovn-nbctl lsp-add ls-l3-2 lsp-l3-2
ovn-nbctl lsp-set-addresses lsp-l3-2 "02:ac:10:ff:02:01 10.0.2.1"
ovn-nbctl lrp-add lr-l3 lrp-l3-1 02:ac:10:ff:01:ff 10.0.1.254/24
ovn-nbctl lrp-add lr-l3 lrp-l3-2 02:ac:10:ff:02:ff 10.0.2.254/24
ovn-nbctl lsp-add ls-l3-1 ls-l3-1-to-lr
ovn-nbctl lsp-set-type ls-l3-1-to-lr router
ovn-nbctl lsp-set-addresses ls-l3-1-to-lr router
ovn-nbctl lsp-set-options ls-l3-1-to-lr router-port=lrp-l3-1
ovn-nbctl lsp-add ls-l3-2 ls-l3-2-to-lr
ovn-nbctl lsp-set-type ls-l3-2-to-lr router
ovn-nbctl lsp-set-addresses ls-l3-2-to-lr router
ovn-nbctl lsp-set-options ls-l3-2-to-lr router-port=lrp-l3-2
ip netns add vm1
ip link add veth-vm1 type veth peer name ovs-vm1
ip link set veth-vm1 netns vm1
ip netns exec vm1 ip link set veth-vm1 address 02:ac:10:ff:01:01
ip netns exec vm1 ip addr replace 10.0.1.1/24 dev veth-vm1
ip netns exec vm1 ip link set veth-vm1 up
ip netns exec vm1 ip link set lo up
ip netns exec vm1 ip route replace default via 10.0.1.254 dev veth-vm1
ip link set ovs-vm1 up
ovs-vsctl add-port br-int ovs-vm1
ovs-vsctl set interface ovs-vm1 external_ids:iface-id=lsp-l3-1
ip netns add vm3
ip link add veth-vm3 type veth peer name ovs-vm3
ip link set veth-vm3 netns vm3
ip netns exec vm3 ip link set veth-vm3 address 02:ac:10:ff:02:01
ip netns exec vm3 ip addr replace 10.0.2.1/24 dev veth-vm3
ip netns exec vm3 ip link set veth-vm3 up
ip netns exec vm3 ip link set lo up
ip netns exec vm3 ip route replace default via 10.0.2.254 dev veth-vm3
ip link set ovs-vm3 up
ovs-vsctl add-port br-int ovs-vm3
ovs-vsctl set interface ovs-vm3 external_ids:iface-id=lsp-l3-2
|
验证:
1
|
ip netns exec vm1 ping -c3 10.0.2.1
|
清理环境:
1
2
3
4
5
6
7
|
ovs-vsctl --if-exists del-port br-int ovs-vm1
ovs-vsctl --if-exists del-port br-int ovs-vm3
ip netns del vm1 2>/dev/null || true
ip netns del vm3 2>/dev/null || true
ovn-nbctl --if-exists lr-del lr-l3
ovn-nbctl --if-exists ls-del ls-l3-1
ovn-nbctl --if-exists ls-del ls-l3-2
|
实验三:ACL 过滤
flowchart TB
subgraph LS["Logical Switch ls-acl (10.0.1.0/24)"]
VM1["vm1 / lsp-acl-1\n10.0.1.1"]
VM2["vm2 / lsp-acl-2\n10.0.1.2"]
VM3["vm3 / lsp-acl-3\n10.0.1.3"]
end
VM2 -->|"ICMP allowed"| VM1
VM3 -. "ICMP denied" .-> VM1
style LS fill:#4a90d9,color:#fff
创建环境:
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
|
ovn-nbctl ls-add ls-acl
ovn-nbctl lsp-add ls-acl lsp-acl-1
ovn-nbctl lsp-set-addresses lsp-acl-1 "02:ac:10:ff:01:01 10.0.1.1"
ovn-nbctl lsp-add ls-acl lsp-acl-2
ovn-nbctl lsp-set-addresses lsp-acl-2 "02:ac:10:ff:01:02 10.0.1.2"
ovn-nbctl lsp-add ls-acl lsp-acl-3
ovn-nbctl lsp-set-addresses lsp-acl-3 "02:ac:10:ff:01:03 10.0.1.3"
ip netns add vm1
ip link add veth-vm1 type veth peer name ovs-vm1
ip link set veth-vm1 netns vm1
ip netns exec vm1 ip link set veth-vm1 address 02:ac:10:ff:01:01
ip netns exec vm1 ip addr replace 10.0.1.1/24 dev veth-vm1
ip netns exec vm1 ip link set veth-vm1 up
ip netns exec vm1 ip link set lo up
ip link set ovs-vm1 up
ovs-vsctl add-port br-int ovs-vm1
ovs-vsctl set interface ovs-vm1 external_ids:iface-id=lsp-acl-1
ip netns add vm2
ip link add veth-vm2 type veth peer name ovs-vm2
ip link set veth-vm2 netns vm2
ip netns exec vm2 ip link set veth-vm2 address 02:ac:10:ff:01:02
ip netns exec vm2 ip addr replace 10.0.1.2/24 dev veth-vm2
ip netns exec vm2 ip link set veth-vm2 up
ip netns exec vm2 ip link set lo up
ip link set ovs-vm2 up
ovs-vsctl add-port br-int ovs-vm2
ovs-vsctl set interface ovs-vm2 external_ids:iface-id=lsp-acl-2
ip netns add vm3
ip link add veth-vm3 type veth peer name ovs-vm3
ip link set veth-vm3 netns vm3
ip netns exec vm3 ip link set veth-vm3 address 02:ac:10:ff:01:03
ip netns exec vm3 ip addr replace 10.0.1.3/24 dev veth-vm3
ip netns exec vm3 ip link set veth-vm3 up
ip netns exec vm3 ip link set lo up
ip link set ovs-vm3 up
ovs-vsctl add-port br-int ovs-vm3
ovs-vsctl set interface ovs-vm3 external_ids:iface-id=lsp-acl-3
# 只放行从 lsp-acl-2 发往 lsp-acl-1 的 ICMP,新连接命中后回程流量会自动放行
ovn-nbctl acl-add ls-acl to-lport 1100 "arp" allow-stateless
ovn-nbctl acl-add ls-acl to-lport 1000 \
"outport == \"lsp-acl-1\" && inport == \"lsp-acl-2\" && ip4 && icmp4" allow-related
ovn-nbctl acl-add ls-acl to-lport 900 \
"outport == \"lsp-acl-1\" && ip" drop
|
验证:
1
2
3
4
5
|
# vm2 -> vm1 应该通
ip netns exec vm2 ping -c2 10.0.1.1
# vm3 -> vm1 应该不通
ip netns exec vm3 ping -c2 -W1 10.0.1.1
|
清理环境:
1
2
3
4
5
6
7
|
ovs-vsctl --if-exists del-port br-int ovs-vm1
ovs-vsctl --if-exists del-port br-int ovs-vm2
ovs-vsctl --if-exists del-port br-int ovs-vm3
ip netns del vm1 2>/dev/null || true
ip netns del vm2 2>/dev/null || true
ip netns del vm3 2>/dev/null || true
ovn-nbctl --if-exists ls-del ls-acl
|
实验四:Port Security 防 IP/MAC 伪造
flowchart TB
subgraph LS["Logical Switch ls-ps (10.0.1.0/24)"]
VM1["vm1 / lsp-ps-1\n10.0.1.1"]
VM2["vm2 / lsp-ps-2\n10.0.1.2"]
end
VM1 -. "spoof 10.0.1.99" .-> VM2
style LS fill:#4a90d9,color:#fff
创建环境:
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
|
ovn-nbctl ls-add ls-ps
ovn-nbctl lsp-add ls-ps lsp-ps-1
ovn-nbctl lsp-set-addresses lsp-ps-1 "02:ac:10:ff:01:01 10.0.1.1"
ovn-nbctl lsp-add ls-ps lsp-ps-2
ovn-nbctl lsp-set-addresses lsp-ps-2 "02:ac:10:ff:01:02 10.0.1.2"
ip netns add vm1
ip link add veth-vm1 type veth peer name ovs-vm1
ip link set veth-vm1 netns vm1
ip netns exec vm1 ip link set veth-vm1 address 02:ac:10:ff:01:01
ip netns exec vm1 ip addr replace 10.0.1.1/24 dev veth-vm1
ip netns exec vm1 ip link set veth-vm1 up
ip netns exec vm1 ip link set lo up
ip link set ovs-vm1 up
ovs-vsctl add-port br-int ovs-vm1
ovs-vsctl set interface ovs-vm1 external_ids:iface-id=lsp-ps-1
ip netns add vm2
ip link add veth-vm2 type veth peer name ovs-vm2
ip link set veth-vm2 netns vm2
ip netns exec vm2 ip link set veth-vm2 address 02:ac:10:ff:01:02
ip netns exec vm2 ip addr replace 10.0.1.2/24 dev veth-vm2
ip netns exec vm2 ip link set veth-vm2 up
ip netns exec vm2 ip link set lo up
ip link set ovs-vm2 up
ovs-vsctl add-port br-int ovs-vm2
ovs-vsctl set interface ovs-vm2 external_ids:iface-id=lsp-ps-2
ovn-nbctl lsp-set-port-security lsp-ps-1 "02:ac:10:ff:01:01 10.0.1.1"
|
验证:
1
2
3
4
5
6
7
8
9
10
11
12
|
# 正常地址下应可访问 vm2
ip netns exec vm1 ping -c2 10.0.1.2
# 伪造源 IP 后应失败
ip netns exec vm1 ip addr flush dev veth-vm1
ip netns exec vm1 ip addr add 10.0.1.99/24 dev veth-vm1
ip netns exec vm1 ping -c2 -W1 10.0.1.2
# 如需继续在当前环境中排查,可恢复原地址
ip netns exec vm1 ip addr flush dev veth-vm1
ip netns exec vm1 ip addr add 10.0.1.1/24 dev veth-vm1
ip netns exec vm1 ip link set veth-vm1 up
|
清理环境:
1
2
3
4
5
|
ovs-vsctl --if-exists del-port br-int ovs-vm1
ovs-vsctl --if-exists del-port br-int ovs-vm2
ip netns del vm1 2>/dev/null || true
ip netns del vm2 2>/dev/null || true
ovn-nbctl --if-exists ls-del ls-ps
|
5. 常用诊断命令
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
50
51
|
# ── OVS ────────────────────────────────────────────────────────
# 查看 OVS 整体配置(bridge、port、interface)
ovs-vsctl show
# 查看 br-int 上的所有 OpenFlow 流表
ovs-ofctl dump-flows br-int
# 查看内核 datapath 中已缓存的流(可能包含多个 bridge)
ovs-dpctl dump-flows
# 按 bridge 查看 datapath 流
ovs-appctl dpif/dump-flows br-int
# 查看某个接口的统计信息
ovs-vsctl list interface ovs-vm1
# ── OVN NB(逻辑层)────────────────────────────────────────────
# 查看整个逻辑拓扑
ovn-nbctl show
# 查看逻辑流表(OVN 翻译后的中间表示,比 OpenFlow 可读)
ovn-sbctl lflow-list
# 按逻辑交换机过滤(将 ls-l2-1 替换成当前实验中的交换机名)
ovn-sbctl lflow-list ls-l2-1
# 查看 ACL(将 ls-acl 替换成当前实验中的交换机名)
ovn-nbctl acl-list ls-acl
# 查看负载均衡器(概念参考;本文实验部分不再包含 LB 实操)
ovn-nbctl lb-list
# ── OVN SB(物理绑定层)────────────────────────────────────────
# 查看所有 Chassis(hypervisor)及其上的端口绑定
ovn-sbctl show
# 查看端口绑定详情(确认逻辑端口落在哪台 Chassis)
ovn-sbctl list Port_Binding
# 查看 IP 到 MAC 的绑定信息(ARP/ND 相关)
ovn-sbctl list MAC_Binding
# ── 联合调试 ────────────────────────────────────────────────────
# 追踪一个包在 OVN 逻辑网络中的路径(下面以实验一的 ls-l2-1 为例)
ovn-trace ls-l2-1 'inport == "lsp-l2-1" && eth.src == 02:ac:10:ff:01:01 &&
eth.dst == 02:ac:10:ff:01:02 && ip4.src == 10.0.1.1 &&
ip4.dst == 10.0.1.2 && ip.ttl == 64 && icmp4'
|
ovn-trace 是调试利器,它模拟一个假想数据包在 OVN 逻辑流表中的完整匹配过程,输出每一步命中的规则和动作,不需要真实发包。
6. 总结
|
OVS |
OVN |
| 定位 |
数据平面:可编程虚拟交换机 |
控制平面:网络虚拟化逻辑层 |
| 配置层级 |
OpenFlow 规则(底层) |
逻辑交换机/路由器/ACL(高层意图) |
| 管理工具 |
ovs-vsctl、ovs-ofctl |
ovn-nbctl、ovn-sbctl |
| 隧道协议 |
支持 GRE/VXLAN/GENEVE |
默认使用 GENEVE |
| 路由 |
无(需外部控制器) |
支持分布式路由和集中式网关 |
| 状态防火墙 |
无(需 conntrack 配合) |
原生 ACL,基于 conntrack |
| 负载均衡 |
无 |
原生 L4 LB |
| 适用场景 |
单机虚拟交换、需要精细控制 OpenFlow 时 |
多主机虚拟网络、OpenStack、Kubernetes |
OVS 是"如何转发"的执行层,OVN 是"转发什么"的决策层。两者配合,OVN 把网络管理员的意图翻译成 OVS 能执行的规则,OVS 负责高速转发。
7. 参考