Remove dependency on gVisor

This commit is contained in:
世界 committed 2026-11-09 15:17:20 +00:00
1 parent 34009e2188
commit 6ba86964d0
68 files changed
+770 -1805

No files matched your search

-1
View File
@@ -2,7 +2,6 @@ version: "2"
run:
go: "1.24"
build-tags:
- with_gvisor
- with_quic
- with_dhcp
- with_wireguard
+1 -1
View File
@@ -63,7 +63,7 @@ func init() {
sharedFlags = append(sharedFlags, "-ldflags", build_shared.LinkerFlags(currentTag, false))
debugFlags = append(debugFlags, "-ldflags", build_shared.LinkerFlags(currentTag, true))
sharedTags = append(sharedTags, "with_gvisor", "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_usbip", "with_openvpn", "with_openconnect", "badlinkname", "tfogo_checklinkname0")
sharedTags = append(sharedTags, "with_quic", "with_wireguard", "with_utls", "with_naive_outbound", "with_clash_api", "with_usbip", "with_openvpn", "with_openconnect", "badlinkname", "tfogo_checklinkname0")
darwinTags = append(darwinTags, "with_dhcp", "grpcnotrace")
// memcTags = append(memcTags, "with_tailscale")
sharedTags = append(sharedTags, "with_tailscale", "ts_omit_logtail", "ts_omit_ssh", "ts_omit_drive", "ts_omit_taildrop", "ts_omit_webclient", "ts_omit_doctor", "ts_omit_capture", "ts_omit_kube", "ts_omit_aws", "ts_omit_synology", "ts_omit_bird")
-2
View File
@@ -76,8 +76,6 @@ Use system interface.
Requires privilege and cannot conflict with exists system interfaces.
Forced if gVisor not included in the build.
#### interface_name
Custom interface name for system interface.
@@ -64,8 +64,6 @@ icon: material/delete-clock
需要特权且不能与已有系统接口冲突。
如果 gVisor 未包含在构建中,则强制执行。
#### interface_name
为系统接口自定义设备名称。
+1 -1
View File
@@ -55,7 +55,7 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
| `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls/). |
| `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). |
| `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). |
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). |
| `with_gvisor` | :material-close: | Build with gVisor support, required by the deprecated `gvisor` / `mixed` [Tun stacks](/configuration/inbound/tun#stack). |
| `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor/). |
| `with_tailscale` | :material-check: | Build with Tailscale support, see [Tailscale endpoint](/configuration/endpoint/tailscale). |
| `with_ccm` | :material-check: | Build with Claude Code Multiplexer service support. |
+1 -1
View File
@@ -59,7 +59,7 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
| `with_acme` | :material-check: | Build with ACME TLS certificate issuer support, see [TLS](/zh/configuration/shared/tls/). |
| `with_clash_api` | :material-check: | Build with Clash API support, see [Experimental](/zh/configuration/experimental#clash-api-fields). |
| `with_v2ray_api` | :material-close:️ | Build with V2Ray API support, see [Experimental](/zh/configuration/experimental#v2ray-api-fields). |
| `with_gvisor` | :material-check: | Build with gVisor support, see [Tun inbound](/zh/configuration/inbound/tun#stack) and [WireGuard outbound](/zh/configuration/outbound/wireguard#system_interface). |
| `with_gvisor` | :material-close: | 构建 gVisor 支持,已弃用的 `gvisor` / `mixed` [Tun 栈](/zh/configuration/inbound/tun#stack) 需要。 |
| `with_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/zh/configuration/outbound/tor/). |
| `with_tailscale` | :material-check: | 构建 Tailscale 支持,参阅 [Tailscale 端点](/zh/configuration/endpoint/tailscale)。 |
| `with_ccm` | :material-check: | 构建 Claude Code Multiplexer 服务支持。 |
-257
View File
@@ -1,257 +0,0 @@
{
"version": 1,
"variables": {
"VERSION": "$(go run github.com/sagernet/sing-box/cmd/internal/read_tag@latest)",
"WORKSPACE_ROOT": "../../..",
"DEPLOY_ANDROID": "${WORKSPACE_ROOT}/sing-box-for-android/app/libs",
"DEPLOY_APPLE": "${WORKSPACE_ROOT}/sing-box-for-apple",
"DEPLOY_WINDOWS": "${WORKSPACE_ROOT}/sing-box-for-windows/local-packages"
},
"packages": [
{
"id": "libbox",
"path": ".",
"java_package": "io.nekohasekai.libbox",
"csharp_namespace": "SagerNet",
"csharp_entrypoint": "Libbox",
"apple_prefix": "Libbox"
}
],
"builds": [
{
"id": "android-main",
"packages": ["libbox"],
"default": {
"tags": [
"with_gvisor",
"with_quic",
"with_wireguard",
"with_utls",
"with_naive_outbound",
"with_clash_api",
"badlinkname",
"tfogo_checklinkname0",
"with_tailscale",
"ts_omit_logtail",
"ts_omit_ssh",
"ts_omit_drive",
"ts_omit_taildrop",
"ts_omit_webclient",
"ts_omit_doctor",
"ts_omit_capture",
"ts_omit_kube",
"ts_omit_aws",
"ts_omit_synology",
"ts_omit_bird"
],
"ldflags": "-X github.com/sagernet/sing-box/constant.Version=${VERSION} -X runtime.godebugDefault=multipathtcp=0,tlssha1=1,tlsunsafeekm=1 -s -w -buildid= -checklinkname=0",
"trimpath": true
}
},
{
"id": "android-legacy",
"packages": ["libbox"],
"default": {
"tags": [
"with_gvisor",
"with_quic",
"with_wireguard",
"with_utls",
"with_clash_api",
"badlinkname",
"tfogo_checklinkname0",
"with_tailscale",
"ts_omit_logtail",
"ts_omit_ssh",
"ts_omit_drive",
"ts_omit_taildrop",
"ts_omit_webclient",
"ts_omit_doctor",
"ts_omit_capture",
"ts_omit_kube",
"ts_omit_aws",
"ts_omit_synology",
"ts_omit_bird"
],
"ldflags": "-X github.com/sagernet/sing-box/constant.Version=${VERSION} -X runtime.godebugDefault=multipathtcp=0,tlssha1=1,tlsunsafeekm=1 -s -w -buildid= -checklinkname=0",
"trimpath": true
}
},
{
"id": "apple",
"packages": ["libbox"],
"default": {
"tags": [
"with_gvisor",
"with_quic",
"with_wireguard",
"with_utls",
"with_naive_outbound",
"with_clash_api",
"badlinkname",
"tfogo_checklinkname0",
"with_dhcp",
"grpcnotrace",
"with_tailscale",
"ts_omit_logtail",
"ts_omit_ssh",
"ts_omit_drive",
"ts_omit_taildrop",
"ts_omit_webclient",
"ts_omit_doctor",
"ts_omit_capture",
"ts_omit_kube",
"ts_omit_aws",
"ts_omit_synology",
"ts_omit_bird"
],
"ldflags": "-X github.com/sagernet/sing-box/constant.Version=${VERSION} -X runtime.godebugDefault=multipathtcp=0,tlssha1=1,tlsunsafeekm=1 -s -w -buildid= -checklinkname=0",
"trimpath": true
},
"overrides": [
{
"match": { "os": "ios" },
"tags_append": ["with_low_memory"]
},
{
"match": { "os": "tvos" },
"tags_append": ["with_low_memory"]
}
]
},
{
"id": "windows",
"packages": ["libbox"],
"default": {
"tags": [
"with_gvisor",
"with_quic",
"with_wireguard",
"with_utls",
"with_naive_outbound",
"with_purego",
"with_clash_api",
"badlinkname",
"tfogo_checklinkname0",
"with_tailscale",
"ts_omit_logtail",
"ts_omit_ssh",
"ts_omit_drive",
"ts_omit_taildrop",
"ts_omit_webclient",
"ts_omit_doctor",
"ts_omit_capture",
"ts_omit_kube",
"ts_omit_aws",
"ts_omit_synology",
"ts_omit_bird"
],
"ldflags": "-X github.com/sagernet/sing-box/constant.Version=${VERSION} -X runtime.godebugDefault=multipathtcp=0,tlssha1=1,tlsunsafeekm=1 -s -w -buildid= -checklinkname=0",
"trimpath": true
}
}
],
"platforms": [
{
"type": "android",
"build": "android-main",
"min_sdk": 23,
"ndk_version": "28.0.13004108",
"lib_name": "box",
"languages": [{ "type": "java" }],
"artifacts": [
{
"type": "aar",
"output_path": "libbox.aar",
"execute_after": [
"if [ -d \"${DEPLOY_ANDROID}\" ]; then",
" rm -f \"${DEPLOY_ANDROID}/$$(basename \"${OUTPUT_PATH}\")\"",
" mv \"${OUTPUT_PATH}\" \"${DEPLOY_ANDROID}/\"",
"fi"
]
}
]
},
{
"type": "android",
"build": "android-legacy",
"min_sdk": 21,
"ndk_version": "28.0.13004108",
"lib_name": "box",
"languages": [{ "type": "java" }],
"artifacts": [
{
"type": "aar",
"output_path": "libbox-legacy.aar",
"execute_after": [
"if [ -d \"${DEPLOY_ANDROID}\" ]; then",
" rm -f \"${DEPLOY_ANDROID}/$$(basename \"${OUTPUT_PATH}\")\"",
" mv \"${OUTPUT_PATH}\" \"${DEPLOY_ANDROID}/\"",
"fi"
]
}
]
},
{
"type": "apple",
"build": "apple",
"targets": [
"ios/arm64",
"ios/simulator/arm64",
"ios/simulator/amd64",
"tvos/arm64",
"tvos/simulator/arm64",
"tvos/simulator/amd64",
"macos/arm64",
"macos/amd64"
],
"languages": [{ "type": "objc" }],
"artifacts": [
{
"type": "xcframework",
"module_name": "Libbox",
"execute_after": [
"if [ -d \"${DEPLOY_APPLE}\" ]; then",
" rm -rf \"${DEPLOY_APPLE}/${MODULE_NAME}.xcframework\"",
" mv \"${OUTPUT_PATH}\" \"${DEPLOY_APPLE}/\"",
"fi"
]
}
]
},
{
"type": "csharp",
"build": "windows",
"targets": [
"windows/amd64"
],
"languages": [{ "type": "csharp" }],
"artifacts": [
{
"type": "nuget",
"package_id": "SagerNet.Libbox",
"package_version": "0.0.0-local",
"execute_after": {
"windows": [
"$$deployPath = '${DEPLOY_WINDOWS}'",
"if (Test-Path $$deployPath) {",
" Remove-Item \"$$deployPath\\${PACKAGE_ID}.*.nupkg\" -ErrorAction SilentlyContinue",
" Move-Item -Force '${OUTPUT_PATH}' \"$$deployPath\\\"",
" $$cachePath = if ($$env:NUGET_PACKAGES) { $$env:NUGET_PACKAGES } else { \"$$env:USERPROFILE\\.nuget\\packages\" }",
" Remove-Item -Recurse -Force \"$$cachePath\\sagernet.libbox\\${PACKAGE_VERSION}\" -ErrorAction SilentlyContinue",
"}"
],
"default": [
"if [ -d \"${DEPLOY_WINDOWS}\" ]; then",
" rm -f \"${DEPLOY_WINDOWS}/${PACKAGE_ID}.*.nupkg\"",
" mv \"${OUTPUT_PATH}\" \"${DEPLOY_WINDOWS}/\"",
" cache_path=\"$${NUGET_PACKAGES:-$${HOME}/.nuget/packages}\"",
" rm -rf \"$${cache_path}/sagernet.libbox/${PACKAGE_VERSION}\"",
"fi"
]
}
}
]
}
]
}
+3 -3
View File
@@ -40,7 +40,6 @@ require (
github.com/sagernet/fswatch v0.1.2
github.com/sagernet/gliderssh v0.3.4-0.20261023092853-e0cad6d28647
github.com/sagernet/gomobile v0.1.12
github.com/sagernet/gvisor v0.0.0-20260727.0-sing-box-mod.1
github.com/sagernet/netlink v0.0.0-20260814022025-64455d367bbf
github.com/sagernet/nftables v0.3.0-mod.4
github.com/sagernet/quic-go v0.61.0-sing-box-mod.13
@@ -59,8 +58,8 @@ require (
github.com/sagernet/sing-usbip v0.0.0-20260817040617-28bd42667eca
github.com/sagernet/sing-vmess v0.2.9-0.20261108133243-b01463cef941
github.com/sagernet/smux v1.5.50-sing-box-mod.1
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.5.0.20261019070639-79c1732442a9
github.com/sagernet/wireguard-go v0.0.8-0.20261022215841-e4d85ef2ed38
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.5.0.20261022105140-481f8d5be5e6
github.com/sagernet/wireguard-go v0.0.8-0.20261022215849-fdf0ad046586
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.12.0
@@ -169,6 +168,7 @@ require (
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20261103043047-3aac4ed44c5c // indirect
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20261103043047-3aac4ed44c5c // indirect
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20261103043047-3aac4ed44c5c // indirect
github.com/sagernet/gvisor v0.0.0-20260727.0-sing-box-mod.1 // indirect
github.com/smallstep/pkcs7 v0.1.1 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d // indirect
+4 -4
View File
@@ -346,10 +346,10 @@ github.com/sagernet/sing-vmess v0.2.9-0.20261108133243-b01463cef941 h1:QiLfQ/yZw
github.com/sagernet/sing-vmess v0.2.9-0.20261108133243-b01463cef941/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.5.0.20261019070639-79c1732442a9 h1:Phb4IDqGdP2Wg8VDComdDy2uMg1ZN3gs0kpJHp6C8O8=
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.5.0.20261019070639-79c1732442a9/go.mod h1:WLUSOPmTcf7VN9gLCe01qUSIvD+/cKC177neENyZPkI=
github.com/sagernet/wireguard-go v0.0.8-0.20261022215841-e4d85ef2ed38 h1:VruOdZAjG+nQi//y9YYL8Pf18+SymIkzBoxNt5gUzuY=
github.com/sagernet/wireguard-go v0.0.8-0.20261022215841-e4d85ef2ed38/go.mod h1:er10sELpmzLXq7S7Pbc1Zsbyapcr+/gxNAHKTo6fzVA=
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.5.0.20261022105140-481f8d5be5e6 h1:RexTvtia7PIj+gm6p3dEimeUw9RtqaJau+ubLc4k70U=
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.5.0.20261022105140-481f8d5be5e6/go.mod h1:Z8Eql5l0q6mQ+etunojfaZxNJVhEkpbKGS/nnx9ZQpE=
github.com/sagernet/wireguard-go v0.0.8-0.20261022215849-fdf0ad046586 h1:lssrGZJRiO6s2ShbBjchpFsNml9cj/7G+aeEL57ZvVc=
github.com/sagernet/wireguard-go v0.0.8-0.20261022215849-fdf0ad046586/go.mod h1:er10sELpmzLXq7S7Pbc1Zsbyapcr+/gxNAHKTo6fzVA=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
github.com/smallstep/pkcs7 v0.1.1 h1:x+rPdt2W088V9Vkjho4KtoggyktZJlMduZAtRHm68LU=
-3
View File
@@ -16,9 +16,6 @@ import (
func registerOpenConnectEndpoint(registry *endpoint.Registry) {
endpoint.Register[option.OpenConnectEndpointOptions](registry, C.TypeOpenConnect, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.OpenConnectEndpointOptions) (adapter.Endpoint, error) {
if !options.System {
return nil, E.New(`OpenConnect is not included in this build, rebuild with -tags with_openconnect,with_gvisor for system:false`)
}
return nil, E.New(`OpenConnect is not included in this build, rebuild with -tags with_openconnect`)
})
}
-6
View File
@@ -16,15 +16,9 @@ import (
func registerOpenVPNEndpoints(registry *endpoint.Registry) {
endpoint.Register[option.OpenVPNClientEndpointOptions](registry, C.TypeOpenVPNClient, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.OpenVPNClientEndpointOptions) (adapter.Endpoint, error) {
if !options.System {
return nil, E.New(`OpenVPN is not included in this build, rebuild with -tags with_openvpn,with_gvisor for system:false`)
}
return nil, E.New(`OpenVPN is not included in this build, rebuild with -tags with_openvpn`)
})
endpoint.Register[option.OpenVPNServerEndpointOptions](registry, C.TypeOpenVPNServer, func(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.OpenVPNServerEndpointOptions) (adapter.Endpoint, error) {
if !options.System {
return nil, E.New(`OpenVPN is not included in this build, rebuild with -tags with_openvpn,with_gvisor for system:false`)
}
return nil, E.New(`OpenVPN is not included in this build, rebuild with -tags with_openvpn`)
})
}
+12 -10
View File
@@ -19,6 +19,7 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/service/oomkiller"
openconnecttransport "github.com/sagernet/sing-box/transport/openconnect"
"github.com/sagernet/sing-openconnect"
"github.com/sagernet/sing-tun"
@@ -48,6 +49,7 @@ type Endpoint struct {
cancelLoop context.CancelFunc
dnsRouter adapter.DNSRouter
client *openconnect.Client
deviceOptions *openconnecttransport.DeviceOptions
device openconnecttransport.Device
onDemand bool
server string
@@ -121,9 +123,6 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
if success {
return
}
if openConnectEndpoint.device != nil {
_ = openConnectEndpoint.device.Close()
}
cancelLoop()
}()
server := options.Server
@@ -160,7 +159,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
udpTimeout = time.Duration(options.UDPTimeout)
}
networkManager := service.FromContext[adapter.NetworkManager](ctx)
device, err := openconnecttransport.NewDevice(openconnecttransport.DeviceOptions{
openConnectEndpoint.deviceOptions = &openconnecttransport.DeviceOptions{
Context: ctx,
Logger: logger,
System: options.System,
@@ -176,12 +175,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
Configuration: openconnecttransport.Configuration{
MTU: openconnecttransport.DefaultMTU,
},
})
if err != nil {
return nil, err
}
openConnectEndpoint.device = device
device.SetPacketWriter(openConnectEndpoint.writePacketBuffers)
clientOptions, err := openConnectEndpoint.buildClientOptions(options, outboundDialer)
if err != nil {
return nil, err
@@ -425,11 +419,19 @@ func (e *Endpoint) Start(stage adapter.StartStage, scope *adapter.Scope) error {
e.notifyStatusUpdated()
return nil
})
scope.Add(e.device.Close)
scope.Add(func() error {
e.cancelLoop()
return nil
})
e.deviceOptions.MemoryPressure = oomkiller.MemoryPressure(e.loopContext)
device, err := openconnecttransport.NewDevice(*e.deviceOptions)
if err != nil {
return err
}
scope.Add(device.Close)
device.SetPacketWriter(e.writePacketBuffers)
e.device = device
e.deviceOptions = nil
case adapter.StartStatePostStart:
var loopGroup sync.WaitGroup
scope.Add(func() error {
+12 -10
View File
@@ -17,6 +17,7 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/service/oomkiller"
ovpntransport "github.com/sagernet/sing-box/transport/openvpn"
ovpn "github.com/sagernet/sing-openvpn"
"github.com/sagernet/sing-tun"
@@ -50,6 +51,7 @@ type ClientEndpoint struct {
outboundDialer N.Dialer
queryOptions adapter.DNSQueryOptions
client *ovpn.Client
deviceOptions *ovpntransport.DeviceOptions
device ovpntransport.Device
onDemand bool
stateAccess sync.Mutex
@@ -92,9 +94,6 @@ func NewClientEndpoint(ctx context.Context, router adapter.Router, logger log.Co
if success {
return
}
if clientEndpoint.device != nil {
_ = clientEndpoint.device.Close()
}
cancelLoop()
}()
clientOptions, err := clientEndpoint.buildClientOptions(options)
@@ -123,7 +122,7 @@ func NewClientEndpoint(ctx context.Context, router adapter.Router, logger log.Co
if options.UDPTimeout != 0 {
udpTimeout = time.Duration(options.UDPTimeout)
}
device, err := ovpntransport.NewDevice(ovpntransport.DeviceOptions{
clientEndpoint.deviceOptions = &ovpntransport.DeviceOptions{
Context: ctx,
Logger: logger,
System: options.System,
@@ -140,12 +139,7 @@ func NewClientEndpoint(ctx context.Context, router adapter.Router, logger log.Co
MTU: options.MTU,
Address: clientOptions.Tunnel.LocalAddress,
},
})
if err != nil {
return nil, err
}
clientEndpoint.device = device
device.SetPacketWriter(clientEndpoint.writePacketBuffers)
client, err := ovpn.NewClient(clientOptions)
if err != nil {
return nil, err
@@ -604,11 +598,19 @@ func (c *ClientEndpoint) Start(stage adapter.StartStage, scope *adapter.Scope) e
c.notifyStatusUpdated()
return nil
})
scope.Add(c.device.Close)
scope.Add(func() error {
c.cancelLoop()
return nil
})
c.deviceOptions.MemoryPressure = oomkiller.MemoryPressure(c.ctx)
device, err := ovpntransport.NewDevice(*c.deviceOptions)
if err != nil {
return err
}
scope.Add(device.Close)
device.SetPacketWriter(c.writePacketBuffers)
c.device = device
c.deviceOptions = nil
case adapter.StartStatePostStart:
var loopGroup sync.WaitGroup
scope.Add(func() error {
+12 -9
View File
@@ -18,6 +18,7 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/service/oomkiller"
ovpntransport "github.com/sagernet/sing-box/transport/openvpn"
ovpn "github.com/sagernet/sing-openvpn"
"github.com/sagernet/sing-tun"
@@ -42,6 +43,7 @@ type ServerEndpoint struct {
dnsRouter adapter.DNSRouter
listener *listener.Listener
server *ovpn.Server
deviceOptions *ovpntransport.DeviceOptions
device ovpntransport.Device
localAddresses []netip.Prefix
started atomic.Bool
@@ -100,7 +102,7 @@ func NewServerEndpoint(ctx context.Context, router adapter.Router, logger log.Co
if options.UDPTimeout != 0 {
udpTimeout = time.Duration(options.UDPTimeout)
}
device, err := ovpntransport.NewDevice(ovpntransport.DeviceOptions{
serverEndpoint.deviceOptions = &ovpntransport.DeviceOptions{
Context: ctx,
Logger: logger,
System: options.System,
@@ -118,13 +120,7 @@ func NewServerEndpoint(ctx context.Context, router adapter.Router, logger log.Co
Address: options.Address,
Topology: options.Topology,
},
})
if err != nil {
cancelLoop()
return nil, err
}
serverEndpoint.device = device
device.SetPacketWriter(serverEndpoint.writePacketBuffersByDestination)
return serverEndpoint, nil
}
@@ -161,11 +157,19 @@ func validateServerTopology(topology string) error {
func (s *ServerEndpoint) Start(stage adapter.StartStage, scope *adapter.Scope) error {
if stage == adapter.StartStateInitialize {
scope.Add(s.device.Close)
scope.Add(func() error {
s.cancelLoop()
return nil
})
s.deviceOptions.MemoryPressure = oomkiller.MemoryPressure(s.ctx)
device, err := ovpntransport.NewDevice(*s.deviceOptions)
if err != nil {
return err
}
scope.Add(device.Close)
device.SetPacketWriter(s.writePacketBuffersByDestination)
s.device = device
s.deviceOptions = nil
return nil
}
if stage != adapter.StartStateStart {
@@ -214,7 +218,6 @@ func (s *ServerEndpoint) Start(stage adapter.StartStage, scope *adapter.Scope) e
Control: listenConfig.Control,
InterfaceFinder: networkManager.InterfaceFinder(),
InterfaceMonitor: networkManager.InterfaceMonitor(),
ExcludeInterface: s.options.Name,
IsExempt: func() bool {
return networkManager.AutoRedirectOutputMark() != 0
},
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailscale
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailscale
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailscale
+31 -138
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailscale
@@ -19,11 +19,6 @@ import (
"syscall"
"time"
"github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
"github.com/sagernet/gvisor/pkg/tcpip/header"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/adapter/endpoint"
"github.com/sagernet/sing-box/common/dialer"
@@ -34,6 +29,7 @@ import (
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/protocol/tailscale/tailssh"
R "github.com/sagernet/sing-box/route/rule"
"github.com/sagernet/sing-box/service/oomkiller"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/bufio"
@@ -96,7 +92,7 @@ type Endpoint struct {
platformInterface adapter.PlatformInterface
detour string
server *tsnet.Server
stack *stack.Stack
stack *tun.Go
returnAccess sync.Mutex
returnPath tun.Return
wgEngine wgengine.ExportedUserspaceEngine
@@ -117,9 +113,6 @@ type Endpoint struct {
relayServerPort *uint16
relayServerStaticEndpoints []netip.AddrPort
udpTimeout time.Duration
icmpTimeout time.Duration
sshServerInstance *tailssh.Server
sshServerOptions *option.TailscaleSSHServerOptions
taildrop *taildropManager
@@ -173,12 +166,6 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
if options.AdvertiseExitNode && options.ExitNode != "" {
return nil, E.New("cannot advertise an exit node and use an exit node at the same time.")
}
var udpTimeout time.Duration
if options.UDPTimeout != 0 {
udpTimeout = time.Duration(options.UDPTimeout)
} else {
udpTimeout = C.UDPTimeout
}
outboundDialer, err := dialer.NewWithOptions(dialer.Options{
Context: ctx,
Options: options.DialerOptions,
@@ -197,7 +184,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
}
taildropDirectory = filemanager.BasePath(ctx, os.ExpandEnv(taildropDirectory))
taildropDirectory, _ = filepath.Abs(taildropDirectory)
return &Endpoint{
tailscaleEndpoint := &Endpoint{
Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, nil),
ctx: ctx,
router: router,
@@ -249,14 +236,14 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
relayServerStaticEndpoints: options.RelayServerStaticEndpoints,
sshServerOptions: options.SSHServer,
taildrop: newTaildropManager(ctx, logger, tag, taildropDirectory, platformInterface),
udpTimeout: udpTimeout,
icmpTimeout: C.ICMPTimeout,
systemInterface: options.SystemInterface,
systemInterfaceName: options.SystemInterfaceName,
systemInterfaceMTU: options.SystemInterfaceMTU,
keyAuth: options.AuthKey != "",
onDemand: options.OnDemand,
}, nil
}
tailscaleEndpoint.server.NetstackHandler = tailscaleEndpoint
return tailscaleEndpoint, nil
}
func (t *Endpoint) References() []string {
@@ -269,6 +256,7 @@ func (t *Endpoint) References() []string {
func (t *Endpoint) Start(stage adapter.StartStage, scope *adapter.Scope) error {
switch stage {
case adapter.StartStateInitialize:
t.server.NetstackMemoryPressure = oomkiller.MemoryPressure(t.ctx)
mkdirErr := filemanager.MkdirAll(t.ctx, t.server.Dir, 0o700)
if mkdirErr != nil {
return E.Cause(mkdirErr, "create state directory")
@@ -473,18 +461,6 @@ func (t *Endpoint) postStart(scope *adapter.Scope) error {
return nil
})
}
fallbackTCPCloser := t.server.RegisterFallbackTCPHandler(func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
return func(conn net.Conn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
t.NewConnectionEx(ctx, conn, source, destination, nil)
}, true
})
scope.Add(func() error {
fallbackTCPCloser()
return nil
})
localBackend := t.server.ExportLocalBackend()
t.localBackend.Store(localBackend)
scope.Add(func() error {
@@ -507,55 +483,7 @@ func (t *Endpoint) postStart(scope *adapter.Scope) error {
wgEngine.SetOnReconfigListener(t.onReconfig)
t.wgEngine = wgEngine
ipStack := t.server.ExportNetstack().ExportIPStack()
gErr := ipStack.SetSpoofing(tun.DefaultNIC, true)
if gErr != nil {
return gonet.TranslateNetstackError(gErr)
}
gErr = ipStack.SetPromiscuousMode(tun.DefaultNIC, true)
if gErr != nil {
return gonet.TranslateNetstackError(gErr)
}
icmpForwarder := tun.NewICMPForwarder(ipStack, t, t.logger)
scope.Add(icmpForwarder.Close)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
t.stack = ipStack
netstack := t.server.ExportNetstack()
if netstack != nil {
previousTCP := netstack.GetTCPHandlerForFlow
netstack.GetTCPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) {
if previousTCP != nil {
handler, intercept = previousTCP(src, dst)
if handler != nil || !intercept {
return handler, intercept
}
}
return func(conn net.Conn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
t.NewConnectionEx(ctx, conn, source, destination, nil)
}, true
}
previousUDP := netstack.GetUDPHandlerForFlow
netstack.GetUDPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(nettype.ConnPacketConn), intercept bool) {
if previousUDP != nil {
handler, intercept = previousUDP(src, dst)
if handler != nil || !intercept {
return handler, intercept
}
}
return func(conn nettype.ConnPacketConn) {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
packetConn := bufio.NewUnbindPacketConnWithAddr(conn, destination)
t.NewPacketConnectionEx(ctx, packetConn, source, destination, nil)
}, true
}
}
t.stack = t.server.ExportNetstack().ExportIPStack()
sshEnabled := t.sshServerOptions != nil && t.sshServerOptions.Enabled
if sshEnabled {
@@ -959,46 +887,27 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination
if t.systemDialer != nil {
return t.systemDialer.DialContext(ctx, network, destination)
}
addr4, addr6 := t.server.TailscaleIPs()
remoteAddr := tcpip.FullAddress{
NIC: 1,
Port: destination.Port,
Addr: addressFromAddr(destination.Addr),
address4, address6 := t.server.TailscaleIPs()
local := address4
if destination.IsIPv6() {
local = address6
}
var localAddr tcpip.FullAddress
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
if !addr4.IsValid() {
return nil, E.New("missing Tailscale IPv4 address")
}
networkProtocol = header.IPv4ProtocolNumber
localAddr = tcpip.FullAddress{
NIC: 1,
Addr: addressFromAddr(addr4),
}
} else {
if !addr6.IsValid() {
return nil, E.New("missing Tailscale IPv6 address")
}
networkProtocol = header.IPv6ProtocolNumber
localAddr = tcpip.FullAddress{
NIC: 1,
Addr: addressFromAddr(addr6),
}
if !local.IsValid() {
return nil, E.New("missing Tailscale address for ", destination)
}
switch N.NetworkName(network) {
case N.NetworkTCP:
tcpConn, err := gonet.DialTCPWithBind(ctx, t.stack, localAddr, remoteAddr, networkProtocol)
conn, err := t.stack.DialTCP(ctx, local, destination.AddrPort())
if err != nil {
return nil, err
}
return tcpConn, nil
return conn, nil
case N.NetworkUDP:
udpConn, err := gonet.DialUDP(t.stack, &localAddr, &remoteAddr, networkProtocol)
conn, err := t.stack.DialUDP(netip.AddrPortFrom(local, 0), destination.AddrPort())
if err != nil {
return nil, err
}
return udpConn, nil
return conn, nil
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
@@ -1015,29 +924,19 @@ func (t *Endpoint) listenPacketWithAddress(ctx context.Context, destination M.So
if t.systemDialer != nil {
return t.systemDialer.ListenPacket(ctx, destination)
}
addr4, addr6 := t.server.TailscaleIPs()
bind := tcpip.FullAddress{
NIC: 1,
address4, address6 := t.server.TailscaleIPs()
local := address4
if destination.IsIPv6() {
local = address6
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
if !addr4.IsValid() {
return nil, E.New("missing Tailscale IPv4 address")
}
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = addressFromAddr(addr4)
} else {
if !addr6.IsValid() {
return nil, E.New("missing Tailscale IPv6 address")
}
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = addressFromAddr(addr6)
if !local.IsValid() {
return nil, E.New("missing Tailscale address for ", destination)
}
udpConn, err := gonet.DialUDP(t.stack, &bind, nil, networkProtocol)
conn, err := t.stack.ListenUDP(netip.AddrPortFrom(local, 0))
if err != nil {
return nil, err
}
return udpConn, nil
return conn, nil
}
func (t *Endpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) {
@@ -1078,7 +977,8 @@ func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
return packetConn, nil
}
func (t *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
func (t *Endpoint) NewConnectionEx(_ context.Context, conn net.Conn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
ctx := log.ContextWithNewID(t.ctx)
var metadata adapter.InboundContext
metadata.Inbound = t.Tag()
metadata.InboundType = t.Type()
@@ -1101,7 +1001,8 @@ func (t *Endpoint) NewConnectionEx(ctx context.Context, conn net.Conn, source M.
t.router.RouteConnectionEx(ctx, conn, metadata, onClose)
}
func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
func (t *Endpoint) NewPacketConnectionEx(_ context.Context, conn N.PacketConn, source M.Socksaddr, destination M.Socksaddr, onClose N.CloseHandlerFunc) {
ctx := log.ContextWithNewID(t.ctx)
var metadata adapter.InboundContext
metadata.Inbound = t.Tag()
metadata.InboundType = t.Type()
@@ -1184,14 +1085,6 @@ func (t *Endpoint) onReconfig(cfg *wgcfg.Config, routerCfg *router.Config, dnsCf
}
}
func addressFromAddr(destination netip.Addr) tcpip.Address {
if destination.Is6() {
return tcpip.AddrFrom16(destination.As16())
} else {
return tcpip.AddrFrom4(destination.As4())
}
}
type endpointDialer struct {
N.Dialer
logger logger.ContextLogger
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor && tvos
//go:build with_tailscale && tvos
package tailscale
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailscale
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailscale
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailscale
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailscale
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailscale
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailssh
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailssh
@@ -1,4 +1,4 @@
//go:build with_gvisor && !windows
//go:build with_tailscale && !windows
package tailssh
@@ -1,4 +1,4 @@
//go:build with_gvisor && windows
//go:build with_tailscale && windows
package tailssh
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailssh
@@ -1,4 +1,4 @@
//go:build with_gvisor && android
//go:build with_tailscale && android
package tailssh
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor && ios
//go:build with_tailscale && ios
package tailssh
@@ -1,4 +1,4 @@
//go:build with_gvisor && !windows
//go:build with_tailscale && !windows
package tailssh
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor && unix && !android && !ios
//go:build with_tailscale && unix && !android && !ios
package tailssh
@@ -1,4 +1,4 @@
//go:build with_gvisor && windows
//go:build with_tailscale && windows
package tailssh
@@ -1,4 +1,4 @@
//go:build with_gvisor && linux
//go:build with_tailscale && linux
package tailssh
@@ -1,4 +1,4 @@
//go:build with_gvisor && !windows && !linux
//go:build with_tailscale && !windows && !linux
package tailssh
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package tailssh
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor && android
//go:build with_tailscale && android
package tailssh
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor && !windows && !android
//go:build with_tailscale && !windows && !android
package tailssh
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor && windows
//go:build with_tailscale && windows
package tailssh
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor && !windows
//go:build with_tailscale && !windows
package tailscale
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor && windows
//go:build with_tailscale && windows
package tailscale
+5 -1
View File
@@ -15,6 +15,7 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/service/oomkiller"
"github.com/sagernet/sing-box/transport/wireguard"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
@@ -97,7 +98,6 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
Logger: logger,
InterfaceFinder: networkManager.InterfaceFinder(),
InterfaceMonitor: networkManager.InterfaceMonitor(),
ExcludeInterface: options.Name,
IsExempt: func() bool {
return networkManager.AutoRedirectOutputMark() != 0
},
@@ -141,6 +141,10 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
func (w *Endpoint) Start(stage adapter.StartStage, scope *adapter.Scope) error {
switch stage {
case adapter.StartStateInitialize:
err := w.endpoint.Initialize(oomkiller.MemoryPressure(w.ctx))
if err != nil {
return err
}
scope.Add(w.endpoint.Close)
case adapter.StartStateStart:
return w.endpoint.Start(false)
+1 -1
View File
@@ -1 +1 @@
with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_cloudflared,with_naive_outbound,with_usbip,with_openvpn,with_openconnect,badlinkname,tfogo_checklinkname0
with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_cloudflared,with_naive_outbound,with_usbip,with_openvpn,with_openconnect,badlinkname,tfogo_checklinkname0
+1 -1
View File
@@ -1 +1 @@
with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_cloudflared,with_usbip,with_openvpn,with_openconnect,badlinkname,tfogo_checklinkname0
with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_cloudflared,with_usbip,with_openvpn,with_openconnect,badlinkname,tfogo_checklinkname0
+1 -1
View File
@@ -1 +1 @@
with_gvisor,with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_cloudflared,with_naive_outbound,with_purego,with_usbip,with_openvpn,with_openconnect,badlinkname,tfogo_checklinkname0
with_quic,with_dhcp,with_wireguard,with_utls,with_acme,with_clash_api,with_tailscale,with_ccm,with_ocm,with_cloudflared,with_naive_outbound,with_purego,with_usbip,with_openvpn,with_openconnect,badlinkname,tfogo_checklinkname0
+1 -1
View File
@@ -1,4 +1,4 @@
//go:build with_gvisor
//go:build with_tailscale
package derp
+8
View File
@@ -49,6 +49,14 @@ func NewService(ctx context.Context, logger log.ContextLogger, tag string, optio
return s, nil
}
func MemoryPressure(ctx context.Context) func() tun.MemoryPressure {
oomKiller := service.FromContext[*Service](ctx)
if oomKiller == nil {
return nil
}
return oomKiller.MemoryPressure
}
func (s *Service) MemoryPressure() tun.MemoryPressure {
return tun.MemoryPressure(s.pressure.Load())
}
+11 -6
View File
@@ -149,9 +149,6 @@ func TestOpenConnectEndpointUDPNATDataPlane(t *testing.T) {
func testEndpointUDPNATDataPlane(t *testing.T, newDevice func(context.Context, tun.Handler) (endpointUDPNATDevice, error)) {
t.Helper()
if !tun.WithGVisor {
t.Skip("requires gVisor")
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
handler := &endpointUDPNATHandler{packets: make(chan endpointUDPNATPacket, 4)}
@@ -182,8 +179,8 @@ func testEndpointUDPNATDataPlane(t *testing.T, newDevice func(context.Context, t
require.Equal(t, M.SocksaddrFromNetIP(firstDestination), firstPacket.destination)
require.Equal(t, []byte("request-one"), firstPacket.payload)
require.NoError(t, firstPacket.session.conn.WritePacket(buf.As([]byte("blocked")), M.SocksaddrFromNetIP(secondDestination)))
require.NoError(t, firstPacket.session.conn.WritePacket(buf.As([]byte("allowed-one")), M.SocksaddrFromNetIP(firstDestination)))
require.NoError(t, firstPacket.session.conn.WritePacket(endpointUDPPacketBuffer(firstPacket.session.conn, "blocked"), M.SocksaddrFromNetIP(secondDestination)))
require.NoError(t, firstPacket.session.conn.WritePacket(endpointUDPPacketBuffer(firstPacket.session.conn, "allowed-one"), M.SocksaddrFromNetIP(firstDestination)))
require.Equal(t, []byte("allowed-one"), waitEndpointUDPResponse(t, outboundPackets))
writeEndpointUDPPacket(t, device, source, secondDestination, []byte("request-two"))
@@ -197,7 +194,7 @@ func testEndpointUDPNATDataPlane(t *testing.T, newDevice func(context.Context, t
t.Fatal("first UDP NAT session was not evicted at max size")
}
require.NoError(t, secondPacket.session.conn.WritePacket(buf.As([]byte("allowed-two")), M.SocksaddrFromNetIP(secondDestination)))
require.NoError(t, secondPacket.session.conn.WritePacket(endpointUDPPacketBuffer(secondPacket.session.conn, "allowed-two"), M.SocksaddrFromNetIP(secondDestination)))
require.Equal(t, []byte("allowed-two"), waitEndpointUDPResponse(t, outboundPackets))
}
@@ -225,6 +222,14 @@ func writeEndpointUDPPacket(t *testing.T, device endpointUDPNATDevice, source ne
packetBuffer.Release()
}
func endpointUDPPacketBuffer(conn N.PacketConn, payload string) *buf.Buffer {
headroom := N.CalculateFrontHeadroom(conn)
buffer := buf.NewSize(headroom + len(payload))
buffer.Resize(headroom, 0)
buffer.WriteString(payload)
return buffer
}
func endpointUDPPayload(packet []byte) ([]byte, bool) {
if len(packet) < header.IPv4MinimumSize {
return nil, false
+51 -49
View File
@@ -11,15 +11,15 @@ require (
github.com/docker/go-connections v0.5.0
github.com/gofrs/uuid/v5 v5.5.1
github.com/opencontainers/image-spec v1.1.0
github.com/sagernet/quic-go v0.61.0-sing-box-mod.4
github.com/sagernet/sing v0.9.0-beta.2
github.com/sagernet/sing-openvpn v0.0.0-20260729104525-103eb5fe5eb6
github.com/sagernet/sing-quic v0.7.0-beta.2
github.com/sagernet/quic-go v0.61.0-sing-box-mod.7
github.com/sagernet/sing v0.9.5-0.20260914135810-80bfa92a0607
github.com/sagernet/sing-openvpn v0.0.0-20260903200517-e060dda5b1f1
github.com/sagernet/sing-quic v0.7.1-0.20260904135313-497364e8ee3e
github.com/sagernet/sing-shadowsocks v0.2.8
github.com/sagernet/sing-shadowsocks2 v0.2.1
github.com/sagernet/sing-tun v0.8.12-0.20260810013717-909ab10ad507
github.com/sagernet/sing-tun v0.9.4-0.20260916041232-89d2e01a748a
github.com/spyzhov/ajson v0.9.4
github.com/stretchr/testify v1.11.1
github.com/stretchr/testify v1.12.0
go.uber.org/goleak v1.3.0
golang.org/x/net v0.57.0
)
@@ -36,7 +36,6 @@ require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/anthropics/anthropic-sdk-go v1.26.0 // indirect
github.com/anytls/sing-anytls v0.0.11 // indirect
github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02 // indirect
github.com/caddyserver/certmagic v0.25.3-0.20260421143802-60d9d8b415d6 // indirect
github.com/caddyserver/zerossl v0.1.5 // indirect
@@ -49,7 +48,6 @@ require (
github.com/cretz/bine v0.2.0 // indirect
github.com/database64128/netx-go v0.1.1 // indirect
github.com/database64128/tfo-go/v2 v2.3.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dblohm7/wingoes v0.0.0-20240119213807-a09d6be7affa // indirect
github.com/dgryski/go-camellia v0.0.0-20191119043421-69a8a13fb23d // indirect
github.com/dgryski/go-metro v0.0.0-20180109044635-280f6062b5bc // indirect
@@ -115,57 +113,57 @@ require (
github.com/pires/go-proxyproto v0.8.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/sftp v1.13.10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/safchain/ethtool v0.3.0 // indirect
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect
github.com/sagernet/bbolt v0.0.0-20260823094646-e24805439c9c // indirect
github.com/sagernet/cors v1.2.1 // indirect
github.com/sagernet/cronet-go v0.0.0-20260807162344-ec9a39c5ba3b // indirect
github.com/sagernet/cronet-go/all v0.0.0-20260807162344-ec9a39c5ba3b // indirect
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260807161529-8d42107dcdfc // indirect
github.com/sagernet/cronet-go v0.0.0-20260912104727-0d28acc44093 // indirect
github.com/sagernet/cronet-go/all v0.0.0-20260912104727-0d28acc44093 // indirect
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260912104006-c10c03c318db // indirect
github.com/sagernet/fswatch v0.1.2 // indirect
github.com/sagernet/gliderssh v0.3.4-0.20260531100337-2194faca5648 // indirect
github.com/sagernet/gvisor v0.0.0-20250822052253-5558536cf237 // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/netlink v0.0.0-20260814022025-64455d367bbf // indirect
github.com/sagernet/nftables v0.3.0-mod.4 // indirect
github.com/sagernet/sing-anytls v0.0.0-20260904135308-cec2d74334be // indirect
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3 // indirect
github.com/sagernet/sing-mux v0.3.5 // indirect
github.com/sagernet/sing-openconnect v0.0.0-20260810065514-53aa8058f8df // indirect
github.com/sagernet/sing-mux v0.3.7-0.20260905054442-91d1502591ce // indirect
github.com/sagernet/sing-openconnect v0.0.0-20260903200519-8b89c968949d // indirect
github.com/sagernet/sing-shadowtls v0.2.1 // indirect
github.com/sagernet/sing-snell v0.0.0-20260727093646-7cb813e07b73 // indirect
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb // indirect
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 // indirect
github.com/sagernet/sing-snell v0.0.0-20260904135315-bc5a12ac736f // indirect
github.com/sagernet/sing-usbip v0.0.0-20260817040617-28bd42667eca // indirect
github.com/sagernet/sing-vmess v0.2.8 // indirect
github.com/sagernet/smux v1.5.50-sing-box-mod.1 // indirect
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.2 // indirect
github.com/sagernet/wireguard-go v0.0.5-0.20260810121456-c6c8a831ef70 // indirect
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.5.0.20260916041743-66c96c36515b // indirect
github.com/sagernet/wireguard-go v0.0.6 // indirect
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
github.com/smallstep/pkcs7 v0.1.1 // indirect
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d // indirect
@@ -215,3 +213,7 @@ require (
lukechampine.com/blake3 v1.3.0 // indirect
zombiezen.com/go/capnproto2 v2.18.2+incompatible // indirect
)
replace github.com/sagernet/sing-tun => ../../sing-tun
replace github.com/sagernet/tailscale => ../../tailscale
+90 -96
View File
@@ -26,8 +26,6 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/anthropics/anthropic-sdk-go v1.26.0 h1:oUTzFaUpAevfuELAP1sjL6CQJ9HHAfT7CoSYSac11PY=
github.com/anthropics/anthropic-sdk-go v1.26.0/go.mod h1:qUKmaW+uuPB64iy1l+4kOSvaLqPXnHTTBKH6RVZ7q5Q=
github.com/anytls/sing-anytls v0.0.11 h1:w8e9Uj1oP3m4zxkyZDewPk0EcQbvVxb7Nn+rapEx4fc=
github.com/anytls/sing-anytls v0.0.11/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8=
github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02 h1:bXAPYSbdYbS5VTy92NIUbeDI1qyggi+JYh5op9IFlcQ=
github.com/axiomhq/hyperloglog v0.0.0-20240319100328-84253e514e02/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c=
github.com/caddyserver/certmagic v0.25.3-0.20260421143802-60d9d8b415d6 h1:LYSB6VgWzKtNrcxElw3c97BP40Oc7bizKxA9K1Vi/5k=
@@ -248,8 +246,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
@@ -257,116 +253,114 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
github.com/sagernet/bbolt v0.0.0-20260823094646-e24805439c9c h1:JdEDkgiwsDxiZb4+dQYgVOU/lND+jZxFmN5y/q49EXk=
github.com/sagernet/bbolt v0.0.0-20260823094646-e24805439c9c/go.mod h1:7/qFPFH5fqK5XQeY8EA1Qpaly08Av77p38Zt50ieQww=
github.com/sagernet/cors v1.2.1 h1:Cv5Z8y9YSD6Gm+qSpNrL3LO4lD3eQVvbFYJSG7JCMHQ=
github.com/sagernet/cors v1.2.1/go.mod h1:O64VyOjjhrkLmQIjF4KGRrJO/5dVXFdpEmCW/eISRAI=
github.com/sagernet/cronet-go v0.0.0-20260807162344-ec9a39c5ba3b h1:SfX10lRxbq9ixHm4OZ4/7q+TSpK4Gh0lEVrPaIAB/K0=
github.com/sagernet/cronet-go v0.0.0-20260807162344-ec9a39c5ba3b/go.mod h1:T/mwtrpC4JlWfScw73CmSBvHzIvc7BatQ1MhRr+cYNw=
github.com/sagernet/cronet-go/all v0.0.0-20260807162344-ec9a39c5ba3b h1:ilEtBdfFgdHmCY3q/Gzp3wRDILYAT8wOh+jVAEAT77U=
github.com/sagernet/cronet-go/all v0.0.0-20260807162344-ec9a39c5ba3b/go.mod h1:Mr2MlNGQYovxvSPGo0+HEgXJ+uITwvPMtp7kdZO8T9w=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260807161529-8d42107dcdfc h1:JxjIRahHFgA766NTMNoQH5FHcSjo3OVdQbqMuqqvOSQ=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260807161529-8d42107dcdfc h1:U1M7GkzYchNnahyGiSGD1xAHJXoCiae3E8DDPWaeliA=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260807161529-8d42107dcdfc h1:Vtm7UuE69wKPNUakYjlxscX16LkQTEX1Uh6qf2YZj3Y=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:w48m0+ic4Y4pmW9ZIAo7o70fq/ioL1pgIKCggvvZiOU=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260807161529-8d42107dcdfc h1:1XkXOj47J9kPTBYZXIPraLG8V3vtQ2p6kI+EuLkNQVs=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:++dHyER9VY0KIfN18i0J2uQcp/DMySqVsUtSEso6mcU=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc h1:Jd84+nEjqapOYqxbbRaYD9WsqV/+UacrPngpzT3JOW4=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:2q/pUYTulaxitrk+Uuwu+nnPeJCwoWCI8YB0b5JU5Pg=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc h1:KuR8ZjycdnaHpdlwER2BJcyQxFV89+Ci1MC7hQuDQtM=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260807161529-8d42107dcdfc h1:5oWj5iZn7tg9wOC4VMltItUE87FDxubwICgtjSp+HAM=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260807161529-8d42107dcdfc h1:EZBIqNjit9xGM1w9xKcQVzU3gtyNdEcgXPfmeVuTinM=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260807161529-8d42107dcdfc h1:yOIHOVXOlmbvcs1Gm2xfcr84+mvAVN0zdXycfLgr3DE=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260807161529-8d42107dcdfc h1:4uVapCorUiSKJ2xTi8BNLOTq878kFv3l+6+aDeYpuiE=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260807161529-8d42107dcdfc h1:fm2GA6JTp48rigyUtuzgu/G9WbMZ+o5QBylks1Noss0=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:kWx/l/TCx2hdUKnNn2uyKZAGkCeqm+vkhr84wZJkSgU=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260807161529-8d42107dcdfc h1:XnHUep8+amHCWW3WWCD6/QAZYAXkcLUeCZqs0SnXp7M=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260807161529-8d42107dcdfc h1:F7rgwvKytTxeU3G3RpZlH0o77J6OmbZpxaUkTWk1+cE=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260807161529-8d42107dcdfc h1:lpq6aztgg4Luspo4V4SresrCg7+bXlpZ45EfAwpWabg=
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:Wt5uFdU3tnmm8YzobYewwdF7Mt6SucRQg6xeTNWC3Tk=
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260807161529-8d42107dcdfc h1:o66baLwgg/aI14jCX6lrQ+1EPkn7RS4cFWJQRKXYXqM=
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:lyIF6wKBLwWa5ZXaAKbAoewewl+yCHo2iYev39Mbj4E=
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260807161529-8d42107dcdfc h1:7tSY8HPMvf/piEi0TywA/dQWyp1lAJH9U/qyn4+DKY8=
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:H46PnSTTZNcZokLLiDeMDaHiS1l14PH3tzWi0eykjD8=
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260807161529-8d42107dcdfc h1:zgHBT1uv5UQ/GHVMn4j6NXelH7ukIo0IQQLsntV+VfA=
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:RBhSUDAKWq7fswtV4nQUQhuaTLcX3ettR7teA7/yf2w=
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260807161529-8d42107dcdfc h1:HNzghnPzq2RK5TXjl5QIVPm44FKzw6pds1GQINAYD80=
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:wRzoIOGG4xbpp3Gh3triLKwMwYriScXzFtunLYhY4w0=
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260807161529-8d42107dcdfc h1:xAaqyzbYqGPJbKCa6onmX7Bs2HXM1mvbEdMtdAiXplA=
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:LNiZXmWil1OPwKCheqQjtakZlJuKGFz+iv2eGF76Hhs=
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260807161529-8d42107dcdfc h1:dHay8gOPaH4kwcTnxQeAEZaIw88Y0V6Gmef3HkdX31U=
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:YFDGKTkpkJGc5+hnX/RYosZyTWg9h+68VB55fYRRLYc=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc h1:NLAel/XTGYWExz5gpFzlsX+BZba3/6Wgz1xwOzux9r0=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:/k71v3Hs2m6mpDTWVm2W6spZNd+do9U+1jBYTOIPj3g=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc h1:HUB2CvXtgggtQNW5ix670s9Qd+vCiy5aFKEFJO5/UL8=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260807161529-8d42107dcdfc h1:rWdo0pRYdrGvZ0turVxLqsXsizj+AS/nivMHX0RHtLw=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260807161529-8d42107dcdfc h1:+3Fe+RdrbyEGyi66Kt8BromMTFGVw6+8ZTEqKF4kCaU=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260807161529-8d42107dcdfc/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
github.com/sagernet/cronet-go v0.0.0-20260912104727-0d28acc44093 h1:CsvsLVIli53L6nzgEpyRjjWnRRhTds4D3eh5sEnF5Sw=
github.com/sagernet/cronet-go v0.0.0-20260912104727-0d28acc44093/go.mod h1:T/mwtrpC4JlWfScw73CmSBvHzIvc7BatQ1MhRr+cYNw=
github.com/sagernet/cronet-go/all v0.0.0-20260912104727-0d28acc44093 h1:1lRUqrLcBz0Q0r/xvyiXS5hZwH5pOq6Lwb4dqbSqJfs=
github.com/sagernet/cronet-go/all v0.0.0-20260912104727-0d28acc44093/go.mod h1:bvm9T9FdwHYYfWdZkLoZ4Qr9Dce0TTQ7ngXlbGZnnJk=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260912104006-c10c03c318db h1:o0NCKT9gZsOLJK5s5wXW5A5EaFCnF5En6SApmHwUMJo=
github.com/sagernet/cronet-go/lib/android_386 v0.0.0-20260912104006-c10c03c318db/go.mod h1:XXDwdjX/T8xftoeJxQmbBoYXZp8MAPFR2CwbFuTpEtw=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260912104006-c10c03c318db h1:75fzfzaz/j1mKsqNE01KTMMP3i/9FgmPlwO2BIV8TpU=
github.com/sagernet/cronet-go/lib/android_amd64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:iNiUGoLtnr8/JTuVNj7XJbmpOAp2C6+B81KDrPxwaZM=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260912104006-c10c03c318db h1:tz6Tm3qqxPKp9QMFDUxbVzu5NgYQZq2hY9CGQ+sTmII=
github.com/sagernet/cronet-go/lib/android_arm v0.0.0-20260912104006-c10c03c318db/go.mod h1:19ILNUOGIzRdOqa2mq+iY0JoHxuieB7/lnjYeaA2vEc=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260912104006-c10c03c318db h1:OO+rUNHeZja1m0n/bBfkWtK2NQkNN9iROP0CRqw9aL8=
github.com/sagernet/cronet-go/lib/android_arm64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:JxzGyQf94Cr6sBShKqODGDyRUlESfJK/Njcz9Lz6qMQ=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260912104006-c10c03c318db h1:78W68WJdZjQDhkp4tcFkLcXBH4W8jlCBK1eFcBKKZwI=
github.com/sagernet/cronet-go/lib/darwin_amd64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:KN+9T9TBycGOLzmKU4QdcHAJEj6Nlx48ifnlTvvHMvs=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260912104006-c10c03c318db h1:qVLzw+rWqzD82/CUwvxfSohUj6aSD9zRAX7WtKiSNeQ=
github.com/sagernet/cronet-go/lib/darwin_arm64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:kojvtUc29KKnk8hs2QIANynVR59921SnGWA9kXohHc0=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260912104006-c10c03c318db h1:EoaFEVn+GVZw2jKXgK+73BdpUZKJ85PGUdbAyxy72pk=
github.com/sagernet/cronet-go/lib/ios_amd64_simulator v0.0.0-20260912104006-c10c03c318db/go.mod h1:hkQzRE5GDbaH1/ioqYh0Taho4L6i0yLRCVEZ5xHz5M0=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260912104006-c10c03c318db h1:rtv8UspnSr157Hp9k6Pj0IV224GvANVx+XTTd2/CjdY=
github.com/sagernet/cronet-go/lib/ios_arm64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:tzVJFTOm66UxLxy6K0ZN5Ic2PC79e+sKKnt+V9puEa4=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260912104006-c10c03c318db h1:TwBUmUKDuQzA5nIshSzVE0tCKK1GpEfy54E1h3o62t8=
github.com/sagernet/cronet-go/lib/ios_arm64_simulator v0.0.0-20260912104006-c10c03c318db/go.mod h1:M/pN6m3j0HFU6/y83n0HU6GLYys3tYdr/xTE8hVEGMo=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260912104006-c10c03c318db h1:T2yA2pXtVKeZniYbp8bfcNTigauAl3EO3AO1UwVXoGE=
github.com/sagernet/cronet-go/lib/linux_386 v0.0.0-20260912104006-c10c03c318db/go.mod h1:cGh5hO6eljCo6KMQ/Cel8Xgq4+etL0awZLRBDVG1EZQ=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260912104006-c10c03c318db h1:w5BfjtEy9mjMiXtmKfB2uBGeRG4pwbuQ6ouTflNnWyU=
github.com/sagernet/cronet-go/lib/linux_386_musl v0.0.0-20260912104006-c10c03c318db/go.mod h1:JFE0/cxaKkx0wqPMZU7MgaplQlU0zudv82dROJjClKU=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260912104006-c10c03c318db h1:yNdO+HC5OkgKgwqVYd49FRVt5A2jfxsyrSKrf2uM0aM=
github.com/sagernet/cronet-go/lib/linux_amd64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:vU8VftFeSt7fURCa3JXD6+k6ss1YAX+idQjPvHmJ2tI=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260912104006-c10c03c318db h1:jJ1WycEj07LsIhtZdgXSiDa9qMkDUvBtr/B8WQm5utQ=
github.com/sagernet/cronet-go/lib/linux_amd64_musl v0.0.0-20260912104006-c10c03c318db/go.mod h1:vCe4OUuL+XOUge9v3MyTD45BnuAXiH+DkjN9quDXJzQ=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260912104006-c10c03c318db h1:HPBv0BqbHC8ktpn1rSMeoREDPumyFFpJHZsUCwzwLqY=
github.com/sagernet/cronet-go/lib/linux_arm v0.0.0-20260912104006-c10c03c318db/go.mod h1:w9amBWrvjtohQzBGCKJ7LCh22LhTIJs4sE7cYaKQzM0=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260912104006-c10c03c318db h1:7mJdu1gjC45rEkiIGtdCuEs3v1Oj2IXBnSh9uIgZEmE=
github.com/sagernet/cronet-go/lib/linux_arm64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:TqlsFtcYS/etTeck46kHBeT8Le0Igw1Q/AV88UnMS3s=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260912104006-c10c03c318db h1:YCBQCFTjlkRkkCXV+ScjN/URW9Mx58f6Dv34a8JRtGw=
github.com/sagernet/cronet-go/lib/linux_arm64_musl v0.0.0-20260912104006-c10c03c318db/go.mod h1:B6Qd0vys8sv9OKVRN6J9RqDzYRGE938Fb2zrYdBDyTQ=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260912104006-c10c03c318db h1:RIp7sKxgUGMidhmi7oVhX8DgnG6UjKk7kvxWvWfKvbY=
github.com/sagernet/cronet-go/lib/linux_arm_musl v0.0.0-20260912104006-c10c03c318db/go.mod h1:3tXMMFY7AHugOVBZ5Al7cL7JKsnFOe5bMVr0hZPk3ow=
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260912104006-c10c03c318db h1:lElglqZOqg3g7/L8uzJKS6yPNgIxbEu4/vVTzlYE7W8=
github.com/sagernet/cronet-go/lib/linux_loong64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:Wt5uFdU3tnmm8YzobYewwdF7Mt6SucRQg6xeTNWC3Tk=
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260912104006-c10c03c318db h1:4HvprIkwKcrPYuyQaH5tdIeh5KVBgkauL6o0VqpCkSo=
github.com/sagernet/cronet-go/lib/linux_loong64_musl v0.0.0-20260912104006-c10c03c318db/go.mod h1:lyIF6wKBLwWa5ZXaAKbAoewewl+yCHo2iYev39Mbj4E=
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260912104006-c10c03c318db h1:rWrNWeyy563mTk3KZas3ore7JPq7M+l8r0XV9lAjc0c=
github.com/sagernet/cronet-go/lib/linux_mips64le v0.0.0-20260912104006-c10c03c318db/go.mod h1:H46PnSTTZNcZokLLiDeMDaHiS1l14PH3tzWi0eykjD8=
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260912104006-c10c03c318db h1:YADxQVD/X9yyV3i/xS3T/lWhpVLslIIl+e+srwpT9p0=
github.com/sagernet/cronet-go/lib/linux_mipsle v0.0.0-20260912104006-c10c03c318db/go.mod h1:RBhSUDAKWq7fswtV4nQUQhuaTLcX3ettR7teA7/yf2w=
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260912104006-c10c03c318db h1:2hgFBooF0jPOGXKcuJgMA4h1aSOULyAI6OgubZBoD7g=
github.com/sagernet/cronet-go/lib/linux_mipsle_musl v0.0.0-20260912104006-c10c03c318db/go.mod h1:wRzoIOGG4xbpp3Gh3triLKwMwYriScXzFtunLYhY4w0=
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260912104006-c10c03c318db h1:fFv7izOOsC/HQCDAwTtPjIlayo8znNlAnPqppDb08Jo=
github.com/sagernet/cronet-go/lib/linux_riscv64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:LNiZXmWil1OPwKCheqQjtakZlJuKGFz+iv2eGF76Hhs=
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260912104006-c10c03c318db h1:meCf6CbZqWpV5ITTjawLJzZfFrx6thSgs7a9PZyqdTM=
github.com/sagernet/cronet-go/lib/linux_riscv64_musl v0.0.0-20260912104006-c10c03c318db/go.mod h1:YFDGKTkpkJGc5+hnX/RYosZyTWg9h+68VB55fYRRLYc=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260912104006-c10c03c318db h1:7bOFA2F/WApDRUamwGM5OThrY5H3+UZpW0yQLlukE8Y=
github.com/sagernet/cronet-go/lib/tvos_amd64_simulator v0.0.0-20260912104006-c10c03c318db/go.mod h1:aaX0YGl8nhGmfRWI8bc3BtDjY8Vzx6O0cS/e1uqxDq4=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260912104006-c10c03c318db h1:9HjQtfjpo5PTr5AMBWgyTVnWI/zQOTDVQ1agftOOoGo=
github.com/sagernet/cronet-go/lib/tvos_arm64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:EdzMKA96xITc42QEI+ct4SwqX8Dn3ltKK8wzdkLWpSc=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260912104006-c10c03c318db h1:9OUQzCcoCvleQVLb3xW3AGB+eKGxPmldK1gg61xh7T8=
github.com/sagernet/cronet-go/lib/tvos_arm64_simulator v0.0.0-20260912104006-c10c03c318db/go.mod h1:qix4kv1TTAJ5tY4lJ9vjhe9EY4mM+B7H5giOhbxDVcc=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260912104006-c10c03c318db h1:fKs7KaruaDhStaiYpaqMQXDCmAc4YmNLI/pBkPYvwH8=
github.com/sagernet/cronet-go/lib/windows_amd64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:lm9w/oCCRyBiUa3G8lDQTT8x/ONUvgVR2iV9fVzUZB8=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260912104006-c10c03c318db h1:K7/F1mWxmf5yUDxSSk+6pRk+yfCoWsqIgt2LsjRpP8w=
github.com/sagernet/cronet-go/lib/windows_arm64 v0.0.0-20260912104006-c10c03c318db/go.mod h1:n34YyLgapgjWdKa0IoeczjAFCwD3/dxbsH5sucKw0bw=
github.com/sagernet/fswatch v0.1.2 h1:/TT7k4mkce1qFPxamLO842WjqBgbTBiXP2mlUjp9PFk=
github.com/sagernet/fswatch v0.1.2/go.mod h1:5BpGmpUQVd3Mc5r313HRpvADHRg3/rKn5QbwFteB880=
github.com/sagernet/gliderssh v0.3.4-0.20260531100337-2194faca5648 h1:IWVjKBARzVjdmH0VUaeTBOBli1qkwKmTG4XfbkpSS20=
github.com/sagernet/gliderssh v0.3.4-0.20260531100337-2194faca5648/go.mod h1:FmW0l0t/PzGIdJMr3iXOL+KuxvJTt6XAfF4GxbMlfZc=
github.com/sagernet/gvisor v0.0.0-20250822052253-5558536cf237 h1:SUPFNB+vSP4RBPrSEgNII+HkfqC8hKMpYLodom4o4EU=
github.com/sagernet/gvisor v0.0.0-20250822052253-5558536cf237/go.mod h1:QkkPEJLw59/tfxgapHta14UL5qMUah5NXhO0Kw2Kan4=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/netlink v0.0.0-20260814022025-64455d367bbf h1:b/3rm+KvxzuYRcnMdLq/hmcqX6/2B8k24l6SDHL2Piw=
github.com/sagernet/netlink v0.0.0-20260814022025-64455d367bbf/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-mod.4 h1:vnOtcDYeSXv2e5RoRuGH0lrpttQFJ8iC4ICS2nhlDSo=
github.com/sagernet/nftables v0.3.0-mod.4/go.mod h1:8kslHG4VvYNihcco+i6uxIX7qbT8A56T0y5q7U44ZaQ=
github.com/sagernet/quic-go v0.61.0-sing-box-mod.4 h1:XoFf8KoBEsamWo3ePlcmS/xs8B7KDurJQfI2U7q/H1c=
github.com/sagernet/quic-go v0.61.0-sing-box-mod.4/go.mod h1:hmLC8GJPp+BrpBgrgJnacvI4fZGIhasuy1ROJJ8GR4E=
github.com/sagernet/sing v0.9.0-beta.2 h1:JZ6bF5cEobx1FjptNvSLfKXP66nU1U21AuEDnebg8u8=
github.com/sagernet/sing v0.9.0-beta.2/go.mod h1:K3Owt3xPhHugvlnlPPxZJ/exXdaJfEPOTNorGk4AXjo=
github.com/sagernet/quic-go v0.61.0-sing-box-mod.7 h1:sW2O+DoNF+my1PGlSZ3zG2N+fdxLR3DC0l4sXCM0lBE=
github.com/sagernet/quic-go v0.61.0-sing-box-mod.7/go.mod h1:hmLC8GJPp+BrpBgrgJnacvI4fZGIhasuy1ROJJ8GR4E=
github.com/sagernet/sing v0.9.5-0.20260914135810-80bfa92a0607 h1:YzA14QltKgRqFKEZYRP1JBL2meuf3Mx3DiDK/U5+/PA=
github.com/sagernet/sing v0.9.5-0.20260914135810-80bfa92a0607/go.mod h1:K3Owt3xPhHugvlnlPPxZJ/exXdaJfEPOTNorGk4AXjo=
github.com/sagernet/sing-anytls v0.0.0-20260904135308-cec2d74334be h1:0uMXu8kcJU0XLc2vfjGH6q6khmECCc5WYpD9dqlu1Xg=
github.com/sagernet/sing-anytls v0.0.0-20260904135308-cec2d74334be/go.mod h1:VRKLnj0ObEsN228h+zuzf91p+6sjtYE8SBaLPVsBy5o=
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3 h1:3y6++yIa8XlDhxPkpR4p+7RUHVY2KTP9CPIGnWmOlO8=
github.com/sagernet/sing-cloudflared v0.1.3-0.20260706062323-d9787e794aa3/go.mod h1:XEqEDYRCAYLaoPjZ1ifVWJg5iWAJHL2gOAXe/PM28Cg=
github.com/sagernet/sing-mux v0.3.5 h1:RHnhVEc+SFqkrK4xMygYjDwwLhzp2Bj3lztSukONfhI=
github.com/sagernet/sing-mux v0.3.5/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
github.com/sagernet/sing-openconnect v0.0.0-20260810065514-53aa8058f8df h1:ogyC7KPTWueN7kXFhKSdHeS5/t/zBjQgAOpWjqnrMos=
github.com/sagernet/sing-openconnect v0.0.0-20260810065514-53aa8058f8df/go.mod h1:4AKZLVcvY3r54UaK2Gbnm7aN8pOwdLz+y4EP0QFZ5Eg=
github.com/sagernet/sing-openvpn v0.0.0-20260729104525-103eb5fe5eb6 h1:ZAvhkor0prJ8KEX9EmTEH+gJ1WlA3ffjS8GZT6KKq9c=
github.com/sagernet/sing-openvpn v0.0.0-20260729104525-103eb5fe5eb6/go.mod h1:PWX7WygD8jpwfqfaGNySXpJYTn0SOwjBI1BKHHC2+Bw=
github.com/sagernet/sing-quic v0.7.0-beta.2 h1:qfMz4IyGIj6WhqhrR5ZU+d4Cqppi6RM6BpGXG6phfy8=
github.com/sagernet/sing-quic v0.7.0-beta.2/go.mod h1:Z+ZpYBLJ7WkPPrGSCYFxna4Dos2XrywEy6YH6SY5L6Q=
github.com/sagernet/sing-mux v0.3.7-0.20260905054442-91d1502591ce h1:ay1pGbzMF4e7iggj+uESNhwMNPbZZajDghcdC4P92Dc=
github.com/sagernet/sing-mux v0.3.7-0.20260905054442-91d1502591ce/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk=
github.com/sagernet/sing-openconnect v0.0.0-20260903200519-8b89c968949d h1:Jt/Tb0boIr/xRr4sWr2vC/rrVlYNoumZITLEQpqZ8UE=
github.com/sagernet/sing-openconnect v0.0.0-20260903200519-8b89c968949d/go.mod h1:4AKZLVcvY3r54UaK2Gbnm7aN8pOwdLz+y4EP0QFZ5Eg=
github.com/sagernet/sing-openvpn v0.0.0-20260903200517-e060dda5b1f1 h1:Zn7+pNY5mX1e4DO8oKhTHz5sxqUKNdCWKbfZUtM26Qg=
github.com/sagernet/sing-openvpn v0.0.0-20260903200517-e060dda5b1f1/go.mod h1:PWX7WygD8jpwfqfaGNySXpJYTn0SOwjBI1BKHHC2+Bw=
github.com/sagernet/sing-quic v0.7.1-0.20260904135313-497364e8ee3e h1:2r0FRN0DBSfggWYtdH/69uW91kqEOByBWw/5hiCq6qU=
github.com/sagernet/sing-quic v0.7.1-0.20260904135313-497364e8ee3e/go.mod h1:rJFeVxbEej2iuyeCJ3drKW+p5rOgtLIW3XhZ6ty7Fig=
github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE=
github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI=
github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo=
github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.2.1 h1:ZiHZdnEnP+YS73NMsxiZmIFCwNd0M4k7PkGCKNXhbaM=
github.com/sagernet/sing-shadowtls v0.2.1/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA=
github.com/sagernet/sing-snell v0.0.0-20260727093646-7cb813e07b73 h1:Iyhoka9XutVbIhkhjn6s2Ez2AEuBG5i4KbkQKqRMsug=
github.com/sagernet/sing-snell v0.0.0-20260727093646-7cb813e07b73/go.mod h1:et8Lws4f5QbOrY65DmjevHGup3mijJkhswkto6cwciM=
github.com/sagernet/sing-tun v0.8.12-0.20260810013717-909ab10ad507 h1:YF57rLEryUvX0OJWpVTebZ9IMpvzRcozhJPcn58lQ9c=
github.com/sagernet/sing-tun v0.8.12-0.20260810013717-909ab10ad507/go.mod h1:3EgPst7agntRO7D6GOsiZ1l9FoqdLeuWmKT5TnWkmf0=
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb h1:KEMbfexD4DvrQGYWwx6r+AwH9Veh8z6cnBZmtCS2G+0=
github.com/sagernet/sing-usbip v0.0.0-20260616101517-efb91521eddb/go.mod h1:D4CnJX3MNAAANhbQUxfIRgBdnvlTEaV7h6ojedcs+pw=
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1 h1:aSwUNYUkVyVvdmBSufR8/nRFonwJeKSIROxHcm5br9o=
github.com/sagernet/sing-vmess v0.2.8-0.20250909125414-3aed155119a1/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
github.com/sagernet/sing-snell v0.0.0-20260904135315-bc5a12ac736f h1:tbTCI+iis5sTmua7a6Q7vebcSRURok/LGgnEafnQAsc=
github.com/sagernet/sing-snell v0.0.0-20260904135315-bc5a12ac736f/go.mod h1:et8Lws4f5QbOrY65DmjevHGup3mijJkhswkto6cwciM=
github.com/sagernet/sing-usbip v0.0.0-20260817040617-28bd42667eca h1:5wA+IE0Fq1CGVLOgSpm0gKKZ03HzugcJ2JyhmDqeX6A=
github.com/sagernet/sing-usbip v0.0.0-20260817040617-28bd42667eca/go.mod h1:ADAZZU85MnM91XNhd2TdZRTaIbA7RjTZuQ1KCHcUbNg=
github.com/sagernet/sing-vmess v0.2.8 h1:xd5nnDOMlC76RgrLksS4jlk3eMt3c3CvQY3NsjWPWeI=
github.com/sagernet/sing-vmess v0.2.8/go.mod h1:P11scgTxMxVVQ8dlM27yNm3Cro40mD0+gHbnqrNGDuY=
github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478=
github.com/sagernet/smux v1.5.50-sing-box-mod.1/go.mod h1:NjhsCEWedJm7eFLyhuBgIEzwfhRmytrUoiLluxs5Sk8=
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.2 h1:JXYUrqxvIYGVVvTz0xzYR6DufhLBfnV5wh5lwhbdJVM=
github.com/sagernet/tailscale v1.102.1-sing-box-1.14-mod.2/go.mod h1:WLUSOPmTcf7VN9gLCe01qUSIvD+/cKC177neENyZPkI=
github.com/sagernet/wireguard-go v0.0.5-0.20260810121456-c6c8a831ef70 h1:WTVgbkDDGnZqxIUnBsE9QyJz2dtSaeoh9mFwxmQqfgI=
github.com/sagernet/wireguard-go v0.0.5-0.20260810121456-c6c8a831ef70/go.mod h1:er10sELpmzLXq7S7Pbc1Zsbyapcr+/gxNAHKTo6fzVA=
github.com/sagernet/wireguard-go v0.0.6 h1:zAjWSzufYThDsyLHxPLyyR+XdpBMlFDWD7WxJAvynSw=
github.com/sagernet/wireguard-go v0.0.6/go.mod h1:er10sELpmzLXq7S7Pbc1Zsbyapcr+/gxNAHKTo6fzVA=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
@@ -377,8 +371,8 @@ github.com/spyzhov/ajson v0.9.4 h1:MVibcTCgO7DY4IlskdqIlCmDOsUOZ9P7oKj8ifdcf84=
github.com/spyzhov/ajson v0.9.4/go.mod h1:a6oSw0MMb7Z5aD2tPoPO+jq11ETKgXUr2XktHdT8Wt8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI=
github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw=
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d h1:JcGKBZAL7ePLwOhUdN8qGQZlP5GueEiIZwY7R62pejE=
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
+7
View File
@@ -6,6 +6,7 @@ import (
"crypto/rand"
"io"
"net"
"net/netip"
"os"
"os/exec"
"path/filepath"
@@ -21,6 +22,7 @@ import (
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json/badjson"
"github.com/sagernet/sing/common/json/badoption"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
@@ -176,6 +178,8 @@ func TestOpenConnectDockerInterop(t *testing.T) {
}
func openConnectInstanceOptions(server string, certificateAuthorityPath string, username string, password string) option.Options {
hosts := new(badjson.TypedMap[string, badoption.Listable[netip.Addr]])
hosts.Put("localhost", []netip.Addr{netip.MustParseAddr("127.0.0.1")})
endpointOptions := option.OpenConnectEndpointOptions{
Server: server,
Flavor: "anyconnect",
@@ -191,6 +195,9 @@ func openConnectInstanceOptions(server string, certificateAuthorityPath string,
},
}
return option.Options{
DNS: &option.DNSOptions{RawDNSOptions: option.RawDNSOptions{
Servers: []option.DNSServerOptions{{Type: C.DNSTypeHosts, Options: &option.HostsDNSServerOptions{Predefined: hosts}}},
}},
Endpoints: []option.Endpoint{
{
Type: C.TypeOpenConnect,
+161
View File
@@ -0,0 +1,161 @@
//go:build with_wireguard
package main
import (
"bytes"
"context"
"crypto/ecdh"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"net"
"net/netip"
"testing"
"time"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
M "github.com/sagernet/sing/common/metadata"
"github.com/stretchr/testify/require"
)
func TestWireGuardEndpointDataPlane(t *testing.T) {
for _, system := range []bool{false, true} {
t.Run(fmt.Sprintf("system=%v", system), func(test *testing.T) {
serverKey, err := ecdh.X25519().GenerateKey(rand.Reader)
require.NoError(test, err)
clientKey, err := ecdh.X25519().GenerateKey(rand.Reader)
require.NoError(test, err)
portReservation, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
require.NoError(test, err)
serverPort := uint16(portReservation.LocalAddr().(*net.UDPAddr).Port)
require.NoError(test, portReservation.Close())
serverAddresses := []netip.Prefix{netip.MustParsePrefix("198.18.17.1/32"), netip.MustParsePrefix("fd17::1/128")}
clientAddresses := []netip.Prefix{netip.MustParsePrefix("198.18.17.2/32"), netip.MustParsePrefix("fd17::2/128")}
instance := startInstance(test, option.Options{
Endpoints: []option.Endpoint{
{
Type: C.TypeWireGuard,
Tag: "server",
Options: &option.WireGuardEndpointOptions{
MTU: 1280, Address: serverAddresses, Workers: 2,
PrivateKey: base64.StdEncoding.EncodeToString(serverKey.Bytes()),
ListenPort: serverPort,
Peers: []option.WireGuardPeer{{
PublicKey: base64.StdEncoding.EncodeToString(clientKey.PublicKey().Bytes()),
AllowedIPs: clientAddresses,
}},
},
},
{
Type: C.TypeWireGuard,
Tag: "client",
Options: &option.WireGuardEndpointOptions{
System: system, MTU: 1280, Address: clientAddresses, Workers: 2,
PrivateKey: base64.StdEncoding.EncodeToString(clientKey.Bytes()),
Peers: []option.WireGuardPeer{{
Address: "127.0.0.1", Port: serverPort,
PublicKey: base64.StdEncoding.EncodeToString(serverKey.PublicKey().Bytes()),
AllowedIPs: serverAddresses,
}},
},
},
},
Outbounds: []option.Outbound{{Type: C.TypeDirect, Tag: "direct", Options: &option.DirectOutboundOptions{}}},
Route: &option.RouteOptions{Final: "direct"},
})
endpoint, loaded := instance.Endpoint().Get("client")
require.True(test, loaded)
for index, address := range serverAddresses {
for _, network := range []string{"tcp", "udp", "packet"} {
test.Run(fmt.Sprintf("ipv6=%v/%s", address.Addr().Is6(), network), func(subtest *testing.T) {
subtest.Parallel()
loopback := netip.MustParseAddr("127.0.0.1")
if index == 1 {
loopback = netip.IPv6Loopback()
}
ctx, cancel := context.WithTimeout(subtest.Context(), 3*time.Second)
defer cancel()
if network == "tcp" {
listener, listenErr := net.ListenTCP("tcp", net.TCPAddrFromAddrPort(netip.AddrPortFrom(loopback, 0)))
require.NoError(subtest, listenErr)
defer listener.Close()
listener.SetDeadline(time.Now().Add(3 * time.Second))
destination := M.SocksaddrFrom(address.Addr(), uint16(listener.Addr().(*net.TCPAddr).Port))
client, dialErr := endpoint.DialContext(ctx, "tcp", destination)
require.NoError(subtest, dialErr)
defer client.Close()
server, acceptErr := listener.Accept()
require.NoError(subtest, acceptErr)
defer server.Close()
client.SetDeadline(time.Now().Add(3 * time.Second))
server.SetDeadline(time.Now().Add(3 * time.Second))
payload := bytes.Repeat([]byte("wireguard-encrypted-tcp"), 8192)
for _, pair := range [][2]net.Conn{{client, server}, {server, client}} {
result := make(chan error, 1)
go func() {
_, writeErr := pair[0].Write(payload)
result <- writeErr
}()
received := make([]byte, len(payload))
_, readErr := io.ReadFull(pair[1], received)
require.NoError(subtest, readErr)
require.Equal(subtest, payload, received)
require.NoError(subtest, <-result)
}
return
}
server, listenErr := net.ListenUDP("udp", net.UDPAddrFromAddrPort(netip.AddrPortFrom(loopback, 0)))
require.NoError(subtest, listenErr)
defer server.Close()
server.SetDeadline(time.Now().Add(3 * time.Second))
destination := M.SocksaddrFrom(address.Addr(), uint16(server.LocalAddr().(*net.UDPAddr).Port))
var send func([]byte) (int, error)
var receive func([]byte) (int, error)
if network == "udp" {
client, dialErr := endpoint.DialContext(ctx, "udp", destination)
require.NoError(subtest, dialErr)
defer client.Close()
client.SetDeadline(time.Now().Add(3 * time.Second))
send, receive = client.Write, client.Read
} else {
client, dialErr := endpoint.ListenPacket(ctx, destination)
require.NoError(subtest, dialErr)
defer client.Close()
client.SetDeadline(time.Now().Add(3 * time.Second))
send = func(payload []byte) (int, error) { return client.WriteTo(payload, destination.UDPAddr()) }
receive = func(payload []byte) (int, error) {
count, source, readErr := client.ReadFrom(payload)
if readErr == nil {
require.Equal(subtest, destination.String(), source.String())
}
return count, readErr
}
}
for _, size := range []int{64, 4096} {
payload := bytes.Repeat([]byte{0x65}, size)
request := payload
if system && size > 1200 {
request = request[:1200]
}
_, writeErr := send(request)
require.NoError(subtest, writeErr)
received := make([]byte, 65535)
count, source, readErr := server.ReadFromUDP(received)
require.NoError(subtest, readErr)
require.Equal(subtest, request, received[:count])
_, writeErr = server.WriteToUDP(payload, source)
require.NoError(subtest, writeErr)
count, readErr = receive(received)
require.NoError(subtest, readErr)
require.Equal(subtest, payload, received[:count])
}
})
}
}
})
}
}
+31 -17
View File
@@ -37,20 +37,20 @@ type Device interface {
}
type DeviceOptions struct {
Context context.Context
Logger logger.ContextLogger
System bool
Handler tun.Handler
UDPTimeout time.Duration
ICMPTimeout time.Duration
UDPMapping tun.NATMapping
UDPFiltering tun.NATFiltering
UDPNATMax uint32
InterfaceFinder control.InterfaceFinder
ExcludeInterface []string
Name string
MTU uint32
Configuration Configuration
Context context.Context
Logger logger.ContextLogger
System bool
Handler tun.Handler
UDPTimeout time.Duration
ICMPTimeout time.Duration
UDPMapping tun.NATMapping
UDPFiltering tun.NATFiltering
UDPNATMax uint32
InterfaceFinder control.InterfaceFinder
MemoryPressure func() tun.MemoryPressure
Name string
MTU uint32
Configuration Configuration
}
type Configuration struct {
@@ -86,12 +86,26 @@ func NewDevice(options DeviceOptions) (Device, error) {
if !options.System {
return newStackDevice(options)
}
if !tun.WithGVisor {
return newSystemDevice(options)
}
return newSystemStackDevice(options)
}
func newStack(options DeviceOptions, memoryTun *tun.MemoryTun) (*tun.Go, error) {
return tun.NewGo(tun.StackOptions{
Context: options.Context,
Tun: memoryTun,
TunOptions: tun.Options{MTU: options.MTU},
UDPTimeout: options.UDPTimeout,
ICMPTimeout: options.ICMPTimeout,
UDPMapping: options.UDPMapping,
UDPFiltering: options.UDPFiltering,
UDPNATMax: options.UDPNATMax,
Handler: options.Handler,
Logger: options.Logger,
InterfaceFinder: options.InterfaceFinder,
MemoryPressure: options.MemoryPressure,
})
}
type baseDevice struct {
packetWriter PacketWriter
returnState atomic.Pointer[returnPathState]
+44 -269
View File
@@ -1,27 +1,13 @@
//go:build with_gvisor
package openconnect
import (
"context"
"net"
"net/netip"
"slices"
"sync"
"sync/atomic"
"github.com/sagernet/gvisor/pkg/buffer"
"github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
"github.com/sagernet/gvisor/pkg/tcpip/header"
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv4"
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv6"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
"github.com/sagernet/sing-box/common/interrupt"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
@@ -32,17 +18,13 @@ var _ Device = (*stackDevice)(nil)
type stackDevice struct {
baseDevice
stateAccess sync.RWMutex
options DeviceOptions
stack *stack.Stack
endpoint *stackEndpoint
inet4Address netip.Addr
inet6Address netip.Addr
inet4UDP interrupt.Group
inet6UDP interrupt.Group
udpForwarder *tun.UDPForwarder
icmpForwarder *tun.ICMPForwarder
closeOnce sync.Once
stateAccess sync.RWMutex
options DeviceOptions
stack *tun.Go
memoryTun *tun.MemoryTun
inet4Address netip.Addr
inet6Address netip.Addr
closeOnce sync.Once
}
func newStackDevice(options DeviceOptions) (*stackDevice, error) {
@@ -52,51 +34,23 @@ func newStackDevice(options DeviceOptions) (*stackDevice, error) {
device := &stackDevice{
options: options,
}
endpoint := &stackEndpoint{
device: device,
done: make(chan struct{}),
}
endpoint.mtu.Store(options.MTU)
ipStack, err := tun.NewGVisorStackWithOptions(endpoint, stack.NICOptions{}, true)
device.inet4Address, device.inet6Address = firstAddresses(options.Configuration.Addresses)
device.memoryTun = tun.NewMemoryTun(tun.MemoryTunOptions{
MTU: int(options.MTU),
Headroom: PacketHeadroom,
RearSpace: systemDevicePacketRearSpace,
Outbound: device.writeOutbound,
})
var err error
device.stack, err = newStack(options, device.memoryTun)
if err != nil {
return nil, err
}
device.stack = ipStack
device.endpoint = endpoint
err = device.updateAddresses(nil, options.Configuration.Addresses)
if err != nil {
ipStack.Close()
return nil, err
}
if options.Handler != nil {
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket)
udpForwarder := tun.NewUDPForwarder(options.Context, ipStack, options.Handler, tun.UDPNatOptions{
Timeout: options.UDPTimeout,
Shared: true,
Mapping: options.UDPMapping,
Filtering: options.UDPFiltering,
MaxSize: options.UDPNATMax,
InterfaceFinder: options.InterfaceFinder,
ExcludeInterface: options.ExcludeInterface,
})
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
device.udpForwarder = udpForwarder
icmpForwarder := tun.NewICMPForwarder(ipStack, options.Handler, options.Logger)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
device.icmpForwarder = icmpForwarder
}
return device, nil
}
func (d *stackDevice) Start() error {
if d.udpForwarder != nil {
err := d.udpForwarder.Start()
if err != nil {
return err
}
}
return nil
return d.stack.Start()
}
func (d *stackDevice) UpdateConfiguration(configuration Configuration) error {
@@ -104,51 +58,10 @@ func (d *stackDevice) UpdateConfiguration(configuration Configuration) error {
defer d.stateAccess.Unlock()
if configuration.MTU != 0 {
d.options.MTU = configuration.MTU
d.endpoint.mtu.Store(configuration.MTU)
d.memoryTun.UpdateMTU(int(configuration.MTU))
}
previousAddresses := d.options.Configuration.Addresses
d.options.Configuration = configuration
return d.updateAddresses(previousAddresses, configuration.Addresses)
}
func (d *stackDevice) updateAddresses(previousAddresses []netip.Prefix, addresses []netip.Prefix) error {
for _, prefix := range previousAddresses {
if slices.Contains(addresses, prefix) {
continue
}
gErr := d.stack.RemoveAddress(tun.DefaultNIC, tun.AddressFromAddr(prefix.Addr()))
if gErr != nil {
return E.New("remove local address ", prefix, ": ", gErr.String())
}
}
for _, prefix := range addresses {
if slices.Contains(previousAddresses, prefix) {
continue
}
protocolAddress := tcpip.ProtocolAddress{
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: tun.AddressFromAddr(prefix.Addr()),
PrefixLen: prefix.Bits(),
},
}
if prefix.Addr().Is4() {
protocolAddress.Protocol = ipv4.ProtocolNumber
} else {
protocolAddress.Protocol = ipv6.ProtocolNumber
}
gErr := d.stack.AddProtocolAddress(tun.DefaultNIC, protocolAddress, stack.AddressProperties{})
if gErr != nil {
return E.New("add local address ", prefix, ": ", gErr.String())
}
}
inet4Address, inet6Address := firstAddresses(addresses)
if d.inet4Address != inet4Address {
d.inet4UDP.Interrupt(true)
}
if d.inet6Address != inet6Address {
d.inet6UDP.Interrupt(true)
}
d.inet4Address, d.inet6Address = inet4Address, inet6Address
d.inet4Address, d.inet6Address = firstAddresses(configuration.Addresses)
return nil
}
@@ -157,92 +70,60 @@ func (d *stackDevice) WriteInboundBuffers(packetBuffers []*buf.Buffer) error {
}
func (d *stackDevice) writeBuffers(packetBuffers []*buf.Buffer) error {
networkProtocols := make([]tcpip.NetworkProtocolNumber, 0, len(packetBuffers))
stackPacketBuffers := make([]*stack.PacketBuffer, 0, len(packetBuffers))
var packetErr error
for _, packetBuffer := range packetBuffers {
packet := packetBuffer.Bytes()
var networkProtocol tcpip.NetworkProtocolNumber
switch header.IPVersion(packet) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
default:
if packetErr == nil {
packetErr = E.New("invalid IP packet")
}
continue
}
networkProtocols = append(networkProtocols, networkProtocol)
stackPacketBuffers = append(stackPacketBuffers, stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: buffer.MakeWithData(packet),
}))
}
d.endpoint.deliverNetworkPackets(networkProtocols, stackPacketBuffers)
for _, packetBuffer := range stackPacketBuffers {
packetBuffer.DecRef()
}
return packetErr
_, err := d.memoryTun.WritePackets(common.Map(packetBuffers, (*buf.Buffer).Bytes))
return err
}
func (d *stackDevice) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
address := tcpip.FullAddress{
NIC: tun.DefaultNIC,
Port: destination.Port,
Addr: tun.AddressFromAddr(destination.Addr),
bind, err := d.bindAddress(destination)
if err != nil {
return nil, err
}
switch N.NetworkName(network) {
case N.NetworkTCP:
d.stateAccess.RLock()
bind, networkProtocol, _, err := d.bindAddressLocked(destination)
d.stateAccess.RUnlock()
var tcpConn *tun.GoConn
tcpConn, err = d.stack.DialTCP(ctx, bind, destination.AddrPort())
if err != nil {
return nil, err
}
return gonet.DialTCPWithBind(ctx, d.stack, bind, address, networkProtocol)
return tcpConn, nil
case N.NetworkUDP:
d.stateAccess.RLock()
defer d.stateAccess.RUnlock()
bind, networkProtocol, udpGroup, err := d.bindAddressLocked(destination)
var udpConn *tun.GoUDPConn
udpConn, err = d.stack.DialUDP(netip.AddrPortFrom(bind, 0), destination.AddrPort())
if err != nil {
return nil, err
}
udpConn, err := gonet.DialUDP(d.stack, &bind, &address, networkProtocol)
if err != nil {
return nil, err
}
return udpGroup.NewConn(udpConn, false), nil
return udpConn, nil
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
}
func (d *stackDevice) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
d.stateAccess.RLock()
defer d.stateAccess.RUnlock()
bind, networkProtocol, udpGroup, err := d.bindAddressLocked(destination)
bind, err := d.bindAddress(destination)
if err != nil {
return nil, err
}
udpConn, err := gonet.DialUDP(d.stack, &bind, nil, networkProtocol)
udpConn, err := d.stack.ListenUDP(netip.AddrPortFrom(bind, 0))
if err != nil {
return nil, err
}
return udpGroup.NewPacketConn(udpConn, false), nil
return udpConn, nil
}
func (d *stackDevice) bindAddressLocked(destination M.Socksaddr) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *interrupt.Group, error) {
func (d *stackDevice) bindAddress(destination M.Socksaddr) (netip.Addr, error) {
d.stateAccess.RLock()
defer d.stateAccess.RUnlock()
if destination.IsIPv4() {
if !d.inet4Address.IsValid() {
return tcpip.FullAddress{}, 0, nil, E.New("missing IPv4 local address")
return netip.Addr{}, E.New("missing IPv4 local address")
}
return tcpip.FullAddress{NIC: tun.DefaultNIC, Addr: tun.AddressFromAddr(d.inet4Address)}, header.IPv4ProtocolNumber, &d.inet4UDP, nil
return d.inet4Address, nil
}
if !d.inet6Address.IsValid() {
return tcpip.FullAddress{}, 0, nil, E.New("missing IPv6 local address")
return netip.Addr{}, E.New("missing IPv6 local address")
}
return tcpip.FullAddress{NIC: tun.DefaultNIC, Addr: tun.AddressFromAddr(d.inet6Address)}, header.IPv6ProtocolNumber, &d.inet6UDP, nil
return d.inet6Address, nil
}
func (d *stackDevice) PortAddresses() (netip.Addr, netip.Addr) {
@@ -258,115 +139,9 @@ func (d *stackDevice) PortMTU() uint32 {
}
func (d *stackDevice) Close() error {
var err error
d.closeOnce.Do(func() {
close(d.endpoint.done)
if d.udpForwarder != nil {
d.udpForwarder.Close()
}
if d.icmpForwarder != nil {
d.icmpForwarder.Close()
}
d.stack.Close()
for _, endpoint := range d.stack.CleanupEndpoints() {
endpoint.Abort()
}
d.stack.Wait()
err = E.Errors(d.stack.Close(), d.memoryTun.Close())
})
return nil
}
type stackEndpoint struct {
device *stackDevice
mtu atomic.Uint32
done chan struct{}
dispatcherAccess sync.RWMutex
dispatcher stack.NetworkDispatcher
}
func (e *stackEndpoint) MTU() uint32 {
return e.mtu.Load()
}
func (e *stackEndpoint) SetMTU(mtu uint32) {
e.mtu.Store(mtu)
}
func (e *stackEndpoint) MaxHeaderLength() uint16 {
return 0
}
func (e *stackEndpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
func (e *stackEndpoint) SetLinkAddress(addr tcpip.LinkAddress) {
}
func (e *stackEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityRXChecksumOffload
}
func (e *stackEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
e.dispatcherAccess.Lock()
defer e.dispatcherAccess.Unlock()
e.dispatcher = dispatcher
}
func (e *stackEndpoint) IsAttached() bool {
e.dispatcherAccess.RLock()
defer e.dispatcherAccess.RUnlock()
return e.dispatcher != nil
}
func (e *stackEndpoint) deliverNetworkPackets(networkProtocols []tcpip.NetworkProtocolNumber, packetBuffers []*stack.PacketBuffer) {
e.dispatcherAccess.RLock()
defer e.dispatcherAccess.RUnlock()
if e.dispatcher == nil {
return
}
for i, packetBuffer := range packetBuffers {
e.dispatcher.DeliverNetworkPacket(networkProtocols[i], packetBuffer)
}
}
func (e *stackEndpoint) Wait() {
}
func (e *stackEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
func (e *stackEndpoint) AddHeader(packetBuffer *stack.PacketBuffer) {
}
func (e *stackEndpoint) ParseHeader(packetBuffer *stack.PacketBuffer) bool {
return true
}
func (e *stackEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
packetBuffers := make([]*buf.Buffer, 0, list.Len())
for _, packetBuffer := range list.AsSlice() {
packetSlices := packetBuffer.AsSlices()
packetLength := 0
for _, packetSlice := range packetSlices {
packetLength += len(packetSlice)
}
outboundBuffer := buf.NewSize(PacketHeadroom + packetLength + systemDevicePacketRearSpace)
outboundBuffer.Resize(PacketHeadroom, 0)
for _, packetSlice := range packetSlices {
_, _ = outboundBuffer.Write(packetSlice)
}
packetBuffers = append(packetBuffers, outboundBuffer)
}
err := e.device.writeOutbound(packetBuffers)
if err != nil {
return 0, &tcpip.ErrClosedForSend{}
}
return list.Len(), nil
}
func (e *stackEndpoint) Close() {
}
func (e *stackEndpoint) SetOnCloseAction(action func()) {
return err
}
@@ -1,13 +0,0 @@
//go:build !with_gvisor
package openconnect
import E "github.com/sagernet/sing/common/exceptions"
func newStackDevice(options DeviceOptions) (Device, error) {
return nil, E.New("system:false requires the with_gvisor build tag")
}
func newSystemStackDevice(options DeviceOptions) (Device, error) {
return nil, E.New("system stack requires the with_gvisor build tag")
}
+1 -7
View File
@@ -1,5 +1,3 @@
//go:build with_gvisor
package openconnect
import (
@@ -22,11 +20,7 @@ func newSystemStackDevice(options DeviceOptions) (*systemStackDevice, error) {
if err != nil {
return nil, err
}
stackOptions := options
stackOptions.System = false
stackOptions.Name = system.options.Name
stackOptions.ExcludeInterface = []string{system.options.Name}
stackDevice, err := newStackDevice(stackOptions)
stackDevice, err := newStackDevice(options)
if err != nil {
system.Close()
return nil, err
+31 -17
View File
@@ -36,20 +36,20 @@ type Device interface {
}
type DeviceOptions struct {
Context context.Context
Logger logger.ContextLogger
System bool
Handler tun.Handler
UDPTimeout time.Duration
ICMPTimeout time.Duration
UDPMapping tun.NATMapping
UDPFiltering tun.NATFiltering
UDPNATMax uint32
InterfaceFinder control.InterfaceFinder
ExcludeInterface []string
Name string
MTU uint32
Configuration Configuration
Context context.Context
Logger logger.ContextLogger
System bool
Handler tun.Handler
UDPTimeout time.Duration
ICMPTimeout time.Duration
UDPMapping tun.NATMapping
UDPFiltering tun.NATFiltering
UDPNATMax uint32
InterfaceFinder control.InterfaceFinder
MemoryPressure func() tun.MemoryPressure
Name string
MTU uint32
Configuration Configuration
}
type Configuration struct {
@@ -85,12 +85,26 @@ func NewDevice(options DeviceOptions) (Device, error) {
if !options.System {
return newStackDevice(options)
}
if !tun.WithGVisor {
return newSystemDevice(options)
}
return newSystemStackDevice(options)
}
func newStack(options DeviceOptions, memoryTun *tun.MemoryTun) (*tun.Go, error) {
return tun.NewGo(tun.StackOptions{
Context: options.Context,
Tun: memoryTun,
TunOptions: tun.Options{MTU: options.MTU},
UDPTimeout: options.UDPTimeout,
ICMPTimeout: options.ICMPTimeout,
UDPMapping: options.UDPMapping,
UDPFiltering: options.UDPFiltering,
UDPNATMax: options.UDPNATMax,
Handler: options.Handler,
Logger: options.Logger,
InterfaceFinder: options.InterfaceFinder,
MemoryPressure: options.MemoryPressure,
})
}
type baseDevice struct {
packetWriter PacketWriter
returnState atomic.Pointer[returnPathState]
+41 -266
View File
@@ -1,27 +1,13 @@
//go:build with_gvisor
package openvpn
import (
"context"
"net"
"net/netip"
"slices"
"sync"
"sync/atomic"
"github.com/sagernet/gvisor/pkg/buffer"
"github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
"github.com/sagernet/gvisor/pkg/tcpip/header"
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv4"
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv6"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
"github.com/sagernet/sing-box/common/interrupt"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
@@ -34,14 +20,10 @@ type stackDevice struct {
baseDevice
stateAccess sync.RWMutex
options DeviceOptions
stack *stack.Stack
endpoint *stackEndpoint
stack *tun.Go
memoryTun *tun.MemoryTun
inet4Address netip.Addr
inet6Address netip.Addr
inet4UDP interrupt.Group
inet6UDP interrupt.Group
udpForwarder *tun.UDPForwarder
icmpForwarder *tun.ICMPForwarder
logRouteOptions bool
closeOnce sync.Once
}
@@ -54,107 +36,38 @@ func newStackDevice(options DeviceOptions) (*stackDevice, error) {
options: options,
logRouteOptions: true,
}
endpoint := &stackEndpoint{
device: device,
done: make(chan struct{}),
}
endpoint.mtu.Store(options.MTU)
ipStack, err := tun.NewGVisorStackWithOptions(endpoint, stack.NICOptions{}, true)
device.inet4Address, device.inet6Address = firstAddresses(options.Configuration.Address)
device.memoryTun = tun.NewMemoryTun(tun.MemoryTunOptions{
MTU: int(options.MTU),
Headroom: PacketHeadroom,
RearSpace: systemDevicePacketRearSpace,
Outbound: device.writeOutbound,
})
var err error
device.stack, err = newStack(options, device.memoryTun)
if err != nil {
return nil, err
}
device.stack = ipStack
device.endpoint = endpoint
err = device.updateAddresses(nil, options.Configuration.Address)
if err != nil {
ipStack.Close()
return nil, err
}
if options.Handler != nil {
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket)
udpForwarder := tun.NewUDPForwarder(options.Context, ipStack, options.Handler, tun.UDPNatOptions{
Timeout: options.UDPTimeout,
Shared: true,
Mapping: options.UDPMapping,
Filtering: options.UDPFiltering,
MaxSize: options.UDPNATMax,
InterfaceFinder: options.InterfaceFinder,
ExcludeInterface: options.ExcludeInterface,
})
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
device.udpForwarder = udpForwarder
icmpForwarder := tun.NewICMPForwarder(ipStack, options.Handler, options.Logger)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
device.icmpForwarder = icmpForwarder
}
return device, nil
}
func (d *stackDevice) Start() error {
if d.udpForwarder != nil {
err := d.udpForwarder.Start()
if err != nil {
return err
}
}
return nil
return d.stack.Start()
}
func (d *stackDevice) UpdateConfiguration(configuration Configuration) error {
d.stateAccess.Lock()
defer d.stateAccess.Unlock()
if d.logRouteOptions && hasRouteOptions(configuration.Routes) {
d.options.Logger.Debug("route gateway and metric options are not representable by the gVisor stack device; routes are installed by prefix")
d.options.Logger.Debug("route gateway and metric options are not representable by the stack device; routes are installed by prefix")
d.logRouteOptions = false
}
if configuration.MTU != 0 {
d.options.MTU = configuration.MTU
d.endpoint.mtu.Store(configuration.MTU)
d.memoryTun.UpdateMTU(int(configuration.MTU))
}
previousAddresses := d.options.Configuration.Address
d.options.Configuration = configuration
return d.updateAddresses(previousAddresses, configuration.Address)
}
func (d *stackDevice) updateAddresses(previousAddresses []netip.Prefix, addresses []netip.Prefix) error {
for _, prefix := range previousAddresses {
if slices.Contains(addresses, prefix) {
continue
}
gErr := d.stack.RemoveAddress(tun.DefaultNIC, tun.AddressFromAddr(prefix.Addr()))
if gErr != nil {
return E.New("remove local address ", prefix, ": ", gErr.String())
}
}
for _, prefix := range addresses {
if slices.Contains(previousAddresses, prefix) {
continue
}
protocolAddress := tcpip.ProtocolAddress{
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: tun.AddressFromAddr(prefix.Addr()),
PrefixLen: prefix.Bits(),
},
}
if prefix.Addr().Is4() {
protocolAddress.Protocol = ipv4.ProtocolNumber
} else {
protocolAddress.Protocol = ipv6.ProtocolNumber
}
gErr := d.stack.AddProtocolAddress(tun.DefaultNIC, protocolAddress, stack.AddressProperties{})
if gErr != nil {
return E.New("add local address ", prefix, ": ", gErr.String())
}
}
inet4Address, inet6Address := firstAddresses(addresses)
if d.inet4Address != inet4Address {
d.inet4UDP.Interrupt(true)
}
if d.inet6Address != inet6Address {
d.inet6UDP.Interrupt(true)
}
d.inet4Address, d.inet6Address = inet4Address, inet6Address
d.inet4Address, d.inet6Address = firstAddresses(configuration.Address)
return nil
}
@@ -163,95 +76,63 @@ func (d *stackDevice) WriteInboundBuffers(packetBuffers []*buf.Buffer) error {
}
func (d *stackDevice) writeBuffers(packetBuffers []*buf.Buffer) error {
networkProtocols := make([]tcpip.NetworkProtocolNumber, 0, len(packetBuffers))
stackPacketBuffers := make([]*stack.PacketBuffer, 0, len(packetBuffers))
var packetErr error
for _, packetBuffer := range packetBuffers {
packet := packetBuffer.Bytes()
var networkProtocol tcpip.NetworkProtocolNumber
switch header.IPVersion(packet) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
default:
if packetErr == nil {
packetErr = E.New("invalid IP packet")
}
continue
}
networkProtocols = append(networkProtocols, networkProtocol)
stackPacketBuffers = append(stackPacketBuffers, stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: buffer.MakeWithData(packet),
}))
}
d.endpoint.deliverNetworkPackets(networkProtocols, stackPacketBuffers)
for _, packetBuffer := range stackPacketBuffers {
packetBuffer.DecRef()
}
return packetErr
_, err := d.memoryTun.WritePackets(common.Map(packetBuffers, (*buf.Buffer).Bytes))
return err
}
func (d *stackDevice) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
address := tcpip.FullAddress{
NIC: tun.DefaultNIC,
Port: destination.Port,
Addr: tun.AddressFromAddr(destination.Addr),
bind, err := d.bindAddress(destination)
if err != nil {
return nil, err
}
switch N.NetworkName(network) {
case N.NetworkTCP:
d.stateAccess.RLock()
bind, networkProtocol, _, err := d.bindAddressLocked(destination)
d.stateAccess.RUnlock()
var tcpConn *tun.GoConn
tcpConn, err = d.stack.DialTCP(ctx, bind, destination.AddrPort())
if err != nil {
return nil, err
}
return gonet.DialTCPWithBind(ctx, d.stack, bind, address, networkProtocol)
return tcpConn, nil
case N.NetworkUDP:
d.stateAccess.RLock()
defer d.stateAccess.RUnlock()
bind, networkProtocol, udpGroup, err := d.bindAddressLocked(destination)
var udpConn *tun.GoUDPConn
udpConn, err = d.stack.DialUDP(netip.AddrPortFrom(bind, 0), destination.AddrPort())
if err != nil {
return nil, err
}
udpConn, err := gonet.DialUDP(d.stack, &bind, &address, networkProtocol)
if err != nil {
return nil, err
}
return udpGroup.NewConn(udpConn, false), nil
return udpConn, nil
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
}
func (d *stackDevice) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
d.stateAccess.RLock()
defer d.stateAccess.RUnlock()
bind, networkProtocol, udpGroup, err := d.bindAddressLocked(destination)
bind, err := d.bindAddress(destination)
if err != nil {
return nil, err
}
udpConn, err := gonet.DialUDP(d.stack, &bind, nil, networkProtocol)
udpConn, err := d.stack.ListenUDP(netip.AddrPortFrom(bind, 0))
if err != nil {
return nil, err
}
return udpGroup.NewPacketConn(udpConn, false), nil
return udpConn, nil
}
func (d *stackDevice) bindAddressLocked(destination M.Socksaddr) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *interrupt.Group, error) {
func (d *stackDevice) bindAddress(destination M.Socksaddr) (netip.Addr, error) {
d.stateAccess.RLock()
defer d.stateAccess.RUnlock()
if destination.IsIPv4() {
if !d.inet4Address.IsValid() {
return tcpip.FullAddress{}, 0, nil, E.New("missing IPv4 local address")
return netip.Addr{}, E.New("missing IPv4 local address")
}
return tcpip.FullAddress{NIC: tun.DefaultNIC, Addr: tun.AddressFromAddr(d.inet4Address)}, header.IPv4ProtocolNumber, &d.inet4UDP, nil
return d.inet4Address, nil
}
if d.options.Configuration.BlockIPv6 {
return tcpip.FullAddress{}, 0, nil, E.New("IPv6 blocked by pushed block-ipv6")
return netip.Addr{}, E.New("IPv6 blocked by pushed block-ipv6")
}
if !d.inet6Address.IsValid() {
return tcpip.FullAddress{}, 0, nil, E.New("missing IPv6 local address")
return netip.Addr{}, E.New("missing IPv6 local address")
}
return tcpip.FullAddress{NIC: tun.DefaultNIC, Addr: tun.AddressFromAddr(d.inet6Address)}, header.IPv6ProtocolNumber, &d.inet6UDP, nil
return d.inet6Address, nil
}
func (d *stackDevice) PortAddresses() (netip.Addr, netip.Addr) {
@@ -267,115 +148,9 @@ func (d *stackDevice) PortMTU() uint32 {
}
func (d *stackDevice) Close() error {
var err error
d.closeOnce.Do(func() {
close(d.endpoint.done)
if d.udpForwarder != nil {
d.udpForwarder.Close()
}
if d.icmpForwarder != nil {
d.icmpForwarder.Close()
}
d.stack.Close()
for _, endpoint := range d.stack.CleanupEndpoints() {
endpoint.Abort()
}
d.stack.Wait()
err = E.Errors(d.stack.Close(), d.memoryTun.Close())
})
return nil
}
type stackEndpoint struct {
device *stackDevice
mtu atomic.Uint32
done chan struct{}
dispatcherAccess sync.RWMutex
dispatcher stack.NetworkDispatcher
}
func (e *stackEndpoint) MTU() uint32 {
return e.mtu.Load()
}
func (e *stackEndpoint) SetMTU(mtu uint32) {
e.mtu.Store(mtu)
}
func (e *stackEndpoint) MaxHeaderLength() uint16 {
return 0
}
func (e *stackEndpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
func (e *stackEndpoint) SetLinkAddress(addr tcpip.LinkAddress) {
}
func (e *stackEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityRXChecksumOffload
}
func (e *stackEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
e.dispatcherAccess.Lock()
defer e.dispatcherAccess.Unlock()
e.dispatcher = dispatcher
}
func (e *stackEndpoint) IsAttached() bool {
e.dispatcherAccess.RLock()
defer e.dispatcherAccess.RUnlock()
return e.dispatcher != nil
}
func (e *stackEndpoint) deliverNetworkPackets(networkProtocols []tcpip.NetworkProtocolNumber, packetBuffers []*stack.PacketBuffer) {
e.dispatcherAccess.RLock()
defer e.dispatcherAccess.RUnlock()
if e.dispatcher == nil {
return
}
for i, packetBuffer := range packetBuffers {
e.dispatcher.DeliverNetworkPacket(networkProtocols[i], packetBuffer)
}
}
func (e *stackEndpoint) Wait() {
}
func (e *stackEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
func (e *stackEndpoint) AddHeader(packetBuffer *stack.PacketBuffer) {
}
func (e *stackEndpoint) ParseHeader(packetBuffer *stack.PacketBuffer) bool {
return true
}
func (e *stackEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
packetBuffers := make([]*buf.Buffer, 0, list.Len())
for _, packetBuffer := range list.AsSlice() {
packetSlices := packetBuffer.AsSlices()
packetLength := 0
for _, packetSlice := range packetSlices {
packetLength += len(packetSlice)
}
outboundBuffer := buf.NewSize(PacketHeadroom + packetLength + systemDevicePacketRearSpace)
outboundBuffer.Resize(PacketHeadroom, 0)
for _, packetSlice := range packetSlices {
_, _ = outboundBuffer.Write(packetSlice)
}
packetBuffers = append(packetBuffers, outboundBuffer)
}
err := e.device.writeOutbound(packetBuffers)
if err != nil {
return 0, &tcpip.ErrClosedForSend{}
}
return list.Len(), nil
}
func (e *stackEndpoint) Close() {
}
func (e *stackEndpoint) SetOnCloseAction(action func()) {
return err
}
-13
View File
@@ -1,13 +0,0 @@
//go:build !with_gvisor
package openvpn
import E "github.com/sagernet/sing/common/exceptions"
func newStackDevice(options DeviceOptions) (Device, error) {
return nil, E.New("system:false requires the with_gvisor build tag")
}
func newSystemStackDevice(options DeviceOptions) (Device, error) {
return nil, E.New("system stack requires the with_gvisor build tag")
}
+1 -7
View File
@@ -1,5 +1,3 @@
//go:build with_gvisor
package openvpn
import (
@@ -22,11 +20,7 @@ func newSystemStackDevice(options DeviceOptions) (*systemStackDevice, error) {
if err != nil {
return nil, err
}
stackOptions := options
stackOptions.System = false
stackOptions.Name = system.options.Name
stackOptions.ExcludeInterface = []string{system.options.Name}
stackDevice, err := newStackDevice(stackOptions)
stackDevice, err := newStackDevice(options)
if err != nil {
system.Close()
return nil, err
+26 -5
View File
@@ -6,6 +6,7 @@ import (
"time"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/control"
"github.com/sagernet/sing/common/logger"
N "github.com/sagernet/sing/common/network"
@@ -32,8 +33,8 @@ type DeviceOptions struct {
UDPMapping tun.NATMapping
UDPFiltering tun.NATFiltering
UDPNATMax uint32
NetworkMonitor tun.NetworkUpdateMonitor
InterfaceFinder control.InterfaceFinder
MemoryPressure func() tun.MemoryPressure
CreateDialer func(interfaceName string) N.Dialer
Name string
MTU uint32
@@ -44,9 +45,29 @@ type DeviceOptions struct {
func NewDevice(options DeviceOptions) (Device, error) {
if !options.System {
return newStackDevice(options)
} else if !tun.WithGVisor {
return newSystemDevice(options)
} else {
return newSystemStackDevice(options)
}
return newSystemStackDevice(options)
}
func newStack(options DeviceOptions, memoryTun *tun.MemoryTun) (*tun.Go, error) {
return tun.NewGo(tun.StackOptions{
Context: options.Context,
Tun: memoryTun,
TunOptions: tun.Options{MTU: options.MTU},
UDPTimeout: options.UDPTimeout,
ICMPTimeout: options.ICMPTimeout,
UDPMapping: options.UDPMapping,
UDPFiltering: options.UDPFiltering,
UDPNATMax: options.UDPNATMax,
Handler: options.Handler,
Logger: options.Logger,
InterfaceFinder: options.InterfaceFinder,
MemoryPressure: options.MemoryPressure,
})
}
func deviceAddresses(addresses []netip.Prefix) (netip.Addr, netip.Addr) {
inet4Prefix := common.Find(addresses, func(it netip.Prefix) bool { return it.Addr().Is4() })
inet6Prefix := common.Find(addresses, func(it netip.Prefix) bool { return it.Addr().Is6() })
return inet4Prefix.Addr(), inet6Prefix.Addr()
}
+59 -240
View File
@@ -1,5 +1,3 @@
//go:build with_gvisor
package wireguard
import (
@@ -8,22 +6,13 @@ import (
"net/netip"
"os"
"sync"
"time"
"github.com/sagernet/gvisor/pkg/buffer"
"github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
"github.com/sagernet/gvisor/pkg/tcpip/header"
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv4"
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv6"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/wireguard-go/conn"
"github.com/sagernet/wireguard-go/device"
wgTun "github.com/sagernet/wireguard-go/tun"
)
@@ -31,105 +20,49 @@ import (
var _ Device = (*stackDevice)(nil)
type stackDevice struct {
stack *stack.Stack
mtu uint32
events chan wgTun.Event
outbound chan *stack.PacketBuffer
packetOutbound chan *buf.Buffer
done chan struct{}
closeOnce sync.Once
dispatcher stack.NetworkDispatcher
inet4Address netip.Addr
inet6Address netip.Addr
icmpForwarder *tun.ICMPForwarder
udpForwarder *tun.UDPForwarder
stack *tun.Go
memoryTun *tun.MemoryTun
mtu uint32
events chan wgTun.Event
closeOnce sync.Once
inet4Address netip.Addr
inet6Address netip.Addr
}
func newStackDevice(options DeviceOptions) (*stackDevice, error) {
tunDevice := &stackDevice{
mtu: options.MTU,
events: make(chan wgTun.Event, 1),
outbound: make(chan *stack.PacketBuffer, 256),
packetOutbound: make(chan *buf.Buffer, 256),
done: make(chan struct{}),
}
ipStack, err := tun.NewGVisorStackWithOptions((*wireEndpoint)(tunDevice), stack.NICOptions{}, true)
memoryTun := tun.NewMemoryTun(tun.MemoryTunOptions{MTU: int(options.MTU)})
stack, err := newStack(options, memoryTun)
if err != nil {
return nil, err
}
for _, prefix := range options.Address {
addr := tun.AddressFromAddr(prefix.Addr())
protoAddr := tcpip.ProtocolAddress{
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: addr,
PrefixLen: prefix.Bits(),
},
}
if prefix.Addr().Is4() {
tunDevice.inet4Address = prefix.Addr()
protoAddr.Protocol = ipv4.ProtocolNumber
} else {
tunDevice.inet6Address = prefix.Addr()
protoAddr.Protocol = ipv6.ProtocolNumber
}
gErr := ipStack.AddProtocolAddress(tun.DefaultNIC, protoAddr, stack.AddressProperties{})
if gErr != nil {
return nil, E.New("parse local address ", protoAddr.AddressWithPrefix, ": ", gErr.String())
}
}
tunDevice.stack = ipStack
if options.Handler != nil {
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket)
udpForwarder := tun.NewUDPForwarder(options.Context, ipStack, options.Handler, tun.UDPNatOptions{
Timeout: options.UDPTimeout,
Shared: true,
Mapping: options.UDPMapping,
Filtering: options.UDPFiltering,
MaxSize: options.UDPNATMax,
InterfaceFinder: options.InterfaceFinder,
})
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
tunDevice.udpForwarder = udpForwarder
icmpForwarder := tun.NewICMPForwarder(ipStack, options.Handler, options.Logger)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
tunDevice.icmpForwarder = icmpForwarder
}
return tunDevice, nil
inet4Address, inet6Address := deviceAddresses(options.Address)
return &stackDevice{
stack: stack,
memoryTun: memoryTun,
mtu: options.MTU,
events: make(chan wgTun.Event, 1),
inet4Address: inet4Address,
inet6Address: inet6Address,
}, nil
}
func (w *stackDevice) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
addr := tcpip.FullAddress{
NIC: tun.DefaultNIC,
Port: destination.Port,
Addr: tun.AddressFromAddr(destination.Addr),
}
bind := tcpip.FullAddress{
NIC: tun.DefaultNIC,
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
if !w.inet4Address.IsValid() {
return nil, E.New("missing IPv4 local address")
}
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = tun.AddressFromAddr(w.inet4Address)
} else {
if !w.inet6Address.IsValid() {
return nil, E.New("missing IPv6 local address")
}
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = tun.AddressFromAddr(w.inet6Address)
bind, err := w.bindAddress(destination)
if err != nil {
return nil, err
}
switch N.NetworkName(network) {
case N.NetworkTCP:
tcpConn, err := DialTCPWithBind(ctx, w.stack, bind, addr, networkProtocol)
var tcpConn *tun.GoConn
tcpConn, err = w.stack.DialTCP(ctx, bind, destination.AddrPort())
if err != nil {
return nil, err
}
tcpConn.SetKeepAliveConfig(net.KeepAliveConfig{Enable: true, Idle: 15 * time.Second, Interval: 15 * time.Second})
return tcpConn, nil
case N.NetworkUDP:
udpConn, err := gonet.DialUDP(w.stack, &bind, &addr, networkProtocol)
var udpConn *tun.GoUDPConn
udpConn, err = w.stack.DialUDP(netip.AddrPortFrom(bind, 0), destination.AddrPort())
if err != nil {
return nil, err
}
@@ -140,30 +73,30 @@ func (w *stackDevice) DialContext(ctx context.Context, network string, destinati
}
func (w *stackDevice) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
bind := tcpip.FullAddress{
NIC: tun.DefaultNIC,
bind, err := w.bindAddress(destination)
if err != nil {
return nil, err
}
var networkProtocol tcpip.NetworkProtocolNumber
if destination.IsIPv4() {
if !w.inet4Address.IsValid() {
return nil, E.New("missing IPv4 local address")
}
networkProtocol = header.IPv4ProtocolNumber
bind.Addr = tun.AddressFromAddr(w.inet4Address)
} else {
if !w.inet6Address.IsValid() {
return nil, E.New("missing IPv6 local address")
}
networkProtocol = header.IPv6ProtocolNumber
bind.Addr = tun.AddressFromAddr(w.inet6Address)
}
udpConn, err := gonet.DialUDP(w.stack, &bind, nil, networkProtocol)
udpConn, err := w.stack.ListenUDP(netip.AddrPortFrom(bind, 0))
if err != nil {
return nil, err
}
return udpConn, nil
}
func (w *stackDevice) bindAddress(destination M.Socksaddr) (netip.Addr, error) {
if destination.IsIPv4() {
if !w.inet4Address.IsValid() {
return netip.Addr{}, E.New("missing IPv4 local address")
}
return w.inet4Address, nil
}
if !w.inet6Address.IsValid() {
return netip.Addr{}, E.New("missing IPv6 local address")
}
return w.inet6Address, nil
}
func (w *stackDevice) Inet4Address() netip.Addr {
return w.inet4Address
}
@@ -176,11 +109,9 @@ func (w *stackDevice) SetDevice(device *device.Device) {
}
func (w *stackDevice) Start() error {
if w.udpForwarder != nil {
err := w.udpForwarder.Start()
if err != nil {
return err
}
err := w.stack.Start()
if err != nil {
return err
}
w.events <- wgTun.EventUp
return nil
@@ -190,52 +121,16 @@ func (w *stackDevice) File() *os.File {
return nil
}
func (w *stackDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
select {
case packet, ok := <-w.outbound:
if !ok {
return 0, os.ErrClosed
}
defer packet.DecRef()
var copyN int
/*rangeIterate(packet.Data().AsRange(), func(view *buffer.View) {
copyN += copy(bufs[0][offset+copyN:], view.AsSlice())
})*/
for _, view := range packet.AsSlices() {
copyN += copy(bufs[0][offset+copyN:], view)
}
sizes[0] = copyN
return 1, nil
case packet := <-w.packetOutbound:
defer packet.Release()
sizes[0] = copy(bufs[0][offset:], packet.Bytes())
return 1, nil
case <-w.done:
return 0, os.ErrClosed
}
func (w *stackDevice) Read(bufs [][]byte, sizes []int, offset int) (int, error) {
return w.memoryTun.ReadPackets(bufs, sizes, offset)
}
func (w *stackDevice) Write(bufs [][]byte, offset int) (count int, err error) {
for _, b := range bufs {
b = b[offset:]
if len(b) == 0 {
continue
}
var networkProtocol tcpip.NetworkProtocolNumber
switch header.IPVersion(b) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
}
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: buffer.MakeWithData(b),
})
w.dispatcher.DeliverNetworkPacket(networkProtocol, packetBuffer)
packetBuffer.DecRef()
count++
func (w *stackDevice) Write(bufs [][]byte, offset int) (int, error) {
packets := make([][]byte, 0, len(bufs))
for _, packet := range bufs {
packets = append(packets, packet[offset:])
}
return
return w.memoryTun.WritePackets(packets)
}
func (w *stackDevice) Flush() error {
@@ -255,90 +150,14 @@ func (w *stackDevice) Events() <-chan wgTun.Event {
}
func (w *stackDevice) Close() error {
var err error
w.closeOnce.Do(func() {
close(w.done)
close(w.events)
if w.icmpForwarder != nil {
w.icmpForwarder.Close()
}
if w.udpForwarder != nil {
_ = w.udpForwarder.Close()
}
w.stack.Close()
for _, endpoint := range w.stack.CleanupEndpoints() {
endpoint.Abort()
}
w.stack.Wait()
err = E.Errors(w.stack.Close(), w.memoryTun.Close())
})
return nil
return err
}
func (w *stackDevice) BatchSize() int {
return 1
}
var _ stack.LinkEndpoint = (*wireEndpoint)(nil)
type wireEndpoint stackDevice
func (ep *wireEndpoint) MTU() uint32 {
return ep.mtu
}
func (ep *wireEndpoint) SetMTU(mtu uint32) {
}
func (ep *wireEndpoint) MaxHeaderLength() uint16 {
return 0
}
func (ep *wireEndpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
func (ep *wireEndpoint) SetLinkAddress(addr tcpip.LinkAddress) {
}
func (ep *wireEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityRXChecksumOffload
}
func (ep *wireEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
ep.dispatcher = dispatcher
}
func (ep *wireEndpoint) IsAttached() bool {
return ep.dispatcher != nil
}
func (ep *wireEndpoint) Wait() {
}
func (ep *wireEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
func (ep *wireEndpoint) AddHeader(buffer *stack.PacketBuffer) {
}
func (ep *wireEndpoint) ParseHeader(ptr *stack.PacketBuffer) bool {
return true
}
func (ep *wireEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
for _, packetBuffer := range list.AsSlice() {
packetBuffer.IncRef()
select {
case <-ep.done:
return 0, &tcpip.ErrClosedForSend{}
case ep.outbound <- packetBuffer:
}
}
return list.Len(), nil
}
func (ep *wireEndpoint) Close() {
}
func (ep *wireEndpoint) SetOnCloseAction(f func()) {
return conn.IdealBatchSize
}
-79
View File
@@ -1,79 +0,0 @@
//go:build with_gvisor
package wireguard
import (
"context"
"errors"
"fmt"
"net"
"net/netip"
"time"
"github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/adapters/gonet"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/waiter"
"github.com/sagernet/sing-tun"
M "github.com/sagernet/sing/common/metadata"
)
func DialTCPWithBind(ctx context.Context, s *stack.Stack, localAddr, remoteAddr tcpip.FullAddress, network tcpip.NetworkProtocolNumber) (*gonet.TCPConn, error) {
// Create TCP endpoint, then connect.
var wq waiter.Queue
ep, err := s.NewEndpoint(tcp.ProtocolNumber, network, &wq)
if err != nil {
return nil, errors.New(err.String())
}
// Create wait queue entry that notifies a channel.
//
// We do this unconditionally as Connect will always return an error.
waitEntry, notifyCh := waiter.NewChannelEntry(waiter.WritableEvents)
wq.EventRegister(&waitEntry)
defer wq.EventUnregister(&waitEntry)
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
// Bind before connect if requested.
if localAddr != (tcpip.FullAddress{}) {
if err = ep.Bind(localAddr); err != nil {
return nil, fmt.Errorf("ep.Bind(%+v) = %s", localAddr, err)
}
}
err = ep.Connect(remoteAddr)
if _, ok := err.(*tcpip.ErrConnectStarted); ok {
select {
case <-ctx.Done():
ep.Close()
return nil, ctx.Err()
case <-notifyCh:
}
err = ep.LastError()
}
if err != nil {
ep.Close()
return nil, &net.OpError{
Op: "connect",
Net: "tcp",
Addr: M.SocksaddrFromNetIP(netip.AddrPortFrom(tun.AddrFromAddress(remoteAddr.Addr), remoteAddr.Port)).TCPAddr(),
Err: errors.New(err.String()),
}
}
// sing-box added: set keepalive
ep.SocketOptions().SetKeepAlive(true)
keepAliveIdle := tcpip.KeepaliveIdleOption(15 * time.Second)
ep.SetSockOpt(&keepAliveIdle)
keepAliveInterval := tcpip.KeepaliveIntervalOption(15 * time.Second)
ep.SetSockOpt(&keepAliveInterval)
return gonet.NewTCPConn(&wq, ep), nil
}
-13
View File
@@ -1,13 +0,0 @@
//go:build !with_gvisor
package wireguard
import "github.com/sagernet/sing-tun"
func newStackDevice(options DeviceOptions) (Device, error) {
return nil, tun.ErrGVisorNotIncluded
}
func newSystemStackDevice(options DeviceOptions) (Device, error) {
return nil, tun.ErrGVisorNotIncluded
}
+1 -16
View File
@@ -35,22 +35,7 @@ func newSystemDevice(options DeviceOptions) (*systemDevice, error) {
if options.Name == "" {
options.Name = tun.CalculateInterfaceName("wg")
}
var inet4Address netip.Addr
var inet6Address netip.Addr
if len(options.Address) > 0 {
if prefix := common.Find(options.Address, func(it netip.Prefix) bool {
return it.Addr().Is4()
}); prefix.IsValid() {
inet4Address = prefix.Addr()
}
}
if len(options.Address) > 0 {
if prefix := common.Find(options.Address, func(it netip.Prefix) bool {
return it.Addr().Is6()
}); prefix.IsValid() {
inet6Address = prefix.Addr()
}
}
inet4Address, inet6Address := deviceAddresses(options.Address)
return &systemDevice{
options: options,
dialer: options.CreateDialer(options.Name),
+77 -201
View File
@@ -1,21 +1,13 @@
//go:build with_gvisor
package wireguard
import (
"net/netip"
"sync"
"sync/atomic"
"github.com/sagernet/gvisor/pkg/buffer"
"github.com/sagernet/gvisor/pkg/tcpip"
"github.com/sagernet/gvisor/pkg/tcpip/header"
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv4"
"github.com/sagernet/gvisor/pkg/tcpip/network/ipv6"
"github.com/sagernet/gvisor/pkg/tcpip/stack"
"github.com/sagernet/gvisor/pkg/tcpip/transport/icmp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/tcp"
"github.com/sagernet/gvisor/pkg/tcpip/transport/udp"
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing-tun/gtcpip/header"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/wireguard-go/device"
)
@@ -24,12 +16,10 @@ var _ Device = (*systemStackDevice)(nil)
type systemStackDevice struct {
*systemDevice
stack *stack.Stack
endpoint *deviceEndpoint
icmpForwarder *tun.ICMPForwarder
udpForwarder *tun.UDPForwarder
writeBufs [][]byte
closeOnce sync.Once
stack *tun.Go
memoryTun *tun.MemoryTun
device atomic.Pointer[device.Device]
closeOnce sync.Once
}
func newSystemStackDevice(options DeviceOptions) (*systemStackDevice, error) {
@@ -37,212 +27,98 @@ func newSystemStackDevice(options DeviceOptions) (*systemStackDevice, error) {
if err != nil {
return nil, err
}
endpoint := &deviceEndpoint{
mtu: options.MTU,
done: make(chan struct{}),
}
ipStack, err := tun.NewGVisorStackWithOptions(endpoint, stack.NICOptions{}, true)
stackDevice := &systemStackDevice{systemDevice: system}
stackDevice.memoryTun = tun.NewMemoryTun(tun.MemoryTunOptions{MTU: int(options.MTU), Outbound: stackDevice.inputPackets})
stackDevice.stack, err = newStack(options, stackDevice.memoryTun)
if err != nil {
return nil, err
}
for _, prefix := range options.Address {
addr := tun.AddressFromAddr(prefix.Addr())
protoAddr := tcpip.ProtocolAddress{
AddressWithPrefix: tcpip.AddressWithPrefix{
Address: addr,
PrefixLen: prefix.Bits(),
},
}
if prefix.Addr().Is4() {
protoAddr.Protocol = ipv4.ProtocolNumber
} else {
protoAddr.Protocol = ipv6.ProtocolNumber
}
gErr := ipStack.AddProtocolAddress(tun.DefaultNIC, protoAddr, stack.AddressProperties{})
if gErr != nil {
return nil, E.New("parse local address ", protoAddr.AddressWithPrefix, ": ", gErr.String())
}
}
stackDevice := &systemStackDevice{
systemDevice: system,
stack: ipStack,
endpoint: endpoint,
}
if options.Handler != nil {
ipStack.SetTransportProtocolHandler(tcp.ProtocolNumber, tun.NewTCPForwarder(options.Context, ipStack, options.Handler).HandlePacket)
udpForwarder := tun.NewUDPForwarder(options.Context, ipStack, options.Handler, tun.UDPNatOptions{
Timeout: options.UDPTimeout,
Shared: true,
Mapping: options.UDPMapping,
Filtering: options.UDPFiltering,
MaxSize: options.UDPNATMax,
InterfaceFinder: options.InterfaceFinder,
ExcludeInterface: []string{options.Name},
})
ipStack.SetTransportProtocolHandler(udp.ProtocolNumber, udpForwarder.HandlePacket)
stackDevice.udpForwarder = udpForwarder
icmpForwarder := tun.NewICMPForwarder(ipStack, options.Handler, options.Logger)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber4, icmpForwarder.HandlePacket)
ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket)
stackDevice.icmpForwarder = icmpForwarder
}
return stackDevice, nil
}
func (w *systemStackDevice) SetDevice(device *device.Device) {
w.endpoint.device = device
w.device.Store(device)
}
func (w *systemStackDevice) Start() error {
if w.udpForwarder != nil {
err := w.udpForwarder.Start()
if err != nil {
return err
}
err := w.stack.Start()
if err != nil {
return err
}
err := w.systemDevice.Start()
if err != nil && w.udpForwarder != nil {
_ = w.udpForwarder.Close()
err = w.systemDevice.Start()
if err != nil {
w.stack.Close()
}
return err
}
func (w *systemStackDevice) Write(bufs [][]byte, offset int) (count int, err error) {
if w.batchDevice != nil {
w.writeBufs = w.writeBufs[:0]
for _, packet := range bufs {
if !w.writeStack(packet[offset:]) {
w.writeBufs = append(w.writeBufs, packet)
}
}
if len(w.writeBufs) > 0 {
return w.batchDevice.BatchWrite(w.writeBufs, offset)
}
} else {
for _, packet := range bufs {
if !w.writeStack(packet[offset:]) {
if tun.PacketOffset > 0 {
clear(packet[offset-tun.PacketOffset : offset])
tun.PacketFillHeader(packet[offset-tun.PacketOffset:], tun.PacketIPVersion(packet[offset:]))
}
_, err = w.device.Write(packet[offset-tun.PacketOffset:])
}
if err != nil {
return
}
func (w *systemStackDevice) Write(bufs [][]byte, offset int) (int, error) {
stackPackets := make([][]byte, 0, len(bufs))
systemPackets := make([][]byte, 0, len(bufs))
for _, packet := range bufs {
if w.isLocalDestination(packet[offset:]) {
systemPackets = append(systemPackets, packet)
} else {
stackPackets = append(stackPackets, packet[offset:])
}
}
// WireGuard will not read count
return
if len(stackPackets) > 0 {
_, err := w.memoryTun.WritePackets(stackPackets)
if err != nil {
return 0, err
}
}
if len(systemPackets) > 0 {
return w.systemDevice.Write(systemPackets, offset)
}
return len(bufs), nil
}
func (w *systemStackDevice) isLocalDestination(packet []byte) bool {
var destination netip.Addr
switch header.IPVersion(packet) {
case header.IPv4Version:
destination = header.IPv4(packet).DestinationAddr()
case header.IPv6Version:
destination = header.IPv6(packet).DestinationAddr()
default:
return true
}
for _, prefix := range w.options.Address {
if prefix.Contains(destination) {
return true
}
}
return false
}
func (w *systemStackDevice) inputPackets(packets []*buf.Buffer) error {
defer buf.ReleaseMulti(packets)
wgDevice := w.device.Load()
if wgDevice == nil {
return nil
}
references := make([]*device.InputPacketRef, 0, len(packets))
for _, packet := range packets {
var destination []byte
switch header.IPVersion(packet.Bytes()) {
case header.IPv4Version:
destination = header.IPv4(packet.Bytes()).DestinationAddressSlice()
case header.IPv6Version:
destination = header.IPv6(packet.Bytes()).DestinationAddressSlice()
default:
continue
}
references = append(references, &device.InputPacketRef{Destination: destination, PacketSlices: [][]byte{packet.Bytes()}})
}
wgDevice.InputPackets(references)
return nil
}
func (w *systemStackDevice) Close() error {
var err error
w.closeOnce.Do(func() {
close(w.endpoint.done)
if w.icmpForwarder != nil {
w.icmpForwarder.Close()
}
if w.udpForwarder != nil {
_ = w.udpForwarder.Close()
}
w.stack.Close()
for _, endpoint := range w.stack.CleanupEndpoints() {
endpoint.Abort()
}
w.stack.Wait()
err = w.systemDevice.Close()
err = E.Errors(w.stack.Close(), w.memoryTun.Close(), w.systemDevice.Close())
})
return err
}
func (w *systemStackDevice) writeStack(packet []byte) bool {
var (
networkProtocol tcpip.NetworkProtocolNumber
destination netip.Addr
)
switch header.IPVersion(packet) {
case header.IPv4Version:
networkProtocol = header.IPv4ProtocolNumber
destination = netip.AddrFrom4(header.IPv4(packet).DestinationAddress().As4())
case header.IPv6Version:
networkProtocol = header.IPv6ProtocolNumber
destination = netip.AddrFrom16(header.IPv6(packet).DestinationAddress().As16())
}
for _, prefix := range w.options.Address {
if prefix.Contains(destination) {
return false
}
}
packetBuffer := stack.NewPacketBuffer(stack.PacketBufferOptions{
Payload: buffer.MakeWithData(packet),
})
w.endpoint.dispatcher.DeliverNetworkPacket(networkProtocol, packetBuffer)
packetBuffer.DecRef()
return true
}
type deviceEndpoint struct {
mtu uint32
done chan struct{}
device *device.Device
dispatcher stack.NetworkDispatcher
}
func (ep *deviceEndpoint) MTU() uint32 {
return ep.mtu
}
func (ep *deviceEndpoint) SetMTU(mtu uint32) {
}
func (ep *deviceEndpoint) MaxHeaderLength() uint16 {
return 0
}
func (ep *deviceEndpoint) LinkAddress() tcpip.LinkAddress {
return ""
}
func (ep *deviceEndpoint) SetLinkAddress(addr tcpip.LinkAddress) {
}
func (ep *deviceEndpoint) Capabilities() stack.LinkEndpointCapabilities {
return stack.CapabilityRXChecksumOffload
}
func (ep *deviceEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
ep.dispatcher = dispatcher
}
func (ep *deviceEndpoint) IsAttached() bool {
return ep.dispatcher != nil
}
func (ep *deviceEndpoint) Wait() {
}
func (ep *deviceEndpoint) ARPHardwareType() header.ARPHardwareType {
return header.ARPHardwareNone
}
func (ep *deviceEndpoint) AddHeader(buffer *stack.PacketBuffer) {
}
func (ep *deviceEndpoint) ParseHeader(ptr *stack.PacketBuffer) bool {
return true
}
func (ep *deviceEndpoint) WritePackets(list stack.PacketBufferList) (int, tcpip.Error) {
for _, packetBuffer := range list.AsSlice() {
destination := packetBuffer.Network().DestinationAddress()
ep.device.InputPacket(destination.AsSlice(), packetBuffer.AsSlices())
}
return list.Len(), nil
}
func (ep *deviceEndpoint) Close() {
}
func (ep *deviceEndpoint) SetOnCloseAction(f func()) {
}
+17 -11
View File
@@ -106,6 +106,16 @@ func NewEndpoint(options EndpointOptions) (*Endpoint, error) {
if options.MTU == 0 {
options.MTU = 1408
}
return &Endpoint{
options: options,
peers: peers,
ipcConf: ipcConf,
allowedAddress: allowedAddresses,
}, nil
}
func (e *Endpoint) Initialize(memoryPressure func() tun.MemoryPressure) error {
options := e.options
deviceOptions := DeviceOptions{
Context: options.Context,
Logger: options.Logger,
@@ -117,24 +127,20 @@ func NewEndpoint(options EndpointOptions) (*Endpoint, error) {
UDPFiltering: options.UDPFiltering,
UDPNATMax: options.UDPNATMax,
InterfaceFinder: options.InterfaceFinder,
MemoryPressure: memoryPressure,
CreateDialer: options.CreateDialer,
Name: options.Name,
MTU: options.MTU,
Address: options.Address,
AllowedAddress: allowedAddresses,
AllowedAddress: e.allowedAddress,
}
tunDevice, err := NewDevice(deviceOptions)
if err != nil {
return nil, E.Cause(err, "create WireGuard device")
return E.Cause(err, "create WireGuard device")
}
return &Endpoint{
options: options,
peers: peers,
ipcConf: ipcConf,
allowedAddress: allowedAddresses,
tunDevice: tunDevice,
returnDevice: &returnDeviceWrapper{Device: tunDevice},
}, nil
e.tunDevice = tunDevice
e.returnDevice = &returnDeviceWrapper{Device: tunDevice}
return nil
}
func (e *Endpoint) Start(postStart bool) error {
@@ -343,7 +349,7 @@ func (e *Endpoint) Close() error {
if wgDevice != nil {
return nil
}
return e.tunDevice.Close()
return common.Close(e.tunDevice)
}
func (e *Endpoint) Lookup(address netip.Addr) *device.Peer {