diff --git a/.golangci.yml b/.golangci.yml index 9196b7dbe..10f422eb6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,7 +2,6 @@ version: "2" run: go: "1.24" build-tags: - - with_gvisor - with_quic - with_dhcp - with_wireguard diff --git a/cmd/internal/build_libbox/main.go b/cmd/internal/build_libbox/main.go index 5b2d9e28f..626d221b8 100644 --- a/cmd/internal/build_libbox/main.go +++ b/cmd/internal/build_libbox/main.go @@ -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") diff --git a/docs/configuration/outbound/wireguard.md b/docs/configuration/outbound/wireguard.md index 648ba6072..a49d9a58c 100644 --- a/docs/configuration/outbound/wireguard.md +++ b/docs/configuration/outbound/wireguard.md @@ -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. diff --git a/docs/configuration/outbound/wireguard.zh.md b/docs/configuration/outbound/wireguard.zh.md index 2b6d4a0aa..8caf34b1c 100644 --- a/docs/configuration/outbound/wireguard.zh.md +++ b/docs/configuration/outbound/wireguard.zh.md @@ -64,8 +64,6 @@ icon: material/delete-clock 需要特权且不能与已有系统接口冲突。 -如果 gVisor 未包含在构建中,则强制执行。 - #### interface_name 为系统接口自定义设备名称。 diff --git a/docs/installation/build-from-source.md b/docs/installation/build-from-source.md index 413b77262..4f11ff87a 100644 --- a/docs/installation/build-from-source.md +++ b/docs/installation/build-from-source.md @@ -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. | diff --git a/docs/installation/build-from-source.zh.md b/docs/installation/build-from-source.zh.md index c99a714c2..5c3f141b4 100644 --- a/docs/installation/build-from-source.zh.md +++ b/docs/installation/build-from-source.zh.md @@ -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 服务支持。 | diff --git a/experimental/libbox/ffi.json b/experimental/libbox/ffi.json deleted file mode 100644 index 90d9bd59f..000000000 --- a/experimental/libbox/ffi.json +++ /dev/null @@ -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" - ] - } - } - ] - } - ] -} diff --git a/go.mod b/go.mod index c51752137..51a9d85c2 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 084ba7b4f..f6939e8d4 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/include/openconnect_stub.go b/include/openconnect_stub.go index 360ed8d9b..e3092a1bf 100644 --- a/include/openconnect_stub.go +++ b/include/openconnect_stub.go @@ -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`) }) } diff --git a/include/openvpn_stub.go b/include/openvpn_stub.go index a1e24c8e7..b96a3dfa0 100644 --- a/include/openvpn_stub.go +++ b/include/openvpn_stub.go @@ -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`) }) } diff --git a/protocol/openconnect/client.go b/protocol/openconnect/client.go index 5665fdc91..471bc0ad6 100644 --- a/protocol/openconnect/client.go +++ b/protocol/openconnect/client.go @@ -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 { diff --git a/protocol/openvpn/client.go b/protocol/openvpn/client.go index cb5b79a4d..fff545173 100644 --- a/protocol/openvpn/client.go +++ b/protocol/openvpn/client.go @@ -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 { diff --git a/protocol/openvpn/server.go b/protocol/openvpn/server.go index 269b7a9e9..8ca813cf1 100644 --- a/protocol/openvpn/server.go +++ b/protocol/openvpn/server.go @@ -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 }, diff --git a/protocol/tailscale/certificate.go b/protocol/tailscale/certificate.go index ebf084f39..2ea89e45e 100644 --- a/protocol/tailscale/certificate.go +++ b/protocol/tailscale/certificate.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailscale diff --git a/protocol/tailscale/certificate_provider.go b/protocol/tailscale/certificate_provider.go index 30db9593f..d56d9e1b4 100644 --- a/protocol/tailscale/certificate_provider.go +++ b/protocol/tailscale/certificate_provider.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailscale diff --git a/protocol/tailscale/dns_transport.go b/protocol/tailscale/dns_transport.go index cd6b2964c..859f819cf 100644 --- a/protocol/tailscale/dns_transport.go +++ b/protocol/tailscale/dns_transport.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailscale diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index 1de0197ad..e536beec9 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -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 diff --git a/protocol/tailscale/hostinfo_tvos.go b/protocol/tailscale/hostinfo_tvos.go index d8e391bb5..618d5ba9d 100644 --- a/protocol/tailscale/hostinfo_tvos.go +++ b/protocol/tailscale/hostinfo_tvos.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && tvos +//go:build with_tailscale && tvos package tailscale diff --git a/protocol/tailscale/ping.go b/protocol/tailscale/ping.go index 5d046a910..4fbeeafc1 100644 --- a/protocol/tailscale/ping.go +++ b/protocol/tailscale/ping.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailscale diff --git a/protocol/tailscale/port.go b/protocol/tailscale/port.go index 488165a40..0483ef2ea 100644 --- a/protocol/tailscale/port.go +++ b/protocol/tailscale/port.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailscale diff --git a/protocol/tailscale/status.go b/protocol/tailscale/status.go index bd1630edf..b9726a7db 100644 --- a/protocol/tailscale/status.go +++ b/protocol/tailscale/status.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailscale diff --git a/protocol/tailscale/taildrop.go b/protocol/tailscale/taildrop.go index 78359a6bf..242d42f2f 100644 --- a/protocol/tailscale/taildrop.go +++ b/protocol/tailscale/taildrop.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailscale diff --git a/protocol/tailscale/taildrop_send.go b/protocol/tailscale/taildrop_send.go index cdd7d112d..cc71f49d2 100644 --- a/protocol/tailscale/taildrop_send.go +++ b/protocol/tailscale/taildrop_send.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailscale diff --git a/protocol/tailscale/tailssh/recording.go b/protocol/tailscale/tailssh/recording.go index 9077eaa65..f854cab0a 100644 --- a/protocol/tailscale/tailssh/recording.go +++ b/protocol/tailscale/tailssh/recording.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailssh diff --git a/protocol/tailscale/tailssh/server.go b/protocol/tailscale/tailssh/server.go index 6cc49dffc..7987477eb 100644 --- a/protocol/tailscale/tailssh/server.go +++ b/protocol/tailscale/tailssh/server.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailssh diff --git a/protocol/tailscale/tailssh/server_helper_unix.go b/protocol/tailscale/tailssh/server_helper_unix.go index d595d0240..006816427 100644 --- a/protocol/tailscale/tailssh/server_helper_unix.go +++ b/protocol/tailscale/tailssh/server_helper_unix.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && !windows +//go:build with_tailscale && !windows package tailssh diff --git a/protocol/tailscale/tailssh/server_helper_windows.go b/protocol/tailscale/tailssh/server_helper_windows.go index 4792fc711..768e256b7 100644 --- a/protocol/tailscale/tailssh/server_helper_windows.go +++ b/protocol/tailscale/tailssh/server_helper_windows.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && windows +//go:build with_tailscale && windows package tailssh diff --git a/protocol/tailscale/tailssh/session.go b/protocol/tailscale/tailssh/session.go index 9afe45f23..bfa053ebd 100644 --- a/protocol/tailscale/tailssh/session.go +++ b/protocol/tailscale/tailssh/session.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailssh diff --git a/protocol/tailscale/tailssh/session_android.go b/protocol/tailscale/tailssh/session_android.go index e2c73554a..4a666eb5c 100644 --- a/protocol/tailscale/tailssh/session_android.go +++ b/protocol/tailscale/tailssh/session_android.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && android +//go:build with_tailscale && android package tailssh diff --git a/protocol/tailscale/tailssh/session_ios.go b/protocol/tailscale/tailssh/session_ios.go index 522ad2bb1..fcc0250cb 100644 --- a/protocol/tailscale/tailssh/session_ios.go +++ b/protocol/tailscale/tailssh/session_ios.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && ios +//go:build with_tailscale && ios package tailssh diff --git a/protocol/tailscale/tailssh/session_platform.go b/protocol/tailscale/tailssh/session_platform.go index b613362cf..e139e99ed 100644 --- a/protocol/tailscale/tailssh/session_platform.go +++ b/protocol/tailscale/tailssh/session_platform.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && !windows +//go:build with_tailscale && !windows package tailssh diff --git a/protocol/tailscale/tailssh/session_unix.go b/protocol/tailscale/tailssh/session_unix.go index dc9d5be77..b50ceadac 100644 --- a/protocol/tailscale/tailssh/session_unix.go +++ b/protocol/tailscale/tailssh/session_unix.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && unix && !android && !ios +//go:build with_tailscale && unix && !android && !ios package tailssh diff --git a/protocol/tailscale/tailssh/session_windows.go b/protocol/tailscale/tailssh/session_windows.go index 4d07f7290..b9f0654b6 100644 --- a/protocol/tailscale/tailssh/session_windows.go +++ b/protocol/tailscale/tailssh/session_windows.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && windows +//go:build with_tailscale && windows package tailssh diff --git a/protocol/tailscale/tailssh/switch_user_linux.go b/protocol/tailscale/tailssh/switch_user_linux.go index 75441d9da..5acec8c2d 100644 --- a/protocol/tailscale/tailssh/switch_user_linux.go +++ b/protocol/tailscale/tailssh/switch_user_linux.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && linux +//go:build with_tailscale && linux package tailssh diff --git a/protocol/tailscale/tailssh/switch_user_other.go b/protocol/tailscale/tailssh/switch_user_other.go index 2e02ced54..4d61a112a 100644 --- a/protocol/tailscale/tailssh/switch_user_other.go +++ b/protocol/tailscale/tailssh/switch_user_other.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && !windows && !linux +//go:build with_tailscale && !windows && !linux package tailssh diff --git a/protocol/tailscale/tailssh/user.go b/protocol/tailscale/tailssh/user.go index a9a9b219b..337b8f6e8 100644 --- a/protocol/tailscale/tailssh/user.go +++ b/protocol/tailscale/tailssh/user.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package tailssh diff --git a/protocol/tailscale/tailssh/user_android.go b/protocol/tailscale/tailssh/user_android.go index 47dfd7e38..966049a92 100644 --- a/protocol/tailscale/tailssh/user_android.go +++ b/protocol/tailscale/tailssh/user_android.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && android +//go:build with_tailscale && android package tailssh diff --git a/protocol/tailscale/tailssh/user_unix.go b/protocol/tailscale/tailssh/user_unix.go index b53cdf44f..6750d67e6 100644 --- a/protocol/tailscale/tailssh/user_unix.go +++ b/protocol/tailscale/tailssh/user_unix.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && !windows && !android +//go:build with_tailscale && !windows && !android package tailssh diff --git a/protocol/tailscale/tailssh/user_windows.go b/protocol/tailscale/tailssh/user_windows.go index 31eb6ace3..d0cbd0ab0 100644 --- a/protocol/tailscale/tailssh/user_windows.go +++ b/protocol/tailscale/tailssh/user_windows.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && windows +//go:build with_tailscale && windows package tailssh diff --git a/protocol/tailscale/tun_device_unix.go b/protocol/tailscale/tun_device_unix.go index 6281da0b5..60790db55 100644 --- a/protocol/tailscale/tun_device_unix.go +++ b/protocol/tailscale/tun_device_unix.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && !windows +//go:build with_tailscale && !windows package tailscale diff --git a/protocol/tailscale/tun_device_windows.go b/protocol/tailscale/tun_device_windows.go index 37f813cb1..19e317a42 100644 --- a/protocol/tailscale/tun_device_windows.go +++ b/protocol/tailscale/tun_device_windows.go @@ -1,4 +1,4 @@ -//go:build with_gvisor && windows +//go:build with_tailscale && windows package tailscale diff --git a/protocol/wireguard/endpoint.go b/protocol/wireguard/endpoint.go index f608d04cd..bc1d09f27 100644 --- a/protocol/wireguard/endpoint.go +++ b/protocol/wireguard/endpoint.go @@ -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) diff --git a/release/DEFAULT_BUILD_TAGS b/release/DEFAULT_BUILD_TAGS index a588309b2..d06c56971 100644 --- a/release/DEFAULT_BUILD_TAGS +++ b/release/DEFAULT_BUILD_TAGS @@ -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 diff --git a/release/DEFAULT_BUILD_TAGS_OTHERS b/release/DEFAULT_BUILD_TAGS_OTHERS index 60b1be3eb..fd06571fd 100644 --- a/release/DEFAULT_BUILD_TAGS_OTHERS +++ b/release/DEFAULT_BUILD_TAGS_OTHERS @@ -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 diff --git a/release/DEFAULT_BUILD_TAGS_WINDOWS b/release/DEFAULT_BUILD_TAGS_WINDOWS index 6a5d4d03f..e4964a1d6 100644 --- a/release/DEFAULT_BUILD_TAGS_WINDOWS +++ b/release/DEFAULT_BUILD_TAGS_WINDOWS @@ -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 diff --git a/service/derp/service.go b/service/derp/service.go index 923fbf7fc..5761a2bd3 100644 --- a/service/derp/service.go +++ b/service/derp/service.go @@ -1,4 +1,4 @@ -//go:build with_gvisor +//go:build with_tailscale package derp diff --git a/service/oomkiller/service.go b/service/oomkiller/service.go index 0fbb313de..27b4ca278 100644 --- a/service/oomkiller/service.go +++ b/service/oomkiller/service.go @@ -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()) } diff --git a/test/endpoint_udp_nat_test.go b/test/endpoint_udp_nat_test.go index b8851043d..df9a54cf9 100644 --- a/test/endpoint_udp_nat_test.go +++ b/test/endpoint_udp_nat_test.go @@ -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 diff --git a/test/go.mod b/test/go.mod index ecbffe1f6..10245160e 100644 --- a/test/go.mod +++ b/test/go.mod @@ -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 diff --git a/test/go.sum b/test/go.sum index 57a78f604..96d448aab 100644 --- a/test/go.sum +++ b/test/go.sum @@ -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= diff --git a/test/openconnect_test.go b/test/openconnect_test.go index 5ec905b3e..479df18c3 100644 --- a/test/openconnect_test.go +++ b/test/openconnect_test.go @@ -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, diff --git a/test/wireguard_test.go b/test/wireguard_test.go new file mode 100644 index 000000000..b99c3516b --- /dev/null +++ b/test/wireguard_test.go @@ -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]) + } + }) + } + } + }) + } +} diff --git a/transport/openconnect/device.go b/transport/openconnect/device.go index 7a2b18fe6..01cbb352b 100644 --- a/transport/openconnect/device.go +++ b/transport/openconnect/device.go @@ -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] diff --git a/transport/openconnect/device_stack.go b/transport/openconnect/device_stack.go index 8c48184d7..05d8de820 100644 --- a/transport/openconnect/device_stack.go +++ b/transport/openconnect/device_stack.go @@ -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 } diff --git a/transport/openconnect/device_stack_stub.go b/transport/openconnect/device_stack_stub.go deleted file mode 100644 index 1a96524c3..000000000 --- a/transport/openconnect/device_stack_stub.go +++ /dev/null @@ -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") -} diff --git a/transport/openconnect/device_system_stack.go b/transport/openconnect/device_system_stack.go index d97fbb0c5..3deafb399 100644 --- a/transport/openconnect/device_system_stack.go +++ b/transport/openconnect/device_system_stack.go @@ -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 diff --git a/transport/openvpn/device.go b/transport/openvpn/device.go index 570d8fac6..7d251a516 100644 --- a/transport/openvpn/device.go +++ b/transport/openvpn/device.go @@ -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] diff --git a/transport/openvpn/device_stack.go b/transport/openvpn/device_stack.go index 16e8dfef3..feec7b9ce 100644 --- a/transport/openvpn/device_stack.go +++ b/transport/openvpn/device_stack.go @@ -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 } diff --git a/transport/openvpn/device_stack_stub.go b/transport/openvpn/device_stack_stub.go deleted file mode 100644 index a9aa08061..000000000 --- a/transport/openvpn/device_stack_stub.go +++ /dev/null @@ -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") -} diff --git a/transport/openvpn/device_system_stack.go b/transport/openvpn/device_system_stack.go index 41cad6585..fedbdb906 100644 --- a/transport/openvpn/device_system_stack.go +++ b/transport/openvpn/device_system_stack.go @@ -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 diff --git a/transport/wireguard/device.go b/transport/wireguard/device.go index 37575e4d7..0dc4ac575 100644 --- a/transport/wireguard/device.go +++ b/transport/wireguard/device.go @@ -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() } diff --git a/transport/wireguard/device_stack.go b/transport/wireguard/device_stack.go index 3321ef8f0..cca3231d9 100644 --- a/transport/wireguard/device_stack.go +++ b/transport/wireguard/device_stack.go @@ -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 } diff --git a/transport/wireguard/device_stack_gonet.go b/transport/wireguard/device_stack_gonet.go deleted file mode 100644 index bc695205b..000000000 --- a/transport/wireguard/device_stack_gonet.go +++ /dev/null @@ -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 -} diff --git a/transport/wireguard/device_stack_stub.go b/transport/wireguard/device_stack_stub.go deleted file mode 100644 index ea413559e..000000000 --- a/transport/wireguard/device_stack_stub.go +++ /dev/null @@ -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 -} diff --git a/transport/wireguard/device_system.go b/transport/wireguard/device_system.go index 7ce992a05..698c4de9c 100644 --- a/transport/wireguard/device_system.go +++ b/transport/wireguard/device_system.go @@ -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), diff --git a/transport/wireguard/device_system_stack.go b/transport/wireguard/device_system_stack.go index ae92235b4..90bfe4ba0 100644 --- a/transport/wireguard/device_system_stack.go +++ b/transport/wireguard/device_system_stack.go @@ -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()) { -} diff --git a/transport/wireguard/endpoint.go b/transport/wireguard/endpoint.go index 828b97efd..50ff0b395 100644 --- a/transport/wireguard/endpoint.go +++ b/transport/wireguard/endpoint.go @@ -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 {