Files
linker/src/linker.doc.web/docs/8、集成和二次开发/8.22、单独使用虚拟网卡.md
snltty 75dd22ccbd 165
2025-01-14 21:33:40 +08:00

3.7 KiB
Raw Blame History

sidebar_position
sidebar_position
22

8.22、单独使用虚拟网卡

1、说明

:::tip[说明] 在你的.NET8.0+项目中集成tun网卡适用于windowslinux,源码在https://github.com/snltty/linker/tree/master/linker.tun

1、windows

  1. 下载wintun,选择适合你系统的 wintun.dll放到项目根目录
  2. 在windows下使用 wintunWintunCreateAdapter创建适配器可以提供一个guid如果不提供将随机一个这会导致注册表不断的产生新的记录
  3. 如果提供一个固定的guid在程序的一次会话内可以重复删除和创建适配器但是如果你在多个会话内使用同一个guid将会非常大概率创建适配器失败
  4. 所以linker.tun选择每次运行程序时生成guid在本次会话内重复使用且提供了LinkerTunDeviceAdapter.Clear()让你选择在合适的适合清理注册表

2、linux

  1. 请确保你的系统拥有tuntap模块,ifconfigipiptables命令 :::

2、编写一个简单的代码

:::tip[说明] nuget 安装 linker.tun,然后编写代码


internal class Program
{
    public static LinkerTunDeviceAdapter linkerTunDeviceAdapter;
    static void Main(string[] args)
    {
        linkerTunDeviceAdapter = new LinkerTunDeviceAdapter();
        //初始化设备名,和读取数据回调
        linkerTunDeviceAdapter.Initialize("linker111", new LinkerTunDeviceCallback());
        //在初始化后可以清理一些数据在windows将会清理适配器的注册表信息
        //linkerTunDeviceAdapter.Clear();

        //启动网卡ip掩码mtu
        linkerTunDeviceAdapter.Setup(IPAddress.Parse("192.168.55.2"), 24, 1416);
        //设置NAT转发这会将来到本网卡且目标IP不是本网卡IP的包转发到其它网卡
        //linkerTunDeviceAdapter.SetNat();

        //如果存在错误
        if (string.IsNullOrWhiteSpace(linkerTunDeviceAdapter.Error))
        {
            Console.WriteLine(linkerTunDeviceAdapter.Error);
            //关闭网卡
            linkerTunDeviceAdapter.Shutdown();
        }
        Console.ReadLine();
    }
}

public sealed class LinkerTunDeviceCallback : ILinkerTunDeviceCallback
{
    //收到IP数据包
    public async Task Callback(LinkerTunDevicPacket packet)
    {
        ICMPAnswer(packet);
        await Task.CompletedTask;
    }
    private unsafe void ICMPAnswer(LinkerTunDevicPacket packet)
    {
        fixed (byte* ptr = packet.IPPacket.Span)
        {
            //ICMP包且是 Request
            if (ptr[9] == 1 && ptr[20] == 8)
            {
                Console.WriteLine($"ICMP to {new IPAddress(packet.IPPacket.Span.Slice(16, 4))}");

                uint dist = BinaryPrimitives.ReadUInt32LittleEndian(packet.IPPacket.Span.Slice(16, 4));
                //目的地址变源地址,
                *(uint*)(ptr + 16) = *(uint*)(ptr + 12);
                //假装是网关回复的
                *(uint*)(ptr + 12) = dist;

                //计算一次IP头校验和
                *(ushort*)(ptr + 10) = 0;
                *(ushort*)(ptr + 10) = Program.linkerTunDeviceAdapter.Checksum((ushort*)ptr, 20);

                //改为ICMP Reply
                *(ushort*)(ptr + 20) = 0;

                //计算ICMP校验和
                *(ushort*)(ptr + 22) = 0;

                int length = packet.IPPacket.Span.Length - 20;
                ushort sum = Program.linkerTunDeviceAdapter.Checksum((ushort*)(ptr + 20), length);
                *(ushort*)(ptr + 22) = sum;

                //写入网卡回应这个ICMP请求
                Program.linkerTunDeviceAdapter.Write(packet.IPPacket);
            }
        }
    }
}

:::