1# VPN 管理(仅对系统应用开放) 2 3## 简介 4 5VPN 即虚拟专网(VPN-Virtual Private Network)在公用网络上建立专用网络的技术。整个 VPN 网络的任意两个节点之间的连接并没有传统专网所需的端到端的物理链路,而是架构在公用网络服务商所提供的网络平台(如 Internet)之上的逻辑网络,用户数据在逻辑链路中传输。 6 7> **说明:** 8> 为了保证应用的运行效率,大部分 API 调用都是异步的,对于异步调用的 API 均提供了 callback 和 Promise 两种方式,以下示例均采用 promise 函数,更多方式可以查阅[API 参考](../reference/apis-network-kit/js-apis-net-vpn-sys.md)。 9 10以下分别介绍具体开发方式。 11 12## 接口说明 13 14完整的 JS API 说明以及实例代码请参考:[VPN API 参考](../reference/apis-network-kit/js-apis-net-vpn-sys.md)。 15 16| 接口名 | 描述 | 17| ----------------------------------------------------------------- | --------------------------------------------------- | 18| setUp(config: VpnConfig, callback: AsyncCallback\<number\>): void | 建立一个 VPN 网络,使用 callback 方式作为异步方法。 | 19| protect(socketFd: number, callback: AsyncCallback\<void\>): void | 保护 VPN 的隧道,使用 callback 方式作为异步方法。 | 20| destroy(callback: AsyncCallback\<void\>): void | 销毁一个 VPN 网络,使用 callback 方式作为异步方法。 | 21 22## 启动 VPN 的流程 23 241. 建立一个 VPN 的网络隧道,下面以 UDP 隧道为例。 252. 保护前一步建立的 UDP 隧道。 263. 建立一个 VPN 网络。 274. 处理虚拟网卡的数据,如:读写操作。 285. 销毁 VPN 网络。 29 30本示例通过 Native C++ 的方式开发应用程序,Native C++ 可参考: [简易 Native C++ 示例(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/NativeAPI/NativeTemplateDemo)。 31 32示例程序主要包含两个部分:js 功能代码和 C++功能代码。 33 34## VPN 示例源码(js 部分) 35 36主要功能:实现业务逻辑,如:创建隧道、建立 VPN 网络、保护 VPN 网络、销毁 VPN 网络。 37 38```js 39import Want from '@ohos.app.ability.Want'; 40import VpnExtensionAbility from '@ohos.app.ability.VpnExtensionAbility'; 41import vpnExt from '@ohos.net.vpnExtension'; 42import hilog from '@ohos.hilog'; 43import common from '@ohos.app.ability.common'; 44 45// vpn_client是一个C语言便携的so,比如import vpn_client from 'libvpn_client.so'; 46 47import socket from '@ohos.net.socket'; 48 49const TAG: string = "[MyVpnExtAbility]"; 50let g_tunFd = -1; 51let g_tunnelFd = -1; 52 53export default class MyVpnExtAbility extends VpnExtensionAbility { 54 private VpnConnection: vpnExt.VpnConnection = vpnExt.createVpnConnection(this.context); 55 private vpnServerIp: string = '192.168.85.185'; 56 private tunIp: string = '10.0.0.8'; 57 private blockedAppName: string = 'com.example.testvpn'; 58 59 onCreate(want: Want) { 60 console.info(TAG, `xdw onCreate, want: ${want.abilityName}`); 61 // this.context.stopVpnExtensionAbility(want); 62 this.VpnConnection = vpnExt.createVpnConnection(this.context); 63 console.info("wmw createVpnConnection success") 64 this.CreateTunnel(); 65 this.Protect(); 66 console.info("xdw step4"); 67 } 68 69 onRequest(want: Want, startId: number) { 70 console.info(TAG, `xdw onRequest, want: ${want.abilityName}`); 71 } 72 73 onConnect(want: Want) { 74 console.info(TAG, `xdw onConnect, want: ${want.abilityName}`); 75 // 返回ServiceExtImpl对象,客户端获取后便可以与ServiceExtensionAbility进行通信 76 let abilityName = want.parameters?.abilityName.toString(); 77 let bundleName = want.parameters?.bundleName.toString(); 78 return null; 79 } 80 81 onDisconnect(want: Want) { 82 console.info(TAG, `xdw onDisconnect, want: ${want.abilityName}`); 83 } 84 85 onDestroy() { 86 console.info(TAG, `xdw onDestroy`); 87 this.Destroy(); 88 } 89 90 Destroy() { 91 hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Destroy'); 92 //关闭VPN 93 this.VpnConnection.destroy().then(() => { 94 hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Destroy Success'); 95 }).catch((err: Error) => { 96 hilog.error(0x0000, 'developTag', 'vpn Destroy Failed: %{public}s', JSON.stringify(err) ?? ''); 97 }) 98 } 99 100 CreateTunnel() { 101 console.info("xdw step1") 102 // 连接VPN服务器 103 } 104 105 Protect() { 106 console.info("xdw step2") 107 hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Protect'); 108 this.VpnConnection.protect(g_tunnelFd).then(() => { 109 hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Protect Success'); 110 this.SetupVpn(); 111 }).catch((err: Error) => { 112 hilog.error(0x0000, 'developTag', 'vpn Protect Failed %{public}s', JSON.stringify(err) ?? ''); 113 }) 114 } 115 116 SetupVpn() { 117 console.info("xdw step3") 118 hilog.info(0x0000, 'developTag', '%{public}s', 'vpn SetupVpn'); 119 120 class Address { 121 address: string; 122 family: number; 123 124 constructor(address: string, family: number) { 125 this.address = address; 126 this.family = family; 127 } 128 } 129 130 class AddressWithPrefix { 131 address: Address; 132 prefixLength: number; 133 134 constructor(address: Address, prefixLength: number) { 135 this.address = address; 136 this.prefixLength = prefixLength; 137 } 138 } 139 140 class Config { 141 addresses: AddressWithPrefix[]; 142 dnsAddresses: string[]; 143 trustedApplications: string[]; 144 blockedApplications: string[]; 145 146 constructor( 147 tunIp: string, 148 blockedAppName: string 149 ) { 150 this.addresses = [ 151 new AddressWithPrefix(new Address(tunIp, 1), 24) 152 ]; 153 this.dnsAddresses = ["114.114.114.114"]; 154 this.trustedApplications = []; 155 this.blockedApplications = []; 156 } 157 } 158 159 let config = new Config(this.tunIp, this.blockedAppName); 160 161 try { 162 this.VpnConnection.create(config); 163 } catch (error) { 164 hilog.error(0x0000, 'developTag', 'vpn setUp fail: %{public}s', JSON.stringify(error) ?? ''); 165 } 166 } 167} 168``` 169 170## VPN 示例源码(c++部分) 171 172主要功能:具体业务的底层实现,如:UDP 隧道 Client 端的实现、虚拟网卡读写数据的实现。 173 174```c++ 175#include "napi/native_api.h" 176#include "hilog/log.h" 177 178#include <cstring> 179#include <thread> 180#include <js_native_api.h> 181#include <js_native_api_types.h> 182#include <unistd.h> 183#include <netinet/in.h> 184#include <sys/socket.h> 185#include <thread> 186#include <sys/time.h> 187 188#include <sys/socket.h> 189#include <netinet/in.h> 190#include <arpa/inet.h> 191 192#define BUFFER_SIZE 2048 193 194#define VPN_LOG_TAG "NetMgrVpn" 195#define VPN_LOG_DOMAIN 0x15b0 196#define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1) 197 198#define NETMANAGER_VPN_LOGE(fmt, ...) \ 199 OH_LOG_Print(LOG_APP, LOG_ERROR, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ 200 __LINE__, ##__VA_ARGS__) 201 202#define NETMANAGER_VPN_LOGI(fmt, ...) \ 203 OH_LOG_Print(LOG_APP, LOG_INFO, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ 204 __LINE__, ##__VA_ARGS__) 205 206#define NETMANAGER_VPN_LOGD(fmt, ...) \ 207 OH_LOG_Print(LOG_APP, LOG_DEBUG, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ 208 __LINE__, ##__VA_ARGS__) 209 210struct FdInfo { 211 int32_t tunFd = 0; 212 int32_t tunnelFd = 0; 213 struct sockaddr_in serverAddr; 214}; 215 216static FdInfo fdInfo; 217static bool threadRunF = false; 218static std::thread threadt1; 219static std::thread threadt2; 220 221//获取对应字符串数据, 用于获取udp server 的IP地址 222static constexpr const int MAX_STRING_LENGTH = 1024; 223std::string GetStringFromValueUtf8(napi_env env, napi_value value) { 224 std::string result; 225 char str[MAX_STRING_LENGTH] = {0}; 226 size_t length = 0; 227 napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length); 228 if (length > 0) { 229 return result.append(str, length); 230 } 231 return result; 232} 233 234void HandleReadTunfd(FdInfo fdInfo) { 235 uint8_t buffer[BUFFER_SIZE] = {0}; 236 while (threadRunF) { 237 int ret = read(fdInfo.tunFd, buffer, sizeof(buffer)); 238 if (ret <= 0) { 239 if (errno != 11) { 240 NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunfd: %{public}d", errno, fdInfo.tunFd); 241 } 242 continue; 243 } 244 245 // 读取到虚拟网卡的数据,通过udp隧道,发送给服务器 246 NETMANAGER_VPN_LOGD("buffer: %{public}s, len: %{public}d", buffer, ret); 247 ret = sendto(fdInfo.tunnelFd, buffer, ret, 0, (struct sockaddr *)&fdInfo.serverAddr, sizeof(fdInfo.serverAddr)); 248 if (ret <= 0) { 249 NETMANAGER_VPN_LOGE("send to server[%{public}s:%{public}d] failed, ret: %{public}d, error: %{public}s", 250 inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), ret, 251 strerror(errno)); 252 continue; 253 } 254 } 255} 256 257void HandleTcpReceived(FdInfo fdInfo) { 258 int addrlen = sizeof(struct sockaddr_in); 259 uint8_t buffer[BUFFER_SIZE] = {0}; 260 while (threadRunF) { 261 int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), 0, (struct sockaddr *)&fdInfo.serverAddr, 262 (socklen_t *)&addrlen); 263 if (length < 0) { 264 if (errno != 11) { 265 NETMANAGER_VPN_LOGE("read tun device error: %{public}d,tunnelfd: %{public}d", errno, fdInfo.tunnelFd); 266 } 267 continue; 268 } 269 270 // 接收到udp server的数据,写入到虚拟网卡中 271 NETMANAGER_VPN_LOGD("from [%{public}s:%{public}d] data: %{public}s, len: %{public}d", 272 inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), buffer, length); 273 int ret = write(fdInfo.tunFd, buffer, length); 274 if (ret <= 0) { 275 NETMANAGER_VPN_LOGE("error Write To Tunfd, errno: %{public}d", errno); 276 } 277 } 278} 279 280static napi_value UdpConnect(napi_env env, napi_callback_info info) { 281 size_t argc = 2; 282 napi_value args[2] = {nullptr}; 283 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 284 285 int32_t port = 0; 286 napi_get_value_int32(env, args[1], &port); 287 std::string ipAddr = GetStringFromValueUtf8(env, args[0]); 288 289 NETMANAGER_VPN_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port); 290 291 // 建立udp隧道 292 int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0); 293 if (sockFd == -1) { 294 NETMANAGER_VPN_LOGE("socket() error"); 295 return 0; 296 } 297 298 struct timeval timeout = {1, 0}; 299 setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)); 300 301 memset(&fdInfo.serverAddr, 0, sizeof(fdInfo.serverAddr)); 302 fdInfo.serverAddr.sin_family = AF_INET; 303 fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr 304 fdInfo.serverAddr.sin_port = htons(port); // port 305 306 NETMANAGER_VPN_LOGI("Connection successful"); 307 308 napi_value tunnelFd; 309 napi_create_int32(env, sockFd, &tunnelFd); 310 return tunnelFd; 311} 312 313static napi_value StartVpn(napi_env env, napi_callback_info info) { 314 size_t argc = 2; 315 napi_value args[2] = {nullptr}; 316 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 317 318 napi_get_value_int32(env, args[0], &fdInfo.tunFd); 319 napi_get_value_int32(env, args[1], &fdInfo.tunnelFd); 320 321 if (threadRunF) { 322 threadRunF = false; 323 threadt1.join(); 324 threadt2.join(); 325 } 326 327 // 启动两个线程, 一个处理读取虚拟网卡的数据,另一个接收服务端的数据 328 threadRunF = true; 329 std::thread tt1(HandleReadTunfd, fdInfo); 330 std::thread tt2(HandleTcpReceived, fdInfo); 331 332 threadt1 = std::move(tt1); 333 threadt2 = std::move(tt2); 334 335 NETMANAGER_VPN_LOGI("StartVpn successful"); 336 337 napi_value retValue; 338 napi_create_int32(env, 0, &retValue); 339 return retValue; 340} 341 342static napi_value StopVpn(napi_env env, napi_callback_info info) { 343 size_t argc = 1; 344 napi_value args[1] = {nullptr}; 345 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 346 347 int32_t tunnelFd; 348 napi_get_value_int32(env, args[0], &tunnelFd); 349 if (tunnelFd) { 350 close(tunnelFd); 351 tunnelFd = 0; 352 } 353 354 // 停止两个线程 355 if (threadRunF) { 356 threadRunF = false; 357 threadt1.join(); 358 threadt2.join(); 359 } 360 361 NETMANAGER_VPN_LOGI("StopVpn successful"); 362 363 napi_value retValue; 364 napi_create_int32(env, 0, &retValue); 365 return retValue; 366} 367 368EXTERN_C_START 369static napi_value Init(napi_env env, napi_value exports) { 370 napi_property_descriptor desc[] = { 371 {"udpConnect", nullptr, UdpConnect, nullptr, nullptr, nullptr, napi_default, nullptr}, 372 {"startVpn", nullptr, StartVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, 373 {"stopVpn", nullptr, StopVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, 374 }; 375 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 376 return exports; 377} 378EXTERN_C_END 379 380static napi_module demoModule = { 381 .nm_version = 1, 382 .nm_flags = 0, 383 .nm_filename = nullptr, 384 .nm_register_func = Init, 385 .nm_modname = "entry", 386 .nm_priv = ((void *)0), 387 .reserved = {0}, 388}; 389 390extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { 391 napi_module_register(&demoModule); 392} 393``` 394