# c#在windows 使用wintun虚拟网卡

你可以可以直接nuget使用 linker.tun

https://www.wintun.net/ 下载wintun,使用适合你系统的wintun.dll

1、P/Invoke wintun api和一些windows api

C#
internal static class WinTun
{

    [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 80)]
    internal 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)]
    internal 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)]
    internal static extern void InitializeUnicastIpAddressEntry(ref MIB_UNICASTIPADDRESS_ROW Row);

    [DllImport("iphlpapi.dll", SetLastError = true)]
    internal static extern uint CreateUnicastIpAddressEntry(ref MIB_UNICASTIPADDRESS_ROW Row);

    [DllImport("iphlpapi.dll", SetLastError = true)]
    internal static extern void InitializeIpForwardEntry(ref MIB_IPFORWARD_ROW2 Row);

    [DllImport("iphlpapi.dll", SetLastError = true)]
    internal static extern uint CreateIpForwardEntry2(ref MIB_IPFORWARD_ROW2 Row);
    [DllImport("kernel32.dll")]
    internal static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
    [DllImport("kernel32.dll")]
    internal static extern bool SetEvent(IntPtr hEvent);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern IntPtr WintunCreateAdapter(
    [MarshalAs(UnmanagedType.LPWStr)]
    string name,
    [MarshalAs(UnmanagedType.LPWStr)]
    string tunnelType,
    ref Guid guid);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern uint WintunGetRunningDriverVersion();

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern void WintunGetAdapterLUID(IntPtr adapter, out ulong luid);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern IntPtr WintunStartSession(IntPtr adapter, uint capacity);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern IntPtr WintunGetReadWaitEvent(IntPtr session);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern IntPtr WintunReceivePacket(IntPtr session, out uint packetSize);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern void WintunSendPacket(IntPtr session, IntPtr packet);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern void WintunEndSession(IntPtr session);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern void WintunCloseAdapter(IntPtr adapter);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern IntPtr WintunAllocateSendPacket(IntPtr session, uint packetSize);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern IntPtr WintunOpenAdapter(
        [MarshalAs(UnmanagedType.LPWStr)]
    string name);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern bool WintunDeleteDriver();

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern void WintunReleaseReceivePacket(IntPtr session, IntPtr packet);

    [DllImport("wintun.dll", SetLastError = true)]
    internal static extern void WintunSetLogger(WINTUN_LOGGER_CALLBACK newLogger);

    internal delegate void WINTUN_LOGGER_CALLBACK(
        WINTUN_LOGGER_LEVEL level,
        ulong timestamp,
        [MarshalAs(UnmanagedType.LPWStr)]
    string message);

    internal enum WINTUN_LOGGER_LEVEL
    {
        WINTUN_LOG_INFO, /**< Informational */
        WINTUN_LOG_WARN, /**< Warning */
        WINTUN_LOG_ERR   /**< Error */
    }

}

2、创建tun网卡

C#
Guid guid = Guid.NewGuid();
//创建网卡
IntPtr adapter = WinTun.WintunCreateAdapter("linker", "linker", ref guid);
//创建一个会话
IntPtr session = WinTun.WintunStartSession(adapter, 0x400000);
//获取一个等待对象
IntPtr waitHandle = WinTun.WintunGetReadWaitEvent(session);

//你的IP和掩码
IPAddress address = IPAddress.Parse("10.18.18.2");
int prefixLength = 24

//设置IP和掩码
WinTun.WintunGetAdapterLUID(adapter, out ulong luid);
{
    WinTun.MIB_UNICASTIPADDRESS_ROW AddressRow = default;
    WinTun.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 = WinTun.CreateUnicastIpAddressEntry(ref AddressRow);
    if (LastError != 0) throw new InvalidOperationException();
}


//设置MTU
netsh interface ipv4 set subinterface 网卡编号  mtu=1400 store=persistent

3、从网卡读取数据

C#

byte[] buffer = new byte[2 * 1024];
for (;;)
{
    IntPtr packet = WinTun.WintunReceivePacket(session, out var packetSize);

    if (packet != 0)
    {
        new Span<byte>((byte*)packet, (int)packetSize).CopyTo(buffer.AsSpan(4, (int)packetSize));
        ((int)packetSize).ToBytes(buffer);
        WinTun.WintunReleaseReceivePacket(session, packet);
        return buffer.AsMemory(0, (int)packetSize + 4);
    }
    else
    {
        if (Marshal.GetLastWin32Error() == 259L)
        {
            WinTun.WaitForSingleObject(waitHandle, 0xFFFFFFFF);
        }
        else
        {
            return new byte[0];
        }
    }
}

4、将数据写入网卡

C#
ReadOnlyMemory<byte> buffer; //你的数据

IntPtr packet = WinTun.WintunAllocateSendPacket(session, (uint)buffer.Length);
if (packet != 0)
{
    buffer.Span.CopyTo(new Span<byte>((byte*)packet, buffer.Length));
    WinTun.WintunSendPacket(session, packet);
    return true;
}
return false;