LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

.NET借助虚拟网卡实现一个简单异地组网工具

freeflydom
2024年6月11日 9:27 本文热度 1906

由于工作需要,经常需要远程客户的服务器,但是并不是所有服务器都能开外网端口,使用向日葵等软件终究还是不太方便,于是找了很多工具,包括zerotier 等,但是由于服务器在国外等有时候还不同,

于是开始自己想办法研究一个属于自己的组网工具,最后找到snltty大佬的 https://github.com/snltty/p2p-tunnel ,学习后发现是基于tun2socks实现的,

tun2socks 的优点是 把虚拟网卡的数据都打包到socket代理了,但是过滤了ping (ICmp)的包,他自行返回了 成功,这不是我要的效果

于是看了一下tun2socks 的实现,是基于tun/tap实现的,于是研究了一下,手动基于tun/tap实现了一个简易的

核心代码

[SupportedOSPlatform("windows")]
 public class WinTunDriveHostedService : TunDriveHostedService
 {
     private readonly static string DriverPath = AppDomain.CurrentDomain.BaseDirectory + "Drivers";
     private const string AdapterKey = "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
     private const string ConnectionKey = "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";




     public const int TAP_WIN_IOCTL_GET_MAC = 1;
     public const int TAP_WIN_IOCTL_GET_VERSION = 2;
     public const int TAP_WIN_IOCTL_GET_MTU = 3;
     public const int TAP_WIN_IOCTL_GET_INFO = 4;
     public const int TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT = 5;
     public const int TAP_WIN_IOCTL_SET_MEDIA_STATUS = 6;
     public const int TAP_WIN_IOCTL_CONFIG_DHCP_MASQ = 7;
     public const int TAP_WIN_IOCTL_GET_LOG_LINE = 8;
     public const int TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT = 9;
     public const int TAP_WIN_IOCTL_CONFIG_TUN = 10;


     public const uint FILE_ATTRIBUTE_SYSTEM = 0x4;
     public const uint FILE_FLAG_OVERLAPPED = 0x40000000;
     public const uint METHOD_BUFFERED = 0;
     public const uint FILE_ANY_ACCESS = 0;
     public const uint FILE_DEVICE_UNKNOWN = 0x22;
     public WinTunDriveHostedService(IOptions<TunDriveConfig> tunDriveConfigOptions, ILogger<WinTunDriveHostedService> logger) : base(tunDriveConfigOptions, logger)
     {
     }
     [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
     public static extern bool DeviceIoControl(SafeHandle device, uint IoControlCode, IntPtr InBuffer, uint InBufferSize, IntPtr OutBuffer, uint OutBufferSize, ref uint BytesReturned, IntPtr Overlapped);




     protected override FileStream OpenDrive()
     {
         var className = InstallOrGetClassNameDrive();
         var safeFileHandle = System.IO.File.OpenHandle($@"\\.\\Global\\{className}.tap", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, FileOptions.Asynchronous);
         return new FileStream(safeFileHandle, FileAccess.ReadWrite, 1500);
     }
     protected virtual string InstallOrGetClassNameDrive()
     {
         using (RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(ConnectionKey))
         {
             var names = registryKey.GetSubKeyNames();
             foreach (var name in names)
             {
                 using (var connectionRegistryKey = registryKey.OpenSubKey(name).OpenSubKey("Connection"))
                 {
                     if (connectionRegistryKey != null && connectionRegistryKey.GetValue("Name").ToString() == TunDriveConfig.TunDriveName)
                     {
                         return name;
                     }
                 }
             }


             Directory.CreateDirectory(DriverPath);
             ZipArchive zipArchive = new ZipArchive(typeof(WinTunDriveHostedService).Assembly.GetManifestResourceStream($"RemoteNetwork.{(Environment.Is64BitOperatingSystem ? "amd64" : "i386")}.zip"), ZipArchiveMode.Read);
             foreach (ZipArchiveEntry entry in zipArchive.Entries)
             {
                 entry.ExtractToFile(Path.Combine(DriverPath, entry.FullName), overwrite: true);
             }
             StartProcess(Path.Combine(DriverPath, "tapinstall.exe"), $"install OemVista.inf TAP0901", "runas", DriverPath);
             foreach (var name in registryKey.GetSubKeyNames())
             {
                 if (!names.Contains(name))
                 {
                     using (var connectionRegistryKey = registryKey.OpenSubKey(name).OpenSubKey("Connection"))
                     {
                         if (connectionRegistryKey != null)
                         {
                             StartProcess("netsh", @$"interface set interface name=""{connectionRegistryKey.GetValue("Name")}"" newname=""{TunDriveConfig.TunDriveName}""");
                             return name;
                         }
                     }
                 }
             }
             return string.Empty;
         }
     }
     private static int ParseIP(string address)
     {
         byte[] addressBytes = address.Split('.').Select(s => byte.Parse(s)).ToArray();
         return addressBytes[0] | (addressBytes[1] << 8) | (addressBytes[2] << 16) | (addressBytes[3] << 24);
     }
     protected override void ConfigIP(string ip, string netmask)
     {
         StartProcess("netsh", $"interface ip set address name=\"{TunDriveConfig.TunDriveName}\" source=static addr={ip} mask={netmask} gateway=none");
         IntPtr intPtr = Marshal.AllocHGlobal(12);
         Marshal.WriteInt32(intPtr, 0, ParseIP(ip));
         Marshal.WriteInt32(intPtr, 4, 0);
         Marshal.WriteInt32(intPtr, 8,0);
         uint lpBytesReturned = 0;
         bool result = DeviceIoControl(TunStream.SafeFileHandle, 2228264, intPtr, 12u, intPtr, 12u, ref lpBytesReturned, IntPtr.Zero);
         Marshal.FreeHGlobal(intPtr);
     }
     private static uint CTL_CODE(uint iDeviceType, uint iFunction, uint iMethod, uint iAccess)
     {
         return ((iDeviceType << 16) | (iAccess << 14) | (iFunction << 2) | iMethod);
     }
     public override bool ConnectionState(bool connection)
     {
         uint Length = 0;
         IntPtr cconfig = Marshal.AllocHGlobal(4);
         Marshal.WriteInt32(cconfig, connection ? 1 : 0);


         var b = DeviceIoControl(TunStream.SafeFileHandle, CTL_CODE(FILE_DEVICE_UNKNOWN, TAP_WIN_IOCTL_SET_MEDIA_STATUS, METHOD_BUFFERED, FILE_ANY_ACCESS), cconfig, 4, cconfig, 4, ref Length, IntPtr.Zero);
         StartProcess("netsh", $"netsh interface ipv4 set subinterface \"{TunDriveConfig.TunDriveName}\" mtu=\"1400\" store=persistent");
         return b;
     }
 }

liunx 核心代码

public class TunNetWorkFrameHostedService : BackgroundService
{
    private  readonly string exchangeHostName = "";
    private readonly int P2PPort = 61000;
    protected readonly ILogger<TunNetWorkFrameHostedService> _logger;
    public static TunNetWorkFrameHostedService Instance { get; private set; }
    private readonly UdpClient udpClient;
    private readonly System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(0, 0);
    public TunNetWorkFrameHostedService(ILogger<TunNetWorkFrameHostedService> logger, IOptions<TunDriveConfig> tunDriveConfigOptions)
    {
        exchangeHostName = tunDriveConfigOptions.Value.DataExchangeHostName;
        _logger = logger;
        Instance = this;
        udpClient = new UdpClient(0); if (Environment.OSVersion.Platform == PlatformID.Win32NT)
        {
            const int SIP_UDP_CONNRESET = -1744830452;
            udpClient.Client.IOControl(SIP_UDP_CONNRESET, new byte[] { 0, 0, 0, 0 }, null);
        }
    }




    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        udpClient.BeginReceive(ReceiveCallback, udpClient);
        while (!stoppingToken.IsCancellationRequested)
        {
            await udpClient.SendAsync(TunDriveHostedService.Instance.Id, exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false);
            await Task.Delay(1000*30, stoppingToken).ConfigureAwait(false);
        }
    }
    void ReceiveCallback(IAsyncResult ar)
    {
        System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(0, 0);
        byte[] bytes = null;
        try
        {


            bytes = udpClient.EndReceive(ar, ref remoteEndPoint);


        }
        finally
        {
            udpClient.BeginReceive(ReceiveCallback, udpClient);
        }
        if (bytes.Length == 4)
        {
            return;
        }
        if (bytes.Length == 5)
        {
            if (bytes[0] == 2)
            {
                P2PUDPSocketHostedService.Instance.TestP2P(bytes.Skip(1).ToArray(),false);
            }
            return;
        }


        TunDriveHostedService.Instance.WriteFrameBuffer(bytes);
    }
    public virtual async Task WriteFrameBufferAsync(Memory<byte> buffer, CancellationToken stoppingToken)
    {
        var destId = BitConverter.ToInt32(buffer.Slice(16, 4).ToArray(), 0);


       var tunNetWorkFrameSend= P2PUDPSocketHostedService.Instance.GetP2PClient(buffer.Slice(16, 4).ToArray());
        if (tunNetWorkFrameSend != null)
        {
            await tunNetWorkFrameSend.SendAsync(buffer, stoppingToken).ConfigureAwait(false);
            return;
        }
        var bytes = new byte[buffer.Length + 8];
        buffer.Slice(12, 8).CopyTo(bytes);
        Array.Copy(buffer.ToArray(), 0,bytes,8,buffer.Length);
        await udpClient.SendAsync(bytes, exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false);
        //var destId = BitConverter.ToInt32(buffer.Slice(16, 4).ToArray(), 0);// string.Join(".", buffer.Slice(16, 4).ToArray());// span[16] << 24 | span[17] << 16 | span[18] << 8 | span[19];
        //var sourceId = BitConverter.ToInt32(buffer.Slice(12, 4).ToArray(), 0);
        //_logger.LogInformation($"{sourceId} 发送到{destId}");
    }
    /// <summary>
    /// 发送打洞请求
    /// </summary>
    /// <param name="destId"></param>
    /// <param name="stoppingToken"></param>
    /// <returns></returns>
    public virtual async Task SendP2PRequestAsync(byte[] destId, CancellationToken stoppingToken)
    {
        using (MemoryStream memoryStream = new MemoryStream()) {
            memoryStream.Write(TunDriveHostedService.Instance.Id);
            memoryStream.Write(destId);
            memoryStream.WriteByte(2);
            memoryStream.Write(TunDriveHostedService.Instance.Id);
            await udpClient.SendAsync(memoryStream.ToArray(), exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false);
        }


    }
}

以下是远程桌面的效果

客户端运行

 打洞成功

 测速

 

代码地址

https://github.com/hn-lyf/RemoteNetwork

测试客户端

 

 https://files.cnblogs.com/files/dotnet-org-cn/linux-x64.zip?t=1717937932&download=true

https://files.cnblogs.com/files/dotnet-org-cn/win-x64.zip?t=1717937932&download=true

https://files.cnblogs.com/files/dotnet-org-cn/win-x86.zip?t=1717937932&download=true



该文章在 2024/6/11 9:27:44 编辑过
关键字查询
相关文章
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved