ᕕ( ᐛ )ᕗ Jimyag's Blog

OVS 与 OVN 简介及实验

· 2746 words · ~ 13 min read

在上一篇文章里我们看到,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

数据流方向:

  1. 用户通过 ovn-nbctl 向 NB DB 写入逻辑网络配置。
  2. ovn-northd 监听 NB DB 变化,将逻辑网络翻译成 Logical Flow(逻辑流表)写入 SB DB。
  3. 每台 hypervisor 上的 ovn-controller 监听 SB DB,读取 Logical Flow 和 Port Binding,转换为实际的 OpenFlow 规则下发给本机 OVS。
  4. 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-relatedallowallow-statelessdroprejectpass。其中 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 个实验:

  1. 逻辑交换机(二层通信)
  2. 逻辑路由器(跨网段通信)
  3. ACL 过滤
  4. 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-remoteovn-encap-typeovn-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-vsctlovs-ofctl ovn-nbctlovn-sbctl
隧道协议 支持 GRE/VXLAN/GENEVE 默认使用 GENEVE
路由 无(需外部控制器) 支持分布式路由和集中式网关
状态防火墙 无(需 conntrack 配合) 原生 ACL,基于 conntrack
负载均衡 原生 L4 LB
适用场景 单机虚拟交换、需要精细控制 OpenFlow 时 多主机虚拟网络、OpenStack、Kubernetes

OVS 是"如何转发"的执行层,OVN 是"转发什么"的决策层。两者配合,OVN 把网络管理员的意图翻译成 OVS 能执行的规则,OVS 负责高速转发。


7. 参考

#Linux #OVS #OVN #Overlay #网络虚拟化 #OpenFlow #GENEVE