From 9c9c8e99497d95131464caa2fafd88c2afa4c76b Mon Sep 17 00:00:00 2001 From: snltty <1069410172@qq.com> Date: Wed, 24 Sep 2025 20:55:52 +0800 Subject: [PATCH] 192 --- src/linker.libs/ChecksumHelper.cs | 42 ------- src/linker.nat/LinkerDstMapping.cs | 173 ++++++++++++++++++++++------- src/linker.nat/LinkerFirewall.cs | 168 ++++++++++++++++++++++------ src/linker.nat/LinkerSrcNat.cs | 34 ++---- version.txt | 2 +- 5 files changed, 273 insertions(+), 146 deletions(-) diff --git a/src/linker.libs/ChecksumHelper.cs b/src/linker.libs/ChecksumHelper.cs index e5b4767e..9ac6bc90 100644 --- a/src/linker.libs/ChecksumHelper.cs +++ b/src/linker.libs/ChecksumHelper.cs @@ -5,48 +5,6 @@ namespace linker.libs { public sealed class ChecksumHelper { - public static unsafe void ClearChecksum(ReadOnlyMemory packet, bool ipHeader = true, bool payload = true) - { - ClearChecksum(packet.Span, ipHeader, payload); - } - public static unsafe void ClearChecksum(ReadOnlySpan packet, bool ipHeader = true, bool payload = true) - { - fixed (byte* ptr = packet) - { - ClearChecksum(ptr, ipHeader, payload); - } - } - /// - /// 清空IP包的校验和 - /// - /// - /// 是否情况IP头校验和 - /// 是否清空荷载协议校验和 - 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; - } - } - } - /// /// 计算IP包的校验和,当校验和为0时才计算 diff --git a/src/linker.nat/LinkerDstMapping.cs b/src/linker.nat/LinkerDstMapping.cs index 86f9bb4d..8576c5cf 100644 --- a/src/linker.nat/LinkerDstMapping.cs +++ b/src/linker.nat/LinkerDstMapping.cs @@ -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,60 @@ namespace linker.nat /// /// 转换为假IP /// - /// TCP/IP - public void ToFakeDst(ReadOnlyMemory packet) + /// TCP/IP + public unsafe void ToFakeDst(ReadOnlyMemory 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; + } } + } /// /// 转换为真IP /// /// TCP/IP /// 是否计算校验和,如果使用了应用层NAT,可以交给应用层NAT去计算校验和 - public void ToRealDst(ReadOnlyMemory packet) + public unsafe void ToRealDst(ReadOnlyMemory 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; + if (natDic.TryGetValue(realDist, out uint value) == false || value != fakeDist) + { + natDic.AddOrUpdate(realDist, fakeDist, (a, b) => fakeDist); + } + break; } - break; } } } - /// - /// 写入新IP - /// - /// IP包 - /// 大端IP - /// 写入位置,源12,目的16 - private unsafe void ReWriteIP(ReadOnlyMemory packet, uint newIP, int pos) - { - fixed (byte* ptr = packet.Span) - { - //修改目标IP,需要小端写入,IP计算都是按大端的,操作是小端的,所以转换一下 - *(uint*)(ptr + pos) = BinaryPrimitives.ReverseEndianness(newIP); - //清除校验和,由于IP头和传输层协议头都修改了,所以都需要重新计算 - ChecksumHelper.ClearChecksum(ptr); - } - } /// /// 映射对象 @@ -145,5 +137,104 @@ namespace linker.nat /// public byte PrefixLength { get; set; } } + + readonly unsafe struct MapPacket + { + private readonly byte* ptr; + + /// + /// 协议版本 + /// + public readonly byte Version => (byte)((*ptr >> 4) & 0b1111); + public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9)); + + /// + /// IP头长度 + /// + public readonly int IPHeadLength => (*ptr & 0b1111) * 4; + /// + /// IP包荷载数据指针,也就是TCP/UDP头指针 + /// + public readonly byte* PayloadPtr => ptr + IPHeadLength; + + /// + /// 源地址 + /// + public readonly uint SrcAddr + { + get + { + return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12)); + } + set + { + *(uint*)(ptr + 12) = BinaryPrimitives.ReverseEndianness(value); + } + } + /// + /// 目的地址 + /// + public readonly uint DstAddr + { + get + { + return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16)); + } + set + { + *(uint*)(ptr + 16) = BinaryPrimitives.ReverseEndianness(value); + } + } + public ReadOnlySpan DstAddrSpan => new Span((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; + } + } + } + + /// + /// 加载TCP/IP包,必须是一个完整的TCP/IP包 + /// + /// 一个完整的TCP/IP包 + public MapPacket(byte* ptr) + { + this.ptr = ptr; + } + } } } diff --git a/src/linker.nat/LinkerFirewall.cs b/src/linker.nat/LinkerFirewall.cs index f6511b2b..2b5b45b9 100644 --- a/src/linker.nat/LinkerFirewall.cs +++ b/src/linker.nat/LinkerFirewall.cs @@ -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 { /// @@ -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时要允许,越过防火墙 /// /// 一个TCP/IP包 - public void AddAllow(ReadOnlyMemory packet) + public unsafe void AddAllow(ReadOnlyMemory 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 /// 客户端Id /// TCP/IP数据包 /// - public bool Check(string srcId, ReadOnlyMemory packet) + public unsafe bool Check(string srcId, ReadOnlyMemory 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; + + /// + /// 协议版本 + /// + public readonly byte Version => (byte)((*ptr >> 4) & 0b1111); + public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9)); + + /// + /// IP头长度 + /// + public readonly int IPHeadLength => (*ptr & 0b1111) * 4; + /// + /// IP包荷载数据指针,也就是TCP/UDP头指针 + /// + public readonly byte* PayloadPtr => ptr + IPHeadLength; + + /// + /// 源地址 + /// + public readonly uint SrcAddr + { + get + { + return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12)); + } + set + { + *(uint*)(ptr + 12) = BinaryPrimitives.ReverseEndianness(value); + } + } + /// + /// 源端口 + /// + public readonly ushort SrcPort + { + get + { + return BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr)); + } + set + { + *(ushort*)(PayloadPtr) = BinaryPrimitives.ReverseEndianness(value); + } + } + /// + /// 目的地址 + /// + public readonly uint DstAddr + { + get + { + return BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16)); + } + set + { + *(uint*)(ptr + 16) = BinaryPrimitives.ReverseEndianness(value); + } + } + /// + /// 目标端口 + /// + public readonly ushort DstPort + { + get + { + return BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 2)); + } + set + { + *(ushort*)(PayloadPtr + 2) = BinaryPrimitives.ReverseEndianness(value); + } + } + + /// + /// 加载TCP/IP包,必须是一个完整的TCP/IP包 + /// + /// 一个完整的TCP/IP包 + 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 - } + + + + } diff --git a/src/linker.nat/LinkerSrcNat.cs b/src/linker.nat/LinkerSrcNat.cs index 987ad6e4..9087828a 100644 --- a/src/linker.nat/LinkerSrcNat.cs +++ b/src/linker.nat/LinkerSrcNat.cs @@ -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 /// private unsafe bool InjectTcp(WinDivertParseResult p, byte* ptr, NetworkIPv4Addr interfaceAddr) { - IPV4Packet ipv4 = new IPV4Packet(ptr); + SrcNatPacket ipv4 = new SrcNatPacket(ptr); //新端口 ValueTuple portKey = (p.IPv4Hdr->SrcAddr.Raw, p.TCPHdr->SrcPort); @@ -358,7 +358,7 @@ namespace linker.nat /// private unsafe bool RecvTcp(WinDivertParseResult p, byte* ptr) { - IPV4Packet ipv4 = new IPV4Packet(ptr); + SrcNatPacket ipv4 = new SrcNatPacket(ptr); ValueTuple 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 /// /// IPV4 包 /// - public unsafe struct IPV4Packet + public unsafe struct SrcNatPacket { byte* ptr; @@ -549,25 +549,11 @@ namespace linker.nat /// 协议版本 /// public byte Version => (byte)((*ptr >> 4) & 0b1111); - public ProtocolType Protocol => (ProtocolType)(*(ptr + 9)); - - /// - /// 源地址 - /// - public uint SrcAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12)); - /// - /// 源端口 - /// - public ushort SrcPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength)); /// /// 目的地址 /// public uint DstAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16)); /// - /// 目标端口 - /// - public ushort DstPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 2)); - /// /// 源地址 /// public ReadOnlySpan SrcAddrSpan => new Span((ptr + 12), 4); @@ -586,10 +572,6 @@ namespace linker.nat /// public byte Flag => (byte)(*(ptr + 6) >> 5); /// - /// 不分片 - /// - public bool DontFragment => (Flag & 0x02) == 2; - /// /// 更多分片 /// 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 span) + public SrcNatPacket(ReadOnlySpan span) { fixed (byte* ptr = span) { diff --git a/version.txt b/version.txt index b435c846..7026664c 100644 --- a/version.txt +++ b/version.txt @@ -1,5 +1,5 @@ v1.9.2 -2025-09-24 20:19:20 +2025-09-24 20:55:51 1. 一些累计更新,一些BUG修复 2. 使用代理方式重写应用层NAT 3. 新增服务端webapi,对外提供公开数据,监听1803端口,设置为0则不监听