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的响应包。这个是什么问题?
猜测:
-
鸿蒙6.0以上的系统内核协议栈这块不允许?所以不给回包?(安卓这个功能没问题),但是我也没有查到相关资料
-
那就是代码实现有问题,目前排查不出来,AI也给不出结果。
所以想请教下大佬们,有没有人做过这块基于虚拟网卡的经验。
更多关于HarmonyOS 鸿蒙Next中vpnExtension tun fd write(dst=本机VIP) 不触发本地 ICMP/TCP 应答的实战教程也可以访问 https://www.itying.com/category-93-b0.html
尊敬的开发者,您好,
为了尽快解决您的问题,需要您进一步提供如下信息:
一、请提供完整的vpnConfig配置路由的文件
二、请按照如下方法提供完整的日志
- hdc shell
- cd data/log/hilog
- hilog -w clear (清除多余日志)
- 重现问题,并记录出现问题的具体时间
- 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 应答。
建议先验证两点:
- VPN 建立后系统是否真正拥有
10.0.0.2这个本地地址。 tun.write()注入一个目的地址为10.0.0.2的 ICMP Echo Request 后,协议栈是否会生成 Echo Reply。
如果这两项都失败,基本可以确定是 HarmonyOS VPN Extension 当前实现限制,而不是你的 SSLVPN 代码逻辑问题。
蹲下后续
在鸿蒙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(默认开启)。


