引言
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 网络至少涉及三个关键观察点:
- Android guest 内部的
eth0 - 宿主侧与 Android 相连的 veth
- 宿主侧的
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 配置仍然保留。
十二、重启验证非常重要
仅在当前运行状态下恢复网络还不够。
应完整执行:
- 停止 Waydroid session;
- 停止 container service;
- 重新启动 container;
- 重新启动 session;
- 等待 Android boot 完成;
- 再次检查 DHCP;
- 再次测试公网和 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 网络故障不应从“重装”开始排查。
更可靠的思路是:
- 确认 Android 是否真正启动;
- 检查 guest
eth0; - 动态找到 host veth;
- 同时抓取 guest、veth 和 bridge;
- 判断 DHCP 包在哪一层消失;
- 做最小 runtime 对照实验;
- 验证成功后再写入永久配置。
本次故障表面上表现为 Android 无法联网,实际根因只是宿主防火墙没有信任 Waydroid 的虚拟网桥。通过三层抓包,问题从模糊的“网络不通”被精确定位为“DHCP 请求到达网桥,但被宿主输入路径阻断”,最终只需一项最小配置变更即可解决。