Files
Archive/netch/Netch/Interops/RouteHelper.cs
2024-03-05 02:32:38 -08:00

110 lines
3.9 KiB
C#

using System.Net.Sockets;
using System.Runtime.InteropServices;
using Windows.Win32.Foundation;
using Windows.Win32.Networking.WinSock;
using Windows.Win32.NetworkManagement.IpHelper;
using static Windows.Win32.PInvoke;
namespace Netch.Interops;
public static unsafe class RouteHelper
{
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern ulong ConvertLuidToIndex(ulong id);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CreateIPv4(string address, string netmask, ulong index);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CreateUnicastIP(AddressFamily inet, string address, byte cidr, ulong index);
public static bool CreateUnicastIPCS(AddressFamily inet, string address, byte cidr, ulong index)
{
MIB_UNICASTIPADDRESS_ROW addr;
InitializeUnicastIpAddressEntry(&addr);
addr.InterfaceIndex = (uint)index;
addr.OnLinkPrefixLength = cidr;
if (inet == AddressFamily.InterNetwork)
{
addr.Address.Ipv4.sin_family = (ushort)ADDRESS_FAMILY.AF_INET;
if (inet_pton((int)inet, address, &addr.Address.Ipv4.sin_addr) == 0)
return false;
}
else if (inet == AddressFamily.InterNetworkV6)
{
addr.Address.Ipv6.sin6_family = (ushort)ADDRESS_FAMILY.AF_INET6;
if (inet_pton((int)inet, address, &addr.Address.Ipv6.sin6_addr) == 0)
return false;
}
else
{
return false;
}
// https://docs.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-createunicastipaddressentry#remarks
HANDLE handle = default;
using var obj = new Semaphore(0, 1);
void Callback(void* context, MIB_UNICASTIPADDRESS_ROW* row, MIB_NOTIFICATION_TYPE type)
{
if (type != MIB_NOTIFICATION_TYPE.MibInitialNotification)
{
NTSTATUS state;
if ((state = GetUnicastIpAddressEntry(row)) != 0)
{
Log.Error("GetUnicastIpAddressEntry failed: {State}", state.Value);
return;
}
if (row -> DadState == NL_DAD_STATE.IpDadStatePreferred)
{
try
{
obj.Release();
}
catch (Exception e)
{
// i don't trust win32 api
Log.Error(e, "semaphore disposed");
}
}
}
}
NotifyUnicastIpAddressChange((ushort)ADDRESS_FAMILY.AF_INET, Callback, null, new BOOLEAN(byte.MaxValue), ref handle);
try
{
NTSTATUS state;
if ((state = CreateUnicastIpAddressEntry(&addr)) != 0)
{
Log.Error("CreateUnicastIpAddressEntry failed: {State}", state.Value);
return false;
}
if (!obj.WaitOne(TimeSpan.FromSeconds(10)))
{
Log.Error("Wait unicast IP usable timeout");
return false;
}
return true;
}
finally
{
CancelMibChangeNotify2(handle);
}
}
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool RefreshIPTable(AddressFamily inet, ulong index);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CreateRoute(AddressFamily inet, string address, byte cidr, string gateway, ulong index, int metric);
[DllImport("RouteHelper.bin", CallingConvention = CallingConvention.Cdecl)]
public static extern bool DeleteRoute(AddressFamily inet, string address, byte cidr, string gateway, ulong index, int metric);
}