• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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