• .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 tunDriveConfigOptions, ILogger 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 核心代码

     1 public class TunNetWorkFrameHostedService : BackgroundService
     2 {
     3     private  readonly string exchangeHostName = "";
     4     private readonly int P2PPort = 61000;
     5     protected readonly ILogger _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 logger, IOptions 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     /// 
    79     /// 发送打洞请求
    80     /// 
    81     /// 
    82     /// 
    83     /// 
    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

     

    以下是远程桌面的效果

    客户端运行

     打洞成功

     测速

     

    代码地址

    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

  • 相关阅读:
    ansible自动化运维工具及常见模块的使用
    LNMP和数据库的安装
    MongoDB增删改查操作
    CAPL编程学习笔记--关于on 事件的详细解释
    2019 ICPC银川 个人题解
    前端常用的CDN
    一文带你了解Spring中的事务管理
    第15章 基数法则
    Idea上传项目到gitlab并创建使用分支
    数据库——知识1
  • 原文地址:https://www.cnblogs.com/dotnet-org-cn/p/18239999