mirror of
https://github.com/snltty/linker.git
synced 2025-09-26 21:15:57 +08:00
应用层SNAT
This commit is contained in:
2
.github/workflows/dotnet.yml
vendored
2
.github/workflows/dotnet.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
release_name: v1.7.3.${{ steps.date.outputs.today }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
body: "1. 优化自动分配IP\r\n2. 优化网卡,排除不明数据包\r\n3. 虚拟网卡点对网IP映射,用于解决网段冲突"
|
||||
body: "1. 优化自动分配IP\r\n2. 优化网卡,排除不明数据包\r\n3. 虚拟网卡点对网IP映射,用于解决网段冲突\r\n4. 内置应用层SNAT,用于无法使用系统NAT的windows系统"
|
||||
- name: publish projects
|
||||
run: ./publish.bat "C:\\Android\\android-sdk"
|
||||
- name: upload-win-x86-oss
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/linker.app/public/web/css/app.73965826.css
Normal file
1
src/linker.app/public/web/css/app.73965826.css
Normal file
File diff suppressed because one or more lines are too long
@@ -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.cfba5739.js"></script><script defer="defer" src="js/app.644fc91c.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.3aab4747.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.cfba5739.js"></script><script defer="defer" src="js/app.1acc2fd0.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.73965826.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>
|
1
src/linker.app/public/web/js/538.7180135c.js
Normal file
1
src/linker.app/public/web/js/538.7180135c.js
Normal file
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[163],{427:function(e,n,a){a.r(n),a.d(n,{default:function(){return O}});var t=a(6768);const s={class:"net-wrap app-wrap"},l={class:"inner absolute flex flex-column flex-nowrap"},i={class:"head"},o={class:"body flex-1 relative"},c={class:"status"};function r(e,n,a,r,u,d){const g=(0,t.g2)("Head"),p=(0,t.g2)("List"),v=(0,t.g2)("Status");return(0,t.uX)(),(0,t.CE)("div",s,[(0,t.Lk)("div",l,[(0,t.Lk)("div",i,[(0,t.bF)(g)]),(0,t.Lk)("div",o,[(0,t.bF)(p)]),(0,t.Lk)("div",c,[(0,t.bF)(v,{config:!1})])])])}a(4114);const u=e=>((0,t.Qi)("data-v-1fd9ef80"),e=e(),(0,t.jt)(),e),d={class:"head-wrap"},g={class:"tools flex"},p=u((()=>(0,t.Lk)("span",{class:"label"},"服务器 ",-1))),v=u((()=>(0,t.Lk)("span",{class:"flex-1"},null,-1))),h={style:{"margin-left":"1rem"}};function f(e,n,a,s,l,i){const o=(0,t.g2)("el-input"),c=(0,t.g2)("Refresh"),r=(0,t.g2)("el-icon"),u=(0,t.g2)("el-button"),f=(0,t.g2)("Background");return(0,t.uX)(),(0,t.CE)("div",d,[(0,t.Lk)("div",g,[p,(0,t.bF)(o,{modelValue:s.state.server,"onUpdate:modelValue":n[0]||(n[0]=e=>s.state.server=e),readonly:"",style:{width:"14rem"},size:"small"},null,8,["modelValue"]),v,(0,t.bF)(u,{size:"small",onClick:s.handleRefresh},{default:(0,t.k6)((()=>[(0,t.eW)(" 刷新(F5)"),(0,t.bF)(r,null,{default:(0,t.k6)((()=>[(0,t.bF)(c)])),_:1})])),_:1},8,["onClick"]),(0,t.Lk)("div",h,[(0,t.bF)(f,{name:"net"})])])])}var k=a(3830),m=a(144),C=a(7477),b=a(5096),L={components:{Edit:C.ffu,Refresh:C.C42,Background:b.A},setup(){const e=(0,k.B)(),n=(0,m.Kh)({server:(0,t.EW)((()=>e.value.config.Client.Server.Host))}),a=()=>{window.location.reload()};return{state:n,handleRefresh:a}}},_=a(1241);const w=(0,_.A)(L,[["render",f],["__scopeId","data-v-1fd9ef80"]]);var F=w;const S=e=>((0,t.Qi)("data-v-68d1c30a"),e=e(),(0,t.jt)(),e),x={class:"net-list-wrap flex flex-column absolute"},T={class:"flex-1 scrollbar"},z={class:"flex"},E=S((()=>(0,t.Lk)("div",{class:"flex-1"},null,-1))),A={class:"tuntap"},I={class:"page t-c"},P={class:"page-wrap t-c"};function B(e,n,a,s,l,i){const o=(0,t.g2)("DeviceName"),c=(0,t.g2)("UpdaterBtn"),r=(0,t.g2)("TuntapShow"),u=(0,t.g2)("el-pagination");return(0,t.uX)(),(0,t.CE)("div",x,[(0,t.Lk)("div",T,[(0,t.Lk)("ul",null,[((0,t.uX)(!0),(0,t.CE)(t.FK,null,(0,t.pI)(s.devices.page.List,((e,n)=>((0,t.uX)(),(0,t.CE)("li",{key:n},[(0,t.Lk)("dl",null,[(0,t.Lk)("dt",z,[(0,t.Lk)("div",null,[(0,t.bF)(o,{item:e},null,8,["item"])]),E,(0,t.Lk)("div",null,[(0,t.bF)(c,{config:!1,item:e},null,8,["item"])])]),(0,t.Lk)("dd",A,[s.tuntap.list[e.MachineId]?((0,t.uX)(),(0,t.Wv)(r,{key:0,item:e},null,8,["item"])):(0,t.Q3)("",!0)])])])))),128))])]),(0,t.Lk)("div",I,[(0,t.Lk)("div",P,[(0,t.bF)(u,{size:"small",background:"",layout:"prev,pager, next","pager-count":5,total:s.devices.page.Count,"page-size":s.devices.page.Request.Size,"current-page":s.devices.page.Request.Page,onCurrentChange:s.handlePageChange,onSizeChange:s.handlePageSizeChange,"page-sizes":[10,20,50,100,255]},null,8,["total","page-size","current-page","onCurrentChange","onSizeChange"])])])])}var y=a(8104),R=a(7985),D=a(9383),U=a(7115),X=a(6588),V=a(9273),N=a(9983),Q={components:{StarFilled:C.BQ2,UpdaterBtn:U.A,DeviceName:X.A,TuntapShow:V.A},setup(e){(0,k.B)();const n=(0,m.Kh)({}),{devices:a,machineId:s,_getSignList:l,_getSignList1:i,handleDeviceEdit:o,handlePageChange:c,handlePageSizeChange:r,handleDel:u,clearDevicesTimeout:d}=(0,R.r)(),{tuntap:g,_getTuntapInfo:p,handleTuntapRefresh:v,clearTuntapTimeout:h,handleTuntapEdit:f,sortTuntapIP:C}=(0,y.O)(),{_getUpdater:b,_subscribeUpdater:L,clearUpdaterTimeout:_}=(0,D.d)(),{connections:w,forwardConnections:F,_getForwardConnections:S,tuntapConnections:x,_getTuntapConnections:T,socks5Connections:z,_getSocks5Connections:E,handleTunnelConnections:A,clearConnectionsTimeout:I}=(0,N.L2)();return(0,t.sV)((()=>{c(),v(),l(),i(),p(),b(),L()})),(0,t.hi)((()=>{d(),h(),_()})),{state:n,devices:a,machineId:s,handlePageChange:c,handlePageSizeChange:r,tuntap:g}}};const H=(0,_.A)(Q,[["render",B],["__scopeId","data-v-68d1c30a"]]);var K=H,W=a(6743),j=a(1387),q={components:{Head:F,List:K,Status:W.A},setup(){document.addEventListener("contextmenu",(function(e){e.preventDefault()}));const e=(0,k.B)(),n=(0,j.rd)();return(0,t.sV)((()=>{0==e.value.hasAccess("NetManager")&&n.push({name:"NoPermission"})})),{}}};const M=(0,_.A)(q,[["render",r],["__scopeId","data-v-6a3f3b43"]]);var O=M}}]);
|
||||
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[743],{427:function(e,n,a){a.r(n),a.d(n,{default:function(){return O}});var t=a(6768);const s={class:"net-wrap app-wrap"},l={class:"inner absolute flex flex-column flex-nowrap"},i={class:"head"},o={class:"body flex-1 relative"},c={class:"status"};function r(e,n,a,r,u,d){const g=(0,t.g2)("Head"),p=(0,t.g2)("List"),v=(0,t.g2)("Status");return(0,t.uX)(),(0,t.CE)("div",s,[(0,t.Lk)("div",l,[(0,t.Lk)("div",i,[(0,t.bF)(g)]),(0,t.Lk)("div",o,[(0,t.bF)(p)]),(0,t.Lk)("div",c,[(0,t.bF)(v,{config:!1})])])])}a(4114);const u=e=>((0,t.Qi)("data-v-1fd9ef80"),e=e(),(0,t.jt)(),e),d={class:"head-wrap"},g={class:"tools flex"},p=u((()=>(0,t.Lk)("span",{class:"label"},"服务器 ",-1))),v=u((()=>(0,t.Lk)("span",{class:"flex-1"},null,-1))),h={style:{"margin-left":"1rem"}};function f(e,n,a,s,l,i){const o=(0,t.g2)("el-input"),c=(0,t.g2)("Refresh"),r=(0,t.g2)("el-icon"),u=(0,t.g2)("el-button"),f=(0,t.g2)("Background");return(0,t.uX)(),(0,t.CE)("div",d,[(0,t.Lk)("div",g,[p,(0,t.bF)(o,{modelValue:s.state.server,"onUpdate:modelValue":n[0]||(n[0]=e=>s.state.server=e),readonly:"",style:{width:"14rem"},size:"small"},null,8,["modelValue"]),v,(0,t.bF)(u,{size:"small",onClick:s.handleRefresh},{default:(0,t.k6)((()=>[(0,t.eW)(" 刷新(F5)"),(0,t.bF)(r,null,{default:(0,t.k6)((()=>[(0,t.bF)(c)])),_:1})])),_:1},8,["onClick"]),(0,t.Lk)("div",h,[(0,t.bF)(f,{name:"net"})])])])}var k=a(3830),m=a(144),C=a(7477),b=a(5096),L={components:{Edit:C.ffu,Refresh:C.C42,Background:b.A},setup(){const e=(0,k.B)(),n=(0,m.Kh)({server:(0,t.EW)((()=>e.value.config.Client.Server.Host))}),a=()=>{window.location.reload()};return{state:n,handleRefresh:a}}},_=a(1241);const w=(0,_.A)(L,[["render",f],["__scopeId","data-v-1fd9ef80"]]);var F=w;const S=e=>((0,t.Qi)("data-v-68d1c30a"),e=e(),(0,t.jt)(),e),x={class:"net-list-wrap flex flex-column absolute"},T={class:"flex-1 scrollbar"},z={class:"flex"},E=S((()=>(0,t.Lk)("div",{class:"flex-1"},null,-1))),A={class:"tuntap"},I={class:"page t-c"},P={class:"page-wrap t-c"};function B(e,n,a,s,l,i){const o=(0,t.g2)("DeviceName"),c=(0,t.g2)("UpdaterBtn"),r=(0,t.g2)("TuntapShow"),u=(0,t.g2)("el-pagination");return(0,t.uX)(),(0,t.CE)("div",x,[(0,t.Lk)("div",T,[(0,t.Lk)("ul",null,[((0,t.uX)(!0),(0,t.CE)(t.FK,null,(0,t.pI)(s.devices.page.List,((e,n)=>((0,t.uX)(),(0,t.CE)("li",{key:n},[(0,t.Lk)("dl",null,[(0,t.Lk)("dt",z,[(0,t.Lk)("div",null,[(0,t.bF)(o,{item:e},null,8,["item"])]),E,(0,t.Lk)("div",null,[(0,t.bF)(c,{config:!1,item:e},null,8,["item"])])]),(0,t.Lk)("dd",A,[s.tuntap.list[e.MachineId]?((0,t.uX)(),(0,t.Wv)(r,{key:0,item:e},null,8,["item"])):(0,t.Q3)("",!0)])])])))),128))])]),(0,t.Lk)("div",I,[(0,t.Lk)("div",P,[(0,t.bF)(u,{size:"small",background:"",layout:"prev,pager, next","pager-count":5,total:s.devices.page.Count,"page-size":s.devices.page.Request.Size,"current-page":s.devices.page.Request.Page,onCurrentChange:s.handlePageChange,onSizeChange:s.handlePageSizeChange,"page-sizes":[10,20,50,100,255]},null,8,["total","page-size","current-page","onCurrentChange","onSizeChange"])])])])}var y=a(8104),R=a(7985),D=a(9383),U=a(7115),X=a(6588),V=a(7163),N=a(9983),Q={components:{StarFilled:C.BQ2,UpdaterBtn:U.A,DeviceName:X.A,TuntapShow:V.A},setup(e){(0,k.B)();const n=(0,m.Kh)({}),{devices:a,machineId:s,_getSignList:l,_getSignList1:i,handleDeviceEdit:o,handlePageChange:c,handlePageSizeChange:r,handleDel:u,clearDevicesTimeout:d}=(0,R.r)(),{tuntap:g,_getTuntapInfo:p,handleTuntapRefresh:v,clearTuntapTimeout:h,handleTuntapEdit:f,sortTuntapIP:C}=(0,y.O)(),{_getUpdater:b,_subscribeUpdater:L,clearUpdaterTimeout:_}=(0,D.d)(),{connections:w,forwardConnections:F,_getForwardConnections:S,tuntapConnections:x,_getTuntapConnections:T,socks5Connections:z,_getSocks5Connections:E,handleTunnelConnections:A,clearConnectionsTimeout:I}=(0,N.L2)();return(0,t.sV)((()=>{c(),v(),l(),i(),p(),b(),L()})),(0,t.hi)((()=>{d(),h(),_()})),{state:n,devices:a,machineId:s,handlePageChange:c,handlePageSizeChange:r,tuntap:g}}};const H=(0,_.A)(Q,[["render",B],["__scopeId","data-v-68d1c30a"]]);var K=H,W=a(6743),j=a(1387),q={components:{Head:F,List:K,Status:W.A},setup(){document.addEventListener("contextmenu",(function(e){e.preventDefault()}));const e=(0,k.B)(),n=(0,j.rd)();return(0,t.sV)((()=>{0==e.value.hasAccess("NetManager")&&n.push({name:"NoPermission"})})),{}}};const M=(0,_.A)(q,[["render",r],["__scopeId","data-v-6a3f3b43"]]);var O=M}}]);
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -61,7 +61,5 @@ sidebar_position: 1
|
||||
2. <a href="https://aka.ms/vs/16/release/vc_redist.x64.exe" target="_blank">Microsoft Visual C++ 2015-2019 Redistributable 运行库</a>
|
||||
3. <a href="https://www.microsoft.com/download/details.aspx?id=47442" target="_blank">KB3063858 运行库</a>
|
||||
4. <a href="https://www.microsoft.com/zh-cn/download/details.aspx?id=46148" target="_blank">KB3033929 全球化补丁</a>
|
||||
5. <a href="https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/thank-you/net462-web-installer" target="_blank">netframework4.6.2 ICS NAT运行库</a>
|
||||
|
||||
:::
|
||||
|
||||
|
@@ -1,11 +0,0 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# 1.1.2、ICS
|
||||
|
||||
:::tip[说明]
|
||||
1. 如果系统没有<a href="https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/thank-you/net462-web-installer" target="_blank">netframework4.6.2</a>,就下载安装一下
|
||||
2. 需要linker v1.7.0+版本
|
||||
3. 剩下的交给linker
|
||||
:::
|
@@ -15,9 +15,7 @@ sidebar_position: 2
|
||||
:::tip[1、情况1,你的设备支持NAT转发时]
|
||||
|
||||
1. linux,已经自动添加NAT转发(在`OpenWrt`,需要在`防火墙 - 区域设置`中将`转发`设置为`接受`)
|
||||
2. windows,暂时找到两种NAT方式
|
||||
1. NetNat,<a href="./1.1.1、NetNat">请参照 1.1.1、NetNat</a>
|
||||
2. ICS,<a href="./1.1.2、ICS">请参照1.1.2、ICS(Internet Connection Sharing)</a>
|
||||
2. windows,优先使用系统NetNat,NetNat失败则启用内置的应用层SNAT,但是性能应该没有NetNat好
|
||||
3. macos,需要你自己在**被访问端**添加NAT转发
|
||||
```
|
||||
# 开启ip转发
|
||||
|
@@ -135,7 +135,6 @@ namespace linker.messenger.tuntap
|
||||
/// 开关,多个bool集合
|
||||
/// </summary>
|
||||
public TuntapSwitch Switch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 延迟ms
|
||||
/// </summary>
|
||||
@@ -310,6 +309,10 @@ namespace linker.messenger.tuntap
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 是否开启了应用层NAT
|
||||
/// </summary>
|
||||
public bool AppNat => (Switch & TuntapSwitch.AppNat) == TuntapSwitch.AppNat;
|
||||
}
|
||||
|
||||
public sealed partial class TuntapForwardInfo
|
||||
@@ -398,6 +401,11 @@ namespace linker.messenger.tuntap
|
||||
/// 调整网卡顺序
|
||||
/// </summary>
|
||||
InterfaceOrder = 128,
|
||||
|
||||
/// <summary>
|
||||
/// 是否开启了应用层NAT
|
||||
/// </summary>
|
||||
AppNat = 256,
|
||||
}
|
||||
|
||||
|
||||
|
@@ -65,7 +65,7 @@ namespace linker.messenger.tuntap
|
||||
SystemInfo = $"{System.Runtime.InteropServices.RuntimeInformation.OSDescription} {(string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("SNLTTY_LINKER_IS_DOCKER")) == false ? "Docker" : "")}",
|
||||
|
||||
Forwards = tuntapConfigTransfer.Info.Forwards,
|
||||
Switch = tuntapConfigTransfer.Info.Switch
|
||||
Switch = tuntapConfigTransfer.Info.Switch | (tuntapTransfer.AppNat ? TuntapSwitch.AppNat : 0)
|
||||
});
|
||||
}
|
||||
public void AddData(Memory<byte> data)
|
||||
|
@@ -14,6 +14,8 @@ namespace linker.messenger.tuntap
|
||||
public string SetupError => linkerTunDeviceAdapter.SetupError;
|
||||
public string NatError => linkerTunDeviceAdapter.NatError;
|
||||
|
||||
public bool AppNat => linkerTunDeviceAdapter.AppNat;
|
||||
|
||||
public Action OnSetupBefore { get; set; } = () => { };
|
||||
public Action OnSetupAfter { get; set; } = () => { };
|
||||
public Action OnSetupSuccess { get; set; } = () => { };
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.245" ProductVersion="0.0.0.245" publishDir="/dist/" dstrip="false" local="false" ignored="false">
|
||||
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.247" ProductVersion="0.0.0.247" publishDir="/dist/" dstrip="false" local="false" ignored="false">
|
||||
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
|
||||
<folder name="资源文件" path="res" embed="true" local="false" ignored="false">
|
||||
<file name="favicon.ico" path="res\favicon.ico" comment="res\favicon.ico"/>
|
||||
|
BIN
src/linker.tray.win/dist/linker.tray.win.exe
vendored
BIN
src/linker.tray.win/dist/linker.tray.win.exe
vendored
Binary file not shown.
1
src/linker.tray.win/web/css/743.0511d33e.css
Normal file
1
src/linker.tray.win/web/css/743.0511d33e.css
Normal file
File diff suppressed because one or more lines are too long
1
src/linker.tray.win/web/css/968.f5edca10.css
Normal file
1
src/linker.tray.win/web/css/968.f5edca10.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
src/linker.tray.win/web/css/app.73965826.css
Normal file
1
src/linker.tray.win/web/css/app.73965826.css
Normal file
File diff suppressed because one or more lines are too long
@@ -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.cfba5739.js"></script><script defer="defer" src="js/app.644fc91c.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.3aab4747.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.cfba5739.js"></script><script defer="defer" src="js/app.1acc2fd0.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.73965826.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
1
src/linker.tray.win/web/js/538.7180135c.js
Normal file
1
src/linker.tray.win/web/js/538.7180135c.js
Normal file
File diff suppressed because one or more lines are too long
1
src/linker.tray.win/web/js/743.767cfe7e.js
Normal file
1
src/linker.tray.win/web/js/743.767cfe7e.js
Normal file
@@ -0,0 +1 @@
|
||||
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[743],{427:function(e,n,a){a.r(n),a.d(n,{default:function(){return O}});var t=a(6768);const s={class:"net-wrap app-wrap"},l={class:"inner absolute flex flex-column flex-nowrap"},i={class:"head"},o={class:"body flex-1 relative"},c={class:"status"};function r(e,n,a,r,u,d){const g=(0,t.g2)("Head"),p=(0,t.g2)("List"),v=(0,t.g2)("Status");return(0,t.uX)(),(0,t.CE)("div",s,[(0,t.Lk)("div",l,[(0,t.Lk)("div",i,[(0,t.bF)(g)]),(0,t.Lk)("div",o,[(0,t.bF)(p)]),(0,t.Lk)("div",c,[(0,t.bF)(v,{config:!1})])])])}a(4114);const u=e=>((0,t.Qi)("data-v-1fd9ef80"),e=e(),(0,t.jt)(),e),d={class:"head-wrap"},g={class:"tools flex"},p=u((()=>(0,t.Lk)("span",{class:"label"},"服务器 ",-1))),v=u((()=>(0,t.Lk)("span",{class:"flex-1"},null,-1))),h={style:{"margin-left":"1rem"}};function f(e,n,a,s,l,i){const o=(0,t.g2)("el-input"),c=(0,t.g2)("Refresh"),r=(0,t.g2)("el-icon"),u=(0,t.g2)("el-button"),f=(0,t.g2)("Background");return(0,t.uX)(),(0,t.CE)("div",d,[(0,t.Lk)("div",g,[p,(0,t.bF)(o,{modelValue:s.state.server,"onUpdate:modelValue":n[0]||(n[0]=e=>s.state.server=e),readonly:"",style:{width:"14rem"},size:"small"},null,8,["modelValue"]),v,(0,t.bF)(u,{size:"small",onClick:s.handleRefresh},{default:(0,t.k6)((()=>[(0,t.eW)(" 刷新(F5)"),(0,t.bF)(r,null,{default:(0,t.k6)((()=>[(0,t.bF)(c)])),_:1})])),_:1},8,["onClick"]),(0,t.Lk)("div",h,[(0,t.bF)(f,{name:"net"})])])])}var k=a(3830),m=a(144),C=a(7477),b=a(5096),L={components:{Edit:C.ffu,Refresh:C.C42,Background:b.A},setup(){const e=(0,k.B)(),n=(0,m.Kh)({server:(0,t.EW)((()=>e.value.config.Client.Server.Host))}),a=()=>{window.location.reload()};return{state:n,handleRefresh:a}}},_=a(1241);const w=(0,_.A)(L,[["render",f],["__scopeId","data-v-1fd9ef80"]]);var F=w;const S=e=>((0,t.Qi)("data-v-68d1c30a"),e=e(),(0,t.jt)(),e),x={class:"net-list-wrap flex flex-column absolute"},T={class:"flex-1 scrollbar"},z={class:"flex"},E=S((()=>(0,t.Lk)("div",{class:"flex-1"},null,-1))),A={class:"tuntap"},I={class:"page t-c"},P={class:"page-wrap t-c"};function B(e,n,a,s,l,i){const o=(0,t.g2)("DeviceName"),c=(0,t.g2)("UpdaterBtn"),r=(0,t.g2)("TuntapShow"),u=(0,t.g2)("el-pagination");return(0,t.uX)(),(0,t.CE)("div",x,[(0,t.Lk)("div",T,[(0,t.Lk)("ul",null,[((0,t.uX)(!0),(0,t.CE)(t.FK,null,(0,t.pI)(s.devices.page.List,((e,n)=>((0,t.uX)(),(0,t.CE)("li",{key:n},[(0,t.Lk)("dl",null,[(0,t.Lk)("dt",z,[(0,t.Lk)("div",null,[(0,t.bF)(o,{item:e},null,8,["item"])]),E,(0,t.Lk)("div",null,[(0,t.bF)(c,{config:!1,item:e},null,8,["item"])])]),(0,t.Lk)("dd",A,[s.tuntap.list[e.MachineId]?((0,t.uX)(),(0,t.Wv)(r,{key:0,item:e},null,8,["item"])):(0,t.Q3)("",!0)])])])))),128))])]),(0,t.Lk)("div",I,[(0,t.Lk)("div",P,[(0,t.bF)(u,{size:"small",background:"",layout:"prev,pager, next","pager-count":5,total:s.devices.page.Count,"page-size":s.devices.page.Request.Size,"current-page":s.devices.page.Request.Page,onCurrentChange:s.handlePageChange,onSizeChange:s.handlePageSizeChange,"page-sizes":[10,20,50,100,255]},null,8,["total","page-size","current-page","onCurrentChange","onSizeChange"])])])])}var y=a(8104),R=a(7985),D=a(9383),U=a(7115),X=a(6588),V=a(7163),N=a(9983),Q={components:{StarFilled:C.BQ2,UpdaterBtn:U.A,DeviceName:X.A,TuntapShow:V.A},setup(e){(0,k.B)();const n=(0,m.Kh)({}),{devices:a,machineId:s,_getSignList:l,_getSignList1:i,handleDeviceEdit:o,handlePageChange:c,handlePageSizeChange:r,handleDel:u,clearDevicesTimeout:d}=(0,R.r)(),{tuntap:g,_getTuntapInfo:p,handleTuntapRefresh:v,clearTuntapTimeout:h,handleTuntapEdit:f,sortTuntapIP:C}=(0,y.O)(),{_getUpdater:b,_subscribeUpdater:L,clearUpdaterTimeout:_}=(0,D.d)(),{connections:w,forwardConnections:F,_getForwardConnections:S,tuntapConnections:x,_getTuntapConnections:T,socks5Connections:z,_getSocks5Connections:E,handleTunnelConnections:A,clearConnectionsTimeout:I}=(0,N.L2)();return(0,t.sV)((()=>{c(),v(),l(),i(),p(),b(),L()})),(0,t.hi)((()=>{d(),h(),_()})),{state:n,devices:a,machineId:s,handlePageChange:c,handlePageSizeChange:r,tuntap:g}}};const H=(0,_.A)(Q,[["render",B],["__scopeId","data-v-68d1c30a"]]);var K=H,W=a(6743),j=a(1387),q={components:{Head:F,List:K,Status:W.A},setup(){document.addEventListener("contextmenu",(function(e){e.preventDefault()}));const e=(0,k.B)(),n=(0,j.rd)();return(0,t.sV)((()=>{0==e.value.hasAccess("NetManager")&&n.push({name:"NoPermission"})})),{}}};const M=(0,_.A)(q,[["render",r],["__scopeId","data-v-6a3f3b43"]]);var O=M}}]);
|
1
src/linker.tray.win/web/js/968.855ee1b4.js
Normal file
1
src/linker.tray.win/web/js/968.855ee1b4.js
Normal file
File diff suppressed because one or more lines are too long
1
src/linker.tray.win/web/js/app.1acc2fd0.js
Normal file
1
src/linker.tray.win/web/js/app.1acc2fd0.js
Normal file
File diff suppressed because one or more lines are too long
@@ -4,7 +4,6 @@ using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Frozen;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace linker.tun
|
||||
{
|
||||
@@ -23,10 +22,11 @@ namespace linker.tun
|
||||
private string natError = string.Empty;
|
||||
public string NatError => natError;
|
||||
|
||||
public bool AppNat=> linkerTunDevice?.AppNat ?? false;
|
||||
|
||||
|
||||
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>();
|
||||
|
||||
|
||||
|
@@ -26,10 +26,11 @@ namespace linker.tun
|
||||
private string defaultInterfaceName = string.Empty;
|
||||
private int defaultInterfaceNumber = 0;
|
||||
private IPAddress defaultInterfaceIP;
|
||||
private uint defaultInterfaceIP32;
|
||||
|
||||
private CancellationTokenSource tokenSource;
|
||||
|
||||
private WinDivertNAT winDivertNAT;
|
||||
private WinDivertNAT winDivertNAT = new WinDivertNAT();
|
||||
|
||||
public LinkerWinTunDevice()
|
||||
{
|
||||
@@ -218,9 +219,8 @@ namespace linker.tun
|
||||
}
|
||||
public void SetAppNat(LinkerTunAppNatItemInfo[] items, out string error)
|
||||
{
|
||||
winDivertNAT?.Dispose();
|
||||
winDivertNAT = new WinDivertNAT(new WinDivertNAT.AddrInfo(address, prefixLength), items.Select(c => new WinDivertNAT.AddrInfo(c.IP, c.PrefixLength)).ToArray(), defaultInterfaceIP);
|
||||
winDivertNAT.Setup(out error);
|
||||
winDivertNAT.Shutdown();
|
||||
winDivertNAT.Setup(address, items.Select(c => new WinDivertNAT.AddrInfo(c.IP, c.PrefixLength)).ToArray(), defaultInterfaceIP, out error);
|
||||
}
|
||||
public void RemoveNat(out string error)
|
||||
{
|
||||
@@ -239,7 +239,7 @@ namespace linker.tun
|
||||
|
||||
try
|
||||
{
|
||||
winDivertNAT?.Dispose();
|
||||
winDivertNAT.Shutdown();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -357,7 +357,7 @@ namespace linker.tun
|
||||
{
|
||||
if (session == 0 || tokenSource.IsCancellationRequested) return false;
|
||||
|
||||
if (ToAppNat(packet)) return true;
|
||||
if (winDivertNAT.Inject(packet)) return true;
|
||||
|
||||
IntPtr packetPtr = WinTun.WintunAllocateSendPacket(session, (uint)packet.Length);
|
||||
if (packetPtr != 0)
|
||||
@@ -375,21 +375,6 @@ namespace linker.tun
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private bool ToAppNat(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
ReadOnlySpan<byte> span = packet.Span;
|
||||
if ((byte)(span[0] >> 4 & 0b1111) == 4 && AppNat) //只支持IPV4
|
||||
{
|
||||
ReadOnlySpan<byte> ip = span.Slice(16, 4);
|
||||
uint distIP = BinaryPrimitives.ReadUInt32BigEndian(ip);
|
||||
//不是虚拟网卡,不是广播,启用了应用层NAT,NAT成功
|
||||
if (distIP != address32 && ip.GetIsBroadcastAddress() == false && winDivertNAT.Inject(packet))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void GetWindowsInterfaceNum()
|
||||
{
|
||||
@@ -420,6 +405,7 @@ namespace linker.tun
|
||||
defaultInterfaceName = inter.Name;
|
||||
defaultInterfaceNumber = inter.GetIPProperties().GetIPv4Properties().Index;
|
||||
defaultInterfaceIP = ip;
|
||||
defaultInterfaceIP32 = NetworkHelper.ToValue(ip);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
using linker.libs;
|
||||
using linker.libs.extends;
|
||||
using linker.libs.timer;
|
||||
using System.Buffers.Binary;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
@@ -9,7 +11,6 @@ namespace linker.tun
|
||||
{
|
||||
/// <summary>
|
||||
/// 应用层简单SNAT
|
||||
/// 大概意思是
|
||||
/// 1,收到【客户端A】的数据包,10.18.18.23(客户端A的虚拟网卡IP)->192.168.56.6(局域网IP)
|
||||
/// 2,改为 192.168.56.2(本机IP)->192.168.56.6(局域网IP)
|
||||
/// 3,回来是 192.168.56.6(局域网IP)->192.168.56.2(本机IP)
|
||||
@@ -18,40 +19,53 @@ namespace linker.tun
|
||||
/// </summary>
|
||||
public sealed class WinDivertNAT
|
||||
{
|
||||
public bool Running => winDivert != null;
|
||||
|
||||
/// <summary>
|
||||
/// 驱动
|
||||
/// </summary>
|
||||
WinDivert winDivert;
|
||||
private WinDivert winDivert;
|
||||
|
||||
/// <summary>
|
||||
/// 源
|
||||
/// 网卡IP,用来作为源地址
|
||||
/// </summary>
|
||||
AddrInfo src;
|
||||
private NetworkIPv4Addr interfaceAddr;
|
||||
|
||||
private uint srcIp;
|
||||
|
||||
/// <summary>
|
||||
/// 目标
|
||||
/// 用来注入数据包
|
||||
/// </summary>
|
||||
AddrInfo[] dsts;
|
||||
|
||||
IPAddress interfaceIp;
|
||||
NetworkIPv4Addr interfaceAddr;
|
||||
|
||||
public bool Running => winDivert != null;
|
||||
|
||||
private CancellationTokenSource cts;
|
||||
private ConcurrentDictionary<(uint src, ushort srcPort, uint dst, ushort dstPort, ProtocolType pro), NatMapInfo> natMap = new ConcurrentDictionary<(uint src, ushort srcPort, uint dst, ushort dstPort, ProtocolType pro), NatMapInfo>();
|
||||
|
||||
public WinDivertNAT(AddrInfo src, AddrInfo[] dsts, IPAddress interfaceIp)
|
||||
private WinDivertAddress addr = new WinDivertAddress
|
||||
{
|
||||
Layer = WinDivert.Layer.Network,
|
||||
Outbound = true,
|
||||
IPv6 = false
|
||||
};
|
||||
private CancellationTokenSource cts;
|
||||
|
||||
/// <summary>
|
||||
/// 五元组NAT映射表
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<(uint src, ushort srcPort, uint dst, ushort dstPort, ProtocolType pro), NatMapInfo> natMap = new ConcurrentDictionary<(uint src, ushort srcPort, uint dst, ushort dstPort, ProtocolType pro), NatMapInfo>();
|
||||
/// <summary>
|
||||
/// 分配端口表
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<(uint src, ushort port), ushort> source2portMap = new ConcurrentDictionary<(uint src, ushort port), ushort>();
|
||||
|
||||
public WinDivertNAT()
|
||||
{
|
||||
this.src = src;
|
||||
this.dsts = dsts;
|
||||
this.interfaceIp = interfaceIp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
/// <param name="error"></param>
|
||||
/// <param name="src">虚拟网卡IP</param>
|
||||
/// <param name="dsts">需要NAT的IP</param>
|
||||
/// <param name="interfaceIp">本地网卡IP</param>
|
||||
/// <param name="error">false的时候会有报错信息</param>
|
||||
/// <returns></returns>
|
||||
public bool Setup(out string error)
|
||||
public bool Setup(IPAddress src, AddrInfo[] dsts, IPAddress interfaceIp, out string error)
|
||||
{
|
||||
error = string.Empty;
|
||||
|
||||
@@ -60,18 +74,21 @@ namespace linker.tun
|
||||
error = "only windows x64,x86";
|
||||
return false;
|
||||
}
|
||||
if (src == null || dsts == null || dsts.Length == 0)
|
||||
if (dsts == null || dsts.Length == 0)
|
||||
{
|
||||
error = "src is null, or dsts empty";
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
srcIp = NetworkHelper.ToValue(src);
|
||||
interfaceAddr = IPv4Addr.Parse(interfaceIp.ToString());
|
||||
winDivert = new WinDivert(BuildFilter(), WinDivert.Layer.Network, 0, 0);
|
||||
winDivert = new WinDivert(BuildFilter(dsts), WinDivert.Layer.Network, 0, 0);
|
||||
|
||||
cts = new CancellationTokenSource();
|
||||
Recv(cts);
|
||||
ClearTask(cts);
|
||||
|
||||
Recv();
|
||||
ClearTask();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -80,14 +97,20 @@ namespace linker.tun
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private string BuildFilter()
|
||||
/// <summary>
|
||||
/// 过滤条件,只过滤一定的数据包
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string BuildFilter(AddrInfo[] dsts)
|
||||
{
|
||||
IEnumerable<string> ipRanges = dsts.Select(c => $"(ip.SrcAddr >= {c.NetworkIP} and ip.SrcAddr <= {c.BroadcastIP})");
|
||||
return $"inbound and ({string.Join(" or ", ipRanges)})";
|
||||
}
|
||||
private void Recv()
|
||||
/// <summary>
|
||||
/// 开始接收数据包
|
||||
/// </summary>
|
||||
private void Recv(CancellationTokenSource cts)
|
||||
{
|
||||
cts = new CancellationTokenSource();
|
||||
TimerHelper.Async(() =>
|
||||
{
|
||||
Memory<byte> packet = new Memory<byte>(new byte[10 * WinDivert.MTUMax]);
|
||||
@@ -112,7 +135,7 @@ namespace linker.tun
|
||||
break;
|
||||
}
|
||||
}
|
||||
Dispose();
|
||||
Shutdown();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -144,28 +167,30 @@ namespace linker.tun
|
||||
/// 注入数据包,让它直接走正确的网卡,路由到目的地
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
public unsafe bool Inject(ReadOnlyMemory<byte> buffer)
|
||||
public unsafe bool Inject(ReadOnlyMemory<byte> packet)
|
||||
{
|
||||
fixed (byte* ptr = buffer.Span)
|
||||
if (winDivert == null) return false;
|
||||
|
||||
IPV4Packet ipv4 = new IPV4Packet(packet.Span);
|
||||
//不是 ipv4,是虚拟网卡ip,是广播,不nat
|
||||
if (ipv4.Version != ProtocolType.IPv4 || ipv4.DstAddr == srcIp || ipv4.DstAddrSpan.GetIsBroadcastAddress()) return false;
|
||||
|
||||
fixed (byte* ptr = packet.Span)
|
||||
{
|
||||
foreach (var (i, p) in new WinDivertIndexedPacketParser(buffer))
|
||||
foreach (var (i, p) in new WinDivertIndexedPacketParser(packet))
|
||||
{
|
||||
bool result = (ProtocolType)p.IPv4Hdr->Protocol switch
|
||||
//本机网卡IP不需要改,直接注入就可以
|
||||
if (p.IPv4Hdr->DstAddr != interfaceAddr)
|
||||
{
|
||||
ProtocolType.Icmp => InjectIcmp(p, ptr),
|
||||
ProtocolType.Tcp => InjectTcp(p, ptr),
|
||||
ProtocolType.Udp => InjectUdp(p, ptr),
|
||||
_ => false,
|
||||
};
|
||||
if (result == false) return false;
|
||||
|
||||
WinDivertAddress addr = new WinDivertAddress
|
||||
{
|
||||
Layer = WinDivert.Layer.Network,
|
||||
Outbound = true,
|
||||
IPv6 = false
|
||||
};
|
||||
|
||||
bool result = (ProtocolType)p.IPv4Hdr->Protocol switch
|
||||
{
|
||||
ProtocolType.Icmp => InjectIcmp(p, ptr),
|
||||
ProtocolType.Tcp => InjectTcp(p, ptr),
|
||||
ProtocolType.Udp => InjectUdp(p, ptr),
|
||||
_ => false,
|
||||
};
|
||||
if (result == false) return false;
|
||||
}
|
||||
WinDivert.CalcChecksums(p.Packet.Span, ref addr, 0);
|
||||
winDivert.SendEx(p.Packet.Span, new ReadOnlySpan<WinDivertAddress>(ref addr));
|
||||
}
|
||||
@@ -184,15 +209,16 @@ namespace linker.tun
|
||||
//只操作response 和 request
|
||||
if (p.ICMPv4Hdr->Type != 0 && p.ICMPv4Hdr->Type != 8) return false;
|
||||
|
||||
//IP头长度
|
||||
byte ipHeaderLength = (byte)((p.Packet.Span[0] & 0b1111) * 4);
|
||||
IPV4Packet ipv4 = new IPV4Packet(ptr);
|
||||
if (ipv4.IsFragment) return false;
|
||||
|
||||
//原标识符,两个字节
|
||||
byte* ptr0 = ptr + ipHeaderLength + 4;
|
||||
byte* ptr1 = ptr + ipHeaderLength + 5;
|
||||
byte* ptr0 = ipv4.IcmpIdentifier0;
|
||||
byte* ptr1 = ipv4.IcmpIdentifier1;
|
||||
|
||||
//用源地址的第三个,第四个字节作为新的标识符
|
||||
byte identifier0 = p.Packet.Span[14];
|
||||
byte identifier1 = p.Packet.Span[15];
|
||||
byte identifier0 = ipv4.SrcAddrSpan[2];
|
||||
byte identifier1 = ipv4.SrcAddrSpan[3];
|
||||
|
||||
//保存,源地址。标识符0,目的地址,标识符1,ICMP
|
||||
//取值,目的地址,标识符0,源地址,标识符1,ICMP
|
||||
@@ -225,12 +251,12 @@ namespace linker.tun
|
||||
{
|
||||
//只操作response 和 request
|
||||
if (p.ICMPv4Hdr->Type != 0 && p.ICMPv4Hdr->Type != 8) return false;
|
||||
//IP头长度
|
||||
byte ipHeaderLength = (byte)((*ptr & 0b1111) * 4);
|
||||
|
||||
IPV4Packet ipv4 = new IPV4Packet(ptr);
|
||||
|
||||
//标识符,两个字节
|
||||
byte* ptr0 = ptr + ipHeaderLength + 4;
|
||||
byte* ptr1 = ptr + ipHeaderLength + 5;
|
||||
byte* ptr0 = ipv4.IcmpIdentifier0;
|
||||
byte* ptr1 = ipv4.IcmpIdentifier1;
|
||||
|
||||
ValueTuple<uint, ushort, uint, ushort, ProtocolType> key = (p.IPv4Hdr->DstAddr.Raw, *ptr0, p.IPv4Hdr->SrcAddr.Raw, *ptr1, ProtocolType.Icmp);
|
||||
if (natMap.TryRemove(key, out NatMapInfo natMapInfo))
|
||||
@@ -244,46 +270,143 @@ namespace linker.tun
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/// <summary>
|
||||
/// 注入TCP
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
/// <param name="ptr"></param>
|
||||
/// <returns></returns>
|
||||
private unsafe bool InjectTcp(WinDivertParseResult p, byte* ptr)
|
||||
{
|
||||
return false;
|
||||
byte ipHeaderLength = (byte)((p.Packet.Span[0] & 0b1111) * 4);
|
||||
IPV4Packet ipv4 = new IPV4Packet(ptr);
|
||||
|
||||
byte* ptr0 = ptr + ipHeaderLength + 4;
|
||||
byte* ptr1 = ptr + ipHeaderLength + 5;
|
||||
|
||||
byte identifier0 = p.Packet.Span[14];
|
||||
byte identifier1 = p.Packet.Span[15];
|
||||
ValueTuple<uint, ushort, uint, ushort, ProtocolType> key = (interfaceAddr.Raw, identifier0, p.IPv4Hdr->DstAddr.Raw, identifier1, ProtocolType.Icmp);
|
||||
NatMapInfo natMapInfo = new NatMapInfo
|
||||
//新端口
|
||||
ValueTuple<uint, ushort> portKey = (p.IPv4Hdr->SrcAddr.Raw, p.TCPHdr->SrcPort);
|
||||
if (source2portMap.TryGetValue(portKey, out ushort newPort) == false)
|
||||
{
|
||||
SrcAddr = p.IPv4Hdr->SrcAddr,
|
||||
Identifier0 = *ptr0,
|
||||
Identifier1 = *ptr1,
|
||||
LastTime = Environment.TickCount64
|
||||
};
|
||||
natMap.AddOrUpdate(key, natMapInfo, (a, b) => natMapInfo);
|
||||
//只在syn时建立
|
||||
if (ipv4.TcpFlagSyn == false || ipv4.TcpFlagAck) return false;
|
||||
newPort = ApplyNewPort();
|
||||
source2portMap.TryAdd(portKey, newPort);
|
||||
}
|
||||
|
||||
//添加映射
|
||||
ValueTuple<uint, ushort, uint, ushort, ProtocolType> key = (interfaceAddr.Raw, newPort, p.IPv4Hdr->DstAddr.Raw, p.TCPHdr->DstPort, ProtocolType.Tcp);
|
||||
if (natMap.TryGetValue(key, out NatMapInfo natMapInfo) == false)
|
||||
{
|
||||
natMapInfo = new NatMapInfo
|
||||
{
|
||||
SrcAddr = p.IPv4Hdr->SrcAddr,
|
||||
SrcPort = p.TCPHdr->SrcPort,
|
||||
LastTime = Environment.TickCount64
|
||||
};
|
||||
natMap.TryAdd(key, natMapInfo);
|
||||
}
|
||||
natMapInfo.LastTime = Environment.TickCount64;
|
||||
//fin+ack 或者 rst 就清除
|
||||
if (ipv4.TcpFlagFin) natMapInfo.Fin0 = ipv4.TcpFlagFin;
|
||||
if (ipv4.TcpFlagRst) natMapInfo.Rst = ipv4.TcpFlagRst;
|
||||
if (natMapInfo.Fin0 && ipv4.TcpFlagAck) natMapInfo.FinAck = ipv4.TcpFlagAck;
|
||||
|
||||
*ptr0 = identifier0;
|
||||
*ptr1 = identifier1;
|
||||
p.IPv4Hdr->SrcAddr = interfaceAddr;
|
||||
|
||||
p.TCPHdr->SrcPort = newPort;
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// 还原TCP
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
/// <param name="ptr"></param>
|
||||
/// <returns></returns>
|
||||
private unsafe bool RecvTcp(WinDivertParseResult p, byte* ptr)
|
||||
{
|
||||
IPV4Packet ipv4 = new IPV4Packet(ptr);
|
||||
|
||||
ValueTuple<uint, ushort, uint, ushort, ProtocolType> key = (p.IPv4Hdr->DstAddr.Raw, p.TCPHdr->DstPort, p.IPv4Hdr->SrcAddr.Raw, p.TCPHdr->SrcPort, ProtocolType.Tcp);
|
||||
if (natMap.TryGetValue(key, out NatMapInfo natMapInfo))
|
||||
{
|
||||
natMapInfo.LastTime = Environment.TickCount64;
|
||||
|
||||
//fin+ack 或者 rst 就清除
|
||||
if (ipv4.TcpFlagFin) natMapInfo.Fin1 = ipv4.TcpFlagFin;
|
||||
if (ipv4.TcpFlagRst) natMapInfo.Rst = ipv4.TcpFlagRst;
|
||||
if (natMapInfo.Fin1 && ipv4.TcpFlagAck) natMapInfo.FinAck = ipv4.TcpFlagAck;
|
||||
|
||||
p.IPv4Hdr->DstAddr = natMapInfo.SrcAddr;
|
||||
p.TCPHdr->DstPort = natMapInfo.SrcPort;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/// <summary>
|
||||
/// 注入UDP
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
/// <param name="ptr"></param>
|
||||
/// <returns></returns>
|
||||
private unsafe bool InjectUdp(WinDivertParseResult p, byte* ptr)
|
||||
{
|
||||
//新端口
|
||||
ValueTuple<uint, ushort> portKey = (p.IPv4Hdr->SrcAddr.Raw, p.UDPHdr->SrcPort);
|
||||
if (source2portMap.TryGetValue(portKey, out ushort newPort) == false)
|
||||
{
|
||||
newPort = ApplyNewPort();
|
||||
source2portMap.TryAdd(portKey, newPort);
|
||||
}
|
||||
//映射
|
||||
ValueTuple<uint, ushort, uint, ushort, ProtocolType> key = (interfaceAddr.Raw, newPort, p.IPv4Hdr->DstAddr.Raw, p.UDPHdr->DstPort, ProtocolType.Tcp);
|
||||
if (natMap.TryGetValue(key, out NatMapInfo natMapInfo) == false)
|
||||
{
|
||||
natMapInfo = new NatMapInfo
|
||||
{
|
||||
SrcAddr = p.IPv4Hdr->SrcAddr,
|
||||
SrcPort = p.UDPHdr->SrcPort,
|
||||
LastTime = Environment.TickCount64
|
||||
};
|
||||
natMap.TryAdd(key, natMapInfo);
|
||||
}
|
||||
natMapInfo.LastTime = Environment.TickCount64;
|
||||
|
||||
p.IPv4Hdr->SrcAddr = interfaceAddr;
|
||||
p.UDPHdr->SrcPort = newPort;
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// 还原UDP
|
||||
/// </summary>
|
||||
/// <param name="p"></param>
|
||||
/// <param name="ptr"></param>
|
||||
/// <returns></returns>
|
||||
private unsafe bool RecvUdp(WinDivertParseResult p, byte* ptr)
|
||||
{
|
||||
ValueTuple<uint, ushort, uint, ushort, ProtocolType> key = (p.IPv4Hdr->DstAddr.Raw, p.UDPHdr->DstPort, p.IPv4Hdr->SrcAddr.Raw, p.UDPHdr->SrcPort, ProtocolType.Tcp);
|
||||
if (natMap.TryGetValue(key, out NatMapInfo natMapInfo))
|
||||
{
|
||||
natMapInfo.LastTime = Environment.TickCount64;
|
||||
p.IPv4Hdr->DstAddr = natMapInfo.SrcAddr;
|
||||
p.UDPHdr->DstPort = natMapInfo.SrcPort;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private unsafe bool RecvUdp(WinDivertParseResult p, byte* ptr) { return false; }
|
||||
|
||||
/// <summary>
|
||||
/// 注销
|
||||
/// 申请一个新的端口
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
/// <returns></returns>
|
||||
private ushort ApplyNewPort()
|
||||
{
|
||||
using Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
socket.Bind(new IPEndPoint(IPAddress.Any, 0));
|
||||
|
||||
return (ushort)(socket.LocalEndPoint as IPEndPoint).Port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭
|
||||
/// </summary>
|
||||
public void Shutdown()
|
||||
{
|
||||
cts?.Cancel();
|
||||
|
||||
@@ -291,17 +414,22 @@ namespace linker.tun
|
||||
winDivert = null;
|
||||
|
||||
natMap.Clear();
|
||||
source2portMap.Clear();
|
||||
}
|
||||
|
||||
private void ClearTask()
|
||||
private void ClearTask(CancellationTokenSource cts)
|
||||
{
|
||||
TimerHelper.SetIntervalLong(() =>
|
||||
{
|
||||
long now = Environment.TickCount64;
|
||||
foreach (var item in natMap.Where(c => now - c.Value.LastTime > 1 * 60 * 60).Select(c => c.Key).ToList())
|
||||
foreach (var item in natMap.Where(c => now - c.Value.LastTime > 1 * 60 * 60 || c.Value.FinAck || c.Value.Rst).Select(c => c.Key).ToList())
|
||||
{
|
||||
natMap.TryRemove(item, out _);
|
||||
if (natMap.TryRemove(item, out NatMapInfo natMapInfo))
|
||||
{
|
||||
source2portMap.TryRemove((natMapInfo.SrcAddr.Raw, natMapInfo.SrcPort), out _);
|
||||
}
|
||||
}
|
||||
return cts.IsCancellationRequested == false;
|
||||
}, 5000);
|
||||
}
|
||||
public sealed class AddrInfo
|
||||
@@ -336,10 +464,112 @@ namespace linker.tun
|
||||
}
|
||||
sealed class NatMapInfo
|
||||
{
|
||||
//IP头
|
||||
public NetworkIPv4Addr SrcAddr { get; set; }
|
||||
|
||||
//TCP/UDP
|
||||
public NetworkUInt16 SrcPort { get; set; }
|
||||
|
||||
//ICMP
|
||||
public byte Identifier0 { get; set; }
|
||||
public byte Identifier1 { get; set; }
|
||||
|
||||
//TCP
|
||||
public bool Fin0 { get; set; }
|
||||
public bool Fin1 { get; set; }
|
||||
public bool FinAck { get; set; }
|
||||
public bool Rst { get; set; }
|
||||
|
||||
public long LastTime { get; set; } = Environment.TickCount64;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// IPV4 包
|
||||
/// </summary>
|
||||
unsafe struct IPV4Packet
|
||||
{
|
||||
byte* ptr;
|
||||
|
||||
/// <summary>
|
||||
/// 协议版本
|
||||
/// </summary>
|
||||
public ProtocolType Version => (ProtocolType)((*ptr >> 4) & 0b1111);
|
||||
|
||||
/// <summary>
|
||||
/// 源地址
|
||||
/// </summary>
|
||||
public uint SrcAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 12));
|
||||
/// <summary>
|
||||
/// 目的地址
|
||||
/// </summary>
|
||||
public uint DstAddr => BinaryPrimitives.ReverseEndianness(*(uint*)(ptr + 16));
|
||||
/// <summary>
|
||||
/// 源地址
|
||||
/// </summary>
|
||||
public ReadOnlySpan<byte> SrcAddrSpan => new Span<byte>((ptr + 12), 4);
|
||||
/// <summary>
|
||||
/// 目的地址
|
||||
/// </summary>
|
||||
public ReadOnlySpan<byte> DstAddrSpan => new Span<byte>((ptr + 16), 4);
|
||||
|
||||
/// <summary>
|
||||
/// IP头长度
|
||||
/// </summary>
|
||||
public int IPHeadLength => (*ptr & 0b1111) * 4;
|
||||
|
||||
/// <summary>
|
||||
/// IP Flag
|
||||
/// </summary>
|
||||
public byte Flag => (byte)(*(ptr + 6) >> 5);
|
||||
/// <summary>
|
||||
/// 不分片
|
||||
/// </summary>
|
||||
public bool DontFragment => (Flag & 0x02) == 2;
|
||||
/// <summary>
|
||||
/// 更多分片
|
||||
/// </summary>
|
||||
public bool MoreFragment => (Flag & 0x01) == 1;
|
||||
/// <summary>
|
||||
/// 分片偏移量
|
||||
/// </summary>
|
||||
public ushort Offset => (ushort)(BinaryPrimitives.ReverseEndianness(*(ushort*)(ptr + 6)) & 0x1fff);
|
||||
/// <summary>
|
||||
/// 是否分片
|
||||
/// </summary>
|
||||
public bool IsFragment => MoreFragment || Offset > 0;
|
||||
|
||||
/// <summary>
|
||||
/// ICMP标志第一个字节
|
||||
/// </summary>
|
||||
public byte* IcmpIdentifier0 => ptr + IPHeadLength + 4;
|
||||
/// <summary>
|
||||
/// ICMP标志第二个字节
|
||||
/// </summary>
|
||||
public byte* IcmpIdentifier1 => ptr + IPHeadLength + 5;
|
||||
|
||||
/// <summary>
|
||||
/// TCP Flag
|
||||
/// </summary>
|
||||
public byte TcpFlag => *(ptr + IPHeadLength + 13);
|
||||
public bool TcpFlagFin => (TcpFlag & 0b000001) != 0;
|
||||
public bool TcpFlagSyn => (TcpFlag & 0b000010) != 0;
|
||||
public bool TcpFlagRst => (TcpFlag & 0b000100) != 0;
|
||||
public bool TcpFlagPsh => (TcpFlag & 0b001000) != 0;
|
||||
public bool TcpFlagAck => (TcpFlag & 0b010000) != 0;
|
||||
public bool TcpFlagUrg => (TcpFlag & 0b100000) != 0;
|
||||
|
||||
public IPV4Packet(byte* ptr)
|
||||
{
|
||||
this.ptr = ptr;
|
||||
}
|
||||
public IPV4Packet(ReadOnlySpan<byte> span)
|
||||
{
|
||||
fixed (byte* ptr = span)
|
||||
{
|
||||
this.ptr = ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -195,6 +195,9 @@ span.split-pad10 {
|
||||
.green {
|
||||
color: green !important;
|
||||
}
|
||||
.app-nat {
|
||||
color: #018a81 !important;
|
||||
}
|
||||
|
||||
.yellow {
|
||||
color: #e68906 !important;
|
||||
|
@@ -10,6 +10,9 @@
|
||||
<template v-else-if="tuntap.list[item.MachineId].Upgrade && tuntap.list[item.MachineId].NatError">
|
||||
<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">
|
||||
<strong class="app-nat" title="虚拟网卡IP,系统NAT失败,已启用内置应用层SNAT">{{ tuntap.list[item.MachineId].IP }}</strong>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if=" item.Connected && tuntap.list[item.MachineId].running">
|
||||
<strong class="green gateway">{{ tuntap.list[item.MachineId].IP }}</strong>
|
||||
|
@@ -22,7 +22,8 @@
|
||||
<Company>snltty</Company>
|
||||
<Description>1. 优化自动分配IP
|
||||
2. 优化网卡,排除不明数据包
|
||||
3. 虚拟网卡点对网IP映射,用于解决网段冲突</Description>
|
||||
3. 虚拟网卡点对网IP映射,用于解决网段冲突
|
||||
4. 内置应用层SNAT,用于无法使用系统NAT的windows系统</Description>
|
||||
<Copyright>snltty</Copyright>
|
||||
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>
|
||||
|
@@ -1,5 +1,6 @@
|
||||
v1.7.3
|
||||
2025-04-20 00:00:20
|
||||
2025-04-20 17:21:43
|
||||
1. 优化自动分配IP
|
||||
2. 优化网卡,排除不明数据包
|
||||
3. 虚拟网卡点对网IP映射,用于解决网段冲突
|
||||
3. 虚拟网卡点对网IP映射,用于解决网段冲突
|
||||
4. 内置应用层SNAT,用于无法使用系统NAT的windows系统
|
Reference in New Issue
Block a user