---
url: /courses/virtualization-tech/09-wifi-host-nat-access/index.md
---
# 第9课：让虚拟机借宿主 Wi‑Fi 上网

::: tip 项目目标
在第 8 课中，虚拟机已经在纯二层隔离网络（`lab-isolated`）中建立了安全的内部通信。但在实际工程实践中，虚拟机往往需要从外部网络更新系统内核、安装软件包或拉取容器镜像；同时，宿主机通常连接在校园或企业无线局域网（Wi-Fi）环境中。

本课将带领你构建企业级经典的**双网卡（Dual-Homed）虚拟机架构**：为 `svr01` 保留原隔离内网网卡的同时，追加第二块 VirtIO 网卡接入 libvirt 的 `default` NAT 网络。通过掌握 Netplan 双网卡配置、NAT 出站全链路跟踪、服务发布双方案（SSH 本地端口转发与 DNAT）以及双默认网关冲突的 Metric 跃点修复，建立严密、可审计且符合现代安全规范的虚拟化网络工程能力。
:::

## 一、虚拟化网络模式全貌与 NAT 核心机理

在设计虚拟化网络拓扑前，必须从宏观视角审视 Linux 虚拟化领域的网络互通方案。不同场景对物理网络、安全边界及通信性能的要求截然不同。

### 1.1 虚拟化网络五大经典模式横向全景对比

Linux 与 KVM 虚拟化环境中常见的网络连接模型可以归纳为五大类：

| 网络模式 | 二层广播域特征 | 三层寻址与路由 | 物理 Wi-Fi 兼容性 | 转发性能损耗 | 典型工业应用场景 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **隔离模式 (Isolated)** | 纯宿主内部虚拟交换机（无物理网卡介入） | 无外网路由，禁止与宿主外部网络通信 | 完美兼容（不触碰物理网卡） | 极低（仅内核内部二层交换） | 安全测试靶场、多机集群内部通信、纯净开发沙箱 |
| **NAT 模式 (Address Translation)** | 私有虚拟广播域（`virbr0`），与外部物理二层隔离 | 通过宿主内核 SNAT 伪装共享宿主 IP 上网；入站需端口重定向 | 完美兼容（只向 AP 暴露宿主物理 MAC 与 IP） | 轻微（软路由与连接跟踪查表开销） | 笔记本开发环境、校园/办公 Wi-Fi 宿主机、云桌面 |
| **物理桥接模式 (Bridged)** | 虚拟机直接接入物理交换机广播域，独占 MAC | 直接从物理网络 DHCP 获取独立 IP，地位等同物理机 | **不支持普通 Wi-Fi**（受限于 802.11 协议） | 极低（硬件交换机直接转发） | IDC 机房有线网络、生产物理服务器集群 |
| **Macvtap 模式** | 在物理网卡上衍生独立 MAC 的虚拟端点 | 直通物理三层网络，但宿主机与来宾默认二层互不可达 | 不支持普通 Wi-Fi 客户端模式 | 最低（绕过 Linux Bridge 传统转发表） | 极致网络 I/O 吞吐场景、高性能网络功能虚拟化 (NFV) |
| **覆盖网络 (Overlay / VXLAN)** | 跨三层物理网络封装二层以太网帧（MAC-in-UDP） | 大二层虚拟私网，支持跨多台物理主机构建扁平网络 | 依赖底层三层 UDP 端口连通即可 | 有封包解包损耗（约 50 字节包头与 MTU 扣减） | OpenStack Neutron、Kubernetes (Calico / Flannel) 云原生集群 |

### 1.2 普通无线网卡（Wi-Fi）802.11 协议三地址限制与桥接约束

许多初学者常有疑问：“为什么在接入有线网线时可以随心所欲地创建 `br0` 网桥直连物理网络，而在连入 Wi-Fi 时却必须使用 NAT 模式？”

根本原因在于 **IEEE 802.11 无线局域网协议帧格式的底层约束**：

1. **以太网（IEEE 802.3）的自由广播**：标准有线以太网交换机具备自主 MAC 地址学习功能。无论一条网线上冒出多少个不同的源 MAC 地址，交换机都能记录在其 MAC 地址表（FDB）中并准确分发帧。
2. **Wi-Fi（IEEE 802.11）的 Station 客户端三地址限制**：
   * 普通 Wi-Fi 数据帧在客户端与无线接入点（AP）之间通信时，帧头部只有 3 个 MAC 地址字段：
     * **Address 1（接收端 RA, Receiver Address）**：AP 的 MAC 地址。
     * **Address 2（发送端 TA, Transmitter Address）**：关联认证的物理无线网卡 MAC 地址。
     * **Address 3（目的端 DA, Destination Address / 源端 SA, Source Address）**：上层最终目的或源 MAC 地址。
   * 无线 AP 与无线客户端在建立关联（Association）握手时，AP 的转发表严格绑定了该 Station 的单唯一物理 MAC。如果宿主机将无线网卡挂载到虚拟网桥上，虚拟机会以自己的虚拟 MAC（如 `52:54:00:xx:xx:xx`）发射无线帧。AP 接收到未经认证关联的陌生 MAC 地址时，会直接判定为非法数据包并予以静默丢弃！
   * 虽然 802.11 标准定义了包含 4 个地址的 WDS（无线分布式系统）模式，但该模式需要无线 AP 硬件与固件的深度支持，绝大多数公共场所（校园网、企业办公 Wi-Fi、家用路由器）均关闭了此功能。

因此，**NAT（网络地址转换）是所有无线宿主机环境下让虚拟机访问外部网络的最佳且事实上的唯一标准工程解法**。

### 1.3 NAT 的现实世界全貌与工业映射

NAT 机制并非虚拟化特有，它深刻构成了当今互联网与云计算基础设施的基石：

* **家用 / 办公无线路由器**：入户光纤分配一个公网 IP（或运营商大内网 IP），多台手机、电脑接入局域网（`192.168.1.0/24`），路由器通过 NAPT（网络地址端口转换）将成百上千个内部私有连接伪装成同一个 IP 发往公网。
* **Docker 容器端口发布（`-p 8080:80`）**：Docker 在宿主机创建 `docker0` 网桥，容器分配 `172.17.0.0/16` 私有 IP。外部请求到达宿主端口时，Linux 内核通过 DNAT 规则将请求精准推入容器。
* **公有云 VPC NAT 网关**：阿里云、AWS、腾讯云的私有网络（VPC）中，未挂载弹性公网 IP（EIP）的内网数据库实例通过集中式 NAT 网关统一访问公网拉取依赖，外部流量无法主动刺穿进入内网，形成单向出站的安全屏障。
* **企业核心业务与堡垒机**：生产环境中的核心应用部署在内部隔离网络，只有管理运维或经严格审核的对外服务才通过反向代理与端口转发对外界暴露。

### 1.4 SNAT、DNAT 与 Linux 连接跟踪（Conntrack）机制剖析

在 Linux 内核中，NAT 由 Netfilter 框架与连接跟踪系统（Conntrack）协同驱动：

1. **源地址转换（SNAT / MASQUERADE）**：
   * 发生在 Netfilter 的 `POSTROUTING` 链（数据包离开本机前的一刹那）。
   * 当虚拟机的出站数据包到达宿主机出口网卡时，NAT 引擎将数据包头部的源私有 IP（如 `192.168.122.100`）改写为宿主机物理出口 IP（如 `192.168.1.50`），并动态分配一个空闲的源端口。
   * 宿主机物理出口如果为动态 IP（如 DHCP 获取的 Wi-Fi），使用 `MASQUERADE` 目标动作，内核会自动抓取当前出口网卡的实时 IP 填充。
2. **目标地址转换（DNAT）**：
   * 发生在 Netfilter 的 `PREROUTING` 链（数据包刚进入本机路由决策前）。
   * 外部机器请求宿主机指定端口时，NAT 引擎拦截并将其目的 IP 改写为虚拟机私网 IP，引导数据包注入虚拟机内部。
3. **连接跟踪（Conntrack）的灵魂作用**：
   * NAT 不是无状态的简单改写。当出站数据包被修改后，内核在内存的 Conntrack 哈希表中记录一个“五元组”映射对（协议、原源 IP/Port、原目的 IP/Port、转换后源 IP/Port）。
   * 当公网服务器应答回包到达宿主机时，内核优先查验 Conntrack 表，发现命中已知会话，立刻逆向将目的 IP 还原为虚拟机的私有 IP，使得虚拟机上的应用程序能够在完全无感知的状态下完成端到端通信。

下面通过交互式动画，完整观察数据包在各网络节点间流转时的包头变化与 Conntrack 记录：

::: tip 观察要点与操作指引

1. 点击\*\*“自动演示”\*\*，观察出站时源 IP 如何从 `192.168.122.100` 被改写为宿主物理 Wi-Fi 的 `192.168.1.50`。
2. 重点留意**数据包报文头实时剖析**面板中校验和（Checksum）的变化：一旦 IP 与端口被改写，TCP/IP 校验和必须重新计算。
3. 切换至\*\*“DNAT 入站模式”\*\*，观察外部客户端如何通过宿主物理 IP 访问到位于私网中的虚拟机服务，体会其与 Docker 容器端口映射的完全一致性。
4. 切换至\*\*“五大虚拟化网络模式横向全景对比”\*\*，加深对不同场景选型的技术判断。
   :::

## 二、企业级“双网卡”（Dual-Homed）架构哲学

### 2.1 生产环境双网卡架构的设计原则与安全隔离价值

在初学阶段，许多人习惯让虚拟机只配置一块单网卡（要么全隔离，要么直接全放通）。但在真实企业级生产架构中，核心服务器往往跨接在两个甚至多个网络平面上，即 **双宿主（Dual-Homed）架构**：

```mermaid
flowchart TD
    subgraph 隔离内网平面 ["内网安全平面（lab-isolated 192.168.77.0/24）"]
        desk01["集群测试节点 (desk01)"]
        db01["内部数据库集群 / 存储"]
    end

    subgraph 虚拟机 svr01 ["双网卡虚拟机 svr01"]
        nic1["网卡 1: ens3 (静态 IP: 192.168.77.101)"]
        nic2["网卡 2: ens7 (DHCP IP: 192.168.122.x)"]
        core["svr01 业务与服务进程"]
        nic1 --- core
        nic2 --- core
    end

    subgraph 外部出口平面 ["外网管理与更新平面（default NAT 192.168.122.0/24）"]
        virbr0["libvirt default 网桥"]
        host_wifi["宿主 Wi-Fi 出口"]
        wan["Ubuntu 软件仓库 / 公共网络"]
    end

    desk01 <--> nic1
    db01 <--> nic1
    nic2 <--> virbr0
    virbr0 <--> host_wifi
    host_wifi <--> wan
```

这种架构具备不可替代的工程优势：

1. **数据面与管理面分离**：内部业务流量、数据库同步、跨节点 RPC 调用在隔离内网（`ens3`）高速且安全地传输，物理或二层隔离保证无外网嗅探风险；系统更新、运维监控、时钟同步走外部 NAT（`ens7`）。
2. **极小化受攻击面（Attack Surface）**：内网网卡无需且严禁暴露到公网；外网接入网卡仅用于按需出站，入站默认全阻断。
3. **防止配置灾难**：即便外网网卡发生抖动或被物理拔除，内部业务集群之间的二层通信依然稳如磐石。

### 2.2 Linux 内核路由选择机制与最长前缀匹配原则

在双网卡虚拟机中，操作系统协议栈面对去往不同目的地的流量，必须依赖内核路由表（FIB）决定数据包交由哪一块网卡发出。

Linux 内核路由匹配遵循以下核心原则：

1. **最长前缀匹配原则（Longest Prefix Match, LPM）**：
   * 当向一个目标 IP 发包时，内核遍历路由表中的所有条目，比较目标 IP 与路由子网掩码按位与（AND）的结果。
   * 匹配长度越长（子网掩码越精确），优先级越高！
   * 例如去往 `192.168.77.102` 时，匹配 `192.168.77.0/24`（24 位匹配），而默认路由 `0.0.0.0/0` 只有 0 位匹配，因此内核绝对优先选择 `192.168.77.0/24` 所在的 `ens3` 网卡直接以太网二层直发！
2. **默认路由（Default Gateway / 0.0.0.0/0）**：
   * 当目标 IP 在路由表中找不到任何明细匹配时（例如公网 IP `223.5.5.5`），最后才由默认路由兜底。
   * **关键避坑指南**：在一台主机上，**通常只能存在一个有效的默认网关**！如果两块网卡同时声明了默认网关，将引发致命的“双默认网关冲突”故障。

下面通过交互组件直观测试不同场景下的内核路由匹配判定与 Metric 跃点控制：

::: tip 观察要点与操作指引

1. 点击\*\*“场景一”**与**“场景二”\*\*，分别点击“发起数据包探测”，观察内核在访问内网与外网时如何分别命中直连路由与默认网关。
2. 切换至\*\*“场景三：双默认网关冲突”\*\*，观察内网网卡错误添加 `gateway4` 后的网络震荡与黑洞丢包现象。
3. 切换至\*\*“场景四：Metric 跃点优先级修复”\*\*，观察通过 Netplan 的 `route-metric` 显式设置优先级后，路由表如何消除冲突并实现双网卡和谐分流。
   :::

## 三、实训任务一：构建企业级经典“双网卡虚拟机”

现在开始进入实操。我们将为虚拟机 `svr01` 保留原有的隔离网卡 `ens3`，并在宿主机为其动态或静态热插第二块 VirtIO 网卡接入 `default` NAT 网络。

### 3.1 宿主机为 svr01 追加添加第二块 VirtIO 网卡

在宿主机执行网卡添加。你有两种等效的工程实施途径：

::: tip 状态检查与热插拔参数说明
在为虚拟机附加虚拟网卡前，建议先在【宿主机终端 Host】检查其当前运行状态：

```bash
virsh --connect qemu:///system domstate svr01
```

* **关机后静态附加（推荐）**：若 `svr01` 处于运行状态（`running`），可先执行优雅关机：
  ```bash
  virsh --connect qemu:///system shutdown svr01
  ```
  关机后使用带有 `--config` 参数的命令，将网卡定义持久化写入虚拟机的 XML 配置文件中。
* **在线热插拔（热添加）**：若不希望中断虚拟机的运行，可在附加命令中同时追加 `--live --config` 参数。其中 `--live` 表示立即生效并热插拔挂载到当前运行的虚拟机中，`--config` 表示将修改持久写入配置文件，保证下次重启后配置依然保留。
  :::

::: tabs
@tab 命令行途径（virsh）

在【宿主机终端 Host】执行，为 `svr01` 附加一块接入 `default` 网络的 VirtIO 网卡：

```bash
# 1. 确认虚拟机当前网卡列表（此时应只有一块 lab-isolated 网卡）
virsh --connect qemu:///system domiflist svr01

# 2. 为 svr01 添加接入 default 虚拟网络的网络接口（推荐关机状态下使用 --config 持久化添加；若虚拟机正在运行且需热插拔，可追加 --live --config）
virsh --connect qemu:///system attach-interface \
  --domain svr01 \
  --type network \
  --source default \
  --model virtio \
  --config

# 3. 再次核验网卡清单，确认出现两个 Interface
virsh --connect qemu:///system domiflist svr01
```

预期终端输出中，`svr01` 应显示两行网卡记录，分别指向 `lab-isolated` 和 `default`：

```text
 Interface   Type      Source         Model    MAC
-------------------------------------------------------------------
 vnet0       network   lab-isolated   virtio   52:54:00:aa:01:01
 -           network   default        virtio   52:54:00:ee:01:07
```

@tab 图形界面途径（virt-manager）

1. 在宿主机打开虚拟系统管理器：`virt-manager`。
2. 双击打开 `svr01`，点击工具栏中的蓝色小灯泡图标（显示虚拟硬件详情）。
3. 点击左下角 **“添加硬件 (Add Hardware)”** -> 选择 **“网络 (Network)”**。
4. 网络源选择 **“网络源：Virtual network 'default': NAT”**。
5. 设备型号选择 **“virtio”**。
6. 点击 **“完成 (Finish)”** 保存。
   :::

### 3.2 启动虚拟机并识别新网卡硬件

在【宿主机终端 Host】启动虚拟机：

```bash
virsh --connect qemu:///system start svr01
```

通过控制台登录进入【虚拟机 svr01】：

```bash
# 登录虚拟机 svr01 串口字符控制台
virsh --connect qemu:///system console svr01
```

::: tip 关键操作技巧：virsh console 控制台逃生快捷键
通过 `virsh console` 进入虚拟机的串口字符终端后，如果需要退出并返回宿主机 shell 命令行，请按下快捷键组合：
**`Ctrl + ]`**（同时按下键盘上的 `Ctrl` 键与右方括号键 `]`）。
切勿强行关闭外部终端窗口，使用 `Ctrl + ]` 即可安全挂起串口连接并切回宿主机终端提示符。
:::

在【虚拟机 svr01】内部执行硬件与接口探测：

```bash
# 查看所有网络接口
ip link show
```

此时你应该观察到三个网络接口：

1. `lo`：本地回环接口（127.0.0.1）。
2. `ens3`：第一块网卡，对应原 `lab-isolated` 隔离网络。
3. `ens7`（或类似编号，如 `ens4`、`enp0s7`）：新识别出的第二块物理虚拟网卡，当前处于 `DOWN` 状态，尚未配置 IP。

```bash
# 简洁格式查看当前 IP 分配情况
ip -4 -br addr
```

### 3.3 编写 Ubuntu 26.04 Netplan 规范配置文件

Ubuntu 26.04 LTS 默认采用 **Netplan + systemd-networkd** 架构管理网络。

我们需要为两块网卡精确规划职责：

* **`ens3`（内网业务卡）**：配置静态 IP `192.168.77.101/24`，**绝对不要配置 gateway4 或 default 路由**！
* **`ens7`（外网出站卡）**：开启 `dhcp4: true`，由 libvirt 内置的 dnsmasq 自动分配 `192.168.122.x` IP，并自动获取默认网关 `192.168.122.1` 与 DNS 地址。

在【虚拟机 svr01】中编辑 `/etc/netplan/01-netcfg.yaml`：

```bash
sudo nano /etc/netplan/01-netcfg.yaml
```

写入以下标准规范配置：

```yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    ens3:
      addresses:
        - 192.168.77.101/24
      # 注意：隔离内网严禁声明 gateway4 或 default 路由！
    ens7:
      dhcp4: true
      dhcp4-overrides:
        route-metric: 100
```

::: warning 关键避坑指南：核对新网卡名与 YAML 缩进

1. **核对实际网卡接口名**：Linux 的可预测网络接口命名规则会依据虚拟 PCI 插槽分配网卡标识符。**若在 3.2 步中查到的新网卡为 `ens4` 或 `enp1s0`，请务必将配置文件中的 `ens7:` 替换为实际网卡名**，切勿机械照抄！
2. **YAML 严格空格缩进**：YAML 对缩进极其敏感，严禁使用 Tab 制表符，必须统一使用**空格**缩进（通常为 2 个空格）。若缩进不齐，执行 Netplan 时会抛出语法解析错误。
   :::

### 3.4 应用 Netplan 并验证链路与 IP 状态

在【虚拟机 svr01】中应用新配置：

```bash
# 应用 Netplan 配置生效
sudo netplan apply

# 验证网卡状态与 IP 分配
ip -4 -br addr
```

预期输出应清晰显示双网卡各司其职：

```text
lo               UNKNOWN        127.0.0.1/8 
ens3             UP             192.168.77.101/24 
ens7             UP             192.168.122.100/24 
```

同时查看路由表，确认只有一条默认路由指向 `ens7`：

```bash
ip route show
```

输出范例：

```text
default via 192.168.122.1 dev ens7 proto dhcp src 192.168.122.100 metric 100 
192.168.77.0/24 dev ens3 proto kernel scope link src 192.168.77.101 
192.168.122.0/24 dev ens7 proto kernel scope link src 192.168.122.100 metric 100 
```

此时，虚拟机成功具备了“内有隔离，外有出口”的双宿主基石！

## 四、实训任务二：NAT 出站全链路与真实软件管理实战

### 4.1 阶梯式出站连通性递进测试

网络排错不可盲目。当网络不通时，应当采取科学的**阶梯式探测法**，自底向上逐层验证：

在【虚拟机 svr01】执行：

```bash
# 阶段 1：探测首跳虚拟网关（验证虚拟交换与网桥链路）
ping -c 2 -W 2 192.168.122.1

# 阶段 2：探测公网 IP 地址（验证宿主 IP 转发与 SNAT 伪装出站）
ping -c 2 -W 2 223.5.5.5

# 阶段 3：探测域名解析（验证 DNS 转发与解析链路）
resolvectl query mirrors.aliyun.com
```

::: tip 避坑技巧：IP 连通但域名解析失败的排查要点
如果阶段 2 成功（能 ping 通 `223.5.5.5`），但阶段 3 域名解析失败，说明网络三层链路完全正常，问题单纯出在 DNS 上。可以使用 `resolvectl status` 查看当前分配的 DNS 服务器是否有效。
:::

### 4.2 配置 Ubuntu 国内开源镜像源并执行软件索引更新

Ubuntu 默认镜像源位于海外官方服务器，下载速度较慢且易受网络波动干扰。我们将虚拟机配置为国内高校或大型云厂商的开源镜像站（如清华大学源或阿里云源）。

在 Ubuntu 24.04 / 26.04 LTS 中，APT 源已迁移为现代的 `deb822` 格式文件 `/etc/apt/sources.list.d/ubuntu.sources`。

在【虚拟机 svr01】中备份并替换源：

```bash
# 备份原配置
sudo cp /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.bak

# 替换为清华大学开源镜像源（或阿里云源）
sudo sed -i 's|http://archive.ubuntu.com/ubuntu/|https://mirrors.tuna.tsinghua.edu.cn/ubuntu/|g' /etc/apt/sources.list.d/ubuntu.sources
sudo sed -i 's|http://security.ubuntu.com/ubuntu/|https://mirrors.tuna.tsinghua.edu.cn/ubuntu/|g' /etc/apt/sources.list.d/ubuntu.sources

# 刷新软件包索引
sudo apt update
```

终端应飞速拉取各仓库索引，以 `All packages are up to date` 或提示可升级数量告终。**这充分证明了虚拟机的 NAT 出站与高吞吐网络链路完全就绪！**

### 4.3 在线安装网络诊断工具套件

在【虚拟机 svr01】中安装后续排错与探测必备的工具套件：

```bash
sudo apt install -y traceroute curl net-tools
```

### 4.4 追踪出站真实路由跳数轨迹

运行 `traceroute` 追踪数据包从虚拟机跃迁到公网的完整物理轨迹：

在【虚拟机 svr01】执行：

```bash
traceroute -n 223.5.5.5
```

观察输出的跳数（Hop）：

```text
traceroute to 223.5.5.5 (223.5.5.5), 30 hops max, 60 byte packets
 1  192.168.122.1  0.281 ms  0.211 ms  0.198 ms
 2  192.168.1.1    2.415 ms  2.891 ms  3.102 ms
 3  10.20.0.1      5.120 ms  5.432 ms  5.890 ms
 4  ... (运营商骨干网跳数)
 8  223.5.5.5      15.231 ms  14.892 ms 15.110 ms
```

::: details 轨迹跳数详细解读

* **第 1 跳（192.168.122.1）**：这是宿主机上的 `virbr0` 虚拟网桥 IP。数据包在此处经历宿主机内核路由裁决与 Netfilter MASQUERADE 源地址改写。
* **第 2 跳（192.168.1.1）**：这是宿主机所连接的校园 Wi-Fi 或家用无线路由器的网关 IP。说明宿主机物理网卡成功将数据包推入外部真实局域网！
* **第 3 跳及后续**：跨越学校汇聚网关、城域网出口及电信/联通/移动骨干网，最终抵达目标公网服务器。
  :::

### 4.5 深入观察宿主侧 nftables 伪装规则与 Conntrack 连接跟踪

::: tip 宿主机工具安装与实际 IP 替换提示

1. **安装工具**：`conntrack` 是 Linux 专用的连接跟踪表查询与状态管理工具。若在宿主机执行时系统提示 `conntrack: command not found`，请先在宿主机安装该工具套件：
   ```bash
   sudo apt install -y conntrack
   ```
2. **IP 过滤替换**：命令中的 `-s 192.168.122.100` 为虚拟机源 IP 过滤示例，**请务必替换为 `svr01` 在 3.4 节中实际获取到的第二网卡 IP**（如 `192.168.122.x`）。
   :::

在【宿主机终端 Host】执行验证：

```bash
# 查看宿主内核是否开启了核心转发开关（返回 1 表示已开启）
sysctl net.ipv4.ip_forward

# 查看当前活跃的连接跟踪条目（将 192.168.122.100 替换为虚拟机实际获取的 IP）
sudo conntrack -L -s 192.168.122.100
```

你将清晰看到形如以下的连接跟踪记录：

```text
tcp      6 431998 ESTABLISHED src=192.168.122.100 dst=223.5.5.5 sport=45678 dport=80 src=223.5.5.5 dst=192.168.1.50 sport=80 dport=52100 [ASSURED] mark=0 use=1
```

这行记录就是 SNAT 最坚实的铁证：内核记录了原始发包的私网源地址 `192.168.122.100`，以及向外网发送时使用的宿主物理源地址 `192.168.1.50`。

## 五、实训任务三：服务发布双方案动手演练

虽然虚拟机借助 SNAT 已经可以随意访问外网，但是由于 NAT 具有天然的单向隔离性，**宿主机局域网内的其他物理机（如局域网内的其他物理测试机或移动设备）或者宿主机自身外部网络是无法直接通过虚拟机的私网 IP（`192.168.122.100`）访问虚拟机的**。

如何将虚拟机内部开发的服务安全发布出来？本节演练两种工业级主流方案。

### 5.1 在 svr01 启动测试 Web 服务

首先在【虚拟机 svr01】中启动一个轻量级 Web 服务用于联调测试。

::: tip 实用操作技巧：后台运行机制与防止单控制台阻塞
在实际操作中，通过 `virsh console` 连接虚拟机的串口控制台通常只有一个活跃会话。如果直接在前台运行服务进程，将直接霸占控制台，导致无法继续输入后续测试命令。

因此，应当使用 `nohup` 结合后台符 `&` 启动服务：

* **`&`（后台运行符）**：通知 Shell 将命令作为后台作业（Background Job）异步执行，命令输入后终端会立即交回交互提示符控制权。
* **`nohup`（不挂断运行）**：让进程忽略终端挂断信号（SIGHUP），即便后续串口断开重连，后台服务依然稳定运行。
* **`> ... 2>&1`（I/O 重定向）**：将标准输出与标准错误全部定向写入日志文件，避免服务访问日志持续刷屏干扰当前终端输入。
  :::

在【虚拟机 svr01】执行：

```bash
# 1. 创建临时网站目录并生成测试页面
mkdir -p ~/web-demo && cd ~/web-demo
cat << 'EOF' > index.html
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title>svr01 Web Service</title></head>
<body>
  <h1>Hello from svr01 Dual-Homed Virtual Machine!</h1>
  <p>Status: Online through libvirt NAT Network.</p>
</body>
</html>
EOF

# 2. 将 Python HTTP 服务置于后台运行，监听 0.0.0.0 的 8080 端口，并将访问日志重定向保存
nohup python3 -m http.server 8080 --bind 0.0.0.0 > ~/web-demo/http.log 2>&1 &

# 3. 在当前控制台直接验证本地端口监听状态
ss -lnt | grep :8080
```

确认输出中有 `0.0.0.0:8080` 或 `*:8080`，表明后台 Web 服务已稳定建立监听。

### 5.2 方案 A：宿主配置 SSH 本地端口转发（安全审计与本地发布）

**原理**：利用 SSH 的安全加密隧道，将宿主机本机的 `127.0.0.1:18080` 端口转发到虚拟机 `svr01` 的 `8080` 端口。

在【宿主机终端 Host】执行：

```bash
# 语法：ssh -N -L 宿主监听地址:宿主端口:虚拟机目标地址:虚拟机服务端口 用户名@虚拟机IP
ssh -N -L 127.0.0.1:18080:127.0.0.1:8080 labuser@192.168.122.100
```

在【宿主机终端 Host】新开一个终端进行访问验证：

```bash
curl -i http://127.0.0.1:18080/
```

预期终端立即输出 HTTP 响应头和 HTML 内容：

```text
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.3
Date: Sun, 06 Sep 2026 10:30:00 GMT
Content-type: text/html; charset=utf-8
Content-Length: 198

<!DOCTYPE html>
<html>
...
```

::: tip 方案 A 的工程价值与安全边界

* 监听地址限定在 `127.0.0.1`（回环地址），意味着只有宿主机本机能访问该服务，外界物理网络无法触及。
* 传输全程经由 SSH 强加密，适合开发阶段调试后台数据库（MySQL 3306、Redis 6379）或管理面板，安全等级极高。
  :::

### 5.3 方案 B：宿主配置端口重定向跨机访问（DNAT 目标地址转换）

如果需要让同一 Wi-Fi 下其他物理机器也能访问 `svr01` 的 Web 服务，就必须在宿主机内核中配置 **目标地址转换（DNAT）**。

在【宿主机终端 Host】配置 nftables 端口重定向（请将 `192.168.122.100` 替换为虚拟机实际获取的 IP）：

```bash
# 允许宿主机内核转发去往该虚拟机的端口流量
sudo nft add rule ip filter FORWARD ip daddr 192.168.122.100 tcp dport 8080 ct state new,related,established accept

# 在 NAT 表的 PREROUTING 链注入 DNAT 规则：将外部访问宿主 18080 的流量重定向至虚拟机的 8080
sudo nft add rule ip nat PREROUTING tcp dport 18080 dnat to 192.168.122.100:8080
```

::: warning 关键原理解析：PREROUTING 链的触发边界与局域网跨机测试
在配置完 DNAT 后，若直接在**宿主机本机**打开浏览器或执行 `curl http://宿主机物理IP:18080/` 测试，常会发现连接被拒绝或超时。这是 Linux Netfilter 架构的经典设计机制所致：

1. **PREROUTING 链的生效范围**：Netfilter 的 `PREROUTING` 链专门处理**从外部物理网络接口流入（Ingress）的数据包**。
2. **本机出站流量走 OUTPUT 链**：当宿主机自身进程发起访问时，流量产生于本机本地并直接进入 `OUTPUT` 链路由决策，根本不会经过 `PREROUTING` 链，因此不会命中刚才添加的 DNAT 规则。
3. **测试验证建议**：
   * **方案 B 验证方式**：建议优先从**同一 Wi-Fi 下的手机、平板或其他局域网测试机**发起访问测试（例如在手机浏览器输入 `http://宿主机物理IP:18080`）；
   * **单机实验场景**：若手头只有当前单台宿主机，请以 **5.2 节方案 A（SSH 本地端口转发）** 作为最准确、最可靠的验收标准。
     :::

从同一局域网下的外部测试设备执行访问：

```bash
curl http://宿主机物理IP:18080/
```

数据包经宿主物理 Wi-Fi 网卡流入并命中 PREROUTING 链，DNAT 将目标 IP 从宿主机改写为 `192.168.122.100:8080`，通过 `virbr0` 网桥顺利推入虚拟机内部！

#### 实训结束后清理临时规则

nftables 在删除具体规则时，必须指定规则内部的句柄编号（`handle`）。通过在查询命令中添加 `-a`（或 `--handle`）选项即可列出具体编号：

```bash
# 1. 查看 NAT 表 PREROUTING 链中的规则及其 handle 编号
sudo nft -a list chain ip nat PREROUTING
```

预期输出中每条规则末尾都会标明 `# handle X` 编号，例如：

```text
table ip nat {
    chain PREROUTING {
        type nat hook prerouting priority dstnat; policy accept;
        tcp dport 18080 dnat to 192.168.122.100:8080 # handle 5
    }
}
```

```bash
# 2. 根据查到的 handle 编号（如 5）精准删除该条临时规则
sudo nft delete rule ip nat PREROUTING handle 5
```

### 5.4 工业横向对比：DNAT 与 Docker 容器端口映射（`-p`）的工业一致性

在掌握了宿主机的 DNAT 配置后，请回顾 Docker 命令：

```bash
docker run -d -p 8080:80 nginx
```

当你执行这条命令时，Docker 底层所做的事情与我们刚才的手动配置完全如出一辙：

1. Docker 在宿主机创建虚拟网桥 `docker0`（类似于 libvirt 的 `virbr0`）。
2. Docker 在 iptables / nftables 的 `PREROUTING` 链中添加一条 DNAT 规则：将宿主机物理端口 `8080` 的流量，目标重写为容器的私有 IP（如 `172.17.0.2:80`）。
3. 同时在 `POSTROUTING` 链配置 MASQUERADE，保证容器出站能正常上网。

**无论技术外壳是 KVM 虚拟机还是 OCI 容器，底层依靠的都是 Linux 内核 Netfilter 的同一套 NAT 规则体系！**

## 六、实训任务四：网络排错演练——双网卡默认网关冲突与 Metric 修复

在运维双网卡系统时，最容易出现的重大网络事故就是 **“双默认网关冲突”（Dual Default Gateways Conflict）**。

### 6.1 故障注入：在内网网卡错误配置 default 网关

我们动手人为注入该故障，体验真实的排错全流程。

在【虚拟机 svr01】中修改 Netplan 配置，在隔离网卡 `ens3` 处**故意错误地添加一个网关**：

```bash
sudo nano /etc/netplan/01-netcfg.yaml
```

修改为以下错误内容：

```yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    ens3:
      addresses:
        - 192.168.77.101/24
      routes:
        - to: default
          via: 192.168.77.1   # ❌ 错误注入：给隔离内网配置了默认网关！
    ens7:
      dhcp4: true
```

应用错误配置：

```bash
sudo netplan apply
```

### 6.2 故障现象复现与观察

查看当前的路由表：

```bash
ip route show
```

你将看到两条同权的 `default` 默认路由：

```text
default via 192.168.77.1 dev ens3 proto static 
default via 192.168.122.1 dev ens7 proto dhcp src 192.168.122.100 metric 100 
192.168.77.0/24 dev ens3 proto kernel scope link src 192.168.77.101 
192.168.122.0/24 dev ens7 proto kernel scope link src 192.168.122.100 metric 100 
```

此时再次尝试外网通信测试：

```bash
# 测试外网 IP
ping -c 4 -W 2 223.5.5.5

# 测试软件包索引拉取
sudo apt update
```

**故障现象出现**：

* `ping` 命令出现严重的间歇性丢包，甚至 `100% packet loss`、`Destination Host Unreachable`！
* `apt update` 出现长时间的卡顿（`0% [Connecting to mirrors.tuna.tsinghua.edu.cn]`），最终报 `Connection timed out` 错误！

### 6.3 根因剖析：Linux 内核路由裁决与黑洞网关

为什么配置了两个默认网关会导致外网瘫痪？

1. **同权竞争**：`ens3` 上的静态路由与 `ens7` 上的 DHCP 路由争抢默认出口。如果静态路由的 Metric 较小（或未指定 Metric，默认为 0），内核将始终把去往外网的流量交由 `ens3` 发出。
2. **黑洞丢弃**：数据包从 `ens3` 发送给 `192.168.77.1`（`virbr-lab` 隔离网桥）。但 `virbr-lab` 根本没有外网转发规则，也没有配置 SNAT，数据包在网桥边界被直接丢弃！
3. **哈希分流震荡**：在支持多路径路由的内核中，不同 TCP 会话可能被交替发往两个网卡，导致一部分握手成功、一部分连接卡死，产生极难排查的幽灵网络抖动。

### 6.4 规范修复：在 Netplan 中显式设置 Metric 跃点数

修复该问题有两种标准工程方案：

::: tabs
@tab 方案一：彻底移除内网网关（最推荐的规范做法）

由于内网网卡 `ens3` 只负责与同网段的伙伴机（`192.168.77.0/24`）通信，根本无需网关即可通过二层直连直通。如果确实需要跨路由访问其他内网子网，应当配置**明细静态路由**，而不是默认网关！

修改 `/etc/netplan/01-netcfg.yaml`：

```yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    ens3:
      addresses:
        - 192.168.77.101/24
      # 如有跨网段内网才配置明细路由，例如：
      # routes:
      #   - to: 10.0.0.0/8
      #     via: 192.168.77.254
    ens7:
      dhcp4: true
      dhcp4-overrides:
        route-metric: 100
```

@tab 方案二：Metric 跃点优先级裁决（多出口网络场景）

如果在复杂企业拓扑中，两个网络都下发了网关，必须通过 **Metric（路由跃点数）** 显式裁决优先级。**在 Linux 路由表中，Metric 数值越小，优先级越高**！

修改 `/etc/netplan/01-netcfg.yaml`：

```yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    ens3:
      addresses:
        - 192.168.77.101/24
      routes:
        - to: default
          via: 192.168.77.1
          metric: 600       # 优先级调低（数值大）
    ens7:
      dhcp4: true
      dhcp4-overrides:
        route-metric: 100   # 优先级调高（数值小），作为绝对优先默认出口
```

:::

### 6.5 应用配置并验证分流恢复正常

在【虚拟机 svr01】中应用修复方案：

```bash
sudo netplan apply

# 查看当前路由表状态
ip route show default
```

此时输出将显示正确的路由裁决（Metric 100 处于绝对优势地位）：

```text
default via 192.168.122.1 dev ens7 proto dhcp src 192.168.122.100 metric 100 
default via 192.168.77.1 dev ens3 proto static metric 600 
```

使用 `ip route get` 验证内核针对不同目的地的实际裁决情况：

```bash
# 测试外网 IP 的出口裁决：必须走 ens7
ip route get 223.5.5.5

# 测试隔离内网 IP 的出口裁决：必须走 ens3
ip route get 192.168.77.102
```

预期输出：

* 访问 `223.5.5.5` 显示 `via 192.168.122.1 dev ens7`！
* 访问 `192.168.77.102` 显示 `dev ens3 src 192.168.77.101`！

再次执行 `curl -I https://mirrors.tuna.tsinghua.edu.cn/`，连接瞬间建立，排错大获全胜！

## 七、常见问题排错矩阵（9.5）

在虚拟化双网卡与 NAT 网络实训中，若遇到异常，请对照下表进行阶梯式分层排查：

| 故障现象 | 优先排查层级 | 定位检查命令 | 根本原因分析 | 规范解决措施 |
| :--- | :--- | :--- | :--- | :--- |
| **虚拟机无法获取 IP，网卡处于 DOWN 状态** | 物理设备与驱动层 | `ip link show``lspci \| grep -i virtio` | 虚拟网卡未正确挂载，或驱动缺失 | 在宿主机使用 `virsh domiflist` 核对网卡是否接入 `default` 网络；检查虚拟机内核 VirtIO 驱动 |
| **能 Ping 通网关，但 Ping 不通外网 IP** | 宿主转发与 SNAT 层 | `sysctl net.ipv4.ip_forward``sudo nft list ruleset` | 宿主机内核未开启三层 IP 转发，或缺少 MASQUERADE 规则 | 在宿主机执行 `sudo sysctl -w net.ipv4.ip_forward=1`；检查 libvirt 的 default 网络是否处于 active 状态 |
| **能 Ping 通外网 IP，但域名无法解析** | 域名解析应用层 | `resolvectl status``cat /etc/resolv.conf` | DNS 服务器地址未正确获取，或上游 DNS 服务不可用 | 使用 `resolvectl dns ens7 223.5.5.5` 临时测试；检查 Netplan 配置中的 nameservers 声明 |
| **虚拟机外网频繁超时，丢包率极高** | 路由表冲突与 Metric 混乱 | `ip route show``ip route get 223.5.5.5` | 双默认网关冲突，流量被错误路由至隔离网卡 `ens3` | 移除内网网卡的 `gateway4`，或显式在 Netplan 中为外网网卡配置较低的 `route-metric: 100` |
| **宿主执行 SSH 端口转发提示 Permission denied** | 服务端认证与权限 | `tail -f /var/log/auth.log` | 虚拟机用户名密码错误，或 SSH 服务禁止密码登录 | 确认虚拟机安装了 `openssh-server`；检查 `/etc/ssh/sshd_config` 中的认证配置 |
| **从宿主外部测试机无法通过 DNAT 访问 Web 服务** | 防火墙过滤拦截 | `sudo nft list chain ip filter FORWARD``ss -lnt` | 宿主机 FORWARD 链策略为 DROP，且未放通该端口转发 | 在宿主机 `filter` 表的 `FORWARD` 链添加针对虚拟机 IP 和端口的明确放行规则 |

## 八、交付与验收标准（9.6）

完成本课实训后，必须提交以下 4 张关键证据截图，用以全面证明双网卡虚拟机的网络能力建设与排错成果：

### 4 张必交截图清单与验证要点

1. **截图一：双网卡配置与 IP 状态截屏**
   * **执行命令**：在【虚拟机 svr01】执行 `ip -4 -br addr`。
   * **验证要点**：必须同时展示 `ens3`（静态 IP `192.168.77.101/24`，UP 状态）与 `ens7`（DHCP IP `192.168.122.x/24`，UP 状态），两块网卡各就各位。
2. **截图二：真实路由表与出站跳数轨迹截屏**
   * **执行命令**：在【虚拟机 svr01】先后执行 `ip route show default` 与 `traceroute -n 223.5.5.5`。
   * **验证要点**：路由表中默认网关必须正确指向 `192.168.122.1 dev ens7`；traceroute 轨迹第一跳必须为 `192.168.122.1`（virbr0），第二跳必须为宿主机上游局域网网关。
3. **截图三：在线镜像源刷新与软件管理安装截屏**
   * **执行命令**：在【虚拟机 svr01】执行 `sudo apt update`。
   * **验证要点**：终端输出完整拉取国内开源镜像源（清华源或阿里源）索引的过程，最后无任何网络报错，显示 `All packages are up to date` 或软件包更新统计。
4. **截图四：服务发布访问成功截屏**
   * **执行命令**：在【宿主机终端 Host】执行 `curl -i http://127.0.0.1:18080/`。
   * **验证要点**：终端输出标准的 HTTP/1.0 200 OK 响应报文，以及由 `svr01` 的 Python 服务返回的页面标题 `svr01 Web Service`，完整闭环验证服务发布成功。

## 本章小结

本课带领你跳出单纯的“单机单网卡”初级运维思维，跨入了企业级虚拟化网络架构的专业大门：

1. **厘清了五大网络模式定位与 Wi-Fi 限制本质**：深刻理解了普通 Wi-Fi Station 模式因 802.11 协议的三地址帧限制无法直接承载虚拟化二层桥接，确立了 NAT 作为无线环境下稳定上行的工业标准地位。
2. **践行了企业级“双网卡”（Dual-Homed）安全架构**：为 `svr01` 成功组装了“内网数据隔离 + 外网 NAT 出站”的经典双网卡体系，做到了业务私密性与软件更新便利性的完美统一。
3. **攻克了双默认网关冲突难题**：剖析了最长前缀匹配与 Metric 跃点裁决的内核级算法，熟练运用 Ubuntu 26.04 Netplan 实现了精准、可靠的路由分流。
4. **打通了双向网络链路**：既掌握了出站 SNAT 的连接跟踪（Conntrack）机理，又掌握了入站服务的 SSH 本地安全转发与 DNAT 端口映射发布，具备了应对复杂现实网络拓扑的综合运维实战技能！
