基于网络的VPN连接实现

"歪屁恩"全称为虚拟私人网络(Virtual Private Network),是常用于连接中、大型企业或团体间私人网络的通讯方法,利用隧道协议(Tunneling Protocol)来达到发送端认证、消息保密与准确性等功能。

使用过程中,外网的用户可以使用"歪屁恩"client 连接组织搭建的 "歪屁恩" server 以建立通信隧道

随后便建立了虚拟的私人网络,处于外网的 worker 和内网中的 server 可以相互通信。

场景一:手机应用配置"歪屁恩"客户端转发请求到远程"歪屁恩"服务端访问互联网,实现建立基本"歪屁恩"服务能力

效果图

启动界面如下图示:

 

点击'启动"歪屁恩"Ext'按钮,会弹窗提示是否使用"歪屁恩"权限连接。

方案描述

当前提供三方"歪屁恩"能力主要用于创建虚拟网卡及配置"歪屁恩"路由信息,连接隧道过程及内部连接的协议需要应用内部自行实现,创建过程可参考如下:

1、项目中“jian”立"歪屁恩"Ability.ets文件,继承调用"歪屁恩"ExtensionAbility提供"歪屁恩"创建、销毁等生命周期能力。

2、ability实现后在entry-module.json5中,添加extensionAbilities相关配置。

3、设置want参数指定的启动目标,启用"歪屁恩"服务。

核心代码

1、项目中“jian”立"歪屁恩"Ability.ets文件,继承调用"歪屁恩"ExtensionAbility提供"歪屁恩"创建、销毁等生命周期能力

参考文档:@ohos.app.ability."歪屁恩"ExtensionAbility

private "歪屁恩"Connection: "歪屁恩"Ext."歪屁恩"Connection;

onCreate(want: Want) {

  console.info(TAG, `onCreate, want: ${want.abilityName}`);

  this."歪屁恩"Connection = "歪屁恩"Ext.create"歪屁恩"Connection(this.context);

  console.info("create"歪屁恩"Connection success");

}

onRequest(want: Want, startId: number) {

  console.info(TAG, `onRequest, want: ${want.abilityName}`);

}

onConnect(want: Want) {

  console.info(TAG, `onConnect, want: ${want.abilityName}`);

  return null;

}

onDisconnect(want: Want) {

  console.info(TAG, `onDisconnect, want: ${want.abilityName}`);

}

onDestroy() {

  this.Destroy();

  console.info(TAG, `onDestroy`);

}

Destroy() {

  hilog.info(0x0000, 'developTag', '%{public}s', '"歪屁恩" Destroy');

  "歪屁恩"_client.stop"歪屁恩"(g_tunnelFd);

  this."歪屁恩"Connection.destroy().then(() => {

    hilog.info(0x0000, 'developTag', '%{public}s', '"歪屁恩" Destroy Success');

  }).catch((err : Error) => {

    hilog.error(0x0000, 'developTag', '"歪屁恩" Destroy Failed: %{public}s', JSON.stringify(err) ?? '');

  })

}

2、module.json5文件中配置extensionAbilities参数,样例如下:

{

  "module": {

    "name": "entry",

    "type": "entry",

    "description": "$string:module_desc",

    "mainElement": "EntryAbility",

    "deviceTypes": [

      "phone",

      "tablet",

      "2in1"

    ],

    "deliveryWithInstall": true,

    "installationFree": false,

    "pages": "$profile:main_pages",

    "abilities": [

      {

        "name": "EntryAbility",

        "srcEntry": "./ets/entryAbility/EntryAbility.ets",

        "description": "$string:EntryAbility_desc",

        "icon": "$media:startIcon",

        "label": "$string:EntryAbility_label",

        "startWindowIcon": "$media:startIcon",

        "startWindowBackground": "$color:start_window_background",

        "exported": true,

        "skills": [

          {

            "entities": [

              "entity.system.home"

            ],

            "actions": [

              "action.system.home"

            ]

          }

        ]

      }

    ],

    "requestPermissions": [

      {

        "name": "ohos.permission.INTERNET"

      },

      {

        "name": "ohos.permission.GET_NETWORK_INFO"

      }

    ],

    "extensionAbilities": [

      {

        "name": "My"歪屁恩"ExtAbility",

        "srcEntry": "./ets/"歪屁恩"Ability/My"歪屁恩"ExtAbility.ets",

        "type": ""歪屁恩""

      }

    ]

  }

}

如首次添加"type": ""歪屁恩""时报红,“ctrl+左键”点击"type",在"enum"中添加“"歪屁恩"”参数

配置修改后界面如下:

 ​

3、设置want参数指定的启动目标,启用"歪屁恩"服务。

let want: Want = {

  deviceId: "",

  bundleName: "com.example.my"歪屁恩"demo",

  abilityName: "My"歪屁恩"ExtAbility",

};

@Entry

@Component

struct Start"歪屁恩" {

  build() {

    Row() {

      Column() {

        Button($r('app.string.btn_start_"歪屁恩"Ext')).onClick(() => {

          "歪屁恩"ext.start"歪屁恩"ExtensionAbility(want);  //启用"歪屁恩"服务

        }).fontSize(50)

      }

      .width('100%')

    }

    .height('100%')

  }

}

4、创建 "歪屁恩"连接 网络

有关参数说明可参考:"歪屁恩"Extension."歪屁恩"Config

class Config {

  addresses: AddressWithPrefix[];

  mtu: number;

  dnsAddresses: string[];

  trustedApplications: string[];

  blockedApplications: string[];

  constructor(

    tunIp: string,

    blockedAppName: string

  ) {

    this.addresses = [

      new AddressWithPrefix(new Address(tunIp, 1), 24)

    ];

    this.mtu = 1400;

    this.dnsAddresses = ["114.114.114.114"];

    this.trustedApplications = [];

    this.blockedApplications = [blockedAppName];

  }

}

let config = new Config(this.tunIp, this.blockedAppName);

try {

  this."歪屁恩"Connection.create(config).then((data) => {

    g_tunFd = data;

    hilog.error(0x0000, 'developTag', 'tunfd: %{public}s', JSON.stringify(data) ?? '');

    "歪屁恩"_client.start"歪屁恩"(g_tunFd, g_tunnelFd);

  })

} catch (error) {

  hilog.error(0x0000, 'developTag', '"歪屁恩" setUp fail: %{public}s', JSON.stringify(error) ?? '');

}

"歪屁恩"创建成功时日志打印如下图:

5、销毁"歪屁恩"连接。

let want: Want = {

  deviceId: "",

  bundleName: "com.example.my"歪屁恩"demo",

  abilityName: "My"歪屁恩"ExtAbility",

};

let g_tunnelFd = -1;

@Entry

@Component

struct Stop"歪屁恩" {

  @State message: string = '"歪屁恩"';

  @State "歪屁恩"ServerIp: string = '192.168.3.49 ';

  @State tunIp: string = '10.0.0.5';

  @State routeAddr: string = '192.168.214.0';

  @State prefix: string = '24';

  @State blockedAppName: string = 'com.example.baidumyapplication';

  private context = getContext(this) as common."歪屁恩"ExtensionContext;

  private "歪屁恩"Connection: "歪屁恩"ext."歪屁恩"Connection = "歪屁恩"ext.create"歪屁恩"Connection(this.context);

  Destroy() {

    hilog.info(0x0000, 'developTag', '%{public}s', '"歪屁恩" Destroy');

    "歪屁恩"_client.stop"歪屁恩"(g_tunnelFd);

    this."歪屁恩"Connection.destroy().then(() => {

      hilog.info(0x0000, 'developTag', '%{public}s', '"歪屁恩" Destroy Success');

    }).catch((err : Error) => {

      hilog.error(0x0000, 'developTag', '"歪屁恩" Destroy Failed: %{public}s', JSON.stringify(err) ?? '');

    })

  }

  build() {

    Row() {

      Column() {

        Text(this.message)

          .fontSize(35)

          .fontWeight(FontWeight.Bold)

          .onClick(() => {

            hilog.info(0x0000, 'developTag', '%{public}s', '"歪屁恩" Client');

          })

        Button('stop "歪屁恩"').onClick(() => {

          this.Destroy();

        }).fontSize(50)

        Button('stop "歪屁恩"Ext').onClick(() => {

          "歪屁恩"ext.stop"歪屁恩"ExtensionAbility(want);

        }).fontSize(50)

      }.width('100%')

    }.height('100%')

  }

}

场景二:使用当前提供"歪屁恩"能力建立隧道连接,实现连接过程中 "歪屁恩数据包的传递

方案描述

建立隧道有关能力可通过NDK侧代码段实现:

1、建立"歪屁恩"_client.cpp文件,写入"歪屁恩"隧道通信启动、停止有关能力。

2、NDK添加可导出配置的使用接口能力。

3、页面中调用能力import引入。

核心代码

1、建立"歪屁恩"_client.cpp文件,写入"歪屁恩"隧道通信有关能力。

#define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1)

#define NETMANAGER_"歪屁恩"_LOGE(fmt, ...)                                                                                  \

OH_LOG_Print(LOG_APP, LOG_ERROR, 0x15b0, "NetMgr"歪屁恩"", ""歪屁恩" [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,  \

__LINE__, ##__VA_ARGS__)

#define NETMANAGER_"歪屁恩"_LOGI(fmt, ...)                                                                                  \

OH_LOG_Print(LOG_APP, LOG_INFO, 0x15b0, "NetMgr"歪屁恩"", ""歪屁恩" [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,   \

__LINE__, ##__VA_ARGS__)

#define NETMANAGER_"歪屁恩"_LOGD(fmt, ...)                                                                                  \

OH_LOG_Print(LOG_APP, LOG_DEBUG, 0x15b0, "NetMgr"歪屁恩"", ""歪屁恩" [%{public}s %{public}d] " fmt, MAKE_FILE_NAME,  \

__LINE__, ##__VA_ARGS__)

constexpr int BUFFER_SIZE = 2048;

constexpr int ERRORAGAIN = 11;

struct FdInfo {

  int32_t tunFd = 0;

  int32_t tunnelFd = 0;

  struct sockaddr_in serverAddr;

};

static FdInfo g_fdInfo;

static bool g_threadRunF = false;

static std::thread g_threadt1;

static std::thread g_threadt2;

static constexpr const int MAX_STRING_LENGTH = 1024;

static 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;

}

//获取隧道能力

static void HandleReadTunfd(FdInfo fdInfo)

{

  uint8_t buffer[BUFFER_SIZE] = {0};

  while (g_threadRunF) {

    if (fdInfo.tunFd <= 0) {

      sleep(1);

      continue;

    }

    int ret = read(fdInfo.tunFd, buffer, sizeof(buffer));

    if (ret <= 0) {

      if (errno != ERRORAGAIN) {

        sleep(1);

      }

      continue;

    }

    // Read the data from the virtual network interface and send it to the client through a TCP tunnel.

    NETMANAGER_"歪屁恩"_LOGD("buffer: %{public}s, len: %{public}d", buffer, ret);

    ret = sendto(fdInfo.tunnelFd, buffer, ret, 0,

      reinterpret_cast<struct sockaddr *>(&fdInfo.serverAddr), sizeof(fdInfo.serverAddr));

    if (ret <= 0) {

      NETMANAGER_"歪屁恩"_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;

    }

  }

}

static void HandleTcpReceived(FdInfo fdInfo)

{

  int addrlen = sizeof(struct sockaddr_in);

  uint8_t buffer[BUFFER_SIZE] = {0};

  while (g_threadRunF) {

    if (fdInfo.tunnelFd <= 0) {

      sleep(1);

      continue;

    }

    int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), 0,

      reinterpret_cast<struct sockaddr *>(&fdInfo.serverAddr)

    reinterpret_cast<socklen_t *>(&addrlen));

    if (length < 0) {

      if (errno != EAGAIN) {

        NETMANAGER_"歪屁恩"_LOGE("read tun device error: %{public}d %{public}d", errno, fdInfo.tunnelFd);

      }

      continue;

    }

    NETMANAGER_"歪屁恩"_LOGI("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_"歪屁恩"_LOGE("error Write To Tunfd, errno: %{public}d", errno);

    }

  }

}

//通信能力创建

static napi_value TcpConnect(napi_env env, napi_callback_info info)

{

  size_t numArgs = 2;

  size_t argc = numArgs;

  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_"歪屁恩"_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port);

  int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0);

  if (sockFd == -1) {

    NETMANAGER_"歪屁恩"_LOGE("socket() error");

    return 0;

  }

  struct timeval timeout = {1, 0};

setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char *>(&timeout), sizeof(struct timeval));

memset(&g_fdInfo.serverAddr, 0, sizeof(g_fdInfo.serverAddr));

g_fdInfo.serverAddr.sin_family = AF_INET;

g_fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr

g_fdInfo.serverAddr.sin_port = htons(port);                      // port

NETMANAGER_"歪屁恩"_LOGI("Connection successful\n");

napi_value tunnelFd;

napi_create_int32(env, sockFd, &tunnelFd);

return tunnelFd;

}

//"歪屁恩"启用当前隧道连接

static napi_value Start"歪屁恩"(napi_env env, napi_callback_info info)

{

  size_t numArgs = 2;

  size_t argc = numArgs;

  napi_value args[2] = {nullptr};

  napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

  napi_get_value_int32(env, args[0], &g_fdInfo.tunFd);

  napi_get_value_int32(env, args[1], &g_fdInfo.tunnelFd);

  if (g_threadRunF) {

    g_threadRunF = false;

    g_threadt1.join();

    g_threadt2.join();

  }

  g_threadRunF = true;

  std::thread tt1(HandleReadTunfd, g_fdInfo);

  std::thread tt2(HandleTcpReceived, g_fdInfo);

  g_threadt1 = std::move(tt1);

  g_threadt2 = std::move(tt2);

  NETMANAGER_"歪屁恩"_LOGI("Start"歪屁恩" successful\n");

  napi_value retValue;

  napi_create_int32(env, 0, &retValue);

  return retValue;

}

//"歪屁恩"停止当前隧道连接

static napi_value Stop"歪屁恩"(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;

  }

  if (g_threadRunF) {

    g_threadRunF = false;

    g_threadt1.join();

    g_threadt2.join();

  }

  NETMANAGER_"歪屁恩"_LOGI("Stop"歪屁恩" successful\n");

  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[] = {

  {"tcpConnect", nullptr, TcpConnect, nullptr, nullptr, nullptr, napi_default, nullptr},

{"start"歪屁恩"", nullptr, Start"歪屁恩", nullptr, nullptr, nullptr, napi_default, nullptr},

{"stop"歪屁恩"", nullptr, Stop"歪屁恩", 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)

{

  NETMANAGER_"歪屁恩"_LOGI(""歪屁恩" 15b0 HELLO ~~~~~~~~~~");

  napi_module_register(&demoModule);

}

2、NDK添加可导出配置的使用接口能力,以当前执行使用项目为例,index.d.ts文件中配置方法:​

3、页面中调用能力import引入。

import "歪屁恩"_client from 'lib"歪屁恩"_client.so';

let want: Want = {

  deviceId: "",

  bundleName: "com.example.my"歪屁恩"demo",

  abilityName: "My"歪屁恩"ExtAbility",

};

//g_tunFd连接前设置生成的标识符,g_tunnelFd表示连接隧道成功后对应的表示符

let g_tunFd = -1;

let g_tunnelFd = -1;

@Entry

@Component

struct Start"歪屁恩" {

  @State message: string = 'Toy "歪屁恩"';

  @State "歪屁恩"ServerIp: string = '192.168.3.49';

  @State tunIp: string = '10.0.0.5';

  @State prefix: string = '24';

  @State blockedAppName: string = 'com.example.baidumyapplication';

  private context = getContext(this) as common."歪屁恩"ExtensionContext;

  private "歪屁恩"Connection: "歪屁恩"ext."歪屁恩"Connection = "歪屁恩"ext.create"歪屁恩"Connection(this.context);

  //创建隧道连接

  CreateTunnel() {

    g_tunnelFd = "歪屁恩"_client.tcpConnect(this."歪屁恩"ServerIp, 8888);

  }

  Protect() {

    hilog.info(0x0000, 'developTag', '%{public}s', '"歪屁恩" Protect');

    this."歪屁恩"Connection.protect(g_tunnelFd).then(() => {

      hilog.info(0x0000, 'developTag', '%{public}s', '"歪屁恩" Protect Success');

    }).catch((err : Error) => {

      hilog.error(0x0000, 'developTag', '"歪屁恩" Protect Failed %{public}s', JSON.stringify(err) ?? '');

    })

  }

}

具体实现可参考:"歪屁恩"连接

常见问题

Q:"歪屁恩"连接后如何判断?

A:可使用connection模块中getNetCapabilities能力获取,返回netBearType参数为4,即当前使用了"歪屁恩"网络。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、国内研究现状 目前国内关于基于ensp的企业网络设计与实现的研究还比较少,主要集中在网络拓扑结构的设计和网络性能的优化方面。 在网络拓扑结构的设计方面,一些研究者通过建立基于ensp的模拟实验环境,提出了一些网络拓扑结构的设计方案,如星型、环形、树型、网状等拓扑结构,以满足不同企业的网络需求。例如,某研究者提出了一种基于ensp的树型网络拓扑结构设计方案,通过优化节点的布局和网络连接方式,提高了网络的稳定性和可靠性。 在网络性能的优化方面,也有一些研究者通过ensp模拟实验,分析网络中的瓶颈和优化策略,如路由协议的选择、带宽的分配、负载均衡等。例如,某研究者通过ensp模拟实验,发现在企业网络中使用OSPF(开放最短路径优先)路由协议可以有效提高网络的性能和稳定性。 二、国外研究现状 相比国内,国外对于基于ensp的企业网络设计与实现的研究更为深入和系统,涵盖了网络拓扑结构、网络性能优化、网络安全等方面。 在网络拓扑结构的设计方面,国外研究者提出了一些新颖的网络拓扑结构,如蜂窝式、星型-树型混合、三角形等拓扑结构,以适应不同企业的网络需求。同时,他们还提出了很多实际应用的场景,如分布式数据中心、云计算、大数据等。 在网络性能优化方面,国外研究者针对不同的网络瓶颈,提出了一系列的优化策略。例如,在网络带宽分配方面,他们提出了一种基于流量监测和QoS(服务质量)策略的带宽分配方案,可以有效地提高网络的性能和可靠性。 在网络安全方面,国外研究者提出了一系列的网络安全解决方案,包括入侵检测、防火墙、VPN等,以保障企业网络的安全。 三、研究背景 随着信息化时代的发展,企业对于网络的需求越来越高。如何构建一套高效、可靠、安全的企业网络已成为一个重要的研究方向。在这一背景下,基于ensp的企业网络设计与实现的研究具有重要的意义。通过ensp模拟实验,可以有效地验证各种网络拓扑结构和优化策略的有效性和可行性,为企业网络的实际应用提供有力的支持和指导。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值