HarmonyOS 鸿蒙Next中vpnExtension tun fd write(dst=本机VIP) 不触发本地 ICMP/TCP 应答

HarmonyOS 鸿蒙Next中vpnExtension tun fd write(dst=本机VIP) 不触发本地 ICMP/TCP 应答 社区的大佬们,请教一个问题。

【背景】

在鸿蒙6.0以上的系统上开发类似openvpn的这种基于虚拟网卡的SSLVPN客户端。

【结果】

SSLVPN客户端与SSLVPN网关服务端建立完SSL隧道后,网关下发虚拟网卡地址和可访问的子网(就是配置路由),例如网关的虚拟网卡地址是10.0.0.1/24。

给客户端分配了一个10.0.0.2/24的虚拟网卡地址。配置了 10.0.11.0/24的路由。

然后在手机上通过浏览器访问 10.0.11.100的一个WEB服务,成功。然后在手机上去ping 网关的虚拟网卡地址10.0.0.1也成功。

但是,我在网关上去ping 手机的虚拟地址,就是ping不通。然后去分析,发现手机上的SS客户端收到数据后,解密。得到源地址为10.0.0.1的,目的地址为10.0.0.2的

ICMP包,然后SSL客户端程序将这个包写入到虚拟网卡中(ping包),但是内核好像不返回icmp的响应包。这个是什么问题?

猜测:

  1. 鸿蒙6.0以上的系统内核协议栈这块不允许?所以不给回包?(安卓这个功能没问题),但是我也没有查到相关资料

  2. 那就是代码实现有问题,目前排查不出来,AI也给不出结果。

所以想请教下大佬们,有没有人做过这块基于虚拟网卡的经验。


更多关于HarmonyOS 鸿蒙Next中vpnExtension tun fd write(dst=本机VIP) 不触发本地 ICMP/TCP 应答的实战教程也可以访问 https://www.itying.com/category-93-b0.html

11 回复

尊敬的开发者,您好,
为了尽快解决您的问题,需要您进一步提供如下信息:

一、请提供完整的vpnConfig配置路由的文件
二、请按照如下方法提供完整的日志

  1. hdc shell
  2. cd data/log/hilog
  3. hilog -w clear (清除多余日志)
  4. 重现问题,并记录出现问题的具体时间
  5. exit后执行hdc file recv /data/log/hilog D:\log

更多关于HarmonyOS 鸿蒙Next中vpnExtension tun fd write(dst=本机VIP) 不触发本地 ICMP/TCP 应答的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


您好,这个是日志。请查收

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

怎么看起来是路由的机制问题,试一下在创建VPN时,通过添加一条指向VPN网关虚拟IP的路由。参考一下:

https://developer.huawei.com/consumer/cn/forum/topic/0201172491201968175

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

有配置路由,手机上测试时配置了两个路由策略。一个路由策略是VPN网关的子网(10.0.11.0/24),另一个是VPN的虚拟网卡地址网段(10.0.0.0/24)。我的策略是没有配置默认网关的。如果上面的路由没有配置的情况下,手机中的浏览器都无法访问10.0.11.100。 现在是反向的问题,由VPN网关(服务端)访问手机的虚拟网卡地址不通

从你描述的现象来看,这个问题更像是 HarmonyOS VPN Extension 的虚拟网卡行为与 Android VpnService 存在差异,而不一定是 SSLVPN 协议本身的问题。

你的数据流大致是:

网关(10.0.0.1)
        |
SSL隧道
        |
手机VPN客户端
        |
tun fd
        |
虚拟IP: 10.0.0.2

目前已经验证:

✅ 手机 → VPN内网(10.0.11.100) 正常

✅ 手机 → 网关VIP(10.0.0.1) ping正常

❌ 网关VIP(10.0.0.1) → 手机VIP(10.0.0.2) ping失败

而且:

tun.read()
收到ICMP Echo Request

源IP:
10.0.0.1

目的IP:
10.0.0.2

然后:

tun.write()
写回系统协议栈

但是:

没有看到 Echo Reply

重点怀疑方向

第一种可能(概率最高)

HarmonyOS VPN Extension 并不会自动为 VPN 分配的虚拟IP创建本地 Host 地址

Android 上:

builder.addAddress("10.0.0.2",24)

之后:

ip addr

能看到:

tun0
  inet 10.0.0.2/24

内核认为:

10.0.0.2 是本机地址

因此:

收到目的IP=10.0.0.2

会自动产生:

ICMP Echo Reply
TCP SYN-ACK
UDP响应

但 HarmonyOS 的 VPN Extension 底层实现可能是:

用户态转发VPN

而不是:

真正向内核注册VIP地址

导致:

目的IP=10.0.0.2

对于内核来说:

不是本机IP

因此:

直接丢弃

不会产生:

ICMP Reply
TCP Reply

这与你观察到的现象完全一致。


如何验证

抓包看看:

收到:
ICMP Echo Request
10.0.0.1 -> 10.0.0.2

之后:

tun.read()

是否还能读到:

ICMP Echo Reply
10.0.0.2 -> 10.0.0.1

如果完全没有:

内核没有生成回复

就说明问题在这里。


第二种可能

VPN Extension只处理出站流量

部分系统实现:

APP
 ↓
协议栈
 ↓
VPN
 ↓
tun

只支持:

本机主动发起

即:

手机 -> VPN

方向。

对于:

VPN -> 手机

方向的数据:

tun.write()

并不会重新注入到协议栈。

如果是这样:

写入成功
≠
系统接收成功

一个简单验证方法

在客户端构造:

ICMP Echo Request

src=10.0.0.1
dst=10.0.0.2

直接:

tun.write(packet)

然后抓:

tun.read()

看是否出现:

Echo Reply

如果没有:

说明协议栈根本没把:

10.0.0.2

当成本机地址。


第三种可能

VPN地址配置缺失

检查:

vpnExtension.createVpnConnection()

时是否配置了:

addAddress("10.0.0.2", 24)

而不仅仅是:

addRoute("10.0.11.0",24)

有些实现:

Route配置成功

不代表:

VIP地址注册成功

为什么 Android 正常

Android 的:

VpnService

本质上:

tun设备
+
内核路由
+
本地地址注册

因此:

ping 10.0.0.2

一定会收到:

Echo Reply

除非防火墙拦截。


如果最终确认是 HarmonyOS 限制

目前比较常见的解决办法是:

VPN客户端自己实现 ICMP 响应。

收到:

ICMP Echo Request
10.0.0.1 -> 10.0.0.2

后:

用户态构造

ICMP Echo Reply

10.0.0.2 -> 10.0.0.1

然后:

SSL隧道发回网关

而不是依赖系统协议栈生成回复。

很多用户态 VPN(WireGuard/OpenVPN 的某些平台实现)实际上也是这么做的。


结合你提供的信息,我目前倾向于:

HarmonyOS VPN Extension 没有像 Android VpnService 那样把 10.0.0.2 真正注册为协议栈的本地地址,因此内核不会自动产生 ICMP/TCP 应答。

建议先验证两点:

  1. VPN 建立后系统是否真正拥有 10.0.0.2 这个本地地址。
  2. tun.write() 注入一个目的地址为 10.0.0.2 的 ICMP Echo Request 后,协议栈是否会生成 Echo Reply。

如果这两项都失败,基本可以确定是 HarmonyOS VPN Extension 当前实现限制,而不是你的 SSLVPN 代码逻辑问题。

  1. VPN 建立后, SSL网关会通过这个SSL隧道分配一个虚拟地址发送给客户端(鸿蒙手机),然后手机收到数据后,调用鸿蒙的接口将vpn-tun虚拟网卡的IP设置好(服务端给的),路由配置好(服务端的子网)。所以针对第一点。10.0.0.2这个地址是肯定有的,我在shell(手机里)通过命令行看到了。
  2. 有去尝试自己做一个测试用例。测试用例的内容就是打开APP后创建虚拟网卡,设置虚拟网卡的IP为10.0.0.2(IP存在后类似有直连路由)。 然后测试用例就是构造源IP为10.0.0.1的包,目的地址为10.0.0.1。往虚拟网卡写的时候,也是获取不到回包。
  3. 关于在用户态自己去构造协议,例如构造icmp响应包。这个不太现实,icmp还好,如果是tcp服务呢,udp服务呢。例如鸿蒙的手机上我搭建了一个WEB服务,手机端通过VPN接入一个SSL网关。正常来说,用户的业务场景就是访问SSL网关保护的子网,很少有SSL网关保护的子网来主动访问鸿蒙手机上的一个APP服务,我感觉像是处于安全角度考虑的。至于为什么我会提到此问题,是因为我当前正在做这种VPN的客户端迁移到鸿蒙上。在以前的安卓手机上,服务端的子网或者服务端上就能够主动访问安卓手机的虚拟地址。但是鸿蒙上目前适配完这个问题没有解决。 此外,感谢赐教!

蹲下后续

在鸿蒙Next的VPN Extension中,向tun fd写入目的地址为本机VIP的数据包后未触发本地协议栈应答,通常原因:

  • tun接口未配置该VIP地址,内核不识别为本地地址;
  • 数据包的源/目的地址、路由标记或TTL不符合本地协议栈处理规则;
  • 系统Netfilter规则(如iptables)拦截了回环流量。
    需检查tun接口IP配置、路由表及防火墙规则。

你的问题大概率是 HarmonyOS 内核的 反向路径过滤(rp_filter) 导致的。
鸿蒙(Linux内核)对于从 tun 接口收到的报文,会做源地址可达性检查:你从 tunnel 写入的 ICMP echo request 源地址为 10.0.0.1,但手机路由表中 10.0.0.1 指向的是 tun0 外的 SSL 隧道,并非来自该接口的直连路由,内核认为源地址不可达或路径非法,直接丢包,不生成应答。

解决方案: 在 VPN 程序创建 tun 接口后,将该接口的 rp_filter 设为 0 或 2(宽松模式)。可通过写 /proc/sys/net/ipv4/conf/<tun_name>/rp_filter 实现,例如:

int fd = open("/proc/sys/net/ipv4/conf/tun0/rp_filter", O_WRONLY);
if (fd >= 0) {
    write(fd, "0", 1);
    close(fd);
}

如果同时需要响应 ARP 请求,可能需要为 tun 接口添加 /32 主机路由,并确认接口已启用 arp(默认开启)。

回到顶部