1# VPN Management (For System Applications Only) 2 3## Overview 4 5A virtual private network (VPN) is a dedicated network established on a public network. On a VPN, the connection between any two nodes does not have an end-to-end physical link required by the traditional private network. Instead, user data is transmitted over a logical link because a VPN is a logical network deployed over the network platform (such as the Internet) provided by the public network service provider. 6 7> **NOTE** 8> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the promise mode. For details about the APIs, see [API Reference](../reference/apis-network-kit/js-apis-net-vpn-sys.md). 9 10The following describes the development procedure specific to each application scenario. 11 12## Available APIs 13 14For the complete list of APIs and example code, see [API Reference](../reference/apis-network-kit/js-apis-net-vpn-sys.md). 15 16| API | Description | 17| ----------------------------------------------------------------- | --------------------------------------------------- | 18| setUp(config: VpnConfig, callback: AsyncCallback\<number\>): void | Establishes a VPN. This API uses an asynchronous callback to return the result.| 19| protect(socketFd: number, callback: AsyncCallback\<void\>): void | Enables VPN tunnel protection. This API uses an asynchronous callback to return the result. | 20| destroy(callback: AsyncCallback\<void\>): void | Destroys a VPN. This API uses an asynchronous callback to return the result.| 21 22## Starting a VPN 23 241. Establish a VPN tunnel. The following uses the UDP tunnel as an example. 252. Enable protection for the UDP tunnel. 263. Establish a VPN. 274. Process data of the virtual network interface card (vNIC), such as reading or writing data. 285. Destroy the VPN. 29 30This example shows how to develop an application using native C++ code. For details, see [Simple Native C++ Example (ArkTS) (API9)](https://gitee.com/openharmony/codelabs/tree/master/NativeAPI/NativeTemplateDemo). 31 32The sample application consists of two parts: JS code and C++ code. 33 34## JS Code 35 36The JS code is used to implement the service logic, such as creating a tunnel, establishing a VPN, enabling VPN protection, and destroying a VPN. 37 38```js 39import { vpn } from '@kit.NetworkKit'; 40import { common } from '@kit.AbilityKit'; 41import vpn_client from "libvpn_client.so"; 42import { BusinessError } from '@kit.BasicServicesKit'; 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. Establish a VPN tunnel. The following uses the UDP tunnel as an example. 55 CreateTunnel() { 56 TunnelFd = vpn_client.udpConnect("192.168.43.208", 8888); 57 } 58 59 // 2. Enable protection for the UDP tunnel. 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 // 3. Create a VPN. 79 this.VpnConnection.setUp(config).then((data: number) => { 80 console.info("tunfd: " + JSON.stringify(data)); 81 // 4. Process data of the virtual vNIC, such as reading or writing data. 82 vpn_client.startVpn(data, TunnelFd) 83 }).catch((err: BusinessError) => { 84 console.info("setUp fail" + JSON.stringify(err)); 85 }); 86 } 87 88 // 5. Destroy the VPN. 89 Destroy() { 90 vpn_client.stopVpn(TunnelFd); 91 this.VpnConnection.destroy().then(() => { 92 console.info("vpn Destroy Success."); 93 }).catch((err: BusinessError) => { 94 console.info("vpn Destroy Failed " + JSON.stringify(err)); 95 }) 96 } 97 98 build() { 99 Row() { 100 Column() { 101 Text(this.message) 102 .fontSize(50) 103 .fontWeight(FontWeight.Bold) 104 .onClick(() => { 105 console.info("vpn Client") 106 }) 107 Button('CreateTunnel').onClick(() => { 108 this.CreateTunnel() 109 }).fontSize(50) 110 Button('Protect').onClick(() => { 111 this.Protect() 112 }).fontSize(50) 113 Button('SetupVpn').onClick(() => { 114 this.SetupVpn() 115 }).fontSize(50) 116 Button('Destroy').onClick(() => { 117 this.Destroy() 118 }).fontSize(50) 119 } 120 .width('100%') 121 } 122 .height('100%') 123 } 124} 125``` 126 127## C++ Code 128 129The C++ code is used for underlying service implementation, such as UDP tunnel client implementation and vNIC data read and write. 130 131```c++ 132#include "napi/native_api.h" 133#include "hilog/log.h" 134 135#include <cstring> 136#include <thread> 137#include <js_native_api.h> 138#include <js_native_api_types.h> 139#include <unistd.h> 140#include <netinet/in.h> 141#include <sys/socket.h> 142#include <thread> 143#include <sys/time.h> 144 145#include <sys/socket.h> 146#include <netinet/in.h> 147#include <arpa/inet.h> 148 149#define BUFFER_SIZE 2048 150 151#define VPN_LOG_TAG "NetMgrVpn" 152#define VPN_LOG_DOMAIN 0x15b0 153#define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1) 154 155#define NETMANAGER_VPN_LOGE(fmt, ...) \ 156 OH_LOG_Print(LOG_APP, LOG_ERROR, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ 157 __LINE__, ##__VA_ARGS__) 158 159#define NETMANAGER_VPN_LOGI(fmt, ...) \ 160 OH_LOG_Print(LOG_APP, LOG_INFO, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ 161 __LINE__, ##__VA_ARGS__) 162 163#define NETMANAGER_VPN_LOGD(fmt, ...) \ 164 OH_LOG_Print(LOG_APP, LOG_DEBUG, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ 165 __LINE__, ##__VA_ARGS__) 166 167struct FdInfo { 168 int32_t tunFd = 0; 169 int32_t tunnelFd = 0; 170 struct sockaddr_in serverAddr; 171}; 172 173static FdInfo fdInfo; 174static bool threadRunF = false; 175static std::thread threadt1; 176static std::thread threadt2; 177 178// Obtain the IP address of the UDP server. 179static constexpr const int MAX_STRING_LENGTH = 1024; 180std::string GetStringFromValueUtf8(napi_env env, napi_value value) { 181 std::string result; 182 char str[MAX_STRING_LENGTH] = {0}; 183 size_t length = 0; 184 napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length); 185 if (length > 0) { 186 return result.append(str, length); 187 } 188 return result; 189} 190 191void HandleReadTunfd(FdInfo fdInfo) { 192 uint8_t buffer[BUFFER_SIZE] = {0}; 193 while (threadRunF) { 194 int ret = read(fdInfo.tunFd, buffer, sizeof(buffer)); 195 if (ret <= 0) { 196 if (errno != 11) { 197 NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunfd: %{public}d", errno, fdInfo.tunFd); 198 } 199 continue; 200 } 201 202 // Read data from the vNIC and send the data to the UDP server through the UDP tunnel. 203 NETMANAGER_VPN_LOGD("buffer: %{public}s, len: %{public}d", buffer, ret); 204 ret = sendto(fdInfo.tunnelFd, buffer, ret, 0, (struct sockaddr *)&fdInfo.serverAddr, sizeof(fdInfo.serverAddr)); 205 if (ret <= 0) { 206 NETMANAGER_VPN_LOGE("send to server[%{public}s:%{public}d] failed, ret: %{public}d, error: %{public}s", 207 inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), ret, 208 strerror(errno)); 209 continue; 210 } 211 } 212} 213 214void HandleTcpReceived(FdInfo fdInfo) { 215 int addrlen = sizeof(struct sockaddr_in); 216 uint8_t buffer[BUFFER_SIZE] = {0}; 217 while (threadRunF) { 218 int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), 0, (struct sockaddr *)&fdInfo.serverAddr, 219 (socklen_t *)&addrlen); 220 if (length < 0) { 221 if (errno != 11) { 222 NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunnelfd: %{public}d", errno, fdInfo.tunnelFd); 223 } 224 continue; 225 } 226 227 // Receive data from the UDP server and write the data to the vNIC. 228 NETMANAGER_VPN_LOGD("from [%{public}s:%{public}d] data: %{public}s, len: %{public}d", 229 inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), buffer, length); 230 int ret = write(fdInfo.tunFd, buffer, length); 231 if (ret <= 0) { 232 NETMANAGER_VPN_LOGE("error Write To Tunfd, errno: %{public}d", errno); 233 } 234 } 235} 236 237static napi_value UdpConnect(napi_env env, napi_callback_info info) { 238 size_t argc = 2; 239 napi_value args[2] = {nullptr}; 240 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 241 242 int32_t port = 0; 243 napi_get_value_int32(env, args[1], &port); 244 std::string ipAddr = GetStringFromValueUtf8(env, args[0]); 245 246 NETMANAGER_VPN_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port); 247 248 // Establish a UDP tunnel. 249 int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0); 250 if (sockFd == -1) { 251 NETMANAGER_VPN_LOGE("socket() error"); 252 return 0; 253 } 254 255 struct timeval timeout = {1, 0}; 256 setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)); 257 258 memset(&fdInfo.serverAddr, 0, sizeof(fdInfo.serverAddr)); 259 fdInfo.serverAddr.sin_family = AF_INET; 260 fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr 261 fdInfo.serverAddr.sin_port = htons(port); // port 262 263 NETMANAGER_VPN_LOGI("Connection successful"); 264 265 napi_value tunnelFd; 266 napi_create_int32(env, sockFd, &tunnelFd); 267 return tunnelFd; 268} 269 270static napi_value StartVpn(napi_env env, napi_callback_info info) { 271 size_t argc = 2; 272 napi_value args[2] = {nullptr}; 273 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 274 275 napi_get_value_int32(env, args[0], &fdInfo.tunFd); 276 napi_get_value_int32(env, args[1], &fdInfo.tunnelFd); 277 278 if (threadRunF) { 279 threadRunF = false; 280 threadt1.join(); 281 threadt2.join(); 282 } 283 284 // Start two threads. One is used to read data from the vNIC, and the other is used to receive data from the server. 285 threadRunF = true; 286 std::thread tt1(HandleReadTunfd, fdInfo); 287 std::thread tt2(HandleTcpReceived, fdInfo); 288 289 threadt1 = std::move(tt1); 290 threadt2 = std::move(tt2); 291 292 NETMANAGER_VPN_LOGI("StartVpn successful"); 293 294 napi_value retValue; 295 napi_create_int32(env, 0, &retValue); 296 return retValue; 297} 298 299static napi_value StopVpn(napi_env env, napi_callback_info info) { 300 size_t argc = 1; 301 napi_value args[1] = {nullptr}; 302 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 303 304 int32_t tunnelFd; 305 napi_get_value_int32(env, args[0], &tunnelFd); 306 if (tunnelFd) { 307 close(tunnelFd); 308 tunnelFd = 0; 309 } 310 311 // Stop the two threads. 312 if (threadRunF) { 313 threadRunF = false; 314 threadt1.join(); 315 threadt2.join(); 316 } 317 318 NETMANAGER_VPN_LOGI("StopVpn successful"); 319 320 napi_value retValue; 321 napi_create_int32(env, 0, &retValue); 322 return retValue; 323} 324 325EXTERN_C_START 326static napi_value Init(napi_env env, napi_value exports) { 327 napi_property_descriptor desc[] = { 328 {"udpConnect", nullptr, UdpConnect, nullptr, nullptr, nullptr, napi_default, nullptr}, 329 {"startVpn", nullptr, StartVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, 330 {"stopVpn", nullptr, StopVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, 331 }; 332 napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); 333 return exports; 334} 335EXTERN_C_END 336 337static napi_module demoModule = { 338 .nm_version = 1, 339 .nm_flags = 0, 340 .nm_filename = nullptr, 341 .nm_register_func = Init, 342 .nm_modname = "entry", 343 .nm_priv = ((void *)0), 344 .reserved = {0}, 345}; 346 347extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { 348 napi_module_register(&demoModule); 349} 350``` 351<!--no_check-->