This commit is contained in:
snltty
2024-08-03 02:09:07 +08:00
parent 84b14228de
commit 850eb8a90f
22 changed files with 278 additions and 99 deletions

View File

@@ -48,19 +48,13 @@ jobs:
env:
GITHUB_TOKEN: '${{ secrets.ACTIONS_TOKEN }}'
with:
tag_name: v1.2.0.2
release_name: v1.2.0.2.${{ steps.date.outputs.today }}
tag_name: v1.2.0.3
release_name: v1.2.0.3.${{ steps.date.outputs.today }}
draft: false
prerelease: false
body: |
1. 网关层级计算修复(在linux下计算错误导致打洞失败)
2. 修复在IPV6环境下域名解析缓慢问题
3. 新增基于端口映射的打洞协议
4. 修复端口转发潜在BUG
5. 虚拟网卡支持广播组播了
6. 修复了socks5的一些问题
7. 增加了docker宿主机访问策略docker也可以用虚拟网卡通信了
8. 请更新服务端
1. 这是一个测试版本请使用v1.2.0.2版本
2. 请勿更新
- name: upload win x64
id: upload-win-x64

View File

@@ -23,11 +23,13 @@ jobs:
run: |
dotnet build ./linker.libs -c release
dotnet build ./linker.tunnel -c release
dotnet build ./linker.tun -c release
- name: Pack
run: |
dotnet pack ./linker.libs -c release
dotnet pack ./linker.tunnel -c release
dotnet pack ./linker.tun -c release
- name: Install Nuget
uses: nuget/setup-nuget@v1
@@ -38,3 +40,4 @@ jobs:
run: |
nuget push ./linker.tunnel/bin/release/linker.tunnel.1.2.0.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
nuget push ./linker.libs/bin/release/linker.libs.1.2.0.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol
nuget push ./linker.tun/bin/release/linker.tun.1.2.0.nupkg -Source https://api.nuget.org/v3/index.json -SkipDuplicate -ApiKey ${{ secrets.NUGET_KEY }} -NoSymbol

View File

@@ -0,0 +1,90 @@
---
sidebar_position: 8
---
# 8、集成tun网卡到你的项目
在你的.NET8.0+项目中集成tun网卡适用于`windows``linux`
## 1、windows
[下载wintun](https://www.wintun.net/),选择适合你系统的 `wintun.dll`放到项目根目录然后nuget 安装 `linker.tun`
## 2、linux
请确保你的系统拥有`tuntap`模块,`ifconfig``ip`命令
## 3、编写一个简单的代码
```c#
internal class Program
{
public static LinkerTunDeviceAdapter linkerTunDeviceAdapter;
static void Main(string[] args)
{
linkerTunDeviceAdapter = new LinkerTunDeviceAdapter();
//设置网卡IP包回调
linkerTunDeviceAdapter.SetReadCallback(new LinkerTunDeviceCallback());
//启动网卡
linkerTunDeviceAdapter.SetUp(
"linker" //网卡名称
//windows下使用一个固定guid否则网卡编号会不断递增注册表不断产生新纪录
, Guid.Parse("dc6d4efa-2b53-41bd-a403-f416c9bf7129")
, IPAddress.Parse("192.168.54.2"), 24); //网卡IP和掩码
//设置MTU
linkerTunDeviceAdapter.SetMtu(1420);
//如果存在错误
if (string.IsNullOrWhiteSpace(linkerTunDeviceAdapter.Error))
{
Console.WriteLine(linkerTunDeviceAdapter.Error);
//关闭网卡
linkerTunDeviceAdapter.Shutdown();
}
Console.ReadLine();
}
}
public sealed class LinkerTunDeviceCallback : ILinkerTunDeviceCallback
{
//收到IP数据包
public async Task Callback(LinkerTunDevicPacket packet)
{
ICMPAnswer(packet);
await Task.CompletedTask;
}
private unsafe void ICMPAnswer(LinkerTunDevicPacket packet)
{
//去掉首部表示包长度的4字节
Memory<byte> writableMemory = MemoryMarshal.AsMemory(packet.Packet.Slice(4));
fixed (byte* ptr = writableMemory.Span)
{
//ICMP包且是 Request
if (ptr[9] == 1 && ptr[20] == 8)
{
Console.WriteLine($"ICMP to {new IPAddress(writableMemory.Span.Slice(16, 4))}");
uint dist = BinaryPrimitives.ReadUInt32LittleEndian(writableMemory.Span.Slice(16, 4));
//目的地址变源地址,
*(uint*)(ptr + 16) = *(uint*)(ptr + 12);
//假装是网关回复的
*(uint*)(ptr + 12) = dist;
//计算一次IP头校验和
*(ushort*)(ptr + 10) = 0;
*(ushort*)(ptr + 10) = Program.linkerTunDeviceAdapter.Checksum((ushort*)ptr, 20);
//改为ICMP Reply
*(ushort*)(ptr + 20) = 0;
//计算ICMP校验和
*(ushort*)(ptr + 22) = 0;
*(ushort*)(ptr + 22) = Program.linkerTunDeviceAdapter.Checksum((ushort*)(ptr + 20), (uint)(writableMemory.Length - 20));
//写入网卡回应这个ICMP请求
Program.linkerTunDeviceAdapter.Write(writableMemory);
}
}
}
}
```

View File

@@ -1,9 +0,0 @@
---
sidebar_position: 8
---
# 8、集成虚拟网卡到你的项目
:::tip[说明]
还没写完,敬请期待
:::

View File

@@ -15,8 +15,8 @@
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
<Version>1.2.0</Version>
<AssemblyVersion>1.2.0.2</AssemblyVersion>
<FileVersion>1.2.0.2</FileVersion>
<AssemblyVersion>1.2.0.3</AssemblyVersion>
<FileVersion>1.2.0.3</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>full</DebugType>

View File

@@ -22,8 +22,8 @@
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
<PackageReleaseNotes>snltty service</PackageReleaseNotes>
<AssemblyVersion>1.2.0.2</AssemblyVersion>
<FileVersion>1.2.0.2</FileVersion>
<AssemblyVersion>1.2.0.3</AssemblyVersion>
<FileVersion>1.2.0.3</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

View File

@@ -10,7 +10,7 @@ namespace linker.tun.test
static void Main(string[] args)
{
linkerTunDeviceAdapter = new LinkerTunDeviceAdapter();
linkerTunDeviceAdapter.SetCallback(new LinkerTunDeviceCallback());
linkerTunDeviceAdapter.SetReadCallback(new LinkerTunDeviceCallback());
linkerTunDeviceAdapter.SetUp("linker111"
, Guid.Parse("dc6d4efa-2b53-41bd-a403-f416c9bf7129")
, IPAddress.Parse("192.168.55.2"), 24);

View File

@@ -3,6 +3,9 @@ using System.Net;
namespace linker.tun
{
/// <summary>
/// linker tun网卡适配器自动选择不同平台的实现
/// </summary>
public sealed class LinkerTunDeviceAdapter
{
private ILinkerTunDevice linkerTunDevice;
@@ -27,20 +30,19 @@ namespace linker.tun
}
}
/// <summary>
/// 构造
/// </summary>
/// <param name="linkerTunDeviceCallback">数据包回调</param>
public LinkerTunDeviceAdapter()
{
}
public void SetCallback(ILinkerTunDeviceCallback linkerTunDeviceCallback)
/// <summary>
/// 设置网卡读取回调
/// </summary>
/// <param name="linkerTunDeviceCallback"></param>
public void SetReadCallback(ILinkerTunDeviceCallback linkerTunDeviceCallback)
{
this.linkerTunDeviceCallback = linkerTunDeviceCallback;
}
/// <summary>
/// 开启网卡
/// </summary>
@@ -48,11 +50,12 @@ namespace linker.tun
/// <param name="guid">windows的时候需要一个固定guid不然网卡编号一直递增注册表一直新增记录</param>
/// <param name="address">网卡IP</param>
/// <param name="prefixLength">掩码。一般24即可</param>
public void SetUp(string name, Guid guid, IPAddress address, byte prefixLength)
public bool SetUp(string name, Guid guid, IPAddress address, byte prefixLength)
{
if (Interlocked.CompareExchange(ref starting, 1, 0) == 1)
{
return;
error = $"setup are operating";
return false;
}
Shutdown();
try
@@ -75,14 +78,14 @@ namespace linker.tun
if (linkerTunDevice == null)
{
error = $"{System.Runtime.InteropServices.RuntimeInformation.OSDescription} not support";
return;
return false;
}
linkerTunDevice.Shutdown();
linkerTunDevice.SetUp(address, NetworkHelper.ToGatewayIP(address, prefixLength), prefixLength, out error);
if (string.IsNullOrWhiteSpace(error) == false)
{
return;
return false;
}
cancellationTokenSource = new CancellationTokenSource();
@@ -127,6 +130,7 @@ namespace linker.tun
}
}
});
return true;
}
catch (Exception ex)
{
@@ -136,6 +140,7 @@ namespace linker.tun
{
Interlocked.Exchange(ref starting, 0);
}
return false;
}
/// <summary>
/// 关闭网卡
@@ -152,15 +157,24 @@ namespace linker.tun
error = string.Empty;
}
/// <summary>
/// 设置MTU
/// </summary>
/// <param name="value"></param>
public void SetMtu(int value)
{
linkerTunDevice?.SetMtu(value);
}
/// <summary>
/// 添加NAT转发
/// </summary>
public void SetNat()
{
linkerTunDevice?.SetNat();
}
/// <summary>
/// 移除NAT转发
/// </summary>
public void RemoveNat()
{
linkerTunDevice?.RemoveNat();

View File

@@ -120,7 +120,6 @@ namespace linker.tun
CommandHelper.PowerShell(string.Empty, new string[] { $"Remove-NetNat -Name {Name}" });
}
public void AddRoute(LinkerTunDeviceRouteItem[] ips, IPAddress ip)
{
if (interfaceNumber > 0)

View File

@@ -17,8 +17,8 @@
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
<PackageReleaseNotes>linker tun</PackageReleaseNotes>
<AssemblyVersion>1.2.0.2</AssemblyVersion>
<FileVersion>1.2.0.2</FileVersion>
<AssemblyVersion>1.2.0.3</AssemblyVersion>
<FileVersion>1.2.0.3</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

View File

@@ -17,8 +17,8 @@
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
<PackageReleaseNotes>linker tunnel</PackageReleaseNotes>
<AssemblyVersion>1.2.0.2</AssemblyVersion>
<FileVersion>1.2.0.2</FileVersion>
<AssemblyVersion>1.2.0.3</AssemblyVersion>
<FileVersion>1.2.0.3</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

View File

@@ -3,8 +3,7 @@
<el-form ref="formDom" :model="state.form" :rules="state.rules" label-width="8rem">
<el-form-item label="" label-width="0">
<div class="t-c w-100">
<p>端口为0则不监听</p>
<p>相同分组名之间的客户端相互可见</p>
<p>端口为0则不监听相同分组名之间的客户端相互可见</p>
</div>
</el-form-item>
<el-form-item label="" label-width="0">
@@ -36,10 +35,47 @@
</el-row>
</el-form-item>
<el-form-item label="" label-width="0">
<el-form-item label="接口密码" prop="password">
<el-input type="password" v-model="state.form.password" show-password maxlength="36"
show-word-limit />
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="接口密码" prop="password">
<el-input style="width:42rem" type="password" v-model="state.form.password" show-password maxlength="36" show-word-limit/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label-width="8rem" prop="hasServer">
<el-checkbox v-model="state.form.hasServer" label="我有服务器" size="large" />
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="" label-width="0" v-if="state.form.hasServer">
<el-row>
<el-col :span="12">
<el-form-item label="服务器" prop="server">
<el-input v-model="state.form.server"/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="更新密钥" prop="updaterSecretKey">
<el-input v-model="state.form.updaterSecretKey" maxlength="36" show-word-limit />
</el-form-item>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="" label-width="0" v-if="state.form.hasServer">
<el-row>
<el-col :span="12">
<el-form-item label="穿透密钥" prop="sForwardSecretKey">
<el-input v-model="state.form.sForwardSecretKey" maxlength="36" show-word-limit />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="中继密钥" prop="relaySecretKey">
<el-input v-model="state.form.relaySecretKey" maxlength="36" show-word-limit />
</el-form-item>
</el-col>
</el-row>
</el-form-item>
</el-form>
</div>
@@ -55,11 +91,17 @@ export default {
const state = reactive({
form: {
name: globalData.value.config.Client.Name,
groupid: globalData.value.config.Client.GroupId,
api: globalData.value.config.Client.CApi.ApiPort,
web: globalData.value.config.Client.CApi.WebPort,
password: globalData.value.config.Client.CApi.ApiPassword
name:step.value.form.client.name || globalData.value.config.Client.Name,
groupid: step.value.form.client.groupid ||globalData.value.config.Client.GroupId,
api: step.value.form.client.api ||globalData.value.config.Client.CApi.ApiPort,
web: step.value.form.client.web ||globalData.value.config.Client.CApi.WebPort,
password:step.value.form.client.password || globalData.value.config.Client.CApi.ApiPassword,
hasServer:step.value.form.client.hasServer ||false,
server:step.value.form.client.server ||globalData.value.config.Client.Server,
sForwardSecretKey:step.value.form.client.sForwardSecretKey ||globalData.value.config.Running.SForwardSecretKey,
relaySecretKey:step.value.form.client.relaySecretKey ||(globalData.value.config.Running.Relay.Servers[0] || {SecretKey:'snltty'}).SecretKey,
updaterSecretKey:step.value.form.client.updaterSecretKey ||globalData.value.config.Running.UpdaterSecretKey,
},
rules: {
name: [{ required: true, message: "必填", trigger: "blur" }],
@@ -99,12 +141,23 @@ export default {
formDom.value.validate((valid) => {
if (valid) {
resolve({
Client:{
name: state.form.name,
groupid: state.form.groupid,
api: +state.form.api,
web: +state.form.web,
password: state.form.password
json:{
Client:{
name: state.form.name,
groupid: state.form.groupid,
api: +state.form.api,
web: +state.form.web,
password: state.form.password,
hasServer: state.form.hasServer,
server: state.form.server,
sForwardSecretKey: state.form.sForwardSecretKey,
relaySecretKey: state.form.relaySecretKey,
updaterSecretKey: state.form.updaterSecretKey,
}
},
form:{
client:JSON.parse(JSON.stringify(state.form))
}
});
} else {

View File

@@ -15,8 +15,8 @@ export default {
const step = inject('step');
const state = reactive({
form: {
client: (step.value.json.Common && step.value.json.Common.client) || false,
server: (step.value.json.Common && step.value.json.Common.server) || false,
client:step.value.form.common.client || (step.value.json.Common && step.value.json.Common.client) || false,
server:step.value.form.common.server || (step.value.json.Common && step.value.json.Common.server) || false,
}
});
const handleValidate = (prevJson) => {
@@ -26,14 +26,17 @@ export default {
reject();
}else{
resolve({
Common:{
client: state.form.client,
server: state.form.server,
modes:[
state.form.client ? 'client' : '',
state.form.server ? 'server' : ''
].filter(c=>!!c)
}
json:{
Common:{
client: state.form.client,
server: state.form.server,
modes:[
state.form.client ? 'client' : '',
state.form.server ? 'server' : ''
].filter(c=>!!c)
}
},
form:{common:JSON.parse(JSON.stringify(state.form))}
});
}
});

View File

@@ -1,6 +1,6 @@
<template>
<div>
<el-dialog v-model="state.show" title="初始化配置" width="500" top="2vh">
<el-dialog v-model="state.show" title="初始化配置" width="600" top="2vh">
<div>
<div class="head">
<el-steps :active="step.step" finish-status="success">
@@ -60,7 +60,8 @@ export default {
const step = ref({
step:1,
increment:1,
json:{}
json:{},
form:{server:{},client:{},common:{}}
});
provide('step',step);
const handlePrev = ()=>{
@@ -70,7 +71,9 @@ export default {
const handleNext = ()=>{
step.value.increment = 1;
currentDom.value.handleValidate().then((json)=>{
step.value.json = Object.assign(step.value.json,json);
step.value.json = Object.assign(step.value.json,json.json);
step.value.form = Object.assign(step.value.form,json.form);
console.log(step.value);
step.value.step ++;
}).catch(()=>{
});

View File

@@ -63,14 +63,14 @@ export default {
const state = reactive({
show: false,
form: {
relaySecretKey:globalData.value.config.Server.Relay.SecretKey,
sForwardSecretKey:globalData.value.config.Server.SForward.SecretKey,
servicePort:globalData.value.config.Server.ServicePort,
webPort:globalData.value.config.Server.SForward.WebPort,
tunnelPort1:globalData.value.config.Server.SForward.TunnelPortRange[0],
tunnelPort2:globalData.value.config.Server.SForward.TunnelPortRange[1],
relaySecretKey:step.value.form.server.relaySecretKey || globalData.value.config.Server.Relay.SecretKey,
sForwardSecretKey:step.value.form.server.sForwardSecretKey ||globalData.value.config.Server.SForward.SecretKey,
servicePort:step.value.form.server.servicePort ||globalData.value.config.Server.ServicePort,
webPort:step.value.form.server.webPort ||globalData.value.config.Server.SForward.WebPort,
tunnelPort1:step.value.form.server.tunnelPort1 ||globalData.value.config.Server.SForward.TunnelPortRange[0],
tunnelPort2:step.value.form.server.tunnelPort2 ||globalData.value.config.Server.SForward.TunnelPortRange[1],
updaterSecretKey:globalData.value.config.Server.Updater.SecretKey,
updaterSecretKey:step.value.form.server.updaterSecretKey ||globalData.value.config.Server.Updater.SecretKey,
},
rules: {
relaySecretKey: [{ required: true, message: "必填", trigger: "blur" }],
@@ -138,20 +138,23 @@ export default {
reject();
}else{
resolve({
Server:{
ServicePort: +state.form.servicePort,
Relay:{
SecretKey: state.form.relaySecretKey
},
SForward:{
SecretKey: state.form.sForwardSecretKey,
WebPort: +state.form.webPort,
TunnelPortRange: [+state.form.tunnelPort1, +state.form.tunnelPort2]
},
Updater:{
SecretKey: state.form.updaterSecretKey
}
}
json:{
Server:{
ServicePort: +state.form.servicePort,
Relay:{
SecretKey: state.form.relaySecretKey
},
SForward:{
SecretKey: state.form.sForwardSecretKey,
WebPort: +state.form.webPort,
TunnelPortRange: [+state.form.tunnelPort1, +state.form.tunnelPort2]
},
Updater:{
SecretKey: state.form.updaterSecretKey
}
}
},
form:{server:JSON.parse(JSON.stringify(state.form))}
});
}

View File

@@ -1,6 +1,6 @@
<template>
<div class="status-wrap flex">
<div class="copy"><a href="https://github.com/snltty/linker" target="_blank">snltty©linker v1.2.0.0</a></div>
<div class="copy"><a href="https://github.com/snltty/linker" target="_blank">snltty©linker v1.2.0.3</a></div>
<div class="flex-1"></div>
<div class="api"><Api></Api></div>
<div class="server"><Server></Server></div>

View File

@@ -31,7 +31,7 @@
<el-input v-model="state.form.name" maxlength="12" show-word-limit />
</el-form-item>
<el-form-item label="分组名" prop="groupid">
<el-input v-model="state.form.groupid" maxlength="36" show-word-limit />
<el-input v-model="state.form.groupid" type="password" show-password maxlength="36" show-word-limit />
</el-form-item>
</el-form>
</div>

View File

@@ -30,7 +30,6 @@
{{ item }} / {{ tuntap.list[scope.row.MachineId].Masks[index] }}
</div>
</div>
<div class="t-r">{{ 1<<tuntap.list[scope.row.MachineId].BufferSize}}KB</div>
</div>
</template>
</el-table-column>

View File

@@ -2,12 +2,15 @@
<el-dialog v-model="state.show" :close-on-click-modal="false" append-to=".app-wrap" title="设置虚拟网卡IP" width="420">
<div>
<el-form ref="ruleFormRef" :model="state.ruleForm" :rules="state.rules" label-width="80">
<el-form-item label="缓冲区" prop="BufferSize">
<!-- <el-form-item label="缓冲区" prop="BufferSize">
<el-select v-model="state.ruleForm.BufferSize" placeholder="Select" style="width:12rem">
<el-option v-for="(item,index) in state.bufferSize" :key="index" :label="item" :value="index"/>
</el-select>
</el-form-item> -->
<el-form-item label="">
<div>局域网IP选填不懂是啥的时候不要填</div>
</el-form-item>
<el-form-item label="网卡IP" prop="IP">
<el-form-item label="虚拟网卡IP" prop="IP">
<el-input v-model="state.ruleForm.IP" style="width:12rem" /> / 24
</el-form-item>
<el-form-item label="局域网IP" prop="LanIP">

View File

@@ -25,8 +25,8 @@
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
<PackageReleaseNotes>linker</PackageReleaseNotes>
<AssemblyVersion>1.2.0.2</AssemblyVersion>
<FileVersion>1.2.0.2</FileVersion>
<AssemblyVersion>1.2.0.3</AssemblyVersion>
<FileVersion>1.2.0.3</FileVersion>
</PropertyGroup>

View File

@@ -34,6 +34,21 @@ namespace linker.plugins.config
config.Data.Client.CApi.WebPort = info.Client.Web;
config.Data.Client.CApi.ApiPort = info.Client.Api;
config.Data.Client.CApi.ApiPassword = info.Client.Password;
if (info.Client.HasServer)
{
config.Data.Client.Server = info.Client.Server;
runningConfig.Data.SForwardSecretKey = info.Client.SForwardSecretKey;
runningConfig.Data.UpdaterSecretKey = info.Client.UpdaterSecretKey;
foreach (var item in runningConfig.Data.Relay.Servers)
{
item.SecretKey = info.Client.RelaySecretKey;
}
foreach (var item in runningConfig.Data.Tunnel.Servers)
{
item.Host = info.Client.Server;
}
}
}
if (info.Common.Modes.Contains("server"))
{
@@ -50,6 +65,9 @@ namespace linker.plugins.config
config.Data.Common.Modes = info.Common.Modes;
config.Data.Common.Install = true;
config.Data.Update();
runningConfig.Data.Update();
return true;
}
}
@@ -67,6 +85,12 @@ namespace linker.plugins.config
public int Api { get; set; }
public int Web { get; set; }
public string Password { get; set; }
public bool HasServer { get; set; }
public string Server { get; set; }
public string SForwardSecretKey { get; set; }
public string RelaySecretKey { get; set; }
public string UpdaterSecretKey { get; set; }
}
public sealed class ConfigInstallServerInfo
{

View File

@@ -58,7 +58,7 @@ namespace linker.plugins.tuntap
}
};
linkerTunDeviceAdapter.SetCallback(tuntapProxy);
linkerTunDeviceAdapter.SetReadCallback(tuntapProxy);
linkerTunDeviceAdapter.Shutdown();
AppDomain.CurrentDomain.ProcessExit += (s, e) => Shutdown();
Console.CancelKeyPress += (s, e) => Shutdown();