代理的DNAT和webapi

This commit is contained in:
snltty
2025-09-21 18:35:15 +08:00
parent 57a565a856
commit a8e5ff1f6f
66 changed files with 1811 additions and 483 deletions

View File

@@ -63,7 +63,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "linker.ics", "src\linker.ic
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.messenger.plan", "src\linker.messenger.plan\linker.messenger.plan.csproj", "{5649D02E-200B-45E0-A82F-8EBE76CF96C6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.snat", "src\linker.snat\linker.snat.csproj", "{A1EA64AA-8C30-4616-B65D-8AF07641807E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.nat", "src\linker.nat\linker.nat.csproj", "{A1EA64AA-8C30-4616-B65D-8AF07641807E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "linker.messenger.firewall", "src\linker.messenger.firewall\linker.messenger.firewall.csproj", "{F97DB5A9-3807-4441-A520-7B1211C1CE8A}"
EndProject

View File

@@ -16,7 +16,7 @@ using linker.libs.web;
using linker.messenger.entry;
using linker.messenger.tuntap;
using linker.messenger.updater;
using linker.tun;
using linker.tun.device;
using linker.tunnel.connection;
using System.Net;
using System.Net.Sockets;

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.e9b4f2f4.js"></script><script defer="defer" src="js/app.166579fa.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.e16885f2.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.e9b4f2f4.js"></script><script defer="defer" src="js/app.a3656579.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.e16885f2.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,15 +1,93 @@
using System;
using System.Buffers.Binary;
using System.Net.Sockets;
namespace linker.libs
{
public sealed class ChecksumHelper
{
/// <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时才计算
/// </summary>
/// <param name="packet">一个完整的IP包</param>
/// <param name="ipHeader">是否计算IP头校验和</param>
/// <param name="payload">是否计算荷载协议校验和</param>
public static unsafe void ChecksumWithZero(ReadOnlyMemory<byte> packet, bool ipHeader = true, bool payload = true)
{
ChecksumWithZero(packet.Span, ipHeader, payload);
}
/// <summary>
/// 计算IP包的校验和当校验和为0时才计算
/// </summary>
/// <param name="packet">一个完整的IP包</param>
/// <param name="ipHeader">是否计算IP头校验和</param>
/// <param name="payload">是否计算荷载协议校验和</param>
public static unsafe void ChecksumWithZero(ReadOnlySpan<byte> packet, bool ipHeader = true, bool payload = true)
{
fixed (byte* ptr = packet)
{
ChecksumWithZero(ptr, ipHeader, payload);
}
}
/// <summary>
/// 计算IP包的校验和当校验和为0时才计算
/// </summary>
/// <param name="ptr">IP包指针</param>
/// <param name="ipHeader">是否计算IP头校验和</param>
/// <param name="payload">是否计算荷载协议校验和</param>
public static unsafe void ChecksumWithZero(byte* ptr, bool ipHeader = true, bool payload = true)
{
byte ipHeaderLength = (byte)((*ptr & 0b1111) * 4);
byte* packetPtr = ptr + ipHeaderLength;
ipHeader = ipHeader && *(ushort*)(ptr + 10) == 0;
payload = payload && ((ProtocolType)(*(ptr + 9)) switch
{
ProtocolType.Icmp => *(ushort*)(packetPtr + 2) == 0,
ProtocolType.Tcp => *(ushort*)(packetPtr + 16) == 0,
ProtocolType.Udp => *(ushort*)(packetPtr + 6) == 0,
_ => false,
});
if (ipHeader || payload)
Checksum(ptr, ipHeader, payload);
}
/// <summary>
/// 计算IP包的校验和
/// </summary>
/// <param name="packet">一个完整的IP包</param>
/// <param name="ipHeader">是否计算IP头校验和</param>
/// <param name="payload">是否计算荷载协议校验和</param>
public static unsafe void Checksum(ReadOnlyMemory<byte> packet, bool ipHeader = true, bool payload = true)
{
@@ -19,6 +97,7 @@ namespace linker.libs
/// 计算IP包的校验和
/// </summary>
/// <param name="packet">一个完整的IP包</param>
/// <param name="ipHeader">是否计算IP头校验和</param>
/// <param name="payload">是否计算荷载协议校验和</param>
public static unsafe void Checksum(ReadOnlySpan<byte> packet, bool ipHeader = true, bool payload = true)
{
@@ -31,7 +110,7 @@ namespace linker.libs
/// 计算IP包的校验和
/// </summary>
/// <param name="ptr">IP包指针</param>
/// <param name="ipHeader">是否计算IP校验和</param>
/// <param name="ipHeader">是否计算IP校验和</param>
/// <param name="payload">是否计算荷载协议校验和</param>
public static unsafe void Checksum(byte* ptr, bool ipHeader = true, bool payload = true)
{

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
namespace linker.libs.web
{
/// <summary>
/// web服务
/// </summary>
public interface IWebApiServer
{
/// <summary>
/// 开始
/// </summary>
public void Start(int port);
public void AddController(IWebApiController controller);
public void AddControllers(List<IWebApiController> controllers);
}
public interface IWebApiController
{
public string Path { get; }
public Memory<byte> Handle(string query);
public void Free();
}
}

View File

@@ -0,0 +1,119 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
namespace linker.libs.web
{
/// <summary>
/// 本地web api 服务器
/// </summary>
public class WebApiServer : IWebApiServer
{
private readonly ConcurrentDictionary<string, IWebApiController> dic = new();
public WebApiServer()
{
}
/// <summary>
/// 开启web api
/// </summary>
public void Start(int port)
{
Task.Factory.StartNew(async () =>
{
try
{
HttpListener http = new HttpListener();
http.IgnoreWriteExceptions = true;
http.Prefixes.Add($"http://+:{port}/");
http.Start();
while (true)
{
try
{
HttpListenerContext context = await http.GetContextAsync();
HandleWeb(context);
}
catch (Exception)
{
}
}
}
catch (Exception ex)
{
LoggerHelper.Instance.Error(ex);
}
}, TaskCreationOptions.LongRunning);
}
public void AddController(IWebApiController controller)
{
if (controller == null || string.IsNullOrWhiteSpace(controller.Path)) return;
dic.TryAdd(controller.Path.ToLower(), controller);
}
public void AddControllers(List<IWebApiController> controllers)
{
foreach (var controller in controllers)
{
if (controller == null || string.IsNullOrWhiteSpace(controller.Path)) continue;
dic.TryAdd(controller.Path.ToLower(), controller);
}
}
private void HandleWeb(HttpListenerContext context)
{
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
try
{
response.Headers.Set("Server", Helper.GlobalString);
string path = request.Url.AbsolutePath.ToLower();
string query = request.Url.Query;
//默认页面
if (path == "/") path = "online.json";
try
{
if (dic.TryGetValue(path, out IWebApiController controller))
{
Memory<byte> memory = controller.Handle(query);
response.ContentLength64 = memory.Length;
response.ContentType = "application/json";
response.OutputStream.Write(memory.Span);
response.OutputStream.Flush();
response.OutputStream.Close();
controller.Free();
}
else
{
response.StatusCode = (int)HttpStatusCode.NotFound;
}
}
catch (Exception ex)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(ex + $"");
response.ContentLength64 = bytes.Length;
response.ContentType = "text/plain; charset=utf-8";
response.OutputStream.Write(bytes, 0, bytes.Length);
response.OutputStream.Flush();
response.OutputStream.Close();
}
}
catch (Exception)
{
response.StatusCode = (int)HttpStatusCode.BadRequest;
}
response.Close();
}
}
}

View File

@@ -4,7 +4,8 @@ using linker.messenger.firewall.hooks;
using linker.messenger.forward.proxy;
using linker.messenger.socks5;
using linker.messenger.sync;
using linker.snat;
using linker.nat;
using linker.tun.hook;
using linker.tun;
using Microsoft.Extensions.DependencyInjection;

View File

@@ -1,6 +1,6 @@
using linker.libs;
using linker.messenger.signin;
using linker.snat;
using linker.nat;
namespace linker.messenger.firewall
{

View File

@@ -1,5 +1,5 @@
using linker.messenger.signin;
using linker.snat;
using linker.nat;
namespace linker.messenger.firewall
{

View File

@@ -1,4 +1,4 @@
using linker.snat;
using linker.nat;
namespace linker.messenger.firewall
{
@@ -17,7 +17,7 @@ namespace linker.messenger.firewall
public bool Check(FirewallCheckInfo info);
}
public sealed class FirewallRuleInfo : linker.snat.LinkerFirewallRuleInfo
public sealed class FirewallRuleInfo : linker.nat.LinkerFirewallRuleInfo
{
public string Id { get; set; }
public string GroupId { get; set; }

View File

@@ -1,6 +1,6 @@

using linker.messenger.forward.proxy;
using linker.snat;
using linker.nat;
using System.Net;
using System.Net.Sockets;

View File

@@ -1,5 +1,5 @@
using linker.messenger.socks5;
using linker.snat;
using linker.nat;
using System.Net;
using System.Net.Sockets;

View File

@@ -1,5 +1,5 @@
using linker.snat;
using linker.tun;
using linker.nat;
using linker.tun.hook;
namespace linker.messenger.firewall.hooks
{
@@ -13,13 +13,13 @@ namespace linker.messenger.firewall.hooks
this.linkerFirewall = linkerFirewall;
}
public unsafe bool ReadAfter(ReadOnlyMemory<byte> packet)
public unsafe bool Read(ReadOnlyMemory<byte> packet)
{
linkerFirewall.AddAllow(packet);
return true;
}
public unsafe bool WriteBefore(string srcId, ReadOnlyMemory<byte> packet)
public unsafe bool Write(string srcId, ReadOnlyMemory<byte> packet)
{
return linkerFirewall.Check(srcId, packet);
}

View File

@@ -44,7 +44,8 @@
<ProjectReference Include="..\linker.messenger.socks5\linker.messenger.socks5.csproj" />
<ProjectReference Include="..\linker.messenger.sync\linker.messenger.sync.csproj" />
<ProjectReference Include="..\linker.messenger\linker.messenger.csproj" />
<ProjectReference Include="..\linker.snat\linker.snat.csproj" />
<ProjectReference Include="..\linker.nat\linker.nat.csproj" />
<ProjectReference Include="..\linker.snat\linker.nat.csproj" />
<ProjectReference Include="..\linker.tun\linker.tun.csproj" />
</ItemGroup>

View File

@@ -1,6 +1,7 @@
using linker.libs.web;
using linker.messenger.flow.history;
using linker.messenger.flow.messenger;
using linker.messenger.flow.webapi;
using linker.messenger.forward.proxy;
using linker.messenger.relay.server;
using linker.messenger.socks5;
@@ -85,6 +86,10 @@ namespace linker.messenger.flow
serviceCollection.AddSingleton<FlowHistoryTransfer>();
serviceCollection.AddSingleton<WebApiOnlineController>();
serviceCollection.AddSingleton<WebApiCitysController>();
return serviceCollection;
}
public static ServiceProvider UseFlowServer(this ServiceProvider serviceProvider)
@@ -108,6 +113,11 @@ namespace linker.messenger.flow
resolverTransfer.AddResolvers(new List<IResolver> { serviceProvider.GetService<FlowResolver>() });
//FlowHistoryTransfer flowHistoryTransfer = serviceProvider.GetService<FlowHistoryTransfer>();
IWebApiServer webApiServer = serviceProvider.GetService<IWebApiServer>();
webApiServer.AddControllers(new List<IWebApiController> {
serviceProvider.GetService<WebApiOnlineController>(),
serviceProvider.GetService<WebApiCitysController>(),
});
return serviceProvider;
}

View File

@@ -5,7 +5,7 @@ using linker.messenger.relay.client;
using linker.messenger.signin;
using linker.messenger.tuntap;
using linker.messenger.tuntap.cidr;
using linker.tun;
using linker.nat;
using linker.tunnel;
using linker.tunnel.connection;
using System.Collections.Concurrent;

View File

@@ -47,5 +47,6 @@
<ProjectReference Include="..\linker.messenger.tunnel\linker.messenger.tunnel.csproj" />
<ProjectReference Include="..\linker.messenger.tuntap\linker.messenger.tuntap.csproj" />
<ProjectReference Include="..\linker.messenger\linker.messenger.csproj" />
<ProjectReference Include="..\linker.nat\linker.nat.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,24 @@
using linker.libs.extends;
using linker.libs.web;
using linker.messenger.signin;
namespace linker.messenger.flow.webapi
{
public sealed class WebApiCitysController : IWebApiController
{
public string Path => "/flow/citys.json";
private readonly FlowResolver flowResolver;
public WebApiCitysController(FlowResolver flowResolver)
{
this.flowResolver = flowResolver;
}
public Memory<byte> Handle(string query)
{
return flowResolver.GetCitys().ToJson().ToBytes();
}
public void Free()
{
}
}
}

View File

@@ -0,0 +1,40 @@
using linker.libs.extends;
using linker.libs.web;
using linker.messenger.signin;
namespace linker.messenger.flow.webapi
{
public sealed class WebApiOnlineController : IWebApiController
{
public string Path => "/flow/online.json";
private readonly SignInServerCaching signCaching;
private readonly FlowResolver flowResolver;
public WebApiOnlineController(SignInServerCaching signCaching, FlowResolver flowResolver)
{
this.signCaching = signCaching;
this.flowResolver = flowResolver;
}
public Memory<byte> Handle(string query)
{
signCaching.GetOnline(out int all, out int online);
return new
{
CurrentServer = new
{
Online7day = all,
Online = online,
},
AllServer = new
{
Online7day = flowResolver.ReceiveBytes & 0xffffffff,
Online = flowResolver.ReceiveBytes >> 32,
Server = flowResolver.SendtBytes,
}
}.ToJson().ToBytes();
}
public void Free()
{
}
}
}

View File

@@ -1,4 +1,5 @@
using linker.libs;
using linker.libs.web;
using Microsoft.Extensions.DependencyInjection;
namespace linker.messenger.listen
{
@@ -7,11 +8,13 @@ namespace linker.messenger.listen
public static ServiceCollection AddListen(this ServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<TcpServer>();
serviceCollection.AddSingleton<IWebApiServer,WebApiServer>();
return serviceCollection;
}
public static ServiceProvider UseListen(this ServiceProvider serviceProvider)
{
TcpServer tcpServer = serviceProvider.GetService<TcpServer>();
IWebApiServer webapiServer = serviceProvider.GetService<IWebApiServer>();
IListenStore listenStore = serviceProvider.GetService<IListenStore>();
LoggerHelper.Instance.Info($"start server");
@@ -25,6 +28,20 @@ namespace linker.messenger.listen
}
LoggerHelper.Instance.Warning($"server listen:{listenStore.Port}");
if (listenStore.ApiPort > 0)
{
LoggerHelper.Instance.Info($"start server web api");
try
{
webapiServer.Start(listenStore.ApiPort);
}
catch (Exception ex)
{
LoggerHelper.Instance.Error(ex);
}
LoggerHelper.Instance.Warning($"server web api listen:{listenStore.Port}");
}
return serviceProvider;
}
}

View File

@@ -6,12 +6,14 @@
/// 端口
/// </summary>
public int Port { get; }
public int ApiPort { get; }
/// <summary>
/// 设置端口
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public bool SetPort(int port);
public bool SetApiPort(int port);
/// <summary>
/// 提交
/// </summary>

View File

@@ -3,6 +3,7 @@ using linker.messenger.relay.client;
using linker.messenger.relay.messenger;
using linker.messenger.relay.server;
using linker.messenger.relay.server.validator;
using linker.messenger.relay.webapi;
using linker.messenger.sync;
using Microsoft.Extensions.DependencyInjection;
namespace linker.messenger.relay
@@ -54,6 +55,8 @@ namespace linker.messenger.relay
serviceCollection.AddSingleton<IRelayServerWhiteListStore, RelayServerWhiteListStore>();
serviceCollection.AddSingleton<IRelayServerCdkeyStore, RelayServerCdkeyStore>();
serviceCollection.AddSingleton<WebApiRelayNodesController>();
return serviceCollection;
}
public static ServiceProvider UseRelayServer(this ServiceProvider serviceProvider)
@@ -70,6 +73,10 @@ namespace linker.messenger.relay
RelayServerNodeTransfer relayServerNodeTransfer = serviceProvider.GetService<RelayServerNodeTransfer>();
RelayServerMasterTransfer relayServerMasterTransfer = serviceProvider.GetService<RelayServerMasterTransfer>();
IWebApiServer webApiServer = serviceProvider.GetService<IWebApiServer>();
webApiServer.AddController(serviceProvider.GetService<WebApiRelayNodesController>());
return serviceProvider;
}
}

View File

@@ -199,6 +199,22 @@ namespace linker.messenger.relay.server
.ThenByDescending(x => x.MaxGbTotalLastBytes == 0 ? long.MaxValue : x.MaxGbTotalLastBytes)
.ToList();
}
public List<RelayServerNodeReportInfo188> GetPublicNodes()
{
var result = reports.Values
.Where(c => Environment.TickCount64 - c.LastTicks < 15000)
.Where(c => c.Public)
.OrderByDescending(c => c.LastTicks);
return result.OrderByDescending(x => x.MaxConnection == 0 ? int.MaxValue : x.MaxConnection)
.ThenBy(x => x.ConnectionRatio)
.ThenBy(x => x.BandwidthRatio)
.ThenByDescending(x => x.MaxBandwidth == 0 ? double.MaxValue : x.MaxBandwidth)
.ThenByDescending(x => x.MaxBandwidthTotal == 0 ? double.MaxValue : x.MaxBandwidthTotal)
.ThenByDescending(x => x.MaxGbTotal == 0 ? double.MaxValue : x.MaxGbTotal)
.ThenByDescending(x => x.MaxGbTotalLastBytes == 0 ? long.MaxValue : x.MaxGbTotalLastBytes)
.ToList();
}

View File

@@ -0,0 +1,48 @@
using linker.libs.extends;
using linker.libs.web;
using linker.messenger.relay.server;
namespace linker.messenger.relay.webapi
{
public sealed class WebApiRelayNodesController : IWebApiController
{
public string Path => "/relay/nodes.json";
private readonly RelayServerMasterTransfer relayServerMasterTransfer;
public WebApiRelayNodesController(RelayServerMasterTransfer relayServerMasterTransfer)
{
this.relayServerMasterTransfer = relayServerMasterTransfer;
}
public Memory<byte> Handle(string query)
{
return relayServerMasterTransfer.GetPublicNodes().Select(c =>
{
return new
{
AllowProtocol = c.AllowProtocol,
Name = c.Name,
Version = c.Version,
BandwidthMaxMbps = c.MaxBandwidthTotal,
BandwidthConnMbps = c.MaxBandwidth,
BandwidthCurrentMbps = c.BandwidthRatio,
BandwidthGbMonth = c.MaxGbTotal,
BandwidthByteAvailable = c.MaxGbTotalLastBytes,
ConnectionMaxNum = c.MaxConnection,
ConnectionCurrentNum = c.ConnectionRatio,
EndPoint = c.EndPoint,
Url = c.Url,
};
}).ToJson().ToBytes();
}
public void Free()
{
}
}
}

View File

@@ -1,5 +1,5 @@
using linker.messenger.firewall;
using linker.snat;
using linker.nat;
using MemoryPack;
namespace linker.messenger.serializer.memorypack
@@ -29,10 +29,10 @@ namespace linker.messenger.serializer.memorypack
string DstPort => info.DstPort;
[MemoryPackInclude]
snat.LinkerFirewallProtocolType Protocol => info.Protocol;
nat.LinkerFirewallProtocolType Protocol => info.Protocol;
[MemoryPackInclude]
snat.LinkerFirewallAction Action => info.Action;
nat.LinkerFirewallAction Action => info.Action;
[MemoryPackInclude]
bool Disabled => info.Disabled;
@@ -45,7 +45,7 @@ namespace linker.messenger.serializer.memorypack
[MemoryPackConstructor]
SerializableFirewallRuleInfo(string id, string srcId, string srcName, string groupId, string dstCIDR, string dstPort,
snat.LinkerFirewallProtocolType protocol, snat.LinkerFirewallAction action, bool disabled, int orderby, string remark)
nat.LinkerFirewallProtocolType protocol, nat.LinkerFirewallAction action, bool disabled, int orderby, string remark)
{
var info = new FirewallRuleInfo
{
@@ -115,14 +115,14 @@ namespace linker.messenger.serializer.memorypack
int Disabled => info.Disabled;
[MemoryPackInclude]
snat.LinkerFirewallProtocolType Protocol => info.Protocol;
nat.LinkerFirewallProtocolType Protocol => info.Protocol;
[MemoryPackInclude]
snat.LinkerFirewallAction Action => info.Action;
nat.LinkerFirewallAction Action => info.Action;
[MemoryPackConstructor]
SerializableFirewallSearchInfo(string groupId, string str, snat.LinkerFirewallProtocolType protocol,
snat.LinkerFirewallAction action, int disabled)
SerializableFirewallSearchInfo(string groupId, string str, nat.LinkerFirewallProtocolType protocol,
nat.LinkerFirewallAction action, int disabled)
{
var info = new FirewallSearchInfo
{

View File

@@ -1,9 +1,9 @@
using linker.libs;
using linker.messenger.exroute;
using linker.messenger.signin;
using linker.snat;
using linker.nat;
using System.Net;
using static linker.snat.LinkerDstMapping;
using static linker.nat.LinkerDstMapping;
namespace linker.messenger.socks5
{

View File

@@ -10,7 +10,6 @@ using linker.messenger.relay.client;
using linker.messenger.channel;
using linker.messenger.signin;
using linker.messenger.pcp;
using static linker.snat.WinDivert;
using System.Buffers;
namespace linker.messenger.socks5

View File

@@ -43,7 +43,8 @@
<ProjectReference Include="..\linker.messenger.exroute\linker.messenger.exroute.csproj" />
<ProjectReference Include="..\linker.messenger.signin\linker.messenger.signin.csproj" />
<ProjectReference Include="..\linker.messenger\linker.messenger.csproj" />
<ProjectReference Include="..\linker.snat\linker.snat.csproj" />
<ProjectReference Include="..\linker.nat\linker.nat.csproj" />
<ProjectReference Include="..\linker.snat\linker.nat.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
using linker.snat;
using linker.nat;
namespace linker.messenger.store.file
{

View File

@@ -16,8 +16,8 @@ namespace linker.messenger.store.file.firewall
this.runningConfig = runningConfig;
}
public snat.LinkerFirewallState State => runningConfig.Data.Firewall.State;
public void SetState(snat.LinkerFirewallState state)
public nat.LinkerFirewallState State => runningConfig.Data.Firewall.State;
public void SetState(nat.LinkerFirewallState state)
{
runningConfig.Data.Firewall.State = state;
runningConfig.Data.Update();

View File

@@ -1,8 +1,8 @@
using linker.libs.extends;
namespace linker.messenger.store.file
namespace linker.messenger.store.file
{
public sealed partial class ConfigServerInfo
{
public int ServicePort { get; set; } = 1802;
public int ApiPort { get; set; } = 1803;
}
}

View File

@@ -5,6 +5,7 @@ namespace linker.messenger.store.file.server
public sealed class ListenStore: IListenStore
{
public int Port => config.Data.Server.ServicePort;
public int ApiPort => config.Data.Server.ApiPort;
private readonly FileConfig config;
public ListenStore(FileConfig config)
@@ -17,6 +18,11 @@ namespace linker.messenger.store.file.server
config.Data.Server.ServicePort = port;
return true;
}
public bool SetApiPort(int port)
{
config.Data.Server.ApiPort = port;
return true;
}
public bool Confirm()
{

View File

@@ -8,6 +8,7 @@ using linker.messenger.tunnel;
using linker.messenger.tuntap.cidr;
using linker.messenger.tuntap.lease;
using linker.messenger.tuntap.messenger;
using linker.nat;
using linker.tun;
using Microsoft.Extensions.DependencyInjection;
using System.Net;
@@ -44,6 +45,7 @@ namespace linker.messenger.tuntap
serviceCollection.AddSingleton<TuntapCidrMapfileManager>();
serviceCollection.AddSingleton<LinkerFakeAckTransfer>();
serviceCollection.AddSingleton<FakeAckTransfer>();

View File

@@ -3,10 +3,10 @@ using linker.libs.extends;
using linker.libs.timer;
using linker.messenger.exroute;
using linker.messenger.signin;
using linker.tun;
using linker.tun.device;
using linker.tunnel.connection;
using System.Net;
using static linker.snat.LinkerDstMapping;
using static linker.nat.LinkerDstMapping;
namespace linker.messenger.tuntap
{

View File

@@ -1,12 +1,13 @@
using linker.tunnel;
using linker.tunnel.connection;
using linker.libs;
using linker.tun;
using System.Buffers.Binary;
using linker.messenger.relay.client;
using linker.messenger.signin;
using linker.messenger.pcp;
using linker.messenger.tuntap.cidr;
using linker.nat;
using linker.tun.device;
namespace linker.messenger.tuntap
{
@@ -103,15 +104,15 @@ namespace linker.messenger.tuntap
uint ip = BinaryPrimitives.ReadUInt32BigEndian(packet.DistIPAddress.Span[^4..]);
if (tuntapCidrConnectionManager.TryGet(ip, out ITunnelConnection connection) && connection.Connected)
{
/*
if (connection.PacketBuffer.Length > 0)
{
fakeAckTransfer.Read(packet.IPPacket);
}
*/
await connection.SendAsync(packet.Buffer, packet.Offset, packet.Length).ConfigureAwait(false);
return;
}
/*
if (connection.PacketBuffer.Length > 0)
{
fakeAckTransfer.Read(packet.IPPacket);
}
*/
//开始操作,开始失败直接丢包
if (operatingMultipleManager.StartOperation(ip) == false)

View File

@@ -2,7 +2,8 @@
using System.Net;
using linker.tun;
using linker.libs.timer;
using static linker.snat.LinkerDstMapping;
using static linker.nat.LinkerDstMapping;
using linker.tun.device;
namespace linker.messenger.tuntap
{

View File

@@ -1,7 +1,7 @@
using linker.libs;
using linker.messenger.exroute;
using linker.messenger.signin;
using linker.tun;
using linker.tun.device;
using System.Net;
namespace linker.messenger.tuntap.cidr

View File

@@ -5,7 +5,7 @@ using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Net;
namespace linker.snat
namespace linker.nat
{
/// <summary>
/// 网段映射
@@ -14,8 +14,8 @@ namespace linker.snat
public sealed class LinkerDstMapping
{
private FrozenDictionary<uint, uint> mapDic = new Dictionary<uint, uint>().ToFrozenDictionary();
private uint[] masks = Array.Empty<uint>();
private ConcurrentDictionary<uint, uint> natDic = new ConcurrentDictionary<uint, uint>();
private uint[] masks = [];
private readonly ConcurrentDictionary<uint, uint> natDic = new ConcurrentDictionary<uint, uint>();
/// <summary>
/// 设置映射目标
@@ -26,7 +26,7 @@ namespace linker.snat
if (maps == null || maps.Length == 0)
{
mapDic = new Dictionary<uint, uint>().ToFrozenDictionary();
masks = Array.Empty<uint>();
masks = [];
natDic.Clear();
return;
}
@@ -83,7 +83,7 @@ namespace linker.snat
/// </summary>
/// <param name="packet">TCP/IP</param>
/// <param name="checksum">是否计算校验和如果使用了应用层NAT可以交给应用层NAT去计算校验和</param>
public void ToRealDst(ReadOnlyMemory<byte> packet, bool checksum = true)
public void ToRealDst(ReadOnlyMemory<byte> packet)
{
//只支持映射IPV4
if ((byte)(packet.Span[0] >> 4 & 0b1111) != 4) return;
@@ -101,8 +101,8 @@ namespace linker.snat
{
uint realDist = realNetwork | (fakeDist & ~masks[i]);
//修改目标IP
ReWriteIP(packet, realDist, 16, checksum);
if(natDic.TryGetValue(realDist,out uint value) == false || value != fakeDist)
ReWriteIP(packet, realDist, 16);
if (natDic.TryGetValue(realDist, out uint value) == false || value != fakeDist)
{
natDic.AddOrUpdate(realDist, fakeDist, (a, b) => fakeDist);
}
@@ -116,18 +116,14 @@ namespace linker.snat
/// <param name="packet">IP包</param>
/// <param name="newIP">大端IP</param>
/// <param name="pos">写入位置源12目的16</param>
/// <param name="checksum">是否计算校验和当windows使用应用层NAT后会计算一次这样可以减少一次计算</param>
private unsafe void ReWriteIP(ReadOnlyMemory<byte> packet, uint newIP, int pos, bool checksum = true)
private unsafe void ReWriteIP(ReadOnlyMemory<byte> packet, uint newIP, int pos)
{
fixed (byte* ptr = packet.Span)
{
//修改目标IP需要小端写入IP计算都是按大端的操作是小端的所以转换一下
*(uint*)(ptr + pos) = BinaryPrimitives.ReverseEndianness(newIP);
if (checksum)
{
//计算校验和
ChecksumHelper.Checksum(ptr);
}
//清空校验和,等待重新计算
*(ushort*)(ptr + 10) = 0;
}
}

View File

@@ -0,0 +1,531 @@
using linker.libs;
using linker.libs.extends;
using linker.libs.timer;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
namespace linker.nat
{
/// <summary>
/// 目标代理实现类似DNAT的功能
/// </summary>
public sealed class LinkerDstProxy
{
public bool Running => listenSocketTcp != null;
private Socket listenSocketTcp;
private Socket listenSocketUdp;
ushort proxyPort = 0;
uint tunIp = 0;
private ValueTuple<uint, uint>[] lans = [];
private readonly ConcurrentDictionary<(uint srcIp, ushort srcPort), DstCacheInfo> dic = new();
private readonly ConcurrentDictionary<(uint srcIp, ushort srcPort, uint dstIp, ushort dstPort), UdpState> udpMap = new();
public LinkerDstProxy()
{
Clear();
}
public bool Setup(IPAddress dstAddr, ValueTuple<IPAddress, byte>[] dsts, ref string error)
{
try
{
if (dsts == null || dsts.Length == 0)
{
lans = [];
dic.Clear();
return false;
}
lans = dsts.Select(c => new ValueTuple<uint, uint>(NetworkHelper.ToNetworkValue(c.Item1, c.Item2), NetworkHelper.ToBroadcastValue(c.Item1, c.Item2))).ToArray();
listenSocketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenSocketTcp.Bind(new IPEndPoint(IPAddress.Any, 0));
listenSocketTcp.Listen(int.MaxValue);
_ = ReceiveTcp();
listenSocketUdp = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
listenSocketUdp.Bind(new IPEndPoint(IPAddress.Any, (listenSocketTcp.LocalEndPoint as IPEndPoint).Port));
_ = ReceiveUdp();
tunIp = NetworkHelper.ToValue(dstAddr);
proxyPort = (ushort)(listenSocketTcp.LocalEndPoint as IPEndPoint).Port;
error = string.Empty;
}
catch (Exception ex)
{
error = ex.Message;
return false;
}
return true;
}
private async Task ReceiveTcp()
{
try
{
while (true)
{
Socket source = await listenSocketTcp.AcceptAsync();
IPEndPoint ep = source.RemoteEndPoint as IPEndPoint;
(uint srcIp, ushort srcPort) key = (NetworkHelper.ToValue(ep.Address), (ushort)ep.Port);
if (dic.TryGetValue(key, out DstCacheInfo cache) == false)
{
source.SafeClose();
continue;
}
Socket dst = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
dst.BeginConnect(new IPEndPoint(NetworkHelper.ToIP(cache.IP), cache.Port), ConnectCallback, new TcpState { Source = source, Target = dst });
}
}
catch (Exception ex)
{
LoggerHelper.Instance.Error(ex);
Shutdown();
}
}
private async void ConnectCallback(IAsyncResult result)
{
TcpState state = result.AsyncState as TcpState;
try
{
state.Target.EndConnect(result);
state.Target.KeepAlive();
state.Source.KeepAlive();
using IMemoryOwner<byte> buffer1 = MemoryPool<byte>.Shared.Rent(8192);
using IMemoryOwner<byte> buffer2 = MemoryPool<byte>.Shared.Rent(8192);
await Task.WhenAll(
CopyToAsync(buffer1.Memory, state.Source, state.Target),
CopyToAsync(buffer2.Memory, state.Target, state.Source)
).ConfigureAwait(false);
}
catch (Exception ex)
{
LoggerHelper.Instance.Error(ex);
}
finally
{
state.Source.SafeClose();
state.Target.SafeClose();
}
}
private async Task CopyToAsync(Memory<byte> buffer, Socket source, Socket target)
{
try
{
int bytesRead;
while ((bytesRead = await source.ReceiveAsync(buffer, SocketFlags.None).ConfigureAwait(false)) != 0)
{
await target.SendAsync(buffer.Slice(0, bytesRead), SocketFlags.None).ConfigureAwait(false);
}
}
catch (Exception ex)
{
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex);
}
}
finally
{
source.SafeClose();
target.SafeClose();
}
}
private async Task ReceiveUdp()
{
try
{
using IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.Rent(65535);
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
SocketReceiveFromResult result = await listenSocketUdp.ReceiveFromAsync(memory.Memory, remoteEndPoint);
if (result.ReceivedBytes == 0) continue;
IPEndPoint ep = result.RemoteEndPoint as IPEndPoint;
(uint srcIp, ushort srcPort) key = (NetworkHelper.ToValue(ep.Address), (ushort)ep.Port);
if (dic.TryGetValue(key, out DstCacheInfo cache) == false) continue;
(uint srcIp, ushort srcPort, uint dstIp, ushort dstPort) keyUdp = (NetworkHelper.ToValue(ep.Address), (ushort)ep.Port, cache.IP, cache.Port);
if (udpMap.TryGetValue(keyUdp, out UdpState state) == false)
{
state = new UdpState
{
Source = listenSocketUdp,
SourceEP = new IPEndPoint(ep.Address, ep.Port),
Target = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp),
TargetEP = new IPEndPoint(NetworkHelper.ToIP(cache.IP), cache.Port)
};
udpMap.AddOrUpdate(keyUdp, state, (a, b) => state);
await state.Target.SendToAsync(memory.Memory.Slice(0, result.ReceivedBytes), state.TargetEP);
ConnectCallback(keyUdp, state);
}
else
{
await state.Target.SendToAsync(memory.Memory.Slice(0, result.ReceivedBytes), state.TargetEP);
}
}
}
catch (Exception ex)
{
LoggerHelper.Instance.Error(ex);
Shutdown();
}
}
private async void ConnectCallback((uint srcIp, ushort srcPort, uint dstIp, ushort dstPort) keyUdp, UdpState state)
{
try
{
using IMemoryOwner<byte> memory = MemoryPool<byte>.Shared.Rent(65535);
while (true)
{
SocketReceiveFromResult result = await state.Target.ReceiveFromAsync(memory.Memory, state.TargetEP);
if (result.ReceivedBytes == 0) continue;
await state.Source.SendToAsync(memory.Memory.Slice(0, result.ReceivedBytes), state.SourceEP);
}
}
catch (Exception ex)
{
LoggerHelper.Instance.Error(ex);
}
finally
{
state.Target.SafeClose();
udpMap.TryRemove(keyUdp, out _);
}
}
public void Shutdown()
{
listenSocketTcp?.SafeClose();
listenSocketTcp = null;
listenSocketUdp?.SafeClose();
listenSocketUdp = null;
}
/// <summary>
/// 从网卡读取之后
/// </summary>
/// <param name="packet"></param>
/// <param name="needChecksum"></param>
public unsafe void Read(ReadOnlyMemory<byte> packet)
{
if (listenSocketTcp == null) return;
fixed (byte* ptr = packet.Span)
{
DstProxyPacket p = new DstProxyPacket(ptr);
if (p.Version != 4) return;
if (p.Protocol == ProtocolType.Tcp || p.Protocol == ProtocolType.Udp)
{
if (dic.TryGetValue((p.DstAddr, p.DstPort), out DstCacheInfo cache))
{
cache.LastTime = Environment.TickCount64;
p.SrcAddr = cache.IP;
p.SrcPort = cache.Port;
p.IPChecksum = 0;
p.PayloadChecksum = 0;
if (p.Protocol == ProtocolType.Tcp && (p.TcpFlagFin || p.TcpFlagRst))
{
cache.Fin = true;
}
}
}
}
}
/// <summary>
/// 写如网卡之前
/// </summary>
/// <param name="packet">单个完整TCP/IP包</param>
/// <returns></returns>
public unsafe bool Write(ReadOnlyMemory<byte> packet)
{
if (listenSocketTcp == null) return true;
fixed (byte* ptr = packet.Span)
{
DstProxyPacket p = new DstProxyPacket(ptr);
if (p.Version != 4 || p.DstAddr == tunIp || p.DstAddrSpan.IsCast()) return true;
if (lans.Any(c => p.DstAddr >= c.Item1 && p.DstAddr <= c.Item2) == false)
{
return true;
}
return p.Protocol switch
{
ProtocolType.Tcp => WriteTcp(p),
ProtocolType.Udp => WriteUdp(p),
ProtocolType.Icmp => WriteIcmp(p),
_ => true,
};
}
}
private bool WriteTcp(DstProxyPacket p)
{
(uint srcIp, ushort srcPort) key = (p.SrcAddr, p.SrcPort);
if (dic.TryGetValue(key, out DstCacheInfo cache) == false || cache.IP != p.DstAddr || cache.Port != p.DstPort)
{
if (p.IsOnlySyn == false) return true;
cache = new DstCacheInfo { IP = p.DstAddr, Port = p.DstPort };
dic.AddOrUpdate(key, cache, (a, b) => cache);
}
cache.LastTime = Environment.TickCount64;
if (p.TcpFlagFin || p.TcpFlagRst)
{
cache.Fin = true;
}
//改为代理地址
p.DstPort = proxyPort;
p.DstAddr = tunIp;
//重新计算校验和
p.IPChecksum = 0;
p.PayloadChecksum = 0;
return true;
}
private bool WriteUdp(DstProxyPacket p)
{
(uint srcIp, ushort srcPort) key = (p.SrcAddr, p.SrcPort);
if (dic.TryGetValue(key, out DstCacheInfo cache) == false || cache.IP != p.DstAddr || cache.Port != p.DstPort)
{
cache = new DstCacheInfo { IP = p.DstAddr, Port = p.DstPort, Fin = true };
dic.AddOrUpdate(key, cache, (a, b) => cache);
}
cache.LastTime = Environment.TickCount64;
//改为代理地址
p.DstPort = proxyPort;
p.DstAddr = tunIp;
//重新计算校验和
p.IPChecksum = 0;
p.PayloadChecksum = 0;
return true;
}
private bool WriteIcmp(DstProxyPacket p)
{
if (p.IcmpType == 8)
{
using Ping ping = new Ping();
PingReply reply = ping.Send(NetworkHelper.ToIP(p.DstAddr), 1000);
if (reply.Status != IPStatus.Success)
{
return false;
}
//交换IP
(p.DstAddr, p.SrcAddr) = (p.SrcAddr, p.DstAddr);
//改为response
p.IcmpType = 0;
//重新计算校验和
p.IPChecksum = 0;
p.PayloadChecksum = 0;
}
return true;
}
private void Clear()
{
TimerHelper.SetIntervalLong(() =>
{
foreach (var item in dic.Where(c => c.Value.Fin && Environment.TickCount64 - c.Value.LastTime > 5 * 60 * 1000).Select(c => c.Key).ToList())
{
dic.TryRemove(item, out _);
}
}, 30000);
}
sealed class DstCacheInfo
{
public long LastTime { get; set; } = Environment.TickCount64;
public uint IP { get; set; }
public ushort Port { get; set; }
public bool Fin { get; set; }
}
sealed class TcpState
{
public Socket Source { get; set; }
public Socket Target { get; set; }
}
sealed class UdpState
{
public Socket Source { get; set; }
public IPEndPoint SourceEP { get; set; }
public Socket Target { get; set; }
public IPEndPoint TargetEP { get; set; }
}
readonly unsafe struct DstProxyPacket
{
private readonly byte* ptr;
/// <summary>
/// 协议版本
/// </summary>
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
public readonly byte IcmpType
{
get
{
return *PayloadPtr;
}
set
{
*PayloadPtr = value;
}
}
/// <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);
}
}
public ReadOnlySpan<byte> DstAddrSpan => new Span<byte>((ptr + 16), 4);
public readonly byte TcpFlag => *(ptr + IPHeadLength + 13);
public readonly bool TcpFlagFin => (TcpFlag & 0b000001) != 0;
public readonly bool TcpFlagSyn => (TcpFlag & 0b000010) != 0;
public readonly bool TcpFlagRst => (TcpFlag & 0b000100) != 0;
public readonly bool TcpFlagPsh => (TcpFlag & 0b001000) != 0;
public readonly bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
public readonly bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
public readonly bool IsPshAck => TcpFlagPsh && TcpFlagAck;
public readonly bool IsOnlyAck => TcpFlag == 0b00010000;
public readonly bool IsOnlySyn => TcpFlag == 0b00000010;
public readonly bool IsSynAck => TcpFlag == 0b00010010;
/// <summary>
/// 目标端口
/// </summary>
public readonly ushort DstPort
{
get
{
return BinaryPrimitives.ReverseEndianness(*(ushort*)(PayloadPtr + 2));
}
set
{
*(ushort*)(PayloadPtr + 2) = BinaryPrimitives.ReverseEndianness(value);
}
}
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 DstProxyPacket(byte* ptr)
{
this.ptr = ptr;
}
}
}
}

View File

@@ -0,0 +1,563 @@
using linker.libs;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Net.Sockets;
namespace linker.nat
{
/// <summary>
/// 伪造ACK操作类
/// </summary>
public unsafe sealed class LinkerFakeAckTransfer
{
private readonly ConcurrentDictionary<FaceAckKey, FackAckState> dic = new(new FackAckKeyComparer());
/// <summary>
/// 发起方
/// </summary>
/// <param name="packet">一个完整的TCP/IP包</param>
/// <param name="fakeBuffer">一个能容纳ACK包的缓冲区如果需要伪造ACK则写入到这里</param>
/// <param name="bufferFree">缓冲区可用字节数会根据这个来计算ack的窗口大小</param>
/// <param name="fakeLength">ack包长度</param>
/// <returns>是否丢包</returns>
public bool Read(ReadOnlyMemory<byte> packet, ReadOnlyMemory<byte> fakeBuffer, long bufferFree, out ushort fakeLength)
{
fakeLength = 0;
fixed (byte* ptr = packet.Span)
{
FakeAckPacket originPacket = new(ptr);
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
{
return false;
}
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
if ((originPacket.IsOnlyAck || originPacket.IsPshAck) && dic.TryGetValue(key, out FackAckState state))
{
if (originPacket.TcpPayloadLength == 0)
{
return state.Ack++ > 0;
}
fixed (byte* pptr = fakeBuffer.Span)
{
ushort win = (ushort)Math.Max(Math.Min(bufferFree * 0.8 / state.WindowScale, 32 * 1024), 4);
fakeLength = originPacket.ToAck(state.Seq, win, pptr);
if (new FakeAckPacket(pptr).Cq <= state.Cq)
{
fakeLength = 0;
}
}
}
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
{
dic.TryRemove(key, out _);
}
}
return false;
}
/// <summary>
/// 接收方
/// </summary>
/// <param name="packet">一个完整的TCP/IP包</param>
/// <returns></returns>
public void Write(ReadOnlyMemory<byte> packet)
{
fixed (byte* ptr = packet.Span)
{
FakeAckPacket originPacket = new(ptr);
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
{
return;
}
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
/*
//更新序列号
if (originPacket.TcpFlagAck && dic.TryGetValue(key, out FackAckState state))
{
state.Cq = originPacket.Cq;
if (originPacket.TcpPayloadLength > 0)
{
state.Seq = originPacket.Seq + (uint)originPacket.TcpPayloadLength;
}
}
else*/
if (originPacket.IsOnlySyn || originPacket.IsSynAck)
{
FackAckState state = new()
{
Ack = (ulong)(originPacket.IsOnlySyn ? 1 : 0),
Seq = originPacket.Seq + 1,
WindowScale = originPacket.FindWindowScale(ptr + originPacket.IPHeadLength)
};
dic.AddOrUpdate(key, state, (a, b) => state);
}
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
{
dic.TryRemove(key, out _);
}
}
}
/// <summary>
/// 状态
/// </summary>
sealed class FackAckState
{
public ulong Ack { get; set; }
public uint Seq { get; set; }
public uint Cq { get; set; }
public int WindowScale { get; set; }
}
/// <summary>
/// 四元组缓存key
/// </summary>
struct FaceAckKey
{
public uint srcAddr;
public ushort srcPort;
public uint dstAddr;
public ushort dstPort;
}
/// <summary>
/// 四元组缓存key比较器
/// </summary>
sealed class FackAckKeyComparer : IEqualityComparer<FaceAckKey>
{
public bool Equals(FaceAckKey x, FaceAckKey y)
{
return (x.srcAddr, x.srcPort, x.dstAddr, x.dstPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort)
|| (x.dstAddr, x.dstPort, x.srcAddr, x.srcPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort);
}
public int GetHashCode(FaceAckKey obj)
{
return (int)obj.srcAddr ^ obj.srcPort ^ (int)obj.dstAddr ^ obj.dstPort;
}
}
/// <summary>
/// 数据包解析
/// </summary>
readonly unsafe struct FakeAckPacket
{
private readonly byte* ptr;
/// <summary>
/// 协议版本
/// </summary>
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
/// <summary>
/// 源地址
/// </summary>
public readonly uint SrcAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
/// <summary>
/// 源端口
/// </summary>
public readonly ushort SrcPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength));
/// <summary>
/// 目的地址
/// </summary>
public readonly uint DstAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
/// <summary>
/// 目标端口
/// </summary>
public readonly ushort DstPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 2));
/// <summary>
/// IP头长度
/// </summary>
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
/// <summary>
/// 序列号
/// </summary>
public readonly uint Seq => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + IPHeadLength + 4));
/// <summary>
/// 确认号
/// </summary>
public readonly uint Cq => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + IPHeadLength + 8));
/// <summary>
/// TCP负载长度
/// </summary>
public readonly int TcpPayloadLength
{
get
{
int ipHeadLength = (*ptr & 0b1111) * 4;
int tcpHeaderLength = (*(ptr + ipHeadLength + 12) >> 4) * 4;
return BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + 2)) - ipHeadLength - tcpHeaderLength;
}
}
/// <summary>
/// TCP Flag
/// </summary>
public readonly byte TcpFlag => *(ptr + IPHeadLength + 13);
public readonly bool TcpFlagFin => (TcpFlag & 0b000001) != 0;
public readonly bool TcpFlagSyn => (TcpFlag & 0b000010) != 0;
public readonly bool TcpFlagRst => (TcpFlag & 0b000100) != 0;
public readonly bool TcpFlagPsh => (TcpFlag & 0b001000) != 0;
public readonly bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
public readonly bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
public readonly bool IsPshAck => TcpFlagPsh && TcpFlagAck;
public readonly bool IsOnlyAck => TcpFlag == 0b00010000;
public readonly bool IsOnlySyn => TcpFlag == 0b00000010;
public readonly bool IsSynAck => TcpFlag == 0b00010010;
/// <summary>
/// 加载TCP/IP包必须是一个完整的TCP/IP包
/// </summary>
/// <param name="ptr">一个完整的TCP/IP包</param>
public FakeAckPacket(byte* ptr)
{
this.ptr = ptr;
}
/// <summary>
/// 制作一个ACK包
/// </summary>
/// <param name="seq">给定一个序列号可以从syn+ack包中+1获得</param>
/// <param name="winSize">窗口大小</param>
/// <param name="ipPtr">目标内存</param>
/// <returns></returns>
public readonly unsafe ushort ToAck(uint seq, ushort winSize, byte* ipPtr)
{
//复制一份IP+TCP头部
int ipHeaderLength = (*ptr & 0b1111) * 4;
int tcpHeaderLength = (*(ptr + ipHeaderLength + 12) >> 4) * 4;
ushort totalLength = BinaryPrimitives.ReverseEndianness(*(ushort*)(ipPtr + 2));
uint payloadLength = (uint)(totalLength - ipHeaderLength - tcpHeaderLength);
new Span<byte>(ptr, ipHeaderLength + tcpHeaderLength).CopyTo(new Span<byte>(ipPtr, ipHeaderLength + tcpHeaderLength));
//TCP头指针
byte* tcpPtr = ipPtr + ipHeaderLength;
//如果有时间戳,就填充时间戳选项
//FullOptionTimestamp(tcpPtr);
*(tcpPtr + 12) = 0b01010000;
//重新计算头部长度
tcpHeaderLength = (*(tcpPtr + 12) >> 4) * 4;
totalLength = (ushort)(ipHeaderLength + tcpHeaderLength);
//交换地址和端口
(*(uint*)(ipPtr + 16), *(uint*)(ipPtr + 12)) = (*(uint*)(ipPtr + 12), *(uint*)(ipPtr + 16));
(*(ushort*)(tcpPtr + 2), *(ushort*)(tcpPtr)) = (*(ushort*)(tcpPtr), *(ushort*)(tcpPtr + 2));
//设置总长度
*(ushort*)(ipPtr + 2) = BinaryPrimitives.ReverseEndianness(totalLength);
//重置分片相关信息
*(ushort*)(ipPtr + 4) = 0; // 清除分片偏移和标志
*(ushort*)(ipPtr + 6) = 0; // 清除更多分片标志
//源序列号
uint _seq = BinaryPrimitives.ReverseEndianness(*(uint*)(tcpPtr + 4));
//设置序列号
*(uint*)(tcpPtr + 4) = BinaryPrimitives.ReverseEndianness(seq);
//设置确认号
*(uint*)(tcpPtr + 8) = BinaryPrimitives.ReverseEndianness(_seq + payloadLength);
//设置TCP标志位为ACK其他标志位清除
*(tcpPtr + 13) = 0b00010000;
//设置窗口大小
*(ushort*)(tcpPtr + 14) = BinaryPrimitives.ReverseEndianness(Math.Max(winSize, (ushort)8));
//计算校验和
ChecksumHelper.Checksum(ipPtr);
//只需要IP头+TCP头
return totalLength;
}
/// <summary>
/// 从TCP的SYN包或SYN+ACK包中获取窗口缩放比例
/// </summary>
/// <param name="ipPtr">一个完整TCP/IP包</param>
/// <returns></returns>
public int FindWindowScale(byte* ipPtr)
{
//指针移动到TCP头开始位置
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
//tcp头固定20所以option从这里开始
int index = 20;
//tcp头结束位置就是option结束位置
int end = (*(tcpPtr + 12) >> 4) * 4;
while (index < end)
{
byte kind = *(tcpPtr + index);
//EOF结束符
if (kind == 0)
{
break;
}
byte length = *(tcpPtr + index + 1);
//NOP 空选项
if (kind == 1)
{
index++;
continue;
}
//Window Scale 1kind 1length 1shiftCount
else if (kind == 3 && length == 3)
{
byte shiftCount = *(tcpPtr + index + 2);
return shiftCount > 14 ? 1 : 1 << shiftCount;
}
index += length;
}
return 1;
}
}
}
public unsafe sealed class FakeAckTransfer
{
private readonly ConcurrentDictionary<FaceAckKey, int> dic = new(new FackAckKeyComparer());
/// <summary>
/// 发起方
/// </summary>
/// <param name="packet">一个完整的TCP/IP包</param>
/// <returns></returns>
public void Read(ReadOnlyMemory<byte> packet)
{
fixed (byte* ptr = packet.Span)
{
FakeAckPacket originPacket = new(ptr);
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
{
return;
}
if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
{
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
dic.TryRemove(key, out _);
}
}
}
/// <summary>
/// 接收方
/// </summary>
/// <param name="packet">一个完整的TCP/IP包</param>
/// <returns></returns>
public void Write(ReadOnlyMemory<byte> packet, long bufferFree)
{
fixed (byte* ptr = packet.Span)
{
FakeAckPacket originPacket = new(ptr);
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
{
return;
}
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
if (originPacket.IsPshAck || originPacket.IsOnlyAck)
{
if (dic.TryGetValue(key, out int wins) && originPacket.Window > 0)
{
ushort win = (ushort)Math.Max(Math.Min(bufferFree / wins, 65535), 4);
originPacket.WriteWindow(ptr, win);
}
}
else if (originPacket.IsOnlySyn || originPacket.IsSynAck)
{
int windowScale = originPacket.FindWindowScale(ptr);
dic.AddOrUpdate(key, windowScale, (a, b) => windowScale);
}
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
{
dic.TryRemove(key, out _);
}
}
}
/// <summary>
/// 四元组缓存key
/// </summary>
struct FaceAckKey
{
public uint srcAddr;
public ushort srcPort;
public uint dstAddr;
public ushort dstPort;
}
/// <summary>
/// 四元组缓存key比较器
/// </summary>
sealed class FackAckKeyComparer : IEqualityComparer<FaceAckKey>
{
public bool Equals(FaceAckKey x, FaceAckKey y)
{
return (x.srcAddr, x.srcPort, x.dstAddr, x.dstPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort)
|| (x.dstAddr, x.dstPort, x.srcAddr, x.srcPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort);
}
public int GetHashCode(FaceAckKey obj)
{
return (int)obj.srcAddr ^ obj.srcPort ^ (int)obj.dstAddr ^ obj.dstPort;
}
}
/// <summary>
/// 数据包解析
/// </summary>
readonly unsafe struct FakeAckPacket
{
private readonly byte* ptr;
/// <summary>
/// 协议版本
/// </summary>
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
/// <summary>
/// 源地址
/// </summary>
public readonly uint SrcAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
/// <summary>
/// 源端口
/// </summary>
public readonly ushort SrcPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength));
/// <summary>
/// 目的地址
/// </summary>
public readonly uint DstAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
/// <summary>
/// 目标端口
/// </summary>
public readonly ushort DstPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 2));
/// <summary>
/// IP头长度
/// </summary>
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
/// <summary>
/// 窗口大小
/// </summary>
public readonly ushort Window => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 14));
/// <summary>
/// TCP Flag
/// </summary>
public readonly byte TcpFlag => *(ptr + IPHeadLength + 13);
public readonly bool TcpFlagFin => (TcpFlag & 0b000001) != 0;
public readonly bool TcpFlagSyn => (TcpFlag & 0b000010) != 0;
public readonly bool TcpFlagRst => (TcpFlag & 0b000100) != 0;
public readonly bool TcpFlagPsh => (TcpFlag & 0b001000) != 0;
public readonly bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
public readonly bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
public readonly bool IsPshAck => TcpFlagPsh && TcpFlagAck;
public readonly bool IsOnlyAck => TcpFlag == 0b00010000;
public readonly bool IsOnlySyn => TcpFlag == 0b00000010;
public readonly bool IsSynAck => TcpFlag == 0b00010010;
/// <summary>
/// 加载TCP/IP包必须是一个完整的TCP/IP包
/// </summary>
/// <param name="ptr">一个完整的TCP/IP包</param>
public FakeAckPacket(byte* ptr)
{
this.ptr = ptr;
}
public int FindWindowScale(byte* ipPtr)
{
//指针移动到TCP头开始位置
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
//tcp头固定20所以option从这里开始
int index = 20;
//tcp头结束位置就是option结束位置
int end = (*(tcpPtr + 12) >> 4) * 4;
while (index < end)
{
byte kind = *(tcpPtr + index);
//EOF结束符
if (kind == 0)
{
break;
}
byte length = *(tcpPtr + index + 1);
//NOP 空选项
if (kind == 1)
{
index++;
continue;
}
//Window Scale 1kind 1length 1shiftCount
else if (kind == 3 && length == 3)
{
return *(tcpPtr + index + 2);
}
index += length;
}
return 0;
}
public int WriteWindowScale(byte* ipPtr, byte windowScale = 7)
{
//指针移动到TCP头开始位置
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
//tcp头固定20所以option从这里开始
int index = 20;
//tcp头结束位置就是option结束位置
int end = (*(tcpPtr + 12) >> 4) * 4;
while (index < end)
{
byte kind = *(tcpPtr + index);
//EOF结束符
if (kind == 0)
{
break;
}
byte length = *(tcpPtr + index + 1);
//NOP 空选项
if (kind == 1)
{
index++;
continue;
}
//Window Scale 1kind 1length 1shiftCount
else if (kind == 3 && length == 3)
{
if (*(tcpPtr + index + 2) < windowScale)
{
*(tcpPtr + index + 2) = windowScale;
ChecksumHelper.Checksum(ipPtr, false, true);
}
return *(tcpPtr + index + 2);
}
index += length;
}
return 0;
}
public void WriteWindow(byte* ipPtr, ushort window)
{
*(ushort*)(ipPtr + ((*ipPtr & 0b1111) * 4) + 14) = BinaryPrimitives.ReverseEndianness(Math.Max(window, (ushort)8));
ChecksumHelper.Checksum(ipPtr, false, true);
}
}
}
}

View File

@@ -3,8 +3,8 @@ using linker.libs.timer;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using static linker.snat.LinkerSrcNat;
namespace linker.snat
using static linker.nat.LinkerSrcNat;
namespace linker.nat
{
/// <summary>
/// 开启后默认阻止

View File

@@ -9,7 +9,7 @@ using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace linker.snat
namespace linker.nat
{
/// <summary>
/// 64位放x64的WinDivert.dll和WinDivert64.sys
@@ -551,8 +551,6 @@ namespace linker.snat
public byte Version => (byte)((*ptr >> 4) & 0b1111);
public ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
/// <summary>
/// 源地址
/// </summary>

View File

@@ -8,7 +8,7 @@ using Microsoft.Win32.SafeHandles;
#pragma warning disable CS1591
namespace linker.snat
namespace linker.nat
{
/// <summary>
/// WinDivert 包装

View File

@@ -1,4 +1,5 @@
using linker.libs;
using linker.tun.device;
using System.Buffers.Binary;
using System.Net;

View File

@@ -1,245 +0,0 @@
using linker.libs;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Net.Sockets;
namespace linker.tun
{
/// <summary>
/// 伪造ACK操作类
/// </summary>
public unsafe sealed class FakeAckTransfer
{
private readonly ConcurrentDictionary<FaceAckKey, int> dic = new(new FackAckKeyComparer());
/// <summary>
/// 发起方
/// </summary>
/// <param name="packet">一个完整的TCP/IP包</param>
/// <returns></returns>
public void Read(ReadOnlyMemory<byte> packet)
{
fixed (byte* ptr = packet.Span)
{
FakeAckPacket originPacket = new(ptr);
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
{
return;
}
if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
{
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
dic.TryRemove(key, out _);
}
}
}
/// <summary>
/// 接收方
/// </summary>
/// <param name="packet">一个完整的TCP/IP包</param>
/// <returns></returns>
public void Write(ReadOnlyMemory<byte> packet, long bufferFree)
{
fixed (byte* ptr = packet.Span)
{
FakeAckPacket originPacket = new(ptr);
if (originPacket.Version != 4 || originPacket.Protocol != ProtocolType.Tcp)
{
return;
}
FaceAckKey key = new() { srcAddr = originPacket.SrcAddr, srcPort = originPacket.SrcPort, dstAddr = originPacket.DstAddr, dstPort = originPacket.DstPort };
if (originPacket.IsPshAck || originPacket.IsOnlyAck)
{
if (dic.TryGetValue(key, out int wins) && originPacket.Window > 0)
{
ushort win = (ushort)Math.Max(Math.Min(bufferFree / wins, 65535), 4);
originPacket.WriteWindow(ptr, win);
}
}
else if (originPacket.IsOnlySyn || originPacket.IsSynAck)
{
int windowScale = originPacket.FindWindowScale(ptr);
dic.AddOrUpdate(key, windowScale, (a, b) => windowScale);
}
else if (originPacket.TcpFlagFin || originPacket.TcpFlagRst)
{
dic.TryRemove(key, out _);
}
}
}
/// <summary>
/// 四元组缓存key
/// </summary>
struct FaceAckKey
{
public uint srcAddr;
public ushort srcPort;
public uint dstAddr;
public ushort dstPort;
}
/// <summary>
/// 四元组缓存key比较器
/// </summary>
sealed class FackAckKeyComparer : IEqualityComparer<FaceAckKey>
{
public bool Equals(FaceAckKey x, FaceAckKey y)
{
return (x.srcAddr, x.srcPort, x.dstAddr, x.dstPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort)
|| (x.dstAddr, x.dstPort, x.srcAddr, x.srcPort) == (y.srcAddr, y.srcPort, y.dstAddr, y.dstPort);
}
public int GetHashCode(FaceAckKey obj)
{
return (int)obj.srcAddr ^ obj.srcPort ^ (int)obj.dstAddr ^ obj.dstPort;
}
}
/// <summary>
/// 数据包解析
/// </summary>
readonly unsafe struct FakeAckPacket
{
private readonly byte* ptr;
/// <summary>
/// 协议版本
/// </summary>
public readonly byte Version => (byte)((*ptr >> 4) & 0b1111);
public readonly ProtocolType Protocol => (ProtocolType)(*(ptr + 9));
/// <summary>
/// 源地址
/// </summary>
public readonly uint SrcAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
/// <summary>
/// 源端口
/// </summary>
public readonly ushort SrcPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength));
/// <summary>
/// 目的地址
/// </summary>
public readonly uint DstAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
/// <summary>
/// 目标端口
/// </summary>
public readonly ushort DstPort => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 2));
/// <summary>
/// IP头长度
/// </summary>
public readonly int IPHeadLength => (*ptr & 0b1111) * 4;
/// <summary>
/// 窗口大小
/// </summary>
public readonly ushort Window => BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + IPHeadLength + 14));
/// <summary>
/// TCP Flag
/// </summary>
public readonly byte TcpFlag => *(ptr + IPHeadLength + 13);
public readonly bool TcpFlagFin => (TcpFlag & 0b000001) != 0;
public readonly bool TcpFlagSyn => (TcpFlag & 0b000010) != 0;
public readonly bool TcpFlagRst => (TcpFlag & 0b000100) != 0;
public readonly bool TcpFlagPsh => (TcpFlag & 0b001000) != 0;
public readonly bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
public readonly bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
public readonly bool IsPshAck => TcpFlagPsh && TcpFlagAck;
public readonly bool IsOnlyAck => TcpFlag == 0b00010000;
public readonly bool IsOnlySyn => TcpFlag == 0b00000010;
public readonly bool IsSynAck => TcpFlag == 0b00010010;
/// <summary>
/// 加载TCP/IP包必须是一个完整的TCP/IP包
/// </summary>
/// <param name="ptr">一个完整的TCP/IP包</param>
public FakeAckPacket(byte* ptr)
{
this.ptr = ptr;
}
public int FindWindowScale(byte* ipPtr)
{
//指针移动到TCP头开始位置
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
//tcp头固定20所以option从这里开始
int index = 20;
//tcp头结束位置就是option结束位置
int end = (*(tcpPtr + 12) >> 4) * 4;
while (index < end)
{
byte kind = *(tcpPtr + index);
//EOF结束符
if (kind == 0)
{
break;
}
byte length = *(tcpPtr + index + 1);
//NOP 空选项
if (kind == 1)
{
index++;
continue;
}
//Window Scale 1kind 1length 1shiftCount
else if (kind == 3 && length == 3)
{
return *(tcpPtr + index + 2);
}
index += length;
}
return 0;
}
public int WriteWindowScale(byte* ipPtr, byte windowScale = 7)
{
//指针移动到TCP头开始位置
byte* tcpPtr = ipPtr + ((*ipPtr & 0b1111) * 4);
//tcp头固定20所以option从这里开始
int index = 20;
//tcp头结束位置就是option结束位置
int end = (*(tcpPtr + 12) >> 4) * 4;
while (index < end)
{
byte kind = *(tcpPtr + index);
//EOF结束符
if (kind == 0)
{
break;
}
byte length = *(tcpPtr + index + 1);
//NOP 空选项
if (kind == 1)
{
index++;
continue;
}
//Window Scale 1kind 1length 1shiftCount
else if (kind == 3 && length == 3)
{
if (*(tcpPtr + index + 2) < windowScale)
{
*(tcpPtr + index + 2) = windowScale;
ChecksumHelper.Checksum(ipPtr, false, true);
}
return *(tcpPtr + index + 2);
}
index += length;
}
return 0;
}
public void WriteWindow(byte* ipPtr, ushort window)
{
*(ushort*)(ipPtr + ((*ipPtr & 0b1111) * 4) + 14) = BinaryPrimitives.ReverseEndianness(Math.Max(window, (ushort)8));
ChecksumHelper.Checksum(ipPtr, false, true);
}
}
}
}

View File

@@ -1,31 +0,0 @@
using linker.snat;
using static linker.snat.LinkerDstMapping;
namespace linker.tun
{
internal sealed class LanMap : ILinkerTunPacketHook
{
public LinkerTunPacketHookLevel Level => LinkerTunPacketHookLevel.Lowest;
private readonly LinkerDstMapping linkerDstMapping = new LinkerDstMapping();
private bool checksum = true;
public void SetMap(DstMapInfo[] maps,bool checksum = true)
{
linkerDstMapping.SetDsts(maps);
this.checksum = checksum;
}
public bool ReadAfter(ReadOnlyMemory<byte> packet)
{
linkerDstMapping.ToFakeDst(packet);
return true;
}
public bool WriteBefore(string srcId, ReadOnlyMemory<byte> packet)
{
linkerDstMapping.ToRealDst(packet, checksum);
return true;
}
}
}

View File

@@ -1,7 +1,9 @@
using linker.libs;
using linker.libs.timer;
using linker.tun.device;
using linker.tun.hook;
using System.Net;
using static linker.snat.LinkerDstMapping;
using static linker.nat.LinkerDstMapping;
namespace linker.tun
{
@@ -19,12 +21,12 @@ namespace linker.tun
private string natError = string.Empty;
public string NatError => natError;
public bool AppNat => lanSnat.Running;
public bool AppNat => lanDnat.Running;
private IPAddress address;
private byte prefixLength;
private readonly LanMap lanMap = new LanMap();
private readonly LanSnat lanSnat = new LanSnat();
private readonly LinkerTunPacketHookLanMap lanMap = new LinkerTunPacketHookLanMap();
private readonly LinkerTunPacketHookLanDstProxy lanDnat = new LinkerTunPacketHookLanDstProxy();
private readonly OperatingManager operatingManager = new OperatingManager();
@@ -43,13 +45,12 @@ namespace linker.tun
}
private ILinkerTunPacketHook[] hooks = [];
private ILinkerTunPacketHook[] hooks1 = [];
public LinkerTunDeviceAdapter()
{
hooks = new ILinkerTunPacketHook[]
{
lanMap,lanSnat
};
hooks = new ILinkerTunPacketHook[] { lanMap, lanDnat };
hooks1 = hooks.OrderByDescending(c => c.Level).ToArray();
}
/// <summary>
@@ -187,7 +188,7 @@ namespace linker.tun
if (linkerTunDevice.Running)
{
linkerTunDevice.SetNat(out natError);
lanSnat.Setup(address, prefixLength, items, ref natError);
lanDnat.Setup(address, prefixLength, items, ref natError);
}
}
/// <summary>
@@ -201,7 +202,7 @@ namespace linker.tun
}
natError = string.Empty;
linkerTunDevice.RemoveNat(out string error);
lanSnat.Shutdown();
lanDnat.Shutdown();
}
/// <summary>
@@ -274,6 +275,7 @@ namespace linker.tun
list.AddRange(hooks);
this.hooks = list.Distinct().OrderBy(c => c.Level).ToArray();
hooks1 = this.hooks.OrderByDescending(c => c.Level).ToArray();
}
private void Read()
@@ -298,7 +300,9 @@ namespace linker.tun
packet.Unpacket(buffer, 0, length);
if (packet.DistIPAddress.Length == 0) continue;
for (int i = 0; i < hooks.Length; i++) if (hooks[i].ReadAfter(packet.IPPacket) == false) goto end;
for (int i = 0; i < hooks1.Length; i++) if (hooks1[i].Read(packet.IPPacket) == false) goto end;
ChecksumHelper.ChecksumWithZero(packet.IPPacket);
await linkerTunDeviceCallback.Callback(packet).ConfigureAwait(false);
end:;
@@ -322,7 +326,9 @@ namespace linker.tun
{
if (linkerTunDevice == null || Status != LinkerTunDeviceStatus.Running || new LinkerTunDevicValidatePacket(buffer).IsValid == false) return false;
for (int i = 0; i < hooks.Length; i++) if (hooks[i].WriteBefore(srcId, buffer) == false) return false;
for (int i = 0; i < hooks.Length; i++) if (hooks[i].Write(srcId, buffer) == false) return false;
ChecksumHelper.ChecksumWithZero(buffer);
return linkerTunDevice.Write(buffer);
}
@@ -332,14 +338,14 @@ namespace linker.tun
/// <param name="maps"></param>
public void SetMap(DstMapInfo[] maps)
{
lanMap.SetMap(maps, AppNat == false);
lanMap.SetMap(maps);
}
/// <summary>
/// 移除映射
/// </summary>
public void RemoveMap()
{
lanMap.SetMap([], AppNat == false);
lanMap.SetMap([]);
}
public async Task<bool> CheckAvailable(bool order = false)

View File

@@ -4,7 +4,7 @@ using System.Buffers.Binary;
using System.Net;
using System.Net.Sockets;
using System.Text.Json.Serialization;
namespace linker.tun
namespace linker.tun.device
{
/// <summary>
/// 设备接口
@@ -136,60 +136,6 @@ namespace linker.tun
public Task Callback(LinkerTunDevicPacket packet);
}
/// <summary>
/// 数据包钩子
/// </summary>
public interface ILinkerTunPacketHook
{
public LinkerTunPacketHookLevel Level { get; }
/// <summary>
/// 从网卡读取到数据包后
/// </summary>
/// <param name="packet"></param>
/// <returns></returns>
public bool ReadAfter(ReadOnlyMemory<byte> packet);
/// <summary>
/// 写入网卡前
/// </summary>
/// <param name="srcId"></param>
/// <param name="packet"></param>
/// <returns></returns>
public bool WriteBefore(string srcId, ReadOnlyMemory<byte> packet);
}
/// <summary>
/// 回调处理级别
/// </summary>
public enum LinkerTunPacketHookLevel
{
/// <summary>
/// 最低的,也是最早执行的,不要用这个
/// </summary>
Lowest = int.MinValue,
Low9 = -9,
Low8 = -8,
Low7 = -7,
Low6 = -6,
Low5 = -5,
Low4 = -4,
Low3 = -3,
Low2 = -2,
Low1 = -1,
Normal = 0,
High1 = 1,
High2 = 2,
High3 = 3,
High4 = 4,
High5 = 5,
High6 = 6,
High7 = 7,
High8 = 8,
High9 = 9,
/// <summary>
/// 最高的,也是最晚执行的,不要用这个
/// </summary>
Highest = int.MaxValue
}
/// <summary>
/// 网卡端口转发

View File

@@ -1,12 +1,11 @@

using linker.libs;
using linker.libs;
using linker.libs.extends;
using Microsoft.Win32.SafeHandles;
using System.Net;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace linker.tun
namespace linker.tun.device
{
internal sealed class LinkerLinuxTunDevice : ILinkerTunDevice
{
@@ -30,13 +29,13 @@ namespace linker.tun
{
error = string.Empty;
this.name = info.Name;
this.address = info.Address;
this.prefixLength = info.PrefixLength;
name = info.Name;
address = info.Address;
prefixLength = info.PrefixLength;
if (Running)
{
error = ($"Adapter already exists");
error = $"Adapter already exists";
return false;
}
if (Create(out error) == false)
@@ -71,7 +70,7 @@ namespace linker.tun
string str = CommandHelper.Linux(string.Empty, new string[] { $"ifconfig" });
if (str.Contains(Name) == false)
{
CommandHelper.Linux(string.Empty, new string[] { $"ip tuntap add mode tun dev {Name}" },out error);
CommandHelper.Linux(string.Empty, new string[] { $"ip tuntap add mode tun dev {Name}" }, out error);
return false;
}
@@ -340,7 +339,7 @@ namespace linker.tun
}
catch (Exception ex)
{
if(LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
if (LoggerHelper.Instance.LoggerLevel <= LoggerTypes.DEBUG)
{
LoggerHelper.Instance.Error(ex.Message);
LoggerHelper.Instance.Error(string.Join(",", buffer.ToArray()));

View File

@@ -4,7 +4,7 @@ using Microsoft.Win32.SafeHandles;
using System.Net;
using System.Runtime.InteropServices;
namespace linker.tun
namespace linker.tun.device
{
/// <summary>
/// osx网卡实现未测试
@@ -31,13 +31,13 @@ namespace linker.tun
public bool Setup(LinkerTunDeviceSetupInfo info, out string error)
{
this.name = "utun0";
name = "utun0";
error = string.Empty;
this.address = info.Address;
this.prefixLength = info.PrefixLength;
address = info.Address;
prefixLength = info.PrefixLength;
IntPtr arg = Marshal.AllocHGlobal(4);
nint arg = Marshal.AllocHGlobal(4);
Marshal.WriteInt32(arg, 0);
try
{
@@ -51,7 +51,7 @@ namespace linker.tun
error = $"open utun failed: {Marshal.GetLastWin32Error()}";
return false;
}
this.name = $"utun{Marshal.ReadInt32(arg)}";
name = $"utun{Marshal.ReadInt32(arg)}";
fsRead = new FileStream(safeFileHandle, FileAccess.Read, 65 * 1024, true);
fsWrite = new FileStream(safeFileHandle, FileAccess.Write, 65 * 1024, true);
@@ -99,7 +99,7 @@ namespace linker.tun
catch (Exception)
{
}
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(this.prefixLength));
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(prefixLength));
CommandHelper.Osx(string.Empty, new string[] { $"route delete -net {network}/{prefixLength} {address}" });
}
public void Refresh()

View File

@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
namespace linker.tun
namespace linker.tun.device
{
[SupportedOSPlatform("windows")]
internal sealed class LinkerWinTunDevice : ILinkerTunDevice
@@ -16,7 +16,7 @@ namespace linker.tun
public string Name => name;
public bool Running => session != 0;
private IntPtr waitHandle = IntPtr.Zero, adapter = IntPtr.Zero, session = IntPtr.Zero;
private nint waitHandle = nint.Zero, adapter = nint.Zero, session = nint.Zero;
private int interfaceNumber = 0;
private IPAddress address;
private byte prefixLength = 24;
@@ -31,15 +31,15 @@ namespace linker.tun
public bool Setup(LinkerTunDeviceSetupInfo info, out string error)
{
this.name = info.Name;
this.address = info.Address;
this.prefixLength = info.PrefixLength;
name = info.Name;
address = info.Address;
prefixLength = info.PrefixLength;
error = string.Empty;
if (adapter != 0)
{
error = ($"Adapter already exists");
error = $"Adapter already exists";
return false;
}
@@ -49,10 +49,10 @@ namespace linker.tun
for (int i = 0; i < 5; i++)
{
if (
(
(adapter = WinTun.WintunCreateAdapter(name, name, ref guid)) == 0
&& (adapter = WinTun.WintunOpenAdapter(name)) == 0
)
|| (session = WinTun.WintunStartSession(adapter, 0x400000)) == 0
)
{
@@ -64,12 +64,12 @@ namespace linker.tun
}
if (adapter == 0)
{
error = ($"Failed to create adapter {Marshal.GetLastWin32Error()}");
error = $"Failed to create adapter {Marshal.GetLastWin32Error()}";
return false;
}
if (session == 0)
{
error = ($"Failed to start session");
error = $"Failed to start session";
Shutdown();
return false;
}
@@ -92,7 +92,7 @@ namespace linker.tun
Thread.Sleep(2000);
}
}
error = ($"Failed to set adapter ip {Marshal.GetLastWin32Error()}");
error = $"Failed to set adapter ip {Marshal.GetLastWin32Error()}";
Shutdown();
return false;
@@ -167,8 +167,8 @@ namespace linker.tun
if (session == 0) return;
try
{
IntPtr oldSession = session;
IntPtr oldWaitHandle = waitHandle;
nint oldSession = session;
nint oldWaitHandle = waitHandle;
CommandHelper.Windows(string.Empty, new string[] { $"netsh interface set interface {Name} enable" });
session = WinTun.WintunStartSession(adapter, 0x400000);
@@ -204,7 +204,7 @@ namespace linker.tun
}
SetupNat();
IPAddress network = NetworkHelper.ToNetworkIP(this.address, NetworkHelper.ToPrefixValue(prefixLength));
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(prefixLength));
RemoveOldNat($"{network}/{prefixLength}");
CommandHelper.PowerShell($"New-NetNat -Name {Name} -InternalIPInterfaceAddressPrefix {network}/{prefixLength}", [], out error);
@@ -243,7 +243,7 @@ namespace linker.tun
.Select(c => c.Split(':')).Where(c => c.Length == 2).Select(c => { c[0] = c[0].Trim(); c[1] = c[1].Trim(); return c; })
.ToDictionary(c => c[0], c => c[1]);
})
.Where(c => (c.TryGetValue("Name", out string name) && name == Name) || (c.TryGetValue("InternalIPInterfaceAddressPrefix", out string ip) && ip == addressPrefix))
.Where(c => c.TryGetValue("Name", out string name) && name == Name || c.TryGetValue("InternalIPInterfaceAddressPrefix", out string ip) && ip == addressPrefix)
.Select(c => c["Name"]);
foreach (var name in names)
{
@@ -351,7 +351,7 @@ namespace linker.tun
if (session == 0) return Helper.EmptyArray;
for (; tokenSource.IsCancellationRequested == false;)
{
IntPtr packetPtr = WinTun.WintunReceivePacket(session, out uint size);
nint packetPtr = WinTun.WintunReceivePacket(session, out uint size);
length = (int)size;
if (packetPtr != 0)
@@ -382,7 +382,7 @@ namespace linker.tun
{
if (session == 0 || tokenSource.IsCancellationRequested) return false;
IntPtr packetPtr = WinTun.WintunAllocateSendPacket(session, (uint)packet.Length);
nint packetPtr = WinTun.WintunAllocateSendPacket(session, (uint)packet.Length);
if (packetPtr != 0)
{
packet.Span.CopyTo(new Span<byte>((byte*)packetPtr, packet.Length));

View File

@@ -1,7 +1,7 @@
using System.Runtime.InteropServices;
using System.Text;
namespace linker.tun
namespace linker.tun.device
{
internal static class LinuxAPI
{
@@ -33,7 +33,7 @@ namespace linker.tun
[DllImport("libc.so.6", EntryPoint = "read", SetLastError = true)]
internal static extern int Read(int handle, byte[] data, int length);
[DllImport("libc.so.6", EntryPoint = "read", SetLastError = true)]
internal static extern int Read(int handle, IntPtr data, int length);
internal static extern int Read(int handle, nint data, int length);
[DllImport("libc.so.6", EntryPoint = "write", SetLastError = true)]
internal static extern int Write(int handle, byte[] data, int length);
@@ -45,7 +45,7 @@ namespace linker.tun
Array.Resize(ref ifreqFREG0, 16);
byte[] ifreqFREG1 = { 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
byte[] ifreq = BytesPlusBytes(ifreqFREG0, ifreqFREG1);
return LinuxAPI.Ioctl(device, request, ifreq);
return Ioctl(device, request, ifreq);
}
internal static byte[] BytesPlusBytes(byte[] A, byte[] B)
{

View File

@@ -1,20 +1,20 @@
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
namespace linker.tun
namespace linker.tun.device
{
internal static class OsxAPI
{
// 定义 macOS 的 ioctl 命令(来自 <net/if_utun.h>
private const uint UTUN_CONTROL = 0x80000000; // 'u' << 24
private const uint UTUN_CTRL_SET_IFNAME = (UTUN_CONTROL | 1);
private const uint UTUN_CTRL_SET_IFNAME = UTUN_CONTROL | 1;
// P/Invoke 声明
[DllImport("libc", EntryPoint = "ioctl", SetLastError = true)]
private static extern int Ioctl(SafeFileHandle fd, uint request, IntPtr arg);
private static extern int Ioctl(SafeFileHandle fd, uint request, nint arg);
public static int Ioctl(SafeFileHandle fd, IntPtr arg)
public static int Ioctl(SafeFileHandle fd, nint arg)
{
return Ioctl(fd, UTUN_CTRL_SET_IFNAME, arg);
}

View File

@@ -1,6 +1,6 @@
using System.Runtime.InteropServices;
namespace linker.tun
namespace linker.tun.device
{
internal static class WinTun
{
@@ -52,12 +52,12 @@ namespace linker.tun
[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);
internal static extern uint WaitForSingleObject(nint hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll")]
internal static extern bool SetEvent(IntPtr hEvent);
internal static extern bool SetEvent(nint hEvent);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern IntPtr WintunCreateAdapter(
internal static extern nint WintunCreateAdapter(
[MarshalAs(UnmanagedType.LPWStr)]
string name,
[MarshalAs(UnmanagedType.LPWStr)]
@@ -68,31 +68,31 @@ namespace linker.tun
internal static extern uint WintunGetRunningDriverVersion();
[DllImport("wintun.dll", SetLastError = true)]
internal static extern void WintunGetAdapterLUID(IntPtr adapter, out ulong luid);
internal static extern void WintunGetAdapterLUID(nint adapter, out ulong luid);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern IntPtr WintunStartSession(IntPtr adapter, uint capacity);
internal static extern nint WintunStartSession(nint adapter, uint capacity);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern IntPtr WintunGetReadWaitEvent(IntPtr session);
internal static extern nint WintunGetReadWaitEvent(nint session);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern IntPtr WintunReceivePacket(IntPtr session, out uint packetSize);
internal static extern nint WintunReceivePacket(nint session, out uint packetSize);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern void WintunSendPacket(IntPtr session, IntPtr packet);
internal static extern void WintunSendPacket(nint session, nint packet);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern void WintunEndSession(IntPtr session);
internal static extern void WintunEndSession(nint session);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern void WintunCloseAdapter(IntPtr adapter);
internal static extern void WintunCloseAdapter(nint adapter);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern IntPtr WintunAllocateSendPacket(IntPtr session, uint packetSize);
internal static extern nint WintunAllocateSendPacket(nint session, uint packetSize);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern IntPtr WintunOpenAdapter(
internal static extern nint WintunOpenAdapter(
[MarshalAs(UnmanagedType.LPWStr)]
string name);
@@ -100,7 +100,7 @@ namespace linker.tun
internal static extern bool WintunDeleteDriver();
[DllImport("wintun.dll", SetLastError = true)]
internal static extern void WintunReleaseReceivePacket(IntPtr session, IntPtr packet);
internal static extern void WintunReleaseReceivePacket(nint session, nint packet);
[DllImport("wintun.dll", SetLastError = true)]
internal static extern void WintunSetLogger(WINTUN_LOGGER_CALLBACK newLogger);

View File

@@ -0,0 +1,61 @@

using linker.tun.device;
namespace linker.tun.hook
{
/// <summary>
/// 数据包钩子
/// </summary>
public interface ILinkerTunPacketHook
{
public LinkerTunPacketHookLevel Level { get; }
/// <summary>
/// 从网卡读取到数据包后
/// </summary>
/// <param name="packet"></param>
/// <returns></returns>
public bool Read(ReadOnlyMemory<byte> packet);
/// <summary>
/// 写入网卡前
/// </summary>
/// <param name="srcId"></param>
/// <param name="packet"></param>
/// <returns></returns>
public bool Write(string srcId, ReadOnlyMemory<byte> packet);
}
/// <summary>
/// 回调处理级别
/// </summary>
public enum LinkerTunPacketHookLevel
{
/// <summary>
/// 最低的,也是最早执行的,不要用这个
/// </summary>
Lowest = int.MinValue,
Low9 = -9,
Low8 = -8,
Low7 = -7,
Low6 = -6,
Low5 = -5,
Low4 = -4,
Low3 = -3,
Low2 = -2,
Low1 = -1,
Normal = 0,
High1 = 1,
High2 = 2,
High3 = 3,
High4 = 4,
High5 = 5,
High6 = 6,
High7 = 7,
High8 = 8,
High9 = 9,
/// <summary>
/// 最高的,也是最晚执行的,不要用这个
/// </summary>
Highest = int.MaxValue
}
}

View File

@@ -0,0 +1,68 @@
using linker.libs;
using linker.nat;
using linker.tun.device;
using System.Net;
namespace linker.tun.hook
{
internal sealed class LinkerTunPacketHookLanDstProxy : ILinkerTunPacketHook
{
public LinkerTunPacketHookLevel Level => LinkerTunPacketHookLevel.Highest;
private LinkerDstProxy linkerDstNat = new LinkerDstProxy();
public bool Running => linkerDstNat.Running;
public LinkerTunPacketHookLanDstProxy()
{
}
public void Setup(IPAddress address, byte prefixLength, LinkerTunAppNatItemInfo[] items, ref string error)
{
if (OperatingSystem.IsWindows() == false) return;
if (address == null || address.Equals(IPAddress.Any) || prefixLength == 0)
{
error = "DProxy need CIDR,like 10.18.18.0/24";
return;
}
try
{
IPAddress network = NetworkHelper.ToNetworkIP(address, NetworkHelper.ToPrefixValue(prefixLength));
string result = CommandHelper.PowerShell($"Get-NetNat", [], out string e);
if (string.IsNullOrWhiteSpace(result) == false && result.Contains($"{network}/{prefixLength}"))
{
return;
}
}
catch (Exception ex)
{
LoggerHelper.Instance.Error(ex);
}
Shutdown();
linkerDstNat.Setup(address, items.Select(c => new ValueTuple<IPAddress, byte>(c.IP, c.PrefixLength)).ToArray(), ref error);
}
public void Shutdown()
{
try
{
linkerDstNat.Shutdown();
}
catch (Exception)
{
}
GC.Collect();
}
public bool Read(ReadOnlyMemory<byte> packet)
{
linkerDstNat.Read(packet);
return true;
}
public bool Write(string srcId, ReadOnlyMemory<byte> packet)
{
return linkerDstNat.Write(packet);
}
}
}

View File

@@ -0,0 +1,29 @@
using linker.nat;
using static linker.nat.LinkerDstMapping;
namespace linker.tun.hook
{
internal sealed class LinkerTunPacketHookLanMap : ILinkerTunPacketHook
{
public LinkerTunPacketHookLevel Level => LinkerTunPacketHookLevel.Lowest;
private readonly LinkerDstMapping linkerDstMapping = new LinkerDstMapping();
public void SetMap(DstMapInfo[] maps)
{
linkerDstMapping.SetDsts(maps);
}
public bool Read(ReadOnlyMemory<byte> packet)
{
linkerDstMapping.ToFakeDst(packet);
return true;
}
public bool Write(string srcId, ReadOnlyMemory<byte> packet)
{
linkerDstMapping.ToRealDst(packet);
return true;
}
}
}

View File

@@ -1,10 +1,11 @@
using linker.libs;
using linker.snat;
using linker.nat;
using linker.tun.device;
using System.Net;
namespace linker.tun
namespace linker.tun.hook
{
internal sealed class LanSnat : ILinkerTunPacketHook
internal sealed class LinkerTunPacketHookLanSrcNat : ILinkerTunPacketHook
{
public LinkerTunPacketHookLevel Level => LinkerTunPacketHookLevel.Highest;
public bool Running => linkerSrcNat.Running;
@@ -12,7 +13,7 @@ namespace linker.tun
private LinkerSrcNat linkerSrcNat = new LinkerSrcNat();
public LanSnat()
public LinkerTunPacketHookLanSrcNat()
{
}
@@ -64,11 +65,11 @@ namespace linker.tun
GC.Collect();
}
public bool ReadAfter(ReadOnlyMemory<byte> packet)
public bool Read(ReadOnlyMemory<byte> packet)
{
return true;
}
public bool WriteBefore(string srcId, ReadOnlyMemory<byte> packet)
public bool Write(string srcId, ReadOnlyMemory<byte> packet)
{
return linkerSrcNat.Running == false || linkerSrcNat.Inject(packet) == false;
}

View File

@@ -38,7 +38,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\linker.libs\linker.libs.csproj" />
<ProjectReference Include="..\linker.snat\linker.snat.csproj" />
<ProjectReference Include="..\linker.nat\linker.nat.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,7 +1,7 @@
<template>
<div class="w-100">
<div>
<span class="yellow">使用系统NAT或应用层SNAT</span>
<span class="yellow">使用系统NAT或应用层DNAT</span>
</div>
<div class="wrap">
<el-table stripe :data="state.lans" border size="small" width="100%" height="400px" @cell-dblclick="handleCellClick">

View File

@@ -18,7 +18,7 @@
<strong class="yellow" :title="tuntap.list[item.MachineId].NatError">{{ tuntap.list[item.MachineId].IP }}</strong>
</template>
<template v-else-if="tuntap.list[item.MachineId].AppNat && tuntap.list[item.MachineId].running">
<strong class="app-nat" :title="`虚拟网卡IP\r\n应用层SNAT\r\n如果无法使用点对网请重启一次系统`">{{ tuntap.list[item.MachineId].IP }}</strong>
<strong class="app-nat" :title="`虚拟网卡IP\r\n应用层DNAT`">{{ tuntap.list[item.MachineId].IP }}</strong>
</template>
<template v-else-if="tuntap.list[item.MachineId].running">
<strong class="green gateway" :title="`虚拟网卡IP\r\n系统NAT`">{{ tuntap.list[item.MachineId].IP }}</strong>

View File

@@ -1,5 +1,5 @@
v1.9.1
2025-09-20 01:21:24
2025-09-21 18:35:14
1. 一些累计更新
2. 服务器转发多节点
3. 虚拟网卡下伪造ACK为TCP-in-TCP隧道提速