xref: /docs/en/application-dev/network/net-vpn.md (revision e41f4b71)
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-->