• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# VPN 管理(仅对系统应用开放)
2<!--Kit: Network Kit-->
3<!--Subsystem: Communication-->
4<!--Owner: @wmyao_mm-->
5<!--Designer: @guo-min_net-->
6<!--Tester: @tongxilin-->
7<!--Adviser: @zhang_yixin13-->
8
9## 简介
10
11VPN 即虚拟专网(VPN-Virtual Private Network)在公用网络上建立专用网络的技术。整个 VPN 网络的任意两个节点之间的连接并没有传统专网所需的端到端的物理链路,而是架构在公用网络服务商所提供的网络平台(如 Internet)之上的逻辑网络,用户数据在逻辑链路中传输。
12
13> **说明:**
14> 为了保证应用的运行效率,大部分 API 调用都是异步的,对于异步调用的 API 均提供了 callback 和 Promise 两种方式,以下示例均采用 promise 函数,更多方式可以查阅[API 参考](../reference/apis-network-kit/js-apis-net-vpn-sys.md)。
15
16以下分别介绍具体开发方式。
17
18## 接口说明
19
20完整的 JS API 说明以及实例代码请参考:[VPN API 参考](../reference/apis-network-kit/js-apis-net-vpn-sys.md)。
21
22| 接口名                                                            | 描述                                          |
23| ----------------------------------------------------------------- | --------------------------------------------------- |
24| setUp(config: VpnConfig, callback: AsyncCallback\<number\>): void | 建立一个 VPN 网络,使用 callback 方式作为异步方法。 |
25| protect(socketFd: number, callback: AsyncCallback\<void\>): void  | 保护 VPN 的隧道,使用 callback 方式作为异步方法。   |
26| destroy(callback: AsyncCallback\<void\>): void                    | 销毁一个 VPN 网络,使用 callback 方式作为异步方法。 |
27
28## 启动 VPN 的流程
29
301. 建立一个 VPN 的网络隧道,下面以 UDP 隧道为例。
312. 保护前一步建立的 UDP 隧道。
323. 建立一个 VPN 网络。
334. 处理虚拟网卡的数据,如:读写操作。
345. 销毁 VPN 网络。
35
36本示例通过 Native C++ 的方式开发应用程序,Native C++ 可参考:[简易 Native C++ 示例(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/NativeAPI/NativeTemplateDemo)37
38示例程序主要包含两个部分:js 功能代码和 C++功能代码。
39
40## VPN 示例源码(js 部分)
41
42主要功能:实现业务逻辑,如:创建隧道、建立 VPN 网络、保护 VPN 网络、销毁 VPN 网络。
43
44```js
45import Want from '@ohos.app.ability.Want';
46import VpnExtensionAbility from '@ohos.app.ability.VpnExtensionAbility';
47import vpnExt from '@ohos.net.vpnExtension';
48import hilog from '@ohos.hilog';
49import common from '@ohos.app.ability.common';
50
51// vpn_client是一个C语言便携的so,比如import vpn_client from 'libvpn_client.so';
52
53import socket from '@ohos.net.socket';
54
55const TAG: string = "[MyVpnExtAbility]";
56let g_tunFd = -1;
57let g_tunnelFd = -1;
58
59export default class MyVpnExtAbility extends VpnExtensionAbility {
60  private VpnConnection: vpnExt.VpnConnection = vpnExt.createVpnConnection(this.context);
61  private vpnServerIp: string = '192.168.85.185';
62  private tunIp: string = '10.0.0.8';
63  private blockedAppName: string = 'com.example.testvpn';
64
65  onCreate(want: Want) {
66    console.info(TAG, `xdw onCreate, want: ${want.abilityName}`);
67    // this.context.stopVpnExtensionAbility(want);
68    this.VpnConnection = vpnExt.createVpnConnection(this.context);
69    console.info("wmw createVpnConnection success")
70    this.CreateTunnel();
71    this.Protect();
72    console.info("xdw step4");
73  }
74
75  onRequest(want: Want, startId: number) {
76    console.info(TAG, `xdw onRequest, want: ${want.abilityName}`);
77  }
78
79  onConnect(want: Want) {
80    console.info(TAG, `xdw onConnect, want: ${want.abilityName}`);
81    // 返回ServiceExtImpl对象,客户端获取后便可以与ServiceExtensionAbility进行通信
82    let abilityName  = want.parameters?.abilityName.toString();
83    let bundleName = want.parameters?.bundleName.toString();
84    return null;
85  }
86
87  onDisconnect(want: Want) {
88    console.info(TAG, `xdw onDisconnect, want: ${want.abilityName}`);
89  }
90
91  onDestroy() {
92    console.info(TAG, `xdw onDestroy`);
93    this.Destroy();
94  }
95
96  Destroy() {
97    hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Destroy');
98    //关闭VPN
99    this.VpnConnection.destroy().then(() => {
100      hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Destroy Success');
101    }).catch((err: Error) => {
102      hilog.error(0x0000, 'developTag', 'vpn Destroy Failed: %{public}s', JSON.stringify(err) ?? '');
103    })
104  }
105
106  CreateTunnel() {
107    console.info("xdw step1")
108    // 连接VPN服务器
109  }
110
111  Protect() {
112    console.info("xdw step2")
113    hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Protect');
114    this.VpnConnection.protect(g_tunnelFd).then(() => {
115      hilog.info(0x0000, 'developTag', '%{public}s', 'vpn Protect Success');
116      this.SetupVpn();
117    }).catch((err: Error) => {
118      hilog.error(0x0000, 'developTag', 'vpn Protect Failed %{public}s', JSON.stringify(err) ?? '');
119    })
120  }
121
122  SetupVpn() {
123    console.info("xdw step3")
124    hilog.info(0x0000, 'developTag', '%{public}s', 'vpn SetupVpn');
125
126    class Address {
127      address: string;
128      family: number;
129
130      constructor(address: string, family: number) {
131        this.address = address;
132        this.family = family;
133      }
134    }
135
136    class AddressWithPrefix {
137      address: Address;
138      prefixLength: number;
139
140      constructor(address: Address, prefixLength: number) {
141        this.address = address;
142        this.prefixLength = prefixLength;
143      }
144    }
145
146    class Config {
147      addresses: AddressWithPrefix[];
148      dnsAddresses: string[];
149      trustedApplications: string[];
150      blockedApplications: string[];
151
152      constructor(
153        tunIp: string,
154        blockedAppName: string
155      ) {
156        this.addresses = [
157          new AddressWithPrefix(new Address(tunIp, 1), 24)
158        ];
159        this.dnsAddresses = ["114.114.114.114"];
160        this.trustedApplications = [];
161        this.blockedApplications = [];
162      }
163    }
164
165    let config = new Config(this.tunIp, this.blockedAppName);
166
167    try {
168      this.VpnConnection.create(config);
169    } catch (error) {
170      hilog.error(0x0000, 'developTag', 'vpn setUp fail: %{public}s', JSON.stringify(error) ?? '');
171    }
172  }
173}
174```
175
176## VPN 示例源码(c++部分)
177
178主要功能:具体业务的底层实现,如:UDP 隧道 Client 端的实现、虚拟网卡读写数据的实现。
179
180```c++
181#include "napi/native_api.h"
182#include "hilog/log.h"
183
184#include <cstring>
185#include <thread>
186#include <js_native_api.h>
187#include <js_native_api_types.h>
188#include <unistd.h>
189#include <netinet/in.h>
190#include <sys/socket.h>
191#include <thread>
192#include <sys/time.h>
193
194#include <sys/socket.h>
195#include <netinet/in.h>
196#include <arpa/inet.h>
197
198#define BUFFER_SIZE 2048
199
200#define VPN_LOG_TAG "NetMgrVpn"
201#define VPN_LOG_DOMAIN 0x15b0
202#define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1)
203
204#define NETMANAGER_VPN_LOGE(fmt, ...)                                                                                  \
205    OH_LOG_Print(LOG_APP, LOG_ERROR, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,  \
206                 __LINE__, ##__VA_ARGS__)
207
208#define NETMANAGER_VPN_LOGI(fmt, ...)                                                                                  \
209    OH_LOG_Print(LOG_APP, LOG_INFO, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,   \
210                 __LINE__, ##__VA_ARGS__)
211
212#define NETMANAGER_VPN_LOGD(fmt, ...)                                                                                  \
213    OH_LOG_Print(LOG_APP, LOG_DEBUG, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,   \
214                 __LINE__, ##__VA_ARGS__)
215
216struct FdInfo {
217    int32_t tunFd = 0;
218    int32_t tunnelFd = 0;
219    struct sockaddr_in serverAddr;
220};
221
222static FdInfo fdInfo;
223static bool threadRunF = false;
224static std::thread threadt1;
225static std::thread threadt2;
226
227//获取对应字符串数据, 用于获取udp server 的IP地址
228static constexpr const int MAX_STRING_LENGTH = 1024;
229std::string GetStringFromValueUtf8(napi_env env, napi_value value) {
230    std::string result;
231    char str[MAX_STRING_LENGTH] = {0};
232    size_t length = 0;
233    napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length);
234    if (length > 0) {
235        return result.append(str, length);
236    }
237    return result;
238}
239
240void HandleReadTunfd(FdInfo fdInfo) {
241    uint8_t buffer[BUFFER_SIZE] = {0};
242    while (threadRunF) {
243        int ret = read(fdInfo.tunFd, buffer, sizeof(buffer));
244        if (ret <= 0) {
245            if (errno != 11) {
246                NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunfd: %{public}d", errno, fdInfo.tunFd);
247            }
248            continue;
249        }
250
251        // 读取到虚拟网卡的数据,通过udp隧道,发送给服务器
252        NETMANAGER_VPN_LOGD("buffer: %{public}s, len: %{public}d", buffer, ret);
253        ret = sendto(fdInfo.tunnelFd, buffer, ret, 0, (struct sockaddr *)&fdInfo.serverAddr, sizeof(fdInfo.serverAddr));
254        if (ret <= 0) {
255            NETMANAGER_VPN_LOGE("send to server[%{public}s:%{public}d] failed, ret: %{public}d, error: %{public}s",
256                                inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), ret,
257                                strerror(errno));
258            continue;
259        }
260    }
261}
262
263void HandleTcpReceived(FdInfo fdInfo) {
264    int addrlen = sizeof(struct sockaddr_in);
265    uint8_t buffer[BUFFER_SIZE] = {0};
266    while (threadRunF) {
267        int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), 0, (struct sockaddr *)&fdInfo.serverAddr,
268                              (socklen_t *)&addrlen);
269        if (length < 0) {
270            if (errno != 11) {
271                NETMANAGER_VPN_LOGE("read tun device error: %{public}d,tunnelfd: %{public}d", errno, fdInfo.tunnelFd);
272            }
273            continue;
274        }
275
276        // 接收到udp server的数据,写入到虚拟网卡中
277        NETMANAGER_VPN_LOGD("from [%{public}s:%{public}d] data: %{public}s, len: %{public}d",
278                            inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), buffer, length);
279        int ret = write(fdInfo.tunFd, buffer, length);
280        if (ret <= 0) {
281            NETMANAGER_VPN_LOGE("error Write To Tunfd, errno: %{public}d", errno);
282        }
283    }
284}
285
286static napi_value UdpConnect(napi_env env, napi_callback_info info) {
287    size_t argc = 2;
288    napi_value args[2] = {nullptr};
289    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
290
291    int32_t port = 0;
292    napi_get_value_int32(env, args[1], &port);
293    std::string ipAddr = GetStringFromValueUtf8(env, args[0]);
294
295    NETMANAGER_VPN_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port);
296
297    // 建立udp隧道
298    int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0);
299    if (sockFd == -1) {
300        NETMANAGER_VPN_LOGE("socket() error");
301        return 0;
302    }
303
304    struct timeval timeout = {1, 0};
305    setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
306
307    memset(&fdInfo.serverAddr, 0, sizeof(fdInfo.serverAddr));
308    fdInfo.serverAddr.sin_family = AF_INET;
309    fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr
310    fdInfo.serverAddr.sin_port = htons(port);                      // port
311
312    NETMANAGER_VPN_LOGI("Connection successful");
313
314    napi_value tunnelFd;
315    napi_create_int32(env, sockFd, &tunnelFd);
316    return tunnelFd;
317}
318
319static napi_value StartVpn(napi_env env, napi_callback_info info) {
320    size_t argc = 2;
321    napi_value args[2] = {nullptr};
322    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
323
324    napi_get_value_int32(env, args[0], &fdInfo.tunFd);
325    napi_get_value_int32(env, args[1], &fdInfo.tunnelFd);
326
327    if (threadRunF) {
328        threadRunF = false;
329        threadt1.join();
330        threadt2.join();
331    }
332
333    // 启动两个线程, 一个处理读取虚拟网卡的数据,另一个接收服务端的数据
334    threadRunF = true;
335    std::thread tt1(HandleReadTunfd, fdInfo);
336    std::thread tt2(HandleTcpReceived, fdInfo);
337
338    threadt1 = std::move(tt1);
339    threadt2 = std::move(tt2);
340
341    NETMANAGER_VPN_LOGI("StartVpn successful");
342
343    napi_value retValue;
344    napi_create_int32(env, 0, &retValue);
345    return retValue;
346}
347
348static napi_value StopVpn(napi_env env, napi_callback_info info) {
349    size_t argc = 1;
350    napi_value args[1] = {nullptr};
351    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
352
353    int32_t tunnelFd;
354    napi_get_value_int32(env, args[0], &tunnelFd);
355    if (tunnelFd) {
356        close(tunnelFd);
357        tunnelFd = 0;
358    }
359
360    // 停止两个线程
361    if (threadRunF) {
362        threadRunF = false;
363        threadt1.join();
364        threadt2.join();
365    }
366
367    NETMANAGER_VPN_LOGI("StopVpn successful");
368
369    napi_value retValue;
370    napi_create_int32(env, 0, &retValue);
371    return retValue;
372}
373
374EXTERN_C_START
375static napi_value Init(napi_env env, napi_value exports) {
376    napi_property_descriptor desc[] = {
377        {"udpConnect", nullptr, UdpConnect, nullptr, nullptr, nullptr, napi_default, nullptr},
378        {"startVpn", nullptr, StartVpn, nullptr, nullptr, nullptr, napi_default, nullptr},
379        {"stopVpn", nullptr, StopVpn, nullptr, nullptr, nullptr, napi_default, nullptr},
380    };
381    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
382    return exports;
383}
384EXTERN_C_END
385
386static napi_module demoModule = {
387    .nm_version = 1,
388    .nm_flags = 0,
389    .nm_filename = nullptr,
390    .nm_register_func = Init,
391    .nm_modname = "entry",
392    .nm_priv = ((void *)0),
393    .reserved = {0},
394};
395
396extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
397    napi_module_register(&demoModule);
398}
399```
400
401## 相关实例
402
403针对 VPN 管理的开发,有以下相关实例可供参考:
404
405- [VPN应用](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/DocsSample/NetWork_Kit/NetWorkKit_NetManager/VPNControl_Case)
406