3 Commits

Author SHA1 Message Date
snltty
fc1ead142f 192 2025-09-24 22:44:14 +08:00
snltty
8dfe9045b5 192 2025-09-24 21:50:47 +08:00
snltty
9c9c8e9949 192 2025-09-24 20:55:52 +08:00
5 changed files with 275 additions and 146 deletions

View File

@@ -5,48 +5,6 @@ namespace linker.libs
{
public sealed class ChecksumHelper
{
public static unsafe void ClearChecksum(ReadOnlyMemory<byte> packet, bool ipHeader = true, bool payload = true)
{
ClearChecksum(packet.Span, ipHeader, payload);
}
public static unsafe void ClearChecksum(ReadOnlySpan<byte> packet, bool ipHeader = true, bool payload = true)
{
fixed (byte* ptr = packet)
{
ClearChecksum(ptr, ipHeader, payload);
}
}
/// <summary>
/// 清空IP包的校验和
/// </summary>
/// <param name="ptr"></param>
/// <param name="ipHeader">是否情况IP头校验和</param>
/// <param name="payload">是否清空荷载协议校验和</param>
public static unsafe void ClearChecksum(byte* ptr, bool ipHeader = true, bool payload = true)
{
byte ipHeaderLength = (byte)((*ptr & 0b1111) * 4);
byte* packetPtr = ptr + ipHeaderLength;
if (ipHeader)
{
*(ushort*)(ptr + 10) = 0;
}
if (payload)
{
int index = (ProtocolType)(*(ptr + 9)) switch
{
ProtocolType.Icmp => 2,
ProtocolType.Tcp => 16,
ProtocolType.Udp => 6,
_ => -1,
};
if (index > 0)
{
*(ushort*)(packetPtr + index) = 0;
}
}
}
/// <summary>
/// 计算IP包的校验和当校验和为0时才计算

View File

@@ -1,9 +1,11 @@
using linker.libs;
using linker.libs.extends;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Net;
using System.Net.Sockets;
namespace linker.nat
{
@@ -62,70 +64,62 @@ namespace linker.nat
/// <summary>
/// 转换为假IP
/// </summary>
/// <param name="packet">TCP/IP</param>
public void ToFakeDst(ReadOnlyMemory<byte> packet)
/// <param name="buffer">TCP/IP</param>
public unsafe void ToFakeDst(ReadOnlyMemory<byte> buffer)
{
//只支持映射IPV4
if ((byte)(packet.Span[0] >> 4 & 0b1111) != 4) return;
//映射表不为空
if (natDic.IsEmpty) return;
//源IP
uint realDist = NetworkHelper.ToValue(packet.Span.Slice(12, 4));
if (natDic.TryGetValue(realDist, out uint fakeDist))
fixed (byte* ptr = buffer.Span)
{
//修改源IP
ReWriteIP(packet, fakeDist, 12);
MapPacket packet = new MapPacket(ptr);
//只支持映射IPV4
if (packet.Version != 4) return;
if (natDic.TryGetValue(packet.SrcAddr, out uint fakeDist))
{
packet.SrcAddr = fakeDist;
packet.IPChecksum = 0;
packet.PayloadChecksum = 0;
}
}
}
/// <summary>
/// 转换为真IP
/// </summary>
/// <param name="packet">TCP/IP</param>
/// <param name="checksum">是否计算校验和如果使用了应用层NAT可以交给应用层NAT去计算校验和</param>
public void ToRealDst(ReadOnlyMemory<byte> packet)
public unsafe void ToRealDst(ReadOnlyMemory<byte> buffer)
{
//只支持映射IPV4
if ((byte)(packet.Span[0] >> 4 & 0b1111) != 4) return;
//映射表不为空
if (masks.Length == 0 || mapDic.Count == 0) return;
//广播包
if (packet.Span[19] == 255) return;
uint fakeDist = NetworkHelper.ToValue(packet.Span.Slice(16, 4));
for (int i = 0; i < masks.Length; i++)
fixed (byte* ptr = buffer.Span)
{
//目标IP网络号存在映射表中找到映射后的真实网络号替换网络号得到最终真实的IP
if (mapDic.TryGetValue(fakeDist & masks[i], out uint realNetwork))
MapPacket packet = new MapPacket(ptr);
//只支持映射IPV4
if (packet.Version != 4 || packet.DstAddrSpan.IsCast()) return;
uint fakeDist = packet.DstAddr;
for (int i = 0; i < masks.Length; i++)
{
uint realDist = realNetwork | (fakeDist & ~masks[i]);
//修改目标IP
ReWriteIP(packet, realDist, 16);
if (natDic.TryGetValue(realDist, out uint value) == false || value != fakeDist)
//目标IP网络号存在映射表中找到映射后的真实网络号替换网络号得到最终真实的IP
if (mapDic.TryGetValue(fakeDist & masks[i], out uint realNetwork))
{
natDic.AddOrUpdate(realDist, fakeDist, (a, b) => fakeDist);
uint realDist = realNetwork | (fakeDist & ~masks[i]);
packet.DstAddr = realDist;
packet.IPChecksum = 0;
packet.PayloadChecksum = 0;
if (natDic.TryGetValue(realDist, out uint value) == false || value != fakeDist)
{
natDic.AddOrUpdate(realDist, fakeDist, (a, b) => fakeDist);
}
break;
}
break;
}
}
}
/// <summary>
/// 写入新IP
/// </summary>
/// <param name="packet">IP包</param>
/// <param name="newIP">大端IP</param>
/// <param name="pos">写入位置源12目的16</param>
private unsafe void ReWriteIP(ReadOnlyMemory<byte> packet, uint newIP, int pos)
{
fixed (byte* ptr = packet.Span)
{
//修改目标IP需要小端写入IP计算都是按大端的操作是小端的所以转换一下
*(uint*)(ptr + pos) = BinaryPrimitives.ReverseEndianness(newIP);
//清除校验和由于IP头和传输层协议头都修改了所以都需要重新计算
ChecksumHelper.ClearChecksum(ptr);
}
}
/// <summary>
/// 映射对象
@@ -145,5 +139,104 @@ namespace linker.nat
/// </summary>
public byte PrefixLength { get; set; }
}
readonly unsafe struct MapPacket
{
private readonly byte* ptr;
/// <summary>
/// 协议版本
/// </summary>
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
/// <summary>
/// IP头长度
/// </summary>
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
/// <summary>
/// IP包荷载数据指针也就是TCP/UDP头指针
/// </summary>
public readonly byte* PayloadPtr => ptr + IPHeadLength;
/// <summary>
/// 源地址
/// </summary>
public readonly uint SrcAddr
{
get
{
return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
}
set
{
*(uint*)(ptr + 12) = BinaryPrimitives.ReverseEndianness(value);
}
}
/// <summary>
/// 目的地址
/// </summary>
public readonly uint DstAddr
{
get
{
return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
}
set
{
*(uint*)(ptr + 16) = BinaryPrimitives.ReverseEndianness(value);
}
}
public ReadOnlySpan<byte> DstAddrSpan => new Span<byte>((ptr + 16), 4);
public readonly ushort IPChecksum
{
get
{
return BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + 10));
}
set
{
*(ushort*)(ptr + 10) = BinaryPrimitives.ReverseEndianness(value);
}
}
public readonly ushort PayloadChecksum
{
get
{
return Protocol switch
{
ProtocolType.Icmp => BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 2)),
ProtocolType.Tcp => BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 16)),
ProtocolType.Udp => BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 6)),
_ => (ushort)0,
};
}
set
{
switch (Protocol)
{
case ProtocolType.Icmp:
*(ushort*)(PayloadPtr + 2) = BinaryPrimitives.ReverseEndianness(value);
break;
case ProtocolType.Tcp:
*(ushort*)(PayloadPtr + 16) = BinaryPrimitives.ReverseEndianness(value);
break;
case ProtocolType.Udp:
*(ushort*)(PayloadPtr + 6) = BinaryPrimitives.ReverseEndianness(value);
break;
}
}
}
/// <summary>
/// 加载TCP/IP包必须是一个完整的TCP/IP包
/// </summary>
/// <param name="ptr">一个完整的TCP/IP包</param>
public MapPacket(byte* ptr)
{
this.ptr = ptr;
}
}
}
}

View File

@@ -1,9 +1,9 @@
using linker.libs;
using linker.libs.timer;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using static linker.nat.LinkerSrcNat;
namespace linker.nat
{
/// <summary>
@@ -123,20 +123,23 @@ namespace linker.nat
/// 添加一个允许,比如 192.168.1.1:12345->192.168.100.100:80等下192.168.100.100:80->192.168.1.1:12345时要允许越过防火墙
/// </summary>
/// <param name="packet">一个TCP/IP包</param>
public void AddAllow(ReadOnlyMemory<byte> packet)
public unsafe void AddAllow(ReadOnlyMemory<byte> packet)
{
if (state != LinkerFirewallState.Enabled) return;
IPV4Packet ipv4 = new IPV4Packet(packet.Span);
if (ipv4.Version == 4 && (ipv4.Protocol == ProtocolType.Udp || ipv4.Protocol == ProtocolType.Tcp))
fixed (byte* ptr = packet.Span)
{
(uint src, ushort srcPort, uint dst, ushort dstPort, ProtocolType pro) key = (ipv4.SrcAddr, ipv4.SrcPort, ipv4.DstAddr, ipv4.DstPort, ipv4.Protocol);
if (cacheSrcMap.TryGetValue(key, out SrcCacheInfo cache) == false)
FirewallPacket ipv4 = new FirewallPacket(ptr);
if (ipv4.Version == 4 && (ipv4.Protocol == ProtocolType.Udp || ipv4.Protocol == ProtocolType.Tcp))
{
cache = new SrcCacheInfo { Type = SrcCacheType.Out };
cacheSrcMap.TryAdd(key, cache);
(uint src, ushort srcPort, uint dst, ushort dstPort, ProtocolType pro) key = (ipv4.SrcAddr, ipv4.SrcPort, ipv4.DstAddr, ipv4.DstPort, ipv4.Protocol);
if (cacheSrcMap.TryGetValue(key, out SrcCacheInfo cache) == false)
{
cache = new SrcCacheInfo { Type = SrcCacheType.Out };
cacheSrcMap.TryAdd(key, cache);
}
cache.LastTime = Environment.TickCount64;
}
cache.LastTime = Environment.TickCount64;
}
}
@@ -157,7 +160,7 @@ namespace linker.nat
return Check(srcId, dst, dstPort, protocol);
}
public bool Check(string srcId, (uint ip,ushort port) dstEP, ProtocolType protocol)
public bool Check(string srcId, (uint ip, ushort port) dstEP, ProtocolType protocol)
{
if (this.state != LinkerFirewallState.Enabled) return true;
return Check(srcId, dstEP.ip, dstEP.port, protocol);
@@ -170,29 +173,32 @@ namespace linker.nat
/// <param name="srcId">客户端Id</param>
/// <param name="packet">TCP/IP数据包</param>
/// <returns></returns>
public bool Check(string srcId, ReadOnlyMemory<byte> packet)
public unsafe bool Check(string srcId, ReadOnlyMemory<byte> packet)
{
if (state != LinkerFirewallState.Enabled) return true;
IPV4Packet ipv4 = new IPV4Packet(packet.Span);
//IPV4 TCP 和 UDP
if (ipv4.Version == 4 && (ipv4.Protocol == ProtocolType.Udp || ipv4.Protocol == ProtocolType.Tcp))
fixed (byte* ptr = packet.Span)
{
//连接状态
(uint src, ushort srcPort, uint dst, ushort dstPort, ProtocolType pro) key = (ipv4.DstAddr, ipv4.DstPort, ipv4.SrcAddr, ipv4.SrcPort, ipv4.Protocol);
if (cacheSrcMap.TryGetValue(key, out SrcCacheInfo cache) == false)
FirewallPacket ipv4 = new FirewallPacket(ptr);
//IPV4 TCP 和 UDP
if (ipv4.Version == 4 && (ipv4.Protocol == ProtocolType.Udp || ipv4.Protocol == ProtocolType.Tcp))
{
cache = new SrcCacheInfo { Type = SrcCacheType.In };
cacheSrcMap.TryAdd(key, cache);
//连接状态
(uint src, ushort srcPort, uint dst, ushort dstPort, ProtocolType pro) key = (ipv4.DstAddr, ipv4.DstPort, ipv4.SrcAddr, ipv4.SrcPort, ipv4.Protocol);
if (cacheSrcMap.TryGetValue(key, out SrcCacheInfo cache) == false)
{
cache = new SrcCacheInfo { Type = SrcCacheType.In };
cacheSrcMap.TryAdd(key, cache);
}
cache.LastTime = Environment.TickCount64;
//有出站标记 或 通过检查
return cache.Type == SrcCacheType.Out
|| Check(srcId, ipv4.SrcAddr, ipv4.SrcPort, ipv4.Protocol);
}
cache.LastTime = Environment.TickCount64;
//有出站标记 或 通过检查
return cache.Type == SrcCacheType.Out
|| Check(srcId, ipv4.SrcAddr, ipv4.SrcPort, ipv4.Protocol);
return true;
}
return true;
}
private bool Check(string srcId, uint ip, ushort port, ProtocolType protocol)
@@ -255,7 +261,104 @@ namespace linker.nat
public LinkerFirewallProtocolType Protocol { get; set; }
public LinkerFirewallAction Action { get; set; }
}
sealed class SrcCacheInfo
{
public long LastTime { get; set; } = Environment.TickCount64;
public SrcCacheType Type { get; set; }
}
enum SrcCacheType
{
In = 0,
Out = 1
}
readonly unsafe struct FirewallPacket
{
private readonly byte* ptr;
/// <summary>
/// 协议版本
/// </summary>
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
/// <summary>
/// IP头长度
/// </summary>
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
/// <summary>
/// IP包荷载数据指针也就是TCP/UDP头指针
/// </summary>
public readonly byte* PayloadPtr => ptr + IPHeadLength;
/// <summary>
/// 源地址
/// </summary>
public readonly uint SrcAddr
{
get
{
return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
}
set
{
*(uint*)(ptr + 12) = BinaryPrimitives.ReverseEndianness(value);
}
}
/// <summary>
/// 源端口
/// </summary>
public readonly ushort SrcPort
{
get
{
return BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr));
}
set
{
*(ushort*)(PayloadPtr) = BinaryPrimitives.ReverseEndianness(value);
}
}
/// <summary>
/// 目的地址
/// </summary>
public readonly uint DstAddr
{
get
{
return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
}
set
{
*(uint*)(ptr + 16) = BinaryPrimitives.ReverseEndianness(value);
}
}
/// <summary>
/// 目标端口
/// </summary>
public readonly ushort DstPort
{
get
{
return BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 2));
}
set
{
*(ushort*)(PayloadPtr + 2) = BinaryPrimitives.ReverseEndianness(value);
}
}
/// <summary>
/// 加载TCP/IP包必须是一个完整的TCP/IP包
/// </summary>
/// <param name="ptr">一个完整的TCP/IP包</param>
public FirewallPacket(byte* ptr)
{
this.ptr = ptr;
}
}
}
public class LinkerFirewallRuleInfo
{
public string SrcId { get; set; } = string.Empty;
@@ -266,7 +369,6 @@ namespace linker.nat
public LinkerFirewallProtocolType Protocol { get; set; }
public LinkerFirewallAction Action { get; set; }
}
public enum LinkerFirewallProtocolType : byte
{
None = 0,
@@ -286,15 +388,9 @@ namespace linker.nat
Disabled = 1
}
public sealed class SrcCacheInfo
{
public long LastTime { get; set; } = Environment.TickCount64;
public SrcCacheType Type { get; set; }
}
public enum SrcCacheType
{
In = 0,
Out = 1
}
}

View File

@@ -194,7 +194,7 @@ namespace linker.nat
{
if (winDivert == null) return false;
IPV4Packet ipv4 = new IPV4Packet(packet.Span);
SrcNatPacket ipv4 = new SrcNatPacket(packet.Span);
//不是 ipv4是虚拟网卡ip是广播不nat
if (ipv4.Version != 4 || ipv4.DstAddr == srcIp || ipv4.DstAddrSpan.IsCast())
{
@@ -241,7 +241,7 @@ namespace linker.nat
//只操作response 和 request
if (p.ICMPv4Hdr->Type != 0 && p.ICMPv4Hdr->Type != 8) return false;
IPV4Packet ipv4 = new IPV4Packet(ptr);
SrcNatPacket ipv4 = new SrcNatPacket(ptr);
if (ipv4.IsFragment) return false;
//原标识符,两个字节
@@ -287,7 +287,7 @@ namespace linker.nat
//只操作response 和 request
if (p.ICMPv4Hdr->Type != 0 && p.ICMPv4Hdr->Type != 8) return false;
IPV4Packet ipv4 = new IPV4Packet(ptr);
SrcNatPacket ipv4 = new SrcNatPacket(ptr);
//标识符,两个字节
byte* ptr0 = ipv4.IcmpIdentifier0;
@@ -316,7 +316,7 @@ namespace linker.nat
/// <returns></returns>
private unsafe bool InjectTcp(WinDivertParseResult p, byte* ptr, NetworkIPv4Addr interfaceAddr)
{
IPV4Packet ipv4 = new IPV4Packet(ptr);
SrcNatPacket ipv4 = new SrcNatPacket(ptr);
//新端口
ValueTuple<uint, ushort> portKey = (p.IPv4Hdr->SrcAddr.Raw, p.TCPHdr->SrcPort);
@@ -358,7 +358,7 @@ namespace linker.nat
/// <returns></returns>
private unsafe bool RecvTcp(WinDivertParseResult p, byte* ptr)
{
IPV4Packet ipv4 = new IPV4Packet(ptr);
SrcNatPacket ipv4 = new SrcNatPacket(ptr);
ValueTuple<uint, ushort, uint, ushort, ProtocolType> key = (p.IPv4Hdr->DstAddr.Raw, p.TCPHdr->DstPort, p.IPv4Hdr->SrcAddr.Raw, p.TCPHdr->SrcPort, ProtocolType.Tcp);
if (natMap.TryGetValue(key, out NatMapInfo natMapInfo))
@@ -541,7 +541,7 @@ namespace linker.nat
/// <summary>
/// IPV4 包
/// </summary>
public unsafe struct IPV4Packet
public unsafe struct SrcNatPacket
{
byte* ptr;
@@ -549,25 +549,11 @@ namespace linker.nat
/// 协议版本
/// </summary>
public byte Version => (byte)((*ptr >> 4) & 0b1111);
public ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
/// <summary>
/// 源地址
/// </summary>
public uint SrcAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
/// <summary>
/// 源端口
/// </summary>
public ushort SrcPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength));
/// <summary>
/// 目的地址
/// </summary>
public uint DstAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
/// <summary>
/// 目标端口
/// </summary>
public ushort DstPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 2));
/// <summary>
/// 源地址
/// </summary>
public ReadOnlySpan<byte> SrcAddrSpan => new Span<byte>((ptr + 12), 4);
@@ -586,10 +572,6 @@ namespace linker.nat
/// </summary>
public byte Flag => (byte)(*(ptr + 6) >> 5);
/// <summary>
/// 不分片
/// </summary>
public bool DontFragment => (Flag & 0x02) == 2;
/// <summary>
/// 更多分片
/// </summary>
public bool MoreFragment => (Flag & 0x01) == 1;
@@ -622,11 +604,11 @@ namespace linker.nat
public bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
public bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
public IPV4Packet(byte* ptr)
public SrcNatPacket(byte* ptr)
{
this.ptr = ptr;
}
public IPV4Packet(ReadOnlySpan<byte> span)
public SrcNatPacket(ReadOnlySpan<byte> span)
{
fixed (byte* ptr = span)
{

View File

@@ -1,5 +1,5 @@
v1.9.2
2025-09-24 20:19:20
2025-09-24 22:44:14
1. 一些累计更新一些BUG修复
2. 使用代理方式重写应用层NAT
3. 新增服务端webapi对外提供公开数据监听1803端口设置为0则不监听