1# VPN 管理 2 3## 简介 4 5VPN 即虚拟专网(VPN-Virtual Private Network)在公用网络上建立专用网络的技术。整个 VPN 网络的任意两个节点之间的连接并没有传统专网所需的端到端的物理链路,而是架构在公用网络服务商所提供的网络平台(如 Internet)之上的逻辑网络,用户数据在逻辑链路中传输。 6 7> **说明:** 8> 为了保证应用的运行效率,大部分 API 调用都是异步的,对于异步调用的 API 均提供了 callback 和 Promise 两种方式,以下示例均采用 callback 函数,更多方式可以查阅[API 参考](../reference/apis/js-apis-net-vpn.md)。 9 10以下分别介绍具体开发方式。 11 12## 接口说明 13 14完整的 JS API 说明以及实例代码请参考:[VPN API 参考](../reference/apis/js-apis-net-vpn.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 vpn from '@ohos.net.vpn'; 40import common from '@ohos.app.ability.common'; 41import vpn_client from "libvpn_client.so"; 42import { BusinessError } from '@ohos.base'; 43 44let TunnelFd: number = -1; 45 46@Entry 47@Component 48struct Index { 49 @State message: string = 'Test VPN'; 50 51 private context = getContext(this) as common.UIAbilityContext; 52 private VpnConnection: vpn.VpnConnection = vpn.createVpnConnection(this.context); 53 54 //1. 建立一个VPN的网络隧道,下面以UDP隧道为例。 55 CreateTunnel() { 56 TunnelFd = vpn_client.udpConnect("192.168.43.208", 8888); 57 } 58 59 //2. 保护前一步建立的UDP隧道。 60 Protect() { 61 this.VpnConnection.protect(TunnelFd).then(() => { 62 console.info("vpn Protect Success."); 63 }).catch((err: BusinessError) => { 64 console.info("vpn Protect Failed " + JSON.stringify(err)); 65 }) 66 } 67 68 SetupVpn() { 69 let tunAddr : vpn.LinkAddress = {} as vpn.LinkAddress; 70 tunAddr.address.address = "10.0.0.5"; 71 tunAddr.address.family = 1; 72 73 let config : vpn.VpnConfig = {} as vpn.VpnConfig; 74 config.addresses.push(tunAddr); 75 config.mtu = 1400; 76 config.dnsAddresses = ["114.114.114.114"]; 77 78 try { 79 //3. 建立一个VPN网络。 80 this.VpnConnection.setUp(config, (error: BusinessError, data: number) => { 81 console.info("tunfd: " + JSON.stringify(data)); 82 //4. 处理虚拟网卡的数据,如:读写操作。 83 vpn_client.startVpn(data, TunnelFd) 84 }) 85 } catch (error) { 86 console.info("vpn setUp fail " + JSON.stringify(error)); 87 } 88 } 89 90 //5.销毁VPN网络。 91 Destroy() { 92 vpn_client.stopVpn(TunnelFd); 93 this.VpnConnection.destroy().then(() => { 94 console.info("vpn Destroy Success."); 95 }).catch((err: BusinessError) => { 96 console.info("vpn Destroy Failed " + JSON.stringify(err)); 97 }) 98 } 99 100 build() { 101 Row() { 102 Column() { 103 Text(this.message) 104 .fontSize(50) 105 .fontWeight(FontWeight.Bold) 106 .onClick(() => { 107 console.info("vpn Client") 108 }) 109 Button('CreateTunnel').onClick(() => { 110 this.CreateTunnel() 111 }).fontSize(50) 112 Button('Protect').onClick(() => { 113 this.Protect() 114 }).fontSize(50) 115 Button('SetupVpn').onClick(() => { 116 this.SetupVpn() 117 }).fontSize(50) 118 Button('Destroy').onClick(() => { 119 this.Destroy() 120 }).fontSize(50) 121 } 122 .width('100%') 123 } 124 .height('100%') 125 } 126} 127``` 128 129## VPN 示例源码(c++部分) 130 131主要功能:具体业务的底层实现,如:UDP 隧道 Client 端的实现、虚拟网卡读写数据的实现 132 133```c++ 134#include "napi/native_api.h" 135#include "hilog/log.h" 136 137#include <cstring> 138#include <thread> 139#include <js_native_api.h> 140#include <js_native_api_types.h> 141#include <unistd.h> 142#include <netinet/in.h> 143#include <sys/socket.h> 144#include <thread> 145#include <sys/time.h> 146 147#include <sys/socket.h> 148#include <netinet/in.h> 149#include <arpa/inet.h> 150 151#define BUFFER_SIZE 2048 152 153#define VPN_LOG_TAG "NetMgrVpn" 154#define VPN_LOG_DOMAIN 0x15b0 155#define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1) 156 157#define NETMANAGER_VPN_LOGE(fmt, ...) \ 158 OH_LOG_Print(LOG_APP, LOG_ERROR, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ 159 __LINE__, ##__VA_ARGS__) 160 161#define NETMANAGER_VPN_LOGI(fmt, ...) \ 162 OH_LOG_Print(LOG_APP, LOG_INFO, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ 163 __LINE__, ##__VA_ARGS__) 164 165#define NETMANAGER_VPN_LOGD(fmt, ...) \ 166 OH_LOG_Print(LOG_APP, LOG_DEBUG, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ 167 __LINE__, ##__VA_ARGS__) 168 169struct FdInfo { 170 int32_t tunFd = 0; 171 int32_t tunnelFd = 0; 172 struct sockaddr_in serverAddr; 173}; 174 175static FdInfo fdInfo; 176static bool threadRunF = false; 177static std::thread threadt1; 178static std::thread threadt2; 179 180//获取对应字符串数据, 用于获取udp server 的IP地址 181static constexpr const int MAX_STRING_LENGTH = 1024; 182std::string GetStringFromValueUtf8(napi_env env, napi_value value) { 183 std::string result; 184 char str[MAX_STRING_LENGTH] = {0}; 185 size_t length = 0; 186 napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length); 187 if (length > 0) { 188 return result.append(str, length); 189 } 190 return result; 191} 192 193void HandleReadTunfd(FdInfo fdInfo) { 194 uint8_t buffer[BUFFER_SIZE] = {0}; 195 while (threadRunF) { 196 int ret = read(fdInfo.tunFd, buffer, sizeof(buffer)); 197 if (ret <= 0) { 198 if (errno != 11) { 199 NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunfd: %{public}d", errno, fdInfo.tunFd); 200 } 201 continue; 202 } 203 204 // 读取到虚拟网卡的数据,通过udp隧道,发送给服务器 205 NETMANAGER_VPN_LOGD("buffer: %{public}s, len: %{public}d", buffer, ret); 206 ret = sendto(fdInfo.tunnelFd, buffer, ret, 0, (struct sockaddr *)&fdInfo.serverAddr, sizeof(fdInfo.serverAddr)); 207 if (ret <= 0) { 208 NETMANAGER_VPN_LOGE("send to server[%{public}s:%{public}d] failed, ret: %{public}d, error: %{public}s", 209 inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), ret, 210 strerror(errno)); 211 continue; 212 } 213 } 214} 215 216void HandleTcpReceived(FdInfo fdInfo) { 217 int addrlen = sizeof(struct sockaddr_in); 218 uint8_t buffer[BUFFER_SIZE] = {0}; 219 while (threadRunF) { 220 int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), 0, (struct sockaddr *)&fdInfo.serverAddr, 221 (socklen_t *)&addrlen); 222 if (length < 0) { 223 if (errno != 11) { 224 NETMANAGER_VPN_LOGE("read tun device error: %{public}d,tunnelfd: %{public}d", errno, fdInfo.tunnelFd); 225 } 226 continue; 227 } 228 229 // 接收到udp server的数据,写入到虚拟网卡中 230 NETMANAGER_VPN_LOGD("from [%{public}s:%{public}d] data: %{public}s, len: %{public}d", 231 inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), buffer, length); 232 int ret = write(fdInfo.tunFd, buffer, length); 233 if (ret <= 0) { 234 NETMANAGER_VPN_LOGE("error Write To Tunfd, errno: %{public}d", errno); 235 } 236 } 237} 238 239static napi_value UdpConnect(napi_env env, napi_callback_info info) { 240 size_t argc = 2; 241 napi_value args[2] = {nullptr}; 242 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 243 244 int32_t port = 0; 245 napi_get_value_int32(env, args[1], &port); 246 std::string ipAddr = GetStringFromValueUtf8(env, args[0]); 247 248 NETMANAGER_VPN_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port); 249 250 // 建立udp隧道 251 int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0); 252 if (sockFd == -1) { 253 NETMANAGER_VPN_LOGE("socket() error"); 254 return 0; 255 } 256 257 struct timeval timeout = {1, 0}; 258 setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)); 259 260 memset(&fdInfo.serverAddr, 0, sizeof(fdInfo.serverAddr)); 261 fdInfo.serverAddr.sin_family = AF_INET; 262 fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr 263 fdInfo.serverAddr.sin_port = htons(port); // port 264 265 NETMANAGER_VPN_LOGI("Connection successful"); 266 267 napi_value tunnelFd; 268 napi_create_int32(env, sockFd, &tunnelFd); 269 return tunnelFd; 270} 271 272static napi_value StartVpn(napi_env env, napi_callback_info info) { 273 size_t argc = 2; 274 napi_value args[2] = {nullptr}; 275 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 276 277 napi_get_value_int32(env, args[0], &fdInfo.tunFd); 278 napi_get_value_int32(env, args[1], &fdInfo.tunnelFd); 279 280 if (threadRunF) { 281 threadRunF = false; 282 threadt1.join(); 283 threadt2.join(); 284 } 285 286 // 启动两个线程, 一个处理读取虚拟网卡的数据,另一个接收服务端的数据 287 threadRunF = true; 288 std::thread tt1(HandleReadTunfd, fdInfo); 289 std::thread tt2(HandleTcpReceived, fdInfo); 290 291 threadt1 = std::move(tt1); 292 threadt2 = std::move(tt2); 293 294 NETMANAGER_VPN_LOGI("StartVpn successful"); 295 296 napi_value retValue; 297 napi_create_int32(env, 0, &retValue); 298 return retValue; 299} 300 301static napi_value StopVpn(napi_env env, napi_callback_info info) { 302 size_t argc = 1; 303 napi_value args[1] = {nullptr}; 304 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 305 306 int32_t tunnelFd; 307 napi_get_value_int32(env, args[0], &tunnelFd); 308 if (tunnelFd) { 309 close(tunnelFd); 310 tunnelFd = 0; 311 } 312 313 // 停止两个线程 314 if (threadRunF) { 315 threadRunF = false; 316 threadt1.join(); 317 threadt2.join(); 318 } 319 320 NETMANAGER_VPN_LOGI("StopVpn successful"); 321 322 napi_value retValue; 323 napi_create_int32(env, 0, &retValue); 324 return retValue; 325} 326 327EXTERN_C_START 328static napi_value Init(napi_env env, napi_value exports) { 329 napi_property_descriptor desc[] = { 330 {"udpConnect", nullptr, UdpConnect, nullptr, nullptr, nullptr, napi_default, nullptr}, 331 {"startVpn", nullptr, StartVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, 332 {"stopVpn", nullptr, StopVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, 333 }; 334 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 335 return exports; 336} 337EXTERN_C_END 338 339static napi_module demoModule = { 340 .nm_version = 1, 341 .nm_flags = 0, 342 .nm_filename = nullptr, 343 .nm_register_func = Init, 344 .nm_modname = "entry", 345 .nm_priv = ((void *)0), 346 .reserved = {0}, 347}; 348 349extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { 350 napi_module_register(&demoModule); 351} 352``` 353