跳到主要内容

9、集成tun网卡到你的项目

1、说明

说明

在你的.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、编写一个简单的代码

说明

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);
}
}
}
}