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 Want from '@ohos.app.ability.Want';
40import VpnExtensionAbility from '@ohos.app.ability.VpnExtensionAbility';
41import vpnExt from '@ohos.net.vpnExtension';
42import hilog from '@ohos.hilog';
43import common from '@ohos.app.ability.common';
44
45// vpn_client is a portable .so file in C language, for example, import vpn_client from 'libvpn_client.so'.
46
47import socket from '@ohos.net.socket';
48
49const TAG: string = "[MyVpnExtAbility]";
50let g_tunFd = -1;
51let g_tunnelFd = -1;
52
53export default class MyVpnExtAbility extends VpnExtensionAbility {
54  private VpnConnection: vpnExt.VpnConnection = vpnExt.createVpnConnection(this.context);
55  private vpnServerIp: string = '192.168.85.185';
56  private tunIp: string = '10.0.0.8';
57  private blockedAppName: string = 'com.example.testvpn';
58
59  onCreate(want: Want) {
60    console.info(TAG, `xdw onCreate, want: ${want.abilityName}`);
61    // this.context.stopVpnExtensionAbility(want);
62    this.VpnConnection = vpnExt.createVpnConnection(this.context);
63    console.info("wmw createVpnConnection success")
64    this.CreateTunnel();
65    this.Protect();
66    console.info("xdw step4");
67  }
68
69  onRequest(want: Want, startId: number) {
70    console.info(TAG, `xdw onRequest, want: ${want.abilityName}`);
71  }
72
73  onConnect(want: Want) {
74    console.info(TAG, `xdw onConnect, want: ${want.abilityName}`);
75    // Return a ServiceExtImpl object, through which the client can communicate with the ServiceExtensionAbility.
76    let abilityName  = want.parameters?.abilityName.toString();
77    let bundleName = want.parameters?.bundleName.toString();
78    return null;
79  }
80
81  onDisconnect(want: Want) {
82    console.info(TAG, `xdw onDisconnect, want: ${want.abilityName}`);
83  }
84
85  onDestroy() {
86    console.info(TAG, `xdw onDestroy`);
87    this.Destroy();
88  }
89
90  Destroy() {
91    hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Destroy');
92    // Disable the VPN.
93    this.VpnConnection.destroy().then(() => {
94      hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Destroy Success');
95    }).catch((err: Error) => {
96      hilog.error(0x0000, 'developTag', 'vpn Destroy Failed: %{public}s', JSON.stringify(err) ?? '');
97    })
98  }
99
100  CreateTunnel() {
101    console.info("xdw step1")
102    // Connect to the VPN server.
103  }
104
105  Protect() {
106    console.info("xdw step2")
107    hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Protect');
108    this.VpnConnection.protect(g_tunnelFd).then(() => {
109      hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Protect Success');
110      this.SetupVpn();
111    }).catch((err: Error) => {
112      hilog.error(0x0000, 'developTag', 'vpn Protect Failed %{public}s', JSON.stringify(err) ?? '');
113    })
114  }
115
116  SetupVpn() {
117    console.info("xdw step3")
118    hilog.info(0x0000, 'developTag', '%{public}s', 'vpn SetupVpn');
119
120    class Address {
121      address: string;
122      family: number;
123
124      constructor(address: string, family: number) {
125        this.address = address;
126        this.family = family;
127      }
128    }
129
130    class AddressWithPrefix {
131      address: Address;
132      prefixLength: number;
133
134      constructor(address: Address, prefixLength: number) {
135        this.address = address;
136        this.prefixLength = prefixLength;
137      }
138    }
139
140    class Config {
141      addresses: AddressWithPrefix[];
142      dnsAddresses: string[];
143      trustedApplications: string[];
144      blockedApplications: string[];
145
146      constructor(
147        tunIp: string,
148        blockedAppName: string
149      ) {
150        this.addresses = [
151          new AddressWithPrefix(new Address(tunIp, 1), 24)
152        ];
153        this.dnsAddresses = ["114.114.114.114"];
154        this.trustedApplications = [];
155        this.blockedApplications = [];
156      }
157    }
158
159    let config = new Config(this.tunIp, this.blockedAppName);
160
161    try {
162      this.VpnConnection.create(config);
163    } catch (error) {
164      hilog.error(0x0000, 'developTag', 'vpn setUp fail: %{public}s', JSON.stringify(error) ?? '');
165    }
166  }
167}
168```
169
170## C++ Code
171
172The C++ code is used for underlying service implementation, such as UDP tunnel client implementation and vNIC data read and write.
173
174```c++
175#include "napi/native_api.h"
176#include "hilog/log.h"
177
178#include <cstring>
179#include <thread>
180#include <js_native_api.h>
181#include <js_native_api_types.h>
182#include <unistd.h>
183#include <netinet/in.h>
184#include <sys/socket.h>
185#include <thread>
186#include <sys/time.h>
187
188#include <sys/socket.h>
189#include <netinet/in.h>
190#include <arpa/inet.h>
191
192#define BUFFER_SIZE 2048
193
194#define VPN_LOG_TAG "NetMgrVpn"
195#define VPN_LOG_DOMAIN 0x15b0
196#define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1)
197
198#define NETMANAGER_VPN_LOGE(fmt, ...)                                                                                  \
199    OH_LOG_Print(LOG_APP, LOG_ERROR, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,  \
200                 __LINE__, ##__VA_ARGS__)
201
202#define NETMANAGER_VPN_LOGI(fmt, ...)                                                                                  \
203    OH_LOG_Print(LOG_APP, LOG_INFO, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,   \
204                 __LINE__, ##__VA_ARGS__)
205
206#define NETMANAGER_VPN_LOGD(fmt, ...)                                                                                  \
207    OH_LOG_Print(LOG_APP, LOG_DEBUG, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,   \
208                 __LINE__, ##__VA_ARGS__)
209
210struct FdInfo {
211    int32_t tunFd = 0;
212    int32_t tunnelFd = 0;
213    struct sockaddr_in serverAddr;
214};
215
216static FdInfo fdInfo;
217static bool threadRunF = false;
218static std::thread threadt1;
219static std::thread threadt2;
220
221// Obtain the IP address of the UDP server.
222static constexpr const int MAX_STRING_LENGTH = 1024;
223std::string GetStringFromValueUtf8(napi_env env, napi_value value) {
224    std::string result;
225    char str[MAX_STRING_LENGTH] = {0};
226    size_t length = 0;
227    napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length);
228    if (length > 0) {
229        return result.append(str, length);
230    }
231    return result;
232}
233
234void HandleReadTunfd(FdInfo fdInfo) {
235    uint8_t buffer[BUFFER_SIZE] = {0};
236    while (threadRunF) {
237        int ret = read(fdInfo.tunFd, buffer, sizeof(buffer));
238        if (ret <= 0) {
239            if (errno != 11) {
240                NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunfd: %{public}d", errno, fdInfo.tunFd);
241            }
242            continue;
243        }
244
245        // Read data from the vNIC and send the data to the UDP server through the UDP tunnel.
246        NETMANAGER_VPN_LOGD("buffer: %{public}s, len: %{public}d", buffer, ret);
247        ret = sendto(fdInfo.tunnelFd, buffer, ret, 0, (struct sockaddr *)&fdInfo.serverAddr, sizeof(fdInfo.serverAddr));
248        if (ret <= 0) {
249            NETMANAGER_VPN_LOGE("send to server[%{public}s:%{public}d] failed, ret: %{public}d, error: %{public}s",
250                                inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), ret,
251                                strerror(errno));
252            continue;
253        }
254    }
255}
256
257void HandleTcpReceived(FdInfo fdInfo) {
258    int addrlen = sizeof(struct sockaddr_in);
259    uint8_t buffer[BUFFER_SIZE] = {0};
260    while (threadRunF) {
261        int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), 0, (struct sockaddr *)&fdInfo.serverAddr,
262                              (socklen_t *)&addrlen);
263        if (length < 0) {
264            if (errno != 11) {
265                NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunnelfd: %{public}d", errno, fdInfo.tunnelFd);
266            }
267            continue;
268        }
269
270        // Receive data from the UDP server and write the data to the vNIC.
271        NETMANAGER_VPN_LOGD("from [%{public}s:%{public}d] data: %{public}s, len: %{public}d",
272                            inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), buffer, length);
273        int ret = write(fdInfo.tunFd, buffer, length);
274        if (ret <= 0) {
275            NETMANAGER_VPN_LOGE("error Write To Tunfd, errno: %{public}d", errno);
276        }
277    }
278}
279
280static napi_value UdpConnect(napi_env env, napi_callback_info info) {
281    size_t argc = 2;
282    napi_value args[2] = {nullptr};
283    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
284
285    int32_t port = 0;
286    napi_get_value_int32(env, args[1], &port);
287    std::string ipAddr = GetStringFromValueUtf8(env, args[0]);
288
289    NETMANAGER_VPN_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port);
290
291    // Establish a UDP tunnel.
292    int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0);
293    if (sockFd == -1) {
294        NETMANAGER_VPN_LOGE("socket() error");
295        return 0;
296    }
297
298    struct timeval timeout = {1, 0};
299    setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
300
301    memset(&fdInfo.serverAddr, 0, sizeof(fdInfo.serverAddr));
302    fdInfo.serverAddr.sin_family = AF_INET;
303    fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr
304    fdInfo.serverAddr.sin_port = htons(port);                      // port
305
306    NETMANAGER_VPN_LOGI("Connection successful");
307
308    napi_value tunnelFd;
309    napi_create_int32(env, sockFd, &tunnelFd);
310    return tunnelFd;
311}
312
313static napi_value StartVpn(napi_env env, napi_callback_info info) {
314    size_t argc = 2;
315    napi_value args[2] = {nullptr};
316    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
317
318    napi_get_value_int32(env, args[0], &fdInfo.tunFd);
319    napi_get_value_int32(env, args[1], &fdInfo.tunnelFd);
320
321    if (threadRunF) {
322        threadRunF = false;
323        threadt1.join();
324        threadt2.join();
325    }
326
327    // Start two threads. One is used to read data from the vNIC, and the other is used to receive data from the server.
328    threadRunF = true;
329    std::thread tt1(HandleReadTunfd, fdInfo);
330    std::thread tt2(HandleTcpReceived, fdInfo);
331
332    threadt1 = std::move(tt1);
333    threadt2 = std::move(tt2);
334
335    NETMANAGER_VPN_LOGI("StartVpn successful");
336
337    napi_value retValue;
338    napi_create_int32(env, 0, &retValue);
339    return retValue;
340}
341
342static napi_value StopVpn(napi_env env, napi_callback_info info) {
343    size_t argc = 1;
344    napi_value args[1] = {nullptr};
345    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
346
347    int32_t tunnelFd;
348    napi_get_value_int32(env, args[0], &tunnelFd);
349    if (tunnelFd) {
350        close(tunnelFd);
351        tunnelFd = 0;
352    }
353
354    // Stop the two threads.
355    if (threadRunF) {
356        threadRunF = false;
357        threadt1.join();
358        threadt2.join();
359    }
360
361    NETMANAGER_VPN_LOGI("StopVpn successful");
362
363    napi_value retValue;
364    napi_create_int32(env, 0, &retValue);
365    return retValue;
366}
367
368EXTERN_C_START
369static napi_value Init(napi_env env, napi_value exports) {
370    napi_property_descriptor desc[] = {
371        {"udpConnect", nullptr, UdpConnect, nullptr, nullptr, nullptr, napi_default, nullptr},
372        {"startVpn", nullptr, StartVpn, nullptr, nullptr, nullptr, napi_default, nullptr},
373        {"stopVpn", nullptr, StopVpn, nullptr, nullptr, nullptr, napi_default, nullptr},
374    };
375    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
376    return exports;
377}
378EXTERN_C_END
379
380static napi_module demoModule = {
381    .nm_version = 1,
382    .nm_flags = 0,
383    .nm_filename = nullptr,
384    .nm_register_func = Init,
385    .nm_modname = "entry",
386    .nm_priv = ((void *)0),
387    .reserved = {0},
388};
389
390extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
391    napi_module_register(&demoModule);
392}
393```
394