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