引言

Waydroid 已经能够启动,Android 桌面也能正常显示,Google 应用和系统组件均存在,但状态始终显示:

Session: RUNNING
IP address: UNKNOWN

Android 内部的网络接口处于 UP 状态,却没有 IPv4 地址、默认路由和 DNS 配置。浏览器、Google Play 和其他联网应用自然全部不可用。

这类问题很容易被误判为 Android 镜像损坏、Waydroid 安装不完整,甚至被建议重新初始化或重装。实际上,本次故障最终与 Android 镜像、LXC 容器和 Waydroid 本身都没有直接关系,而是宿主机防火墙没有正确放行 Waydroid 的虚拟网络接口。

整个排查过程的关键,不是反复修改配置,而是逐层确认 DHCP 数据包究竟消失在哪里。


一、故障现象

宿主环境为:

  • Arch Linux
  • KDE Plasma
  • Wayland
  • Waydroid 1.6.x
  • 使用带 Google 服务的 Android 镜像
  • 宿主启用了 firewalld

Waydroid 启动后可以观察到:

Session: RUNNING
Container: RUNNING 或 FROZEN
IP address: UNKNOWN

Android 系统本身已经完成启动:

sys.boot_completed=1

图形界面、启动器、系统服务和应用管理器均可使用。

宿主侧也能看到:

  • waydroid0 网桥存在;
  • 一个动态生成的 veth 接口已加入网桥;
  • dnsmasq 进程正在运行;
  • IPv4 forwarding 已开启;
  • Android 内部存在 eth0
  • eth0 链路状态为 UP

但 Android 内部没有:

  • IPv4 地址;
  • 默认路由;
  • DNS 配置;
  • DHCP 租约。

这说明问题集中在 DHCP 获取阶段。


二、不要被 FROZEN 状态误导

Waydroid 空闲时可能将容器置于冻结状态:

Container: FROZEN

这并不等于容器崩溃,也不等于安装损坏。

只有在进行实时网络测试、抓包或进入 Android shell 时,才需要解除冻结或重新激活容器。判断 Waydroid 是否正常,应结合以下信息:

  • Android 是否完成启动;
  • LXC 进程是否存在;
  • Binder 是否可用;
  • Android 服务是否正常;
  • 图形界面是否可显示;
  • 网络接口是否建立;
  • 日志中是否存在崩溃或重启循环。

单看 FROZEN 无法得出故障结论。


三、确认 Waydroid 的 LXC 实例路径

Waydroid 不一定使用 LXC 默认路径。

直接执行:

lxc-info -n waydroid

可能得到容器不存在的错误,即使 Waydroid 实际正在运行。

正确方式需要指定 Waydroid 使用的 LXC 路径:

lxc-info \
  -P /var/lib/waydroid/lxc \
  -n waydroid \
  -pH

该命令可以返回 Android 容器的 init PID。

得到 PID 后,可以进入 Android 的网络命名空间:

nsenter -t "$initpid" -n ip -br link
nsenter -t "$initpid" -n ip -4 addr
nsenter -t "$initpid" -n ip route

这样可以直接查看 guest 侧真实的网络状态,而不是只依赖 waydroid status


四、建立三层 DHCP 抓包模型

Waydroid 网络至少涉及三个关键观察点:

  1. Android guest 内部的 eth0
  2. 宿主侧与 Android 相连的 veth
  3. 宿主侧的 waydroid0 网桥

veth 名称每次启动都可能变化,不能写死。可以从网桥成员关系中动态查找:

ip -o link show master waydroid0

随后同时在三个位置抓取 DHCP、ARP 和 ICMP:

tcpdump -ni eth0
tcpdump -ni <动态veth名称>
tcpdump -ni waydroid0

guest 侧抓包需要进入 Android 网络命名空间:

nsenter -t "$initpid" -n \
  tcpdump -ni eth0 -e -vvv \
  'arp or (udp and (port 67 or port 68))'

三个抓包必须尽量同步进行,否则无法准确比较同一个 DHCP 请求在不同层级的传播情况。


五、DHCP 故障分类

通过三层抓包,可以把故障大致分为以下几类。

A:guest 没有发出 DHCPDISCOVER

说明问题在 Android 的 NetworkStack、IpClient、DhcpClient 或接口初始化。

B:guest 有 DHCPDISCOVER,host veth 没有

说明问题位于网络命名空间或 veth 对端连接。

C:host veth 有 DHCPDISCOVER,waydroid0 没有

说明问题位于 Linux bridge、bridge netfilter 或接口成员关系。

D:waydroid0 收到 DHCPDISCOVER,但没有 DHCPOFFER

说明请求已经到达宿主网桥,但没有进入或没有被 dnsmasq 正常处理。

E:dnsmasq 发出 DHCPOFFER,但 guest 收不到

说明问题位于二层回程、bridge 或 veth 接收路径。

F:DHCP 成功,但无法访问公网

说明问题集中在转发、NAT 或宿主出口。

G:公网 IP 可访问,但域名无法解析

说明问题只剩 DNS。

本次问题最终属于 D 类。


六、抓包得到的关键证据

同步抓包显示:

  • Android eth0 发出了 DHCPDISCOVER;
  • host veth 看到了同一个 DHCPDISCOVER;
  • waydroid0 也看到了同一个 DHCPDISCOVER;
  • dnsmasq 没有发送 DHCPOFFER;
  • Android 没有发送 DHCPREQUEST;
  • dnsmasq 没有发送 DHCPACK;
  • 租约文件没有新增记录。

这组证据排除了:

  • Android 没有发包;
  • veth 断开;
  • bridge membership 错误;
  • guest 到 host 的二层路径故障。

DHCP 请求已经完整到达宿主网桥,却没有被 dnsmasq处理。


七、检查 firewalld zone

宿主机运行 firewalld,实际联网接口属于默认的 public zone。

检查 Waydroid 接口:

firewall-cmd --get-zone-of-interface=waydroid0

结果显示:

no zone

这意味着 waydroid0 虽然已经存在,但没有被明确分配给可信 zone。

直接运行:

firewall-cmd --list-all

只会显示默认 zone,通常是 public,因此看不到 waydroid0 并不能证明它没有永久配置。

需要明确查看:

firewall-cmd \
  --permanent \
  --zone=trusted \
  --query-interface=waydroid0

八、先做 runtime 测试

在没有确认根因前,不应立刻写入永久配置。

先把 waydroid0 临时加入 trusted

firewall-cmd \
  --zone=trusted \
  --add-interface=waydroid0

如果它已经属于其他 zone,则使用:

firewall-cmd \
  --zone=trusted \
  --change-interface=waydroid0

此时不要添加 --permanent

随后重新触发 DHCP。可通过重新激活 Waydroid、切换 guest eth0 链路状态或重启当前 session 实现。

再次抓包后,完整的 DHCP 四步握手出现:

DHCPDISCOVER
DHCPOFFER
DHCPREQUEST
DHCPACK

Android 随即获得:

192.168.240.x/24

并出现默认路由:

default via 192.168.240.1 dev eth0

dnsmasq 租约文件也生成了对应记录。

这证明根因确实是 firewalld 没有信任 waydroid0


九、验证公网、DNS 和应用联网

DHCP 恢复后,应分层验证:

网关

waydroid shell ping -c 3 192.168.240.1

公网 IP

waydroid shell ping -c 3 1.1.1.1

DNS

waydroid shell ping -c 3 google.com

随后还要在 Android 浏览器或 Google Play 中进行真实联网测试。

只有命令行与应用层都通过,才可以认为网络完全恢复。


十、写入永久配置

runtime 测试成功后,再写入永久规则:

firewall-cmd \
  --permanent \
  --zone=trusted \
  --add-interface=waydroid0

随后重载:

firewall-cmd --reload

验证:

firewall-cmd \
  --permanent \
  --zone=trusted \
  --query-interface=waydroid0

正常返回:

yes

Waydroid 启动后还可以检查 runtime zone:

firewall-cmd --get-zone-of-interface=waydroid0

应返回:

trusted

十一、为什么 firewall-cmd --list-all 看不到

命令:

firewall-cmd --list-all

只显示默认 zone。

如果默认 zone 是 public,输出中只会看到宿主真实联网接口,例如无线网卡,而不会自动显示 trusted 的配置。

正确查看方式是:

firewall-cmd \
  --zone=trusted \
  --list-all

永久配置则使用:

firewall-cmd \
  --permanent \
  --zone=trusted \
  --list-all

当 Waydroid 停止、waydroid0 接口不存在时,runtime active zones 中也可能暂时不显示它,但永久 XML 配置仍然保留。


十二、重启验证非常重要

仅在当前运行状态下恢复网络还不够。

应完整执行:

  1. 停止 Waydroid session;
  2. 停止 container service;
  3. 重新启动 container;
  4. 重新启动 session;
  5. 等待 Android boot 完成;
  6. 再次检查 DHCP;
  7. 再次测试公网和 DNS。

本次完整重启后,Android 仍然自动获得 IPv4 地址和默认路由,说明修复不是一次性的 runtime 偶然状态。


十三、最终修复结果

最终只修改了 firewalld 的 trusted zone 配置:

waydroid0 → trusted

没有修改:

  • Waydroid 镜像;
  • Android 数据;
  • LXC 配置;
  • dnsmasq 参数;
  • Waydroid 网络脚本;
  • nftables 原始规则;
  • Android NetworkStack。

修复后的状态为:

  • DHCP 四步握手正常;
  • Android 获得 IPv4;
  • 默认路由正常;
  • 公网访问正常;
  • DNS 正常;
  • 浏览器正常;
  • Google Play 可用;
  • firewalld reload 后仍然有效;
  • Waydroid 完整重启后仍然有效。

结语

Waydroid 网络故障不应从“重装”开始排查。

更可靠的思路是:

  1. 确认 Android 是否真正启动;
  2. 检查 guest eth0
  3. 动态找到 host veth;
  4. 同时抓取 guest、veth 和 bridge;
  5. 判断 DHCP 包在哪一层消失;
  6. 做最小 runtime 对照实验;
  7. 验证成功后再写入永久配置。

本次故障表面上表现为 Android 无法联网,实际根因只是宿主防火墙没有信任 Waydroid 的虚拟网桥。通过三层抓包,问题从模糊的“网络不通”被精确定位为“DHCP 请求到达网桥,但被宿主输入路径阻断”,最终只需一项最小配置变更即可解决。

Leave a Reply

Your email address will not be published. Required fields are marked *