diff --git a/linker.sln b/linker.sln index 2d2273fc..f7dd5a43 100644 --- a/linker.sln +++ b/linker.sln @@ -13,6 +13,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.service", "linker.se EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.tunnel", "linker.tunnel\linker.tunnel.csproj", "{AFADE8D6-AB00-456B-9F43-53BC95B7B608}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.tun", "linker.tun\linker.tun.csproj", "{0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linker.tun.test", "linker.tun.test\linker.tun.test.csproj", "{4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -83,6 +87,30 @@ Global {AFADE8D6-AB00-456B-9F43-53BC95B7B608}.Release|x64.Build.0 = Release|Any CPU {AFADE8D6-AB00-456B-9F43-53BC95B7B608}.Release|x86.ActiveCfg = Release|Any CPU {AFADE8D6-AB00-456B-9F43-53BC95B7B608}.Release|x86.Build.0 = Release|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Debug|x64.ActiveCfg = Debug|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Debug|x64.Build.0 = Debug|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Debug|x86.ActiveCfg = Debug|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Debug|x86.Build.0 = Debug|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Release|Any CPU.Build.0 = Release|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Release|x64.ActiveCfg = Release|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Release|x64.Build.0 = Release|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Release|x86.ActiveCfg = Release|Any CPU + {0DE134E0-7CD8-4DCF-8D2A-325CEBE5895F}.Release|x86.Build.0 = Release|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Debug|x64.ActiveCfg = Debug|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Debug|x64.Build.0 = Debug|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Debug|x86.ActiveCfg = Debug|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Debug|x86.Build.0 = Debug|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Release|Any CPU.Build.0 = Release|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Release|x64.ActiveCfg = Release|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Release|x64.Build.0 = Release|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Release|x86.ActiveCfg = Release|Any CPU + {4A660D3B-76DE-4E6F-9E90-90BA0DBE906A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/linker.tun.test/Program.cs b/linker.tun.test/Program.cs new file mode 100644 index 00000000..c10ae1c8 --- /dev/null +++ b/linker.tun.test/Program.cs @@ -0,0 +1,18 @@ +using System.Net; + +namespace linker.tun.test +{ + internal class Program + { + static void Main(string[] args) + { + + LinkerWinTunDevice linkerWinTunDevice = new LinkerWinTunDevice("linker111", Guid.Parse("d9f71e4f-ba49-4cba-be5e-69a5694df8cb")); + linkerWinTunDevice.SetUp(IPAddress.Parse("192.168.55.2"), IPAddress.Parse("192.168.55.1"), 24, out string error); + + Console.WriteLine(error); + + Console.ReadLine(); + } + } +} diff --git a/linker.tun.test/linker.tun.test.csproj b/linker.tun.test/linker.tun.test.csproj new file mode 100644 index 00000000..998cba7c --- /dev/null +++ b/linker.tun.test/linker.tun.test.csproj @@ -0,0 +1,15 @@ + + + + Exe + net8.0 + enable + enable + true + + + + + + + diff --git a/linker.tun/ILinkerTunDevice.cs b/linker.tun/ILinkerTunDevice.cs new file mode 100644 index 00000000..ce61d0dd --- /dev/null +++ b/linker.tun/ILinkerTunDevice.cs @@ -0,0 +1,26 @@ +using System.Net; + +namespace linker.tun +{ + public interface ILinkerTunDevice + { + public string Name { get; } + public bool Running { get; } + + public bool SetUp(IPAddress address, IPAddress gateway, byte prefixLength, out string error); + public void Shutdown(); + + public void AddRoute(LinkerTunDeviceRouteItem[] ips, IPAddress ip); + public void DelRoute(LinkerTunDeviceRouteItem[] ip); + + public ReadOnlyMemory Read(); + public bool Write(ReadOnlyMemory buffer); + } + + + public sealed class LinkerTunDeviceRouteItem + { + public IPAddress Address { get; } + public byte Mask { get; } + } +} diff --git a/linker.tun/LinkerLinuxTunDevice.cs b/linker.tun/LinkerLinuxTunDevice.cs new file mode 100644 index 00000000..95b99ac4 --- /dev/null +++ b/linker.tun/LinkerLinuxTunDevice.cs @@ -0,0 +1,147 @@ +using linker.libs; +using Microsoft.Win32.SafeHandles; +using System.Net; +using System.Runtime.InteropServices; +using System.Text; + +namespace linker.tun +{ + public sealed class LinkerLinuxTunDevice : ILinkerTunDevice + { + + private string name = string.Empty; + public string Name => name; + public bool Running => fs != null; + + private string interfaceLinux = string.Empty; + private FileStream fs = null; + + public LinkerLinuxTunDevice(string name) + { + this.name = name; + } + + public bool SetUp(IPAddress address, IPAddress gateway, byte prefixLength, out string error) + { + error = string.Empty; + if (fs != null) + { + error = ($"Adapter already exists"); + return false; + } + + CommandHelper.Linux(string.Empty, new string[] { + $"ip tuntap add mode tun dev {Name}", + $"ip addr del {address}/{prefixLength} dev {Name}", + $"ip addr add {address}/{prefixLength} dev {Name}", + $"ip link set dev {Name} up" + }); + + string str = CommandHelper.Linux(string.Empty, new string[] { $"ifconfig" }); + if (str.Contains(Name) == false) + { + error = CommandHelper.Linux(string.Empty, new string[] { $"ip tuntap add mode tun dev {Name}" }); + return false; + } + + interfaceLinux = GetLinuxInterfaceNum(); + + SafeFileHandle safeFileHandle = File.OpenHandle("/dev/net/tun", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, FileOptions.Asynchronous); + + byte[] ifreqFREG0 = Encoding.ASCII.GetBytes(this.Name); + Array.Resize(ref ifreqFREG0, 16); + byte[] ifreqFREG1 = { 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + byte[] ifreq = BytesPlusBytes(ifreqFREG0, ifreqFREG1); + Ioctl(safeFileHandle, 1074025674, ifreq); + fs = new FileStream(safeFileHandle, FileAccess.ReadWrite, 1500); + + return true; + } + public void Shutdown() + { + if (fs != null) + { + interfaceLinux = string.Empty; + fs.Close(); + fs.Dispose(); + fs = null; + } + CommandHelper.Linux(string.Empty, new string[] { $"ip tuntap del mode tun dev {Name}" }); + } + + public void AddRoute(LinkerTunDeviceRouteItem[] ips, IPAddress ip) + { + string[] commands = ips.Select(item => + { + uint maskValue = NetworkHelper.MaskValue(item.Mask); + IPAddress _ip = NetworkHelper.ToNetworkIp(item.Address, maskValue); + + return $"ip route add {_ip}/{item.Mask} via {ip} dev {Name} metric 1 "; + }).ToArray(); + if (commands.Length > 0) + { + CommandHelper.Linux(string.Empty, commands); + } + } + public void DelRoute(LinkerTunDeviceRouteItem[] ip) + { + string[] commands = ip.Select(item => + { + uint maskValue = NetworkHelper.MaskValue(item.Mask); + IPAddress _ip = NetworkHelper.ToNetworkIp(item.Address, maskValue); + return $"ip route del {_ip}/{item.Mask}"; + }).ToArray(); + CommandHelper.Linux(string.Empty, commands); + } + + + private byte[] buffer = new byte[2 * 1024]; + public ReadOnlyMemory Read() + { + int length = fs.Read(buffer, 0, buffer.Length); + return buffer.AsMemory(0, length); + } + public bool Write(ReadOnlyMemory buffer) + { + fs.Write(buffer.Span); + return true; + } + + private string GetLinuxInterfaceNum() + { + string output = CommandHelper.Linux(string.Empty, new string[] { "ip route" }); + foreach (var item in output.Split(Environment.NewLine)) + { + if (item.StartsWith("default via")) + { + var strs = item.Split(' '); + for (int i = 0; i < strs.Length; i++) + { + if (strs[i] == "dev") + { + return strs[i + 1]; + } + } + } + } + return string.Empty; + } + + + [DllImport("libc.so.6", EntryPoint = "ioctl", SetLastError = true)] + private static extern int Ioctl(SafeHandle device, UInt32 request, byte[] dat); + private byte[] BytesPlusBytes(byte[] A, byte[] B) + { + byte[] ret = new byte[A.Length + B.Length - 1 + 1]; + int k = 0; + for (var i = 0; i <= A.Length - 1; i++) + ret[i] = A[i]; + k = A.Length; + for (var i = k; i <= ret.Length - 1; i++) + ret[i] = B[i - k]; + return ret; + } + + + } +} diff --git a/linker.tun/LinkerWinTunDevice.cs b/linker.tun/LinkerWinTunDevice.cs new file mode 100644 index 00000000..a874f623 --- /dev/null +++ b/linker.tun/LinkerWinTunDevice.cs @@ -0,0 +1,289 @@ +using linker.libs; +using System.Buffers.Binary; +using System.Net; +using System.Net.NetworkInformation; +using System.Runtime.InteropServices; + +namespace linker.tun +{ + public sealed class LinkerWinTunDevice : ILinkerTunDevice + { + private string name = string.Empty; + public string Name => name; + public bool Running => session != 0; + + private IntPtr waitHandle = IntPtr.Zero, adapter = IntPtr.Zero, session = IntPtr.Zero; + private Guid guid; + private int interfaceNumber = 0; + + public LinkerWinTunDevice(string name, Guid guid) + { + this.name = name; + this.guid = guid; + } + + public bool SetUp(IPAddress address, IPAddress gateway, byte prefixLength, out string error) + { + error = string.Empty; + if (adapter != 0) + { + error = ($"Adapter already exists"); + return false; + } + + adapter = WintunCreateAdapter(name, name, ref guid); + if (adapter == 0) + { + error = ($"Failed to create adapter {Marshal.GetLastWin32Error():x2}"); + return false; + } + uint version = WintunGetRunningDriverVersion(); + session = WintunStartSession(adapter, 0x400000); + if (session == 0) + { + error = ($"Failed to create adapter"); + return false; + } + + waitHandle = WintunGetReadWaitEvent(session); + + WintunGetAdapterLUID(adapter, out ulong luid); + { + MIB_UNICASTIPADDRESS_ROW AddressRow = default; + InitializeUnicastIpAddressEntry(ref AddressRow); + AddressRow.sin_family = 2; + AddressRow.sin_addr = BinaryPrimitives.ReadUInt32LittleEndian(address.GetAddressBytes()); + AddressRow.OnLinkPrefixLength = prefixLength; + AddressRow.DadState = 4; + AddressRow.InterfaceLuid = luid; + uint LastError = CreateUnicastIpAddressEntry(ref AddressRow); + if (LastError != 0) throw new InvalidOperationException(); + } + { + MIB_IPFORWARD_ROW2 row = default; + InitializeIpForwardEntry(ref row); + row.InterfaceLuid = luid; + row.PrefixLength = 0; + row.si_family = 2; + row.NextHop_si_family = 2; + row.sin_addr = 0; + row.NextHop_sin_addr = BinaryPrimitives.ReadUInt32LittleEndian(gateway.GetAddressBytes()); + uint LastError = CreateIpForwardEntry2(ref row); + if (LastError != 0) throw new InvalidOperationException(); + } + + GetWindowsInterfaceNum(); + + return true; + } + public void Shutdown() + { + if (session != 0) + { + WintunEndSession(session); + WintunCloseAdapter(adapter); + WintunDeleteDriver(); + } + session = 0; + adapter = 0; + interfaceNumber = 0; + } + + public void AddRoute(LinkerTunDeviceRouteItem[] ips, IPAddress ip) + { + if (interfaceNumber > 0) + { + string[] commands = ips.Select(item => + { + uint maskValue = NetworkHelper.MaskValue(item.Mask); + IPAddress mask = NetworkHelper.GetMaskIp(maskValue); + IPAddress _ip = NetworkHelper.ToNetworkIp(item.Address, maskValue); + + return $"route add {_ip} mask {mask} {ip} metric 5 if {interfaceNumber}"; + }).ToArray(); + if (commands.Length > 0) + { + CommandHelper.Windows(string.Empty, commands); + } + } + } + public void DelRoute(LinkerTunDeviceRouteItem[] ip) + { + string[] commands = ip.Select(item => + { + uint maskValue = NetworkHelper.MaskValue(item.Mask); + IPAddress mask = NetworkHelper.GetMaskIp(maskValue); + IPAddress _ip = NetworkHelper.ToNetworkIp(item.Address, maskValue); + return $"route delete {_ip}"; + }).ToArray(); + if (commands.Length > 0) + { + CommandHelper.Windows(string.Empty, commands.ToArray()); + } + } + + + private byte[] buffer = new byte[2 * 1024]; + public unsafe ReadOnlyMemory Read() + { + if (session == 0) return Helper.EmptyArray; + for (; ; ) + { + IntPtr packet = WintunReceivePacket(session, out var packetSize); + if (packet != 0) + { + new Span((byte*)packet, (int)packetSize).CopyTo(buffer.AsSpan(0, (int)packetSize)); + WintunReleaseReceivePacket(session, packet); + return buffer.AsMemory(0, (int)packetSize); + } + else + { + if (Marshal.GetLastWin32Error() == 259L) + { + WaitForSingleObject(waitHandle, 0xFFFFFFFF); + } + } + } + } + public unsafe bool Write(ReadOnlyMemory buffer) + { + if (session == 0) return false; + + IntPtr packet = WintunAllocateSendPacket(session, (uint)buffer.Length); + if (packet != 0) + { + buffer.Span.CopyTo(new Span((byte*)packet, buffer.Length)); + WintunSendPacket(session, packet); + } + else + { + if (Marshal.GetLastWin32Error() == 111L) + { + return false; + } + } + return true; + } + + private void GetWindowsInterfaceNum() + { + NetworkInterface adapter = NetworkInterface.GetAllNetworkInterfaces() + .FirstOrDefault(c => c.Name == Name); + if (adapter != null) + { + interfaceNumber = adapter.GetIPProperties().GetIPv4Properties().Index; + } + } + + [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 80)] + private struct MIB_UNICASTIPADDRESS_ROW + { + [FieldOffset(0)] + public ushort sin_family; + [FieldOffset(4)] + public uint sin_addr; + [FieldOffset(32)] + public ulong InterfaceLuid; + [FieldOffset(60)] + public byte OnLinkPrefixLength; + [FieldOffset(64)] + public int DadState; + } + + [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 104)] + private struct MIB_IPFORWARD_ROW2 + { + [FieldOffset(0)] + public ulong InterfaceLuid; + [FieldOffset(12)] + public ushort si_family; + [FieldOffset(16)] + public uint sin_addr; + [FieldOffset(40)] + public byte PrefixLength; + [FieldOffset(48)] + public uint NextHop_sin_addr; + [FieldOffset(44)] + public ushort NextHop_si_family; + } + + [DllImport("iphlpapi.dll", SetLastError = true)] + private static extern void InitializeUnicastIpAddressEntry(ref MIB_UNICASTIPADDRESS_ROW Row); + + [DllImport("iphlpapi.dll", SetLastError = true)] + private static extern uint CreateUnicastIpAddressEntry(ref MIB_UNICASTIPADDRESS_ROW Row); + + [DllImport("iphlpapi.dll", SetLastError = true)] + private static extern void InitializeIpForwardEntry(ref MIB_IPFORWARD_ROW2 Row); + + [DllImport("iphlpapi.dll", SetLastError = true)] + private static extern uint CreateIpForwardEntry2(ref MIB_IPFORWARD_ROW2 Row); + [DllImport("kernel32.dll")] + private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern IntPtr WintunCreateAdapter( + [MarshalAs(UnmanagedType.LPWStr)] + string name, + [MarshalAs(UnmanagedType.LPWStr)] + string tunnelType, + ref Guid guid); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern uint WintunGetRunningDriverVersion(); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern void WintunGetAdapterLUID(IntPtr adapter, out ulong luid); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern IntPtr WintunStartSession(IntPtr adapter, uint capacity); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern IntPtr WintunGetReadWaitEvent(IntPtr session); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern IntPtr WintunReceivePacket(IntPtr session, out uint packetSize); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern void WintunSendPacket(IntPtr session, IntPtr packet); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern void WintunEndSession(IntPtr session); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern void WintunCloseAdapter(IntPtr adapter); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern IntPtr WintunAllocateSendPacket(IntPtr session, uint packetSize); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern IntPtr WintunOpenAdapter( + [MarshalAs(UnmanagedType.LPWStr)] + string name); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern bool WintunDeleteDriver(); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern void WintunReleaseReceivePacket(IntPtr session, IntPtr packet); + + [DllImport("wintun.dll", SetLastError = true)] + private static extern void WintunSetLogger(WINTUN_LOGGER_CALLBACK newLogger); + + + private delegate void WINTUN_LOGGER_CALLBACK( + WINTUN_LOGGER_LEVEL level, + ulong timestamp, + [MarshalAs(UnmanagedType.LPWStr)] + string message); + + private enum WINTUN_LOGGER_LEVEL + { + WINTUN_LOG_INFO, /**< Informational */ + WINTUN_LOG_WARN, /**< Warning */ + WINTUN_LOG_ERR /**< Error */ + } + + + } +} diff --git a/linker.tun/linker.tun.csproj b/linker.tun/linker.tun.csproj new file mode 100644 index 00000000..6137061d --- /dev/null +++ b/linker.tun/linker.tun.csproj @@ -0,0 +1,37 @@ + + + + net8.0 + enable + disable + true + false + true + true + linker tun + 1.2.0 + snltty + snltty + linker tunnel + snltty + https://github.com/snltty/linker + https://github.com/snltty/linker + linker tun + 1.2.0.2 + 1.2.0.2 + + + + full + true + + + none + false + True + + + + + + diff --git a/linker.tunnel/linker.tunnel.csproj b/linker.tunnel/linker.tunnel.csproj index 845c3e4c..5b77e360 100644 --- a/linker.tunnel/linker.tunnel.csproj +++ b/linker.tunnel/linker.tunnel.csproj @@ -1,25 +1,25 @@  - - net8.0 - enable - disable - true - false - true - true - linker tunnel - 1.2.0 - snltty - snltty - linker tunnel - snltty - https://github.com/snltty/linker - https://github.com/snltty/linker - linker tunnel - 1.2.0.2 - 1.2.0.2 - + + net8.0 + enable + disable + true + false + true + true + linker tunnel + 1.2.0 + snltty + snltty + linker tunnel + snltty + https://github.com/snltty/linker + https://github.com/snltty/linker + linker tunnel + 1.2.0.2 + 1.2.0.2 + full @@ -31,8 +31,8 @@ True - - - + + +