# VPN Management ## Overview A 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. > **NOTE** > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [Traffic Management](../reference/apis/js-apis-net-vpn.md). The following describes the development procedure specific to each application scenario. ## Available APIs For the complete list of APIs and example code, see [VPN Management](../reference/apis/js-apis-net-vpn.md). | Type| API| Description| | ---- | ---- | ---- | | ohos.net.vpn | setUp(config: VpnConfig, callback: AsyncCallback\): void | Establishes a VPN. This API uses an asynchronous callback to return the result.| | ohos.net.vpn | protect(socketFd: number, callback: AsyncCallback\): void | Enables VPN tunnel protection. This API uses an asynchronous callback to return the result.| | ohos.net.vpn | destroy(callback: AsyncCallback\): void | Destroys a VPN. This API uses an asynchronous callback to return the result.| ## Starting a VPN 1. Establish a VPN tunnel. The following uses the UDP tunnel as an example. 2. Enable protection for the UDP tunnel. 3. Establish a VPN. 4. Process data of the virtual network interface card (vNIC), such as reading or writing data. 5. Destroy the VPN. This 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). The sample application consists of two parts: JS code and C++ code. ## JS Code The JS code is used to implement the service logic, such as creating a tunnel, establishing a VPN, enabling VPN protection, and destroying a VPN. ```js import hilog from '@ohos.hilog'; import vpn from '@ohos.net.vpn'; import UIAbility from '@ohos.app.ability.UIAbility'; import vpn_client from "libvpn_client.so" class EntryAbility extends UIAbility { onWindowStageCreate(windowStage) { globalThis.context = this.context; } } let TunnelFd = -1 let VpnConnection = vpn.createVpnConnection(globalThis.context) @Entry @Component struct Index { @State message: string = 'Test VPN' //1. Establish a VPN tunnel. The following uses the UDP tunnel as an example. CreateTunnel() { TunnelFd = vpn_client.udpConnect("192.168.43.208", 8888) } // 2. Enable protection for the UDP tunnel. Protect() { VpnConnection.protect(TunnelFd).then(function () { console.info("vpn Protect Success.") }).catch(function (err) { console.info("vpn Protect Failed " + JSON.stringify(err)) }) } SetupVpn() { let config = { addresses: [{ address: { address: "10.0.0.5", family: 1 }, prefixLength: 24, }], routes: [], mtu: 1400, dnsAddresses: [ "114.114.114.114" ], acceptedApplications: [], refusedApplications: [] } try { // 3. Create a VPN. VpnConnection.setUp(config, (error, data) => { console.info("tunfd: " + JSON.stringify(data)); // 4. Process data of the virtual vNIC, such as reading or writing data. vpn_client.startVpn(data, TunnelFd) }) } catch (error) { console.info("vpn setUp fail " + JSON.stringify(error)); } } // 5. Destroy the VPN. Destroy() { vpn_client.stopVpn(TunnelFd) VpnConnection.destroy().then(function () { console.info("vpn Destroy Success.") }).catch(function (err) { console.info("vpn Destroy Failed " + JSON.stringify(err)) }) } build() { Row() { Column() { Text(this.message) .fontSize(50) .fontWeight(FontWeight.Bold) .onClick(() => { console.info("vpn Client") }) Button('CreateTunnel').onClick(() => { this.CreateTunnel() }).fontSize(50) Button('Protect').onClick(() => { this.Protect() }).fontSize(50) Button('SetupVpn').onClick(() => { this.SetupVpn() }).fontSize(50) Button('Destroy').onClick(() => { this.Destroy() }).fontSize(50) } .width('100%') } .height('100%') } } ``` ## C++ Code The C++ code is used for underlying service implementation, such as UDP tunnel client implementation and vNIC data read and write. ```c++ #include "napi/native_api.h" #include "hilog/log.h" #include #include #include #include #include #include #include #include #include #include #include #include #define BUFFER_SIZE 2048 #define VPN_LOG_TAG "NetMgrVpn" #define VPN_LOG_DOMAIN 0x15b0 #define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1) #define NETMANAGER_VPN_LOGE(fmt, ...) \ OH_LOG_Print(LOG_APP, LOG_ERROR, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ __LINE__, ##__VA_ARGS__) #define NETMANAGER_VPN_LOGI(fmt, ...) \ OH_LOG_Print(LOG_APP, LOG_INFO, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ __LINE__, ##__VA_ARGS__) #define NETMANAGER_VPN_LOGD(fmt, ...) \ OH_LOG_Print(LOG_APP, LOG_DEBUG, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \ __LINE__, ##__VA_ARGS__) struct FdInfo { int32_t tunFd = 0; int32_t tunnelFd = 0; struct sockaddr_in serverAddr; }; static FdInfo fdInfo; static bool threadRunF = false; static std::thread threadt1; static std::thread threadt2; // Obtain the IP address of the UDP server. static constexpr const int MAX_STRING_LENGTH = 1024; std::string GetStringFromValueUtf8(napi_env env, napi_value value) { std::string result; char str[MAX_STRING_LENGTH] = {0}; size_t length = 0; napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length); if (length > 0) { return result.append(str, length); } return result; } void HandleReadTunfd(FdInfo fdInfo) { uint8_t buffer[BUFFER_SIZE] = {0}; while (threadRunF) { int ret = read(fdInfo.tunFd, buffer, sizeof(buffer)); if (ret <= 0) { if (errno != 11) { NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunfd: %{public}d", errno, fdInfo.tunFd); } continue; } // Read data from the vNIC and send the data to the UDP server through the UDP tunnel. NETMANAGER_VPN_LOGD("buffer: %{public}s, len: %{public}d", buffer, ret); ret = sendto(fdInfo.tunnelFd, buffer, ret, 0, (struct sockaddr *)&fdInfo.serverAddr, sizeof(fdInfo.serverAddr)); if (ret <= 0) { NETMANAGER_VPN_LOGE("send to server[%{public}s:%{public}d] failed, ret: %{public}d, error: %{public}s", inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), ret, strerror(errno)); continue; } } } void HandleTcpReceived(FdInfo fdInfo) { int addrlen = sizeof(struct sockaddr_in); uint8_t buffer[BUFFER_SIZE] = {0}; while (threadRunF) { int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), 0, (struct sockaddr *)&fdInfo.serverAddr, (socklen_t *)&addrlen); if (length < 0) { if (errno != 11) { NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunnelfd: %{public}d", errno, fdInfo.tunnelFd); } continue; } // Receive data from the UDP server and write the data to the vNIC. NETMANAGER_VPN_LOGD("from [%{public}s:%{public}d] data: %{public}s, len: %{public}d", inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), buffer, length); int ret = write(fdInfo.tunFd, buffer, length); if (ret <= 0) { NETMANAGER_VPN_LOGE("error Write To Tunfd, errno: %{public}d", errno); } } } static napi_value UdpConnect(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value args[2] = {nullptr}; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); int32_t port = 0; napi_get_value_int32(env, args[1], &port); std::string ipAddr = GetStringFromValueUtf8(env, args[0]); NETMANAGER_VPN_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port); // Establish a UDP tunnel. int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0); if (sockFd == -1) { NETMANAGER_VPN_LOGE("socket() error"); return 0; } struct timeval timeout = {1, 0}; setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval)); memset(&fdInfo.serverAddr, 0, sizeof(fdInfo.serverAddr)); fdInfo.serverAddr.sin_family = AF_INET; fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr fdInfo.serverAddr.sin_port = htons(port); // port NETMANAGER_VPN_LOGI("Connection successful"); napi_value tunnelFd; napi_create_int32(env, sockFd, &tunnelFd); return tunnelFd; } static napi_value StartVpn(napi_env env, napi_callback_info info) { size_t argc = 2; napi_value args[2] = {nullptr}; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); napi_get_value_int32(env, args[0], &fdInfo.tunFd); napi_get_value_int32(env, args[1], &fdInfo.tunnelFd); if (threadRunF) { threadRunF = false; threadt1.join(); threadt2.join(); } // Start two threads. One is used to read data from the vNIC, and the other is used to receive data from the server. threadRunF = true; std::thread tt1(HandleReadTunfd, fdInfo); std::thread tt2(HandleTcpReceived, fdInfo); threadt1 = std::move(tt1); threadt2 = std::move(tt2); NETMANAGER_VPN_LOGI("StartVpn successful"); napi_value retValue; napi_create_int32(env, 0, &retValue); return retValue; } static napi_value StopVpn(napi_env env, napi_callback_info info) { size_t argc = 1; napi_value args[1] = {nullptr}; napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); int32_t tunnelFd; napi_get_value_int32(env, args[0], &tunnelFd); if (tunnelFd) { close(tunnelFd); tunnelFd = 0; } // Stop the two threads. if (threadRunF) { threadRunF = false; threadt1.join(); threadt2.join(); } NETMANAGER_VPN_LOGI("StopVpn successful"); napi_value retValue; napi_create_int32(env, 0, &retValue); return retValue; } EXTERN_C_START static napi_value Init(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { {"udpConnect", nullptr, UdpConnect, nullptr, nullptr, nullptr, napi_default, nullptr}, {"startVpn", nullptr, StartVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, {"stopVpn", nullptr, StopVpn, nullptr, nullptr, nullptr, napi_default, nullptr}, }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); return exports; } EXTERN_C_END static napi_module demoModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = Init, .nm_modname = "entry", .nm_priv = ((void *)0), .reserved = {0}, }; extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } ```