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

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

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

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

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

核心代码

  1  [SupportedOSPlatform("windows")]   2  public class WinTunDriveHostedService : TunDriveHostedService   3  {   4      private readonly static string DriverPath = AppDomain.CurrentDomain.BaseDirectory + "Drivers";   5      private const string AdapterKey = "SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}";   6      private const string ConnectionKey = "SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}";   7    8    9      public const int TAP_WIN_IOCTL_GET_MAC = 1;  10      public const int TAP_WIN_IOCTL_GET_VERSION = 2;  11      public const int TAP_WIN_IOCTL_GET_MTU = 3;  12      public const int TAP_WIN_IOCTL_GET_INFO = 4;  13      public const int TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT = 5;  14      public const int TAP_WIN_IOCTL_SET_MEDIA_STATUS = 6;  15      public const int TAP_WIN_IOCTL_CONFIG_DHCP_MASQ = 7;  16      public const int TAP_WIN_IOCTL_GET_LOG_LINE = 8;  17      public const int TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT = 9;  18      public const int TAP_WIN_IOCTL_CONFIG_TUN = 10;  19   20      public const uint FILE_ATTRIBUTE_SYSTEM = 0x4;  21      public const uint FILE_FLAG_OVERLAPPED = 0x40000000;  22      public const uint METHOD_BUFFERED = 0;  23      public const uint FILE_ANY_ACCESS = 0;  24      public const uint FILE_DEVICE_UNKNOWN = 0x22;  25      public WinTunDriveHostedService(IOptions<TunDriveConfig> tunDriveConfigOptions, ILogger<WinTunDriveHostedService> logger) : base(tunDriveConfigOptions, logger)  26      {  27      }  28      [DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]  29      public static extern bool DeviceIoControl(SafeHandle device, uint IoControlCode, IntPtr InBuffer, uint InBufferSize, IntPtr OutBuffer, uint OutBufferSize, ref uint BytesReturned, IntPtr Overlapped);  30   31   32      protected override FileStream OpenDrive()  33      {  34          var className = InstallOrGetClassNameDrive();  35          var safeFileHandle = System.IO.File.OpenHandle($@"\.\Global\{className}.tap", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, FileOptions.Asynchronous);  36          return new FileStream(safeFileHandle, FileAccess.ReadWrite, 1500);  37      }  38      protected virtual string InstallOrGetClassNameDrive()  39      {  40          using (RegistryKey registryKey = Registry.LocalMachine.OpenSubKey(ConnectionKey))  41          {  42              var names = registryKey.GetSubKeyNames();  43              foreach (var name in names)  44              {  45                  using (var connectionRegistryKey = registryKey.OpenSubKey(name).OpenSubKey("Connection"))  46                  {  47                      if (connectionRegistryKey != null && connectionRegistryKey.GetValue("Name").ToString() == TunDriveConfig.TunDriveName)  48                      {  49                          return name;  50                      }  51                  }  52              }  53   54              Directory.CreateDirectory(DriverPath);  55              ZipArchive zipArchive = new ZipArchive(typeof(WinTunDriveHostedService).Assembly.GetManifestResourceStream($"RemoteNetwork.{(Environment.Is64BitOperatingSystem ? "amd64" : "i386")}.zip"), ZipArchiveMode.Read);  56              foreach (ZipArchiveEntry entry in zipArchive.Entries)  57              {  58                  entry.ExtractToFile(Path.Combine(DriverPath, entry.FullName), overwrite: true);  59              }  60              StartProcess(Path.Combine(DriverPath, "tapinstall.exe"), $"install OemVista.inf TAP0901", "runas", DriverPath);  61              foreach (var name in registryKey.GetSubKeyNames())  62              {  63                  if (!names.Contains(name))  64                  {  65                      using (var connectionRegistryKey = registryKey.OpenSubKey(name).OpenSubKey("Connection"))  66                      {  67                          if (connectionRegistryKey != null)  68                          {  69                              StartProcess("netsh", @$"interface set interface name=""{connectionRegistryKey.GetValue("Name")}"" newname=""{TunDriveConfig.TunDriveName}""");  70                              return name;  71                          }  72                      }  73                  }  74              }  75              return string.Empty;  76          }  77      }  78      private static int ParseIP(string address)  79      {  80          byte[] addressBytes = address.Split('.').Select(s => byte.Parse(s)).ToArray();  81          return addressBytes[0] | (addressBytes[1] << 8) | (addressBytes[2] << 16) | (addressBytes[3] << 24);  82      }  83      protected override void ConfigIP(string ip, string netmask)  84      {  85          StartProcess("netsh", $"interface ip set address name="{TunDriveConfig.TunDriveName}" source=static addr={ip} mask={netmask} gateway=none");  86          IntPtr intPtr = Marshal.AllocHGlobal(12);  87          Marshal.WriteInt32(intPtr, 0, ParseIP(ip));  88          Marshal.WriteInt32(intPtr, 4, 0);  89          Marshal.WriteInt32(intPtr, 8,0);  90          uint lpBytesReturned = 0;  91          bool result = DeviceIoControl(TunStream.SafeFileHandle, 2228264, intPtr, 12u, intPtr, 12u, ref lpBytesReturned, IntPtr.Zero);  92          Marshal.FreeHGlobal(intPtr);  93      }  94      private static uint CTL_CODE(uint iDeviceType, uint iFunction, uint iMethod, uint iAccess)  95      {  96          return ((iDeviceType << 16) | (iAccess << 14) | (iFunction << 2) | iMethod);  97      }  98      public override bool ConnectionState(bool connection)  99      { 100          uint Length = 0; 101          IntPtr cconfig = Marshal.AllocHGlobal(4); 102          Marshal.WriteInt32(cconfig, connection ? 1 : 0); 103  104          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); 105          StartProcess("netsh", $"netsh interface ipv4 set subinterface "{TunDriveConfig.TunDriveName}" mtu="1400" store=persistent"); 106          return b; 107      } 108  }

 

liunx 核心代码

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

 1 public class TunNetWorkFrameHostedService : BackgroundService  2 {  3     private  readonly string exchangeHostName = "";  4     private readonly int P2PPort = 61000;  5     protected readonly ILogger<TunNetWorkFrameHostedService> _logger;  6     public static TunNetWorkFrameHostedService Instance { get; private set; }  7     private readonly UdpClient udpClient;  8     private readonly System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(0, 0);  9     public TunNetWorkFrameHostedService(ILogger<TunNetWorkFrameHostedService> logger, IOptions<TunDriveConfig> tunDriveConfigOptions) 10     { 11         exchangeHostName = tunDriveConfigOptions.Value.DataExchangeHostName; 12         _logger = logger; 13         Instance = this; 14         udpClient = new UdpClient(0); if (Environment.OSVersion.Platform == PlatformID.Win32NT) 15         { 16             const int SIP_UDP_CONNRESET = -1744830452; 17             udpClient.Client.IOControl(SIP_UDP_CONNRESET, new byte[] { 0, 0, 0, 0 }, null); 18         } 19     } 20  21  22     protected override async Task ExecuteAsync(CancellationToken stoppingToken) 23     { 24         udpClient.BeginReceive(ReceiveCallback, udpClient); 25         while (!stoppingToken.IsCancellationRequested) 26         { 27             await udpClient.SendAsync(TunDriveHostedService.Instance.Id, exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false); 28             await Task.Delay(1000*30, stoppingToken).ConfigureAwait(false); 29         } 30     } 31     void ReceiveCallback(IAsyncResult ar) 32     { 33         System.Net.IPEndPoint remoteEndPoint = new System.Net.IPEndPoint(0, 0); 34         byte[] bytes = null; 35         try 36         { 37  38             bytes = udpClient.EndReceive(ar, ref remoteEndPoint); 39  40         } 41         finally 42         { 43             udpClient.BeginReceive(ReceiveCallback, udpClient); 44         } 45         if (bytes.Length == 4) 46         { 47             return; 48         } 49         if (bytes.Length == 5) 50         { 51             if (bytes[0] == 2) 52             { 53                 P2PUDPSocketHostedService.Instance.TestP2P(bytes.Skip(1).ToArray(),false); 54             } 55             return; 56         } 57         58         TunDriveHostedService.Instance.WriteFrameBuffer(bytes); 59     } 60     public virtual async Task WriteFrameBufferAsync(Memory<byte> buffer, CancellationToken stoppingToken) 61     {  62         var destId = BitConverter.ToInt32(buffer.Slice(16, 4).ToArray(), 0); 63  64        var tunNetWorkFrameSend= P2PUDPSocketHostedService.Instance.GetP2PClient(buffer.Slice(16, 4).ToArray()); 65         if (tunNetWorkFrameSend != null) 66         { 67             await tunNetWorkFrameSend.SendAsync(buffer, stoppingToken).ConfigureAwait(false); 68             return; 69         } 70         var bytes = new byte[buffer.Length + 8]; 71         buffer.Slice(12, 8).CopyTo(bytes); 72         Array.Copy(buffer.ToArray(), 0,bytes,8,buffer.Length); 73         await udpClient.SendAsync(bytes, exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false); 74         //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]; 75         //var sourceId = BitConverter.ToInt32(buffer.Slice(12, 4).ToArray(), 0); 76         //_logger.LogInformation($"{sourceId} 发送到{destId}"); 77     } 78     /// <summary> 79     /// 发送打洞请求 80     /// </summary> 81     /// <param name="destId"></param> 82     /// <param name="stoppingToken"></param> 83     /// <returns></returns> 84     public virtual async Task SendP2PRequestAsync(byte[] destId, CancellationToken stoppingToken) 85     { 86         using (MemoryStream memoryStream = new MemoryStream()) { 87             memoryStream.Write(TunDriveHostedService.Instance.Id); 88             memoryStream.Write(destId); 89             memoryStream.WriteByte(2); 90             memoryStream.Write(TunDriveHostedService.Instance.Id); 91             await udpClient.SendAsync(memoryStream.ToArray(), exchangeHostName, P2PPort, stoppingToken).ConfigureAwait(false); 92         } 93          94     } 95 }

View Code

 

以下是远程桌面的效果

客户端运行

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

 打洞成功

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

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

 测速

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

 

代码地址

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

发表评论

相关文章