diff --git a/common/badtls/badtls.go b/common/badtls/badtls.go deleted file mode 100644 index c5c55e3c..00000000 --- a/common/badtls/badtls.go +++ /dev/null @@ -1,233 +0,0 @@ -//go:build go1.20 && !go1.21 - -package badtls - -import ( - "crypto/cipher" - "crypto/rand" - "crypto/tls" - "encoding/binary" - "io" - "net" - "reflect" - "sync" - "sync/atomic" - "unsafe" - - "github.com/sagernet/sing-box/log" - "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/buf" - "github.com/sagernet/sing/common/bufio" - E "github.com/sagernet/sing/common/exceptions" - N "github.com/sagernet/sing/common/network" - aTLS "github.com/sagernet/sing/common/tls" -) - -type Conn struct { - *tls.Conn - writer N.ExtendedWriter - isHandshakeComplete *atomic.Bool - activeCall *atomic.Int32 - closeNotifySent *bool - version *uint16 - rand io.Reader - halfAccess *sync.Mutex - halfError *error - cipher cipher.AEAD - explicitNonceLen int - halfPtr uintptr - halfSeq []byte - halfScratchBuf []byte -} - -func TryCreate(conn aTLS.Conn) aTLS.Conn { - tlsConn, ok := conn.(*tls.Conn) - if !ok { - return conn - } - badConn, err := Create(tlsConn) - if err != nil { - log.Warn("initialize badtls: ", err) - return conn - } - return badConn -} - -func Create(conn *tls.Conn) (aTLS.Conn, error) { - rawConn := reflect.Indirect(reflect.ValueOf(conn)) - rawIsHandshakeComplete := rawConn.FieldByName("isHandshakeComplete") - if !rawIsHandshakeComplete.IsValid() || rawIsHandshakeComplete.Kind() != reflect.Struct { - return nil, E.New("badtls: invalid isHandshakeComplete") - } - isHandshakeComplete := (*atomic.Bool)(unsafe.Pointer(rawIsHandshakeComplete.UnsafeAddr())) - if !isHandshakeComplete.Load() { - return nil, E.New("handshake not finished") - } - rawActiveCall := rawConn.FieldByName("activeCall") - if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Struct { - return nil, E.New("badtls: invalid active call") - } - activeCall := (*atomic.Int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr())) - rawHalfConn := rawConn.FieldByName("out") - if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct { - return nil, E.New("badtls: invalid half conn") - } - rawVersion := rawConn.FieldByName("vers") - if !rawVersion.IsValid() || rawVersion.Kind() != reflect.Uint16 { - return nil, E.New("badtls: invalid version") - } - version := (*uint16)(unsafe.Pointer(rawVersion.UnsafeAddr())) - rawCloseNotifySent := rawConn.FieldByName("closeNotifySent") - if !rawCloseNotifySent.IsValid() || rawCloseNotifySent.Kind() != reflect.Bool { - return nil, E.New("badtls: invalid notify") - } - closeNotifySent := (*bool)(unsafe.Pointer(rawCloseNotifySent.UnsafeAddr())) - rawConfig := reflect.Indirect(rawConn.FieldByName("config")) - if !rawConfig.IsValid() || rawConfig.Kind() != reflect.Struct { - return nil, E.New("badtls: bad config") - } - config := (*tls.Config)(unsafe.Pointer(rawConfig.UnsafeAddr())) - randReader := config.Rand - if randReader == nil { - randReader = rand.Reader - } - rawHalfMutex := rawHalfConn.FieldByName("Mutex") - if !rawHalfMutex.IsValid() || rawHalfMutex.Kind() != reflect.Struct { - return nil, E.New("badtls: invalid half mutex") - } - halfAccess := (*sync.Mutex)(unsafe.Pointer(rawHalfMutex.UnsafeAddr())) - rawHalfError := rawHalfConn.FieldByName("err") - if !rawHalfError.IsValid() || rawHalfError.Kind() != reflect.Interface { - return nil, E.New("badtls: invalid half error") - } - halfError := (*error)(unsafe.Pointer(rawHalfError.UnsafeAddr())) - rawHalfCipherInterface := rawHalfConn.FieldByName("cipher") - if !rawHalfCipherInterface.IsValid() || rawHalfCipherInterface.Kind() != reflect.Interface { - return nil, E.New("badtls: invalid cipher interface") - } - rawHalfCipher := rawHalfCipherInterface.Elem() - aeadCipher, loaded := valueInterface(rawHalfCipher, false).(cipher.AEAD) - if !loaded { - return nil, E.New("badtls: invalid AEAD cipher") - } - var explicitNonceLen int - switch cipherName := reflect.Indirect(rawHalfCipher).Type().String(); cipherName { - case "tls.prefixNonceAEAD": - explicitNonceLen = aeadCipher.NonceSize() - case "tls.xorNonceAEAD": - default: - return nil, E.New("badtls: unknown cipher type: ", cipherName) - } - rawHalfSeq := rawHalfConn.FieldByName("seq") - if !rawHalfSeq.IsValid() || rawHalfSeq.Kind() != reflect.Array { - return nil, E.New("badtls: invalid seq") - } - halfSeq := rawHalfSeq.Bytes() - rawHalfScratchBuf := rawHalfConn.FieldByName("scratchBuf") - if !rawHalfScratchBuf.IsValid() || rawHalfScratchBuf.Kind() != reflect.Array { - return nil, E.New("badtls: invalid scratchBuf") - } - halfScratchBuf := rawHalfScratchBuf.Bytes() - return &Conn{ - Conn: conn, - writer: bufio.NewExtendedWriter(conn.NetConn()), - isHandshakeComplete: isHandshakeComplete, - activeCall: activeCall, - closeNotifySent: closeNotifySent, - version: version, - halfAccess: halfAccess, - halfError: halfError, - cipher: aeadCipher, - explicitNonceLen: explicitNonceLen, - rand: randReader, - halfPtr: rawHalfConn.UnsafeAddr(), - halfSeq: halfSeq, - halfScratchBuf: halfScratchBuf, - }, nil -} - -func (c *Conn) WriteBuffer(buffer *buf.Buffer) error { - if buffer.Len() > maxPlaintext { - defer buffer.Release() - return common.Error(c.Write(buffer.Bytes())) - } - for { - x := c.activeCall.Load() - if x&1 != 0 { - return net.ErrClosed - } - if c.activeCall.CompareAndSwap(x, x+2) { - break - } - } - defer c.activeCall.Add(-2) - c.halfAccess.Lock() - defer c.halfAccess.Unlock() - if err := *c.halfError; err != nil { - return err - } - if *c.closeNotifySent { - return errShutdown - } - dataLen := buffer.Len() - dataBytes := buffer.Bytes() - outBuf := buffer.ExtendHeader(recordHeaderLen + c.explicitNonceLen) - outBuf[0] = 23 - version := *c.version - if version == 0 { - version = tls.VersionTLS10 - } else if version == tls.VersionTLS13 { - version = tls.VersionTLS12 - } - binary.BigEndian.PutUint16(outBuf[1:], version) - var nonce []byte - if c.explicitNonceLen > 0 { - nonce = outBuf[5 : 5+c.explicitNonceLen] - if c.explicitNonceLen < 16 { - copy(nonce, c.halfSeq) - } else { - if _, err := io.ReadFull(c.rand, nonce); err != nil { - return err - } - } - } - if len(nonce) == 0 { - nonce = c.halfSeq - } - if *c.version == tls.VersionTLS13 { - buffer.FreeBytes()[0] = 23 - binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen+1+c.cipher.Overhead())) - c.cipher.Seal(outBuf, nonce, outBuf[recordHeaderLen:recordHeaderLen+c.explicitNonceLen+dataLen+1], outBuf[:recordHeaderLen]) - buffer.Extend(1 + c.cipher.Overhead()) - } else { - binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen)) - additionalData := append(c.halfScratchBuf[:0], c.halfSeq...) - additionalData = append(additionalData, outBuf[:recordHeaderLen]...) - c.cipher.Seal(outBuf, nonce, dataBytes, additionalData) - buffer.Extend(c.cipher.Overhead()) - binary.BigEndian.PutUint16(outBuf[3:], uint16(dataLen+c.explicitNonceLen+c.cipher.Overhead())) - } - incSeq(c.halfPtr) - log.Trace("badtls write ", buffer.Len()) - return c.writer.WriteBuffer(buffer) -} - -func (c *Conn) FrontHeadroom() int { - return recordHeaderLen + c.explicitNonceLen -} - -func (c *Conn) RearHeadroom() int { - return 1 + c.cipher.Overhead() -} - -func (c *Conn) WriterMTU() int { - return maxPlaintext -} - -func (c *Conn) Upstream() any { - return c.Conn -} - -func (c *Conn) UpstreamWriter() any { - return c.NetConn() -} diff --git a/common/badtls/badtls_stub.go b/common/badtls/badtls_stub.go deleted file mode 100644 index 2f0028f6..00000000 --- a/common/badtls/badtls_stub.go +++ /dev/null @@ -1,14 +0,0 @@ -//go:build !go1.19 || go1.21 - -package badtls - -import ( - "crypto/tls" - "os" - - aTLS "github.com/sagernet/sing/common/tls" -) - -func Create(conn *tls.Conn) (aTLS.Conn, error) { - return nil, os.ErrInvalid -} diff --git a/common/badtls/link.go b/common/badtls/link.go deleted file mode 100644 index b8d5f4bd..00000000 --- a/common/badtls/link.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build go1.20 && !go.1.21 - -package badtls - -import ( - "reflect" - _ "unsafe" -) - -const ( - maxPlaintext = 16384 // maximum plaintext payload length - recordHeaderLen = 5 // record header length -) - -//go:linkname errShutdown crypto/tls.errShutdown -var errShutdown error - -//go:linkname incSeq crypto/tls.(*halfConn).incSeq -func incSeq(conn uintptr) - -//go:linkname valueInterface reflect.valueInterface -func valueInterface(v reflect.Value, safe bool) any diff --git a/common/badtls/read_wait.go b/common/badtls/read_wait.go new file mode 100644 index 00000000..4657bc5b --- /dev/null +++ b/common/badtls/read_wait.go @@ -0,0 +1,115 @@ +//go:build go1.21 && !without_badtls + +package badtls + +import ( + "bytes" + "os" + "reflect" + "sync" + "unsafe" + + "github.com/sagernet/sing/common/buf" + E "github.com/sagernet/sing/common/exceptions" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/tls" +) + +var _ N.ReadWaiter = (*ReadWaitConn)(nil) + +type ReadWaitConn struct { + *tls.STDConn + halfAccess *sync.Mutex + rawInput *bytes.Buffer + input *bytes.Reader + hand *bytes.Buffer + readWaitOptions N.ReadWaitOptions +} + +func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) { + stdConn, isSTDConn := conn.(*tls.STDConn) + if !isSTDConn { + return nil, os.ErrInvalid + } + rawConn := reflect.Indirect(reflect.ValueOf(stdConn)) + rawHalfConn := rawConn.FieldByName("in") + if !rawHalfConn.IsValid() || rawHalfConn.Kind() != reflect.Struct { + return nil, E.New("badtls: invalid half conn") + } + rawHalfMutex := rawHalfConn.FieldByName("Mutex") + if !rawHalfMutex.IsValid() || rawHalfMutex.Kind() != reflect.Struct { + return nil, E.New("badtls: invalid half mutex") + } + halfAccess := (*sync.Mutex)(unsafe.Pointer(rawHalfMutex.UnsafeAddr())) + rawRawInput := rawConn.FieldByName("rawInput") + if !rawRawInput.IsValid() || rawRawInput.Kind() != reflect.Struct { + return nil, E.New("badtls: invalid raw input") + } + rawInput := (*bytes.Buffer)(unsafe.Pointer(rawRawInput.UnsafeAddr())) + rawInput0 := rawConn.FieldByName("input") + if !rawInput0.IsValid() || rawInput0.Kind() != reflect.Struct { + return nil, E.New("badtls: invalid input") + } + input := (*bytes.Reader)(unsafe.Pointer(rawInput0.UnsafeAddr())) + rawHand := rawConn.FieldByName("hand") + if !rawHand.IsValid() || rawHand.Kind() != reflect.Struct { + return nil, E.New("badtls: invalid hand") + } + hand := (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr())) + return &ReadWaitConn{ + STDConn: stdConn, + halfAccess: halfAccess, + rawInput: rawInput, + input: input, + hand: hand, + }, nil +} + +func (c *ReadWaitConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) { + c.readWaitOptions = options + return false +} + +func (c *ReadWaitConn) WaitReadBuffer() (buffer *buf.Buffer, err error) { + err = c.Handshake() + if err != nil { + return + } + c.halfAccess.Lock() + defer c.halfAccess.Unlock() + for c.input.Len() == 0 { + err = tlsReadRecord(c.STDConn) + if err != nil { + return + } + for c.hand.Len() > 0 { + err = tlsHandlePostHandshakeMessage(c.STDConn) + if err != nil { + return + } + } + } + buffer = c.readWaitOptions.NewBuffer() + n, err := c.input.Read(buffer.FreeBytes()) + if err != nil { + buffer.Release() + return + } + buffer.Truncate(n) + + if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 && + // recordType(c.rawInput.Bytes()[0]) == recordTypeAlert { + c.rawInput.Bytes()[0] == 21 { + _ = tlsReadRecord(c.STDConn) + // return n, err // will be io.EOF on closeNotify + } + + c.readWaitOptions.PostReturn(buffer) + return +} + +//go:linkname tlsReadRecord crypto/tls.(*Conn).readRecord +func tlsReadRecord(c *tls.STDConn) error + +//go:linkname tlsHandlePostHandshakeMessage crypto/tls.(*Conn).handlePostHandshakeMessage +func tlsHandlePostHandshakeMessage(c *tls.STDConn) error diff --git a/common/badtls/read_wait_stub.go b/common/badtls/read_wait_stub.go new file mode 100644 index 00000000..c5c9946f --- /dev/null +++ b/common/badtls/read_wait_stub.go @@ -0,0 +1,13 @@ +//go:build !go1.21 || without_badtls + +package badtls + +import ( + "os" + + "github.com/sagernet/sing/common/tls" +) + +func NewReadWaitConn(conn tls.Conn) (tls.Conn, error) { + return nil, os.ErrInvalid +} diff --git a/common/tls/client.go b/common/tls/client.go index d1c9475a..4d6b0c54 100644 --- a/common/tls/client.go +++ b/common/tls/client.go @@ -6,6 +6,7 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/common/badtls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/option" M "github.com/sagernet/sing/common/metadata" @@ -42,7 +43,17 @@ func NewClient(ctx context.Context, serverAddress string, options option.Outboun func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) { ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout) defer cancel() - return aTLS.ClientHandshake(ctx, conn, config) + tlsConn, err := aTLS.ClientHandshake(ctx, conn, config) + if err != nil { + return nil, err + } + readWaitConn, err := badtls.NewReadWaitConn(tlsConn) + if err == nil { + return readWaitConn, nil + } else if err != os.ErrInvalid { + return nil, err + } + return tlsConn, nil } type Dialer struct { diff --git a/common/tls/server.go b/common/tls/server.go index ac6d0a2e..6afd89d6 100644 --- a/common/tls/server.go +++ b/common/tls/server.go @@ -3,7 +3,9 @@ package tls import ( "context" "net" + "os" + "github.com/sagernet/sing-box/common/badtls" C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" @@ -26,5 +28,15 @@ func NewServer(ctx context.Context, logger log.Logger, options option.InboundTLS func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) { ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout) defer cancel() - return aTLS.ServerHandshake(ctx, conn, config) + tlsConn, err := aTLS.ServerHandshake(ctx, conn, config) + if err != nil { + return nil, err + } + readWaitConn, err := badtls.NewReadWaitConn(tlsConn) + if err == nil { + return readWaitConn, nil + } else if err != os.ErrInvalid { + return nil, err + } + return tlsConn, nil } diff --git a/docs/clients/android/features.md b/docs/clients/android/features.md index 2702e6fa..346976cc 100644 --- a/docs/clients/android/features.md +++ b/docs/clients/android/features.md @@ -18,6 +18,8 @@ SFA provides an unprivileged TUN implementation through Android VpnService. | `inet4_address` | :material-check: | / | | `inet6_address` | :material-check: | / | | `mtu` | :material-check: | / | +| `gso` | :material-close: | No permission | +| `gso_max_size` | :material-close: | No permission | | `auto_route` | :material-check: | / | | `strict_route` | :material-close: | Not implemented | | `inet4_route_address` | :material-check: | / | diff --git a/docs/clients/apple/features.md b/docs/clients/apple/features.md index 95143d98..7c7b8c9b 100644 --- a/docs/clients/apple/features.md +++ b/docs/clients/apple/features.md @@ -14,28 +14,30 @@ SFI/SFM/SFT allows you to run sing-box through NetworkExtension with Application SFI/SFM/SFT provides an unprivileged TUN implementation through NetworkExtension. -| TUN inbound option | Available | Note | -|-------------------------------|-----------|-------------------| -| `interface_name` | ✖️ | Managed by Darwin | -| `inet4_address` | ✔️ | / | -| `inet6_address` | ✔️ | / | -| `mtu` | ✔️ | / | -| `auto_route` | ✔️ | / | -| `strict_route` | ✖️ | Not implemented | -| `inet4_route_address` | ✔️ | / | -| `inet6_route_address` | ✔️ | / | -| `inet4_route_exclude_address` | ✔️ | / | -| `inet6_route_exclude_address` | ✔️ | / | -| `endpoint_independent_nat` | ✔️ | / | -| `stack` | ✔️ | / | -| `include_interface` | ✖️ | Not implemented | -| `exclude_interface` | ✖️ | Not implemented | -| `include_uid` | ✖️ | Not implemented | -| `exclude_uid` | ✖️ | Not implemented | -| `include_android_user` | ✖️ | Not implemented | -| `include_package` | ✖️ | Not implemented | -| `exclude_package` | ✖️ | Not implemented | -| `platform` | ✔️ | / | +| TUN inbound option | Available | Note | +|-------------------------------|-------------------|-------------------| +| `interface_name` | :material-close:️ | Managed by Darwin | +| `inet4_address` | :material-check: | / | +| `inet6_address` | :material-check: | / | +| `mtu` | :material-check: | / | +| `gso` | :material-close: | Not implemented | +| `gso_max_size` | :material-close: | Not implemented | +| `auto_route` | :material-check: | / | +| `strict_route` | :material-close:️ | Not implemented | +| `inet4_route_address` | :material-check: | / | +| `inet6_route_address` | :material-check: | / | +| `inet4_route_exclude_address` | :material-check: | / | +| `inet6_route_exclude_address` | :material-check: | / | +| `endpoint_independent_nat` | :material-check: | / | +| `stack` | :material-check: | / | +| `include_interface` | :material-close:️ | Not implemented | +| `exclude_interface` | :material-close:️ | Not implemented | +| `include_uid` | :material-close:️ | Not implemented | +| `exclude_uid` | :material-close:️ | Not implemented | +| `include_android_user` | :material-close:️ | Not implemented | +| `include_package` | :material-close:️ | Not implemented | +| `exclude_package` | :material-close:️ | Not implemented | +| `platform` | :material-check: | / | | Route/DNS rule option | Available | Note | |-----------------------|------------------|-----------------------| diff --git a/docs/configuration/inbound/tun.md b/docs/configuration/inbound/tun.md index 00c5e050..d899bccd 100644 --- a/docs/configuration/inbound/tun.md +++ b/docs/configuration/inbound/tun.md @@ -1,3 +1,13 @@ +--- +icon: material/alert-decagram +--- + +!!! quote "Changes in sing-box 1.8.0" + + :material-plus: [gso](#gso) + :material-plus: [gso_max_size](#gso_max_size) + :material-alert-decagram: [stack](#stack) + !!! quote "" Only supported on Linux, Windows and macOS. @@ -12,6 +22,8 @@ "inet4_address": "172.19.0.1/30", "inet6_address": "fdfe:dcba:9876::1/126", "mtu": 9000, + "gso": false, + "gso_max_size": 65536, "auto_route": true, "strict_route": true, "inet4_route_address": [ @@ -98,6 +110,28 @@ IPv6 prefix for the tun interface. The maximum transmission unit. +#### gso + +!!! question "Since sing-box 1.8.0" + +!!! quote "" + + Only supported on Linux. + +Enable generic segmentation offload. + +#### gso_max_size + +!!! question "Since sing-box 1.8.0" + +!!! quote "" + + Only supported on Linux. + +Maximum GSO packet size. + +`65536` is used by default. + #### auto_route Set the default route to the Tun. @@ -160,18 +194,19 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes). #### stack +!!! quote "Changes in sing-box 1.8.0" + + :material-delete-alert: The legacy LWIP stack has been deprecated and removed. + TCP/IP stack. -| Stack | Description | Status | -|--------|----------------------------------------------------------------------------------|-------------------| -| system | Sometimes better performance | recommended | -| gVisor | Better compatibility, based on [google/gvisor](https://github.com/google/gvisor) | recommended | -| mixed | Mixed `system` TCP stack and `gVisor` UDP stack | recommended | -| LWIP | Based on [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | upstream archived | +| Stack | Description | +|----------|-------------------------------------------------------------------------------------------------------| +| `system` | Perform L3 to L4 translation using the system network stack | +| `gvisor` | Perform L3 to L4 translation using [gVisor](https://github.com/google/gvisor)'s virtual network stack | +| `mixed` | Mixed `system` TCP stack and `gvisor` UDP stack | -!!! warning "" - - LWIP stacks is not included by default, see [Installation](/installation/build-from-source/#build-tags). +Defaults to the `mixed` stack if the gVisor build tag is enabled, otherwise defaults to the `system` stack. #### include_interface @@ -217,10 +252,10 @@ Exclude users in route, but in range. Limit android users in route. -| Common user | ID | -|--------------|-----| -| Main | 0 | -| Work Profile | 10 | +| Common user | ID | +|--------------|----| +| Main | 0 | +| Work Profile | 10 | #### include_package diff --git a/docs/configuration/inbound/tun.zh.md b/docs/configuration/inbound/tun.zh.md index d22009c2..e030587e 100644 --- a/docs/configuration/inbound/tun.zh.md +++ b/docs/configuration/inbound/tun.zh.md @@ -1,3 +1,13 @@ +--- +icon: material/alert-decagram +--- + +!!! quote "sing-box 1.8.0 中的更改" + + :material-plus: [gso](#gso) + :material-plus: [gso_max_size](#gso_max_size) + :material-alert-decagram: [stack](#stack) + !!! quote "" 仅支持 Linux、Windows 和 macOS。 @@ -12,6 +22,8 @@ "inet4_address": "172.19.0.1/30", "inet6_address": "fdfe:dcba:9876::1/126", "mtu": 9000, + "gso": false, + "gso_max_size": 65536, "auto_route": true, "strict_route": true, "inet4_route_address": [ @@ -98,6 +110,28 @@ tun 接口的 IPv6 前缀。 最大传输单元。 +#### gso + +!!! question "自 sing-box 1.8.0 起" + +!!! quote "" + + 仅支持 Linux。 + +启用通用分段卸载。 + +#### gso_max_size + +!!! question "自 sing-box 1.8.0 起" + +!!! quote "" + + 仅支持 Linux。 + +通用分段卸载包的最大大小。 + +默认使用 `65536`。 + #### auto_route 设置到 Tun 的默认路由。 @@ -157,17 +191,19 @@ UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。 #### stack +!!! quote "sing-box 1.8.0 中的更改" + + :material-delete-alert: 旧的 LWIP 栈已被弃用并移除。 + TCP/IP 栈。 -| 栈 | 描述 | 状态 | -|-------------|--------------------------------------------------------------------------|-------| -| system (默认) | 有时性能更好 | 推荐 | -| gVisor | 兼容性较好,基于 [google/gvisor](https://github.com/google/gvisor) | 推荐 | -| LWIP | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 | +| 栈 | 描述 | +|--------|------------------------------------------------------------------| +| system | 基于系统网络栈执行 L3 到 L4 转换 | +| gVisor | 基于 [gVisor](https://github.com/google/gvisor) 虚拟网络栈执行 L3 到 L4 转换 | +| mixed | 混合 `system` TCP 栈与 `gvisor` UDP 栈 | -!!! warning "" - - 默认安装不包含 LWIP 栈,参阅 [安装](/zh/installation/build-from-source/#_5)。 +默认使用 `mixed` 栈如果 gVisor 构建标记已启用,否则默认使用 `system` 栈。 #### include_interface @@ -214,8 +250,8 @@ TCP/IP 栈。 限制被路由的 Android 用户。 | 常用用户 | ID | -|--|-----| -| 您 | 0 | +|------|----| +| 您 | 0 | | 工作资料 | 10 | #### include_package diff --git a/docs/configuration/outbound/wireguard.md b/docs/configuration/outbound/wireguard.md index 3fefb7a4..53c39c2c 100644 --- a/docs/configuration/outbound/wireguard.md +++ b/docs/configuration/outbound/wireguard.md @@ -1,3 +1,12 @@ +--- +icon: material/new-box +--- + +!!! quote "Changes in sing-box 1.8.0" + + :material-plus: [gso](#gso) + :material-plus: [gso_max_size](#gso_max_size) + ### Structure ```json @@ -8,6 +17,8 @@ "server": "127.0.0.1", "server_port": 1080, "system_interface": false, + "gso": false, + "gso_max_size": 65536, "interface_name": "wg0", "local_address": [ "10.0.0.2/32" @@ -52,15 +63,37 @@ The server port. #### system_interface -Use system tun support. +Use system interface. -Requires privilege and cannot conflict with system interfaces. +Requires privilege and cannot conflict with exists system interfaces. Forced if gVisor not included in the build. #### interface_name -Custom device name when `system_interface` enabled. +Custom interface name for system interface. + +#### gso + +!!! question "Since sing-box 1.8.0" + +!!! quote "" + + Only supported on Linux. + +Enable generic segmentation offload for system interface. + +#### gso_max_size + +!!! question "Since sing-box 1.8.0" + +!!! quote "" + + Only supported on Linux. + +Maximum GSO packet size. + +`65536` is used by default. #### local_address diff --git a/docs/configuration/outbound/wireguard.zh.md b/docs/configuration/outbound/wireguard.zh.md index 150dda6d..b416e932 100644 --- a/docs/configuration/outbound/wireguard.zh.md +++ b/docs/configuration/outbound/wireguard.zh.md @@ -1,3 +1,12 @@ +--- +icon: material/new-box +--- + +!!! quote "sing-box 1.8.0 中的更改" + + :material-plus: [gso](#gso) + :material-plus: [gso_max_size](#gso_max_size) + ### 结构 ```json @@ -8,6 +17,8 @@ "server": "127.0.0.1", "server_port": 1080, "system_interface": false, + "gso": false, + "gso_max_size": 65536, "interface_name": "wg0", "local_address": [ "10.0.0.2/32" @@ -40,15 +51,37 @@ #### system_interface -使用系统 tun 支持。 +使用系统设备。 -需要特权且不能与系统接口冲突。 +需要特权且不能与已有系统接口冲突。 如果 gVisor 未包含在构建中,则强制执行。 #### interface_name -启用 `system_interface` 时的自定义设备名称。 +为系统接口自定义设备名称。 + +#### gso + +!!! question "自 sing-box 1.8.0 起" + +!!! quote "" + + 仅支持 Linux。 + +为系统接口启用通用分段卸载。 + +#### gso_max_size + +!!! question "自 sing-box 1.8.0 起" + +!!! quote "" + + 仅支持 Linux。 + +通用分段卸载包的最大大小。 + +默认使用 `65536`。 #### local_address diff --git a/docs/installation/build-from-source.md b/docs/installation/build-from-source.md index 3b098317..f30e69ad 100644 --- a/docs/installation/build-from-source.md +++ b/docs/installation/build-from-source.md @@ -45,19 +45,18 @@ go build -tags "tag_a tag_b" ./cmd/sing-box | Build Tag | Enabled by default | Description | |------------------------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `with_quic` | ✔ | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server), [Naive inbound](/configuration/inbound/naive), [Hysteria Inbound](/configuration/inbound/hysteria), [Hysteria Outbound](/configuration/outbound/hysteria) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | -| `with_grpc` | ✖️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | -| `with_dhcp` | ✔ | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server). | -| `with_wireguard` | ✔ | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard). | -| `with_ech` | ✔ | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | -| `with_utls` | ✔ | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | -| `with_reality_server` | ✔ | Build with reality TLS server support, see [TLS](/configuration/shared/tls). | -| `with_acme` | ✔ | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls). | -| `with_clash_api` | ✔ | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). | -| `with_v2ray_api` | ✖️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | -| `with_gvisor` | ✔ | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | -| `with_embedded_tor` (CGO required) | ✖️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor). | -| `with_lwip` (CGO required) | ✖️ | Build with LWIP Tun stack support, see [Tun inbound](/configuration/inbound/tun#stack). | +| `with_quic` | :material-check: | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server), [Naive inbound](/configuration/inbound/naive), [Hysteria Inbound](/configuration/inbound/hysteria), [Hysteria Outbound](/configuration/outbound/hysteria) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | +| `with_grpc` | :material-close:️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | +| `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server). | +| `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard). | +| `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | +| `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | +| `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls). | +| `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_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor). | It is not recommended to change the default build tag list unless you really know what you are adding. diff --git a/docs/installation/build-from-source.zh.md b/docs/installation/build-from-source.zh.md index 69e41925..3e76c2b1 100644 --- a/docs/installation/build-from-source.zh.md +++ b/docs/installation/build-from-source.zh.md @@ -43,21 +43,19 @@ go build -tags "tag_a tag_b" ./cmd/sing-box ## :material-folder-settings: 构建标记 -| 构建标记 | 默认启动 | 说明 | -|------------------------------------|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `with_quic` | ✔ | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server), [Naive inbound](/configuration/inbound/naive), [Hysteria Inbound](/configuration/inbound/hysteria), [Hysteria Outbound](/configuration/outbound/hysteria) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | -| `with_grpc` | ✖️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | -| `with_dhcp` | ✔ | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server). | -| `with_wireguard` | ✔ | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard). | -| `with_ech` | ✔ | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | -| `with_utls` | ✔ | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | -| `with_reality_server` | ✔ | Build with reality TLS server support, see [TLS](/configuration/shared/tls). | -| `with_acme` | ✔ | Build with ACME TLS certificate issuer support, see [TLS](/configuration/shared/tls). | -| `with_clash_api` | ✔ | Build with Clash API support, see [Experimental](/configuration/experimental#clash-api-fields). | -| `with_v2ray_api` | ✖️ | Build with V2Ray API support, see [Experimental](/configuration/experimental#v2ray-api-fields). | -| `with_gvisor` | ✔ | Build with gVisor support, see [Tun inbound](/configuration/inbound/tun#stack) and [WireGuard outbound](/configuration/outbound/wireguard#system_interface). | -| `with_embedded_tor` (CGO required) | ✖️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor). | -| `with_lwip` (CGO required) | ✖️ | Build with LWIP Tun stack support, see [Tun inbound](/configuration/inbound/tun#stack). | - +| 构建标记 | 默认启动 | 说明 | +|------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `with_quic` | :material-check: | Build with QUIC support, see [QUIC and HTTP3 DNS transports](/configuration/dns/server), [Naive inbound](/configuration/inbound/naive), [Hysteria Inbound](/configuration/inbound/hysteria), [Hysteria Outbound](/configuration/outbound/hysteria) and [V2Ray Transport#QUIC](/configuration/shared/v2ray-transport#quic). | +| `with_grpc` | :material-close:️ | Build with standard gRPC support, see [V2Ray Transport#gRPC](/configuration/shared/v2ray-transport#grpc). | +| `with_dhcp` | :material-check: | Build with DHCP support, see [DHCP DNS transport](/configuration/dns/server). | +| `with_wireguard` | :material-check: | Build with WireGuard support, see [WireGuard outbound](/configuration/outbound/wireguard). | +| `with_ech` | :material-check: | Build with TLS ECH extension support for TLS outbound, see [TLS](/configuration/shared/tls#ech). | +| `with_utls` | :material-check: | Build with [uTLS](https://github.com/refraction-networking/utls) support for TLS outbound, see [TLS](/configuration/shared/tls#utls). | +| `with_reality_server` | :material-check: | Build with reality TLS server support, see [TLS](/configuration/shared/tls). | +| `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_embedded_tor` (CGO required) | :material-close:️ | Build with embedded Tor support, see [Tor outbound](/configuration/outbound/tor). | 除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。 diff --git a/go.mod b/go.mod index b21b48ab..740510c6 100644 --- a/go.mod +++ b/go.mod @@ -23,19 +23,19 @@ require ( github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950 - github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930 + github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e github.com/sagernet/quic-go v0.40.0 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 - github.com/sagernet/sing v0.2.20-0.20231208065534-70794cb91cc5 + github.com/sagernet/sing v0.2.20-0.20231211084415-35e7014b0898 github.com/sagernet/sing-dns v0.1.11 - github.com/sagernet/sing-mux v0.1.5 - github.com/sagernet/sing-quic v0.1.5 + github.com/sagernet/sing-mux v0.1.6-0.20231208180947-9053c29513a2 + github.com/sagernet/sing-quic v0.1.6-0.20231207143711-eb3cbf9ed054 github.com/sagernet/sing-shadowsocks v0.2.6 - github.com/sagernet/sing-shadowsocks2 v0.1.5 + github.com/sagernet/sing-shadowsocks2 v0.1.6-0.20231207143709-50439739601a github.com/sagernet/sing-shadowtls v0.1.4 - github.com/sagernet/sing-tun v0.1.24-0.20231212055255-69c3b72eec62 + github.com/sagernet/sing-tun v0.1.24-0.20231212060935-6a1419aeae11 github.com/sagernet/sing-vmess v0.1.8 - github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 + github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f @@ -78,7 +78,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect - github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/spf13/pflag v1.0.5 // indirect @@ -89,7 +88,7 @@ require ( golang.org/x/exp v0.0.0-20231127185646-65229373498e // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.4.0 // indirect + golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.16.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect diff --git a/go.sum b/go.sum index 1403a220..dc1eb422 100644 --- a/go.sum +++ b/go.sum @@ -96,40 +96,37 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a h1:wZHruBxZCsQLXHAozWpnJBL3wJ/XufDpz0qKtgpSnA4= github.com/sagernet/cloudflare-tls v0.0.0-20230829051644-4a68352d0c4a/go.mod h1:dNV1ZP9y3qx5ltULeKaQZTZWTLHflgW5DES+Ses7cMI= -github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= -github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950 h1:hUz/2mJLgi7l2H36JGpDY+jou9FmI6kAm0ZkU+xPpgE= github.com/sagernet/gomobile v0.0.0-20230915142329-c6740b6d2950/go.mod h1:5YE39YkJkCcMsfq1jMKkjsrM2GfBoF9JVWnvU89hmvU= -github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930 h1:dSPgjIw0CT6ISLeEh8Q20dZMBMFCcEceo23+LncRcNQ= -github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930/go.mod h1:JpKHkOYgh4wLwrX2BhH3ZIvCvazCkTnPeEcmigZJfHY= +github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e h1:DOkjByVeAR56dkszjnMZke4wr7yM/1xHaJF3G9olkEE= +github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e/go.mod h1:fLxq/gtp0qzkaEwywlRRiGmjOK5ES/xUzyIKIFP2Asw= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/quic-go v0.40.0 h1:DvQNPb72lzvNQDe9tcUyHTw8eRv6PLtM2mNYmdlzUMo= github.com/sagernet/quic-go v0.40.0/go.mod h1:VqtdhlbkeeG5Okhb3eDMb/9o0EoglReHunNT9ukrJAI= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= -github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.20-0.20231208065534-70794cb91cc5 h1:rDt9HgEcIjiSv/o2OBN/SyqFiMhx16qAlHPFkEpvcq8= -github.com/sagernet/sing v0.2.20-0.20231208065534-70794cb91cc5/go.mod h1:Ce5LNojQOgOiWhiD8pPD6E9H7e2KgtOe3Zxx4Ou5u80= +github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing v0.2.20-0.20231211084415-35e7014b0898 h1:ZR0wpw4/0NCICOX10SIUW8jpPVV7+D98nGA6p4zWICo= +github.com/sagernet/sing v0.2.20-0.20231211084415-35e7014b0898/go.mod h1:Ce5LNojQOgOiWhiD8pPD6E9H7e2KgtOe3Zxx4Ou5u80= github.com/sagernet/sing-dns v0.1.11 h1:PPrMCVVrAeR3f5X23I+cmvacXJ+kzuyAsBiWyUKhGSE= github.com/sagernet/sing-dns v0.1.11/go.mod h1:zJ/YjnYB61SYE+ubMcMqVdpaSvsyQ2iShQGO3vuLvvE= -github.com/sagernet/sing-mux v0.1.5 h1:jUbYth9QQd1wsDmU8Ush+fKce7lNo9TMv2dp8PJtSOY= -github.com/sagernet/sing-mux v0.1.5/go.mod h1:MoH6Soz1R+CYZcCeIXZWx6fkZa6hQc9o3HZu9G6CDTw= -github.com/sagernet/sing-quic v0.1.5 h1:PIQzE4cGrry+JkkMEJH/EH3wRkv/QgD48+ScNr/2oig= -github.com/sagernet/sing-quic v0.1.5/go.mod h1:n2mXukpubasyV4SlWyyW0+LCdAn7DZ8/brAkUxZujrw= +github.com/sagernet/sing-mux v0.1.6-0.20231208180947-9053c29513a2 h1:rRlYQPbMKmzKX+43XC04gEQvxc45/AxfteRWfcl2/rw= +github.com/sagernet/sing-mux v0.1.6-0.20231208180947-9053c29513a2/go.mod h1:IdSrwwqBeJTrjLZJRFXE+F8mYXNI/rPAjzlgTFuEVmo= +github.com/sagernet/sing-quic v0.1.6-0.20231207143711-eb3cbf9ed054 h1:Ed7FskwQcep5oQ+QahgVK0F6jPPSV8Nqwjr9MwGatMU= +github.com/sagernet/sing-quic v0.1.6-0.20231207143711-eb3cbf9ed054/go.mod h1:u758WWv3G1OITG365CYblL0NfAruFL1PpLD9DUVTv1o= github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s= github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM= -github.com/sagernet/sing-shadowsocks2 v0.1.5 h1:JDeAJ4ZWlYZ7F6qEVdDKPhQEangxKw/JtmU+i/YfCYE= -github.com/sagernet/sing-shadowsocks2 v0.1.5/go.mod h1:KF65y8lI5PGHyMgRZGYXYsH9ilgRc/yr+NYbSNGuBm4= +github.com/sagernet/sing-shadowsocks2 v0.1.6-0.20231207143709-50439739601a h1:uYIKfpE1/EJpa+1Bja7b006VixeRuVduOpeuesMk2lU= +github.com/sagernet/sing-shadowsocks2 v0.1.6-0.20231207143709-50439739601a/go.mod h1:pjeylQ4ApvpEH7B4PUBrdyJf4xmQkg8BaIzT5fI2fR0= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= -github.com/sagernet/sing-tun v0.1.24-0.20231212055255-69c3b72eec62 h1:2UxHpIewr/cakD7qspPtQUI1UzLS8V9HvAYezE8JiVA= -github.com/sagernet/sing-tun v0.1.24-0.20231212055255-69c3b72eec62/go.mod h1:Mnd7+8iGNb9uGnMAh3bp0ZA+nPFBZNaMHZPMEGdAQJM= +github.com/sagernet/sing-tun v0.1.24-0.20231212060935-6a1419aeae11 h1:crTOVPJGOGWOW+Q2a0FQiiS/G2+W6uCLKtOofFMisQc= +github.com/sagernet/sing-tun v0.1.24-0.20231212060935-6a1419aeae11/go.mod h1:DgXPnBqtqWrZj37Mun/W61dW0Q56eLqTZYhcuNLaCtY= github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA= -github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= -github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= +github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= +github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q= github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= @@ -185,10 +182,10 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -197,8 +194,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= -golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= diff --git a/inbound/http.go b/inbound/http.go index fa6c3d58..b4663319 100644 --- a/inbound/http.go +++ b/inbound/http.go @@ -26,7 +26,7 @@ var ( type HTTP struct { myInboundAdapter - authenticator auth.Authenticator + authenticator *auth.Authenticator tlsConfig tls.ServerConfig } diff --git a/inbound/mixed.go b/inbound/mixed.go index 8c3bf3b4..982842ef 100644 --- a/inbound/mixed.go +++ b/inbound/mixed.go @@ -29,7 +29,7 @@ var ( type Mixed struct { myInboundAdapter - authenticator auth.Authenticator + authenticator *auth.Authenticator } func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) *Mixed { diff --git a/inbound/naive.go b/inbound/naive.go index 1ff159d1..36bda492 100644 --- a/inbound/naive.go +++ b/inbound/naive.go @@ -32,7 +32,7 @@ var _ adapter.Inbound = (*Naive)(nil) type Naive struct { myInboundAdapter - authenticator auth.Authenticator + authenticator *auth.Authenticator tlsConfig tls.ServerConfig httpServer *http.Server h3Server any diff --git a/inbound/socks.go b/inbound/socks.go index 65dfb1e6..7c5183c9 100644 --- a/inbound/socks.go +++ b/inbound/socks.go @@ -22,7 +22,7 @@ var ( type Socks struct { myInboundAdapter - authenticator auth.Authenticator + authenticator *auth.Authenticator } func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) *Socks { diff --git a/inbound/tun.go b/inbound/tun.go index f37e0825..4aa11f69 100644 --- a/inbound/tun.go +++ b/inbound/tun.go @@ -43,6 +43,10 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger if tunMTU == 0 { tunMTU = 9000 } + gsoMaxSize := options.GSOMaxSize + if gsoMaxSize == 0 { + gsoMaxSize = 65536 + } var udpTimeout int64 if options.UDPTimeout != 0 { udpTimeout = options.UDPTimeout @@ -74,6 +78,8 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger tunOptions: tun.Options{ Name: options.InterfaceName, MTU: tunMTU, + GSO: options.GSO, + GSOMaxSize: gsoMaxSize, Inet4Address: options.Inet4Address, Inet6Address: options.Inet6Address, AutoRoute: options.AutoRoute, @@ -167,10 +173,7 @@ func (t *Tun) Start() error { t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{ Context: t.ctx, Tun: tunInterface, - MTU: t.tunOptions.MTU, - Name: t.tunOptions.Name, - Inet4Address: t.tunOptions.Inet4Address, - Inet6Address: t.tunOptions.Inet6Address, + TunOptions: t.tunOptions, EndpointIndependentNat: t.endpointIndependentNat, UDPTimeout: t.udpTimeout, Handler: t, diff --git a/option/tun.go b/option/tun.go index 306d4541..51e8f915 100644 --- a/option/tun.go +++ b/option/tun.go @@ -5,6 +5,8 @@ import "net/netip" type TunInboundOptions struct { InterfaceName string `json:"interface_name,omitempty"` MTU uint32 `json:"mtu,omitempty"` + GSO bool `json:"gso,omitempty"` + GSOMaxSize uint32 `json:"gso_max_size,omitempty"` Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"` Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"` AutoRoute bool `json:"auto_route,omitempty"` diff --git a/option/wireguard.go b/option/wireguard.go index 5ede7a61..78d7d1f3 100644 --- a/option/wireguard.go +++ b/option/wireguard.go @@ -5,6 +5,8 @@ import "net/netip" type WireGuardOutboundOptions struct { DialerOptions SystemInterface bool `json:"system_interface,omitempty"` + GSO bool `json:"gso,omitempty"` + GSOMaxSize uint32 `json:"gso_max_size,omitempty"` InterfaceName string `json:"interface_name,omitempty"` LocalAddress Listable[netip.Prefix] `json:"local_address"` PrivateKey string `json:"private_key"` diff --git a/outbound/dns.go b/outbound/dns.go index 74adb3ae..fcb67d45 100644 --- a/outbound/dns.go +++ b/outbound/dns.go @@ -111,6 +111,9 @@ func (d *DNS) NewPacketConnection(ctx context.Context, conn N.PacketConn, metada } } if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created { + readWaiter.InitializeReadWaiter(N.ReadWaitOptions{ + MTU: dns.FixedPacketSize, + }) return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata) } break @@ -193,15 +196,13 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa timeout := canceler.New(fastClose, cancel, C.DNSTimeout) var group task.Group group.Append0(func(ctx context.Context) error { - var buffer *buf.Buffer - readWaiter.InitializeReadWaiter(func() *buf.Buffer { - return buf.NewSize(dns.FixedPacketSize) - }) - defer readWaiter.InitializeReadWaiter(nil) for { - var message mDNS.Msg - var destination M.Socksaddr - var err error + var ( + message mDNS.Msg + destination M.Socksaddr + err error + buffer *buf.Buffer + ) if len(cached) > 0 { packet := cached[0] cached = cached[1:] @@ -216,9 +217,8 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa } destination = packet.Destination } else { - destination, err = readWaiter.WaitReadPacket() + buffer, destination, err = readWaiter.WaitReadPacket() if err != nil { - buffer.Release() cancel(err) return err } diff --git a/outbound/proxy.go b/outbound/proxy.go index dae0d9c5..6127f0f2 100644 --- a/outbound/proxy.go +++ b/outbound/proxy.go @@ -30,7 +30,7 @@ type ProxyListener struct { tcpListener *net.TCPListener username string password string - authenticator auth.Authenticator + authenticator *auth.Authenticator } func NewProxyListener(ctx context.Context, logger log.ContextLogger, dialer N.Dialer) *ProxyListener { diff --git a/outbound/wireguard.go b/outbound/wireguard.go index e645f056..1a5e5dc5 100644 --- a/outbound/wireguard.go +++ b/outbound/wireguard.go @@ -163,7 +163,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context if !options.SystemInterface && tun.WithGVisor { wireTunDevice, err = wireguard.NewStackDevice(options.LocalAddress, mtu) } else { - wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, options.LocalAddress, mtu) + wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, options.LocalAddress, mtu, options.GSO, options.GSOMaxSize) } if err != nil { return nil, E.Cause(err, "create WireGuard device") diff --git a/route/rule_set_remote.go b/route/rule_set_remote.go index ac939a45..4e687e21 100644 --- a/route/rule_set_remote.go +++ b/route/rule_set_remote.go @@ -126,7 +126,7 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error { err error ) switch s.options.Format { - case C.RuleSetFormatSource, "": + case C.RuleSetFormatSource: var compat option.PlainRuleSetCompat decoder := json.NewDecoder(json.NewCommentFilter(bytes.NewReader(content))) decoder.DisallowUnknownFields() diff --git a/transport/fakeip/packet_wait.go b/transport/fakeip/packet_wait.go index 3e3fd89f..9fa4a5bd 100644 --- a/transport/fakeip/packet_wait.go +++ b/transport/fakeip/packet_wait.go @@ -17,16 +17,16 @@ func (c *NATPacketConn) CreatePacketReadWaiter() (N.PacketReadWaiter, bool) { type waitNATPacketConn struct { *NATPacketConn - waiter N.PacketReadWaiter + readWaiter N.PacketReadWaiter } -func (c *waitNATPacketConn) InitializeReadWaiter(newBuffer func() *buf.Buffer) { - c.waiter.InitializeReadWaiter(newBuffer) +func (c *waitNATPacketConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) { + return c.readWaiter.InitializeReadWaiter(options) } -func (c *waitNATPacketConn) WaitReadPacket() (destination M.Socksaddr, err error) { - destination, err = c.waiter.WaitReadPacket() - if socksaddrWithoutPort(destination) == c.origin { +func (c *waitNATPacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) { + buffer, destination, err = c.readWaiter.WaitReadPacket() + if err == nil && socksaddrWithoutPort(destination) == c.origin { destination = M.Socksaddr{ Addr: c.destination.Addr, Fqdn: c.destination.Fqdn, diff --git a/transport/trojan/mux.go b/transport/trojan/mux.go index 77324000..13ac1e83 100644 --- a/transport/trojan/mux.go +++ b/transport/trojan/mux.go @@ -53,7 +53,7 @@ func newMuxConnection0(ctx context.Context, stream net.Conn, metadata M.Metadata case CommandTCP: return handler.NewConnection(ctx, stream, metadata) case CommandUDP: - return handler.NewPacketConnection(ctx, &PacketConn{stream}, metadata) + return handler.NewPacketConnection(ctx, &PacketConn{Conn: stream}, metadata) default: return E.New("unknown command ", command) } diff --git a/transport/trojan/protocol.go b/transport/trojan/protocol.go index 09e18782..394ba291 100644 --- a/transport/trojan/protocol.go +++ b/transport/trojan/protocol.go @@ -85,9 +85,10 @@ func (c *ClientConn) Upstream() any { type ClientPacketConn struct { net.Conn - access sync.Mutex - key [KeyLength]byte - headerWritten bool + access sync.Mutex + key [KeyLength]byte + headerWritten bool + readWaitOptions N.ReadWaitOptions } func NewClientPacketConn(conn net.Conn, key [KeyLength]byte) *ClientPacketConn { diff --git a/transport/trojan/protocol_wait.go b/transport/trojan/protocol_wait.go new file mode 100644 index 00000000..c6b4ec06 --- /dev/null +++ b/transport/trojan/protocol_wait.go @@ -0,0 +1,45 @@ +package trojan + +import ( + "encoding/binary" + + "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/sing/common/rw" +) + +var _ N.PacketReadWaiter = (*ClientPacketConn)(nil) + +func (c *ClientPacketConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) { + c.readWaitOptions = options + return false +} + +func (c *ClientPacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) { + destination, err = M.SocksaddrSerializer.ReadAddrPort(c.Conn) + if err != nil { + return nil, M.Socksaddr{}, E.Cause(err, "read destination") + } + + var length uint16 + err = binary.Read(c.Conn, binary.BigEndian, &length) + if err != nil { + return nil, M.Socksaddr{}, E.Cause(err, "read chunk length") + } + + err = rw.SkipN(c.Conn, 2) + if err != nil { + return nil, M.Socksaddr{}, E.Cause(err, "skip crlf") + } + + buffer = c.readWaitOptions.NewPacketBuffer() + _, err = buffer.ReadFullFrom(c.Conn, int(length)) + if err != nil { + buffer.Release() + return + } + c.readWaitOptions.PostReturn(buffer) + return +} diff --git a/transport/trojan/service.go b/transport/trojan/service.go index de6bd7e8..9078276c 100644 --- a/transport/trojan/service.go +++ b/transport/trojan/service.go @@ -105,7 +105,7 @@ func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata case CommandTCP: return s.handler.NewConnection(ctx, conn, metadata) case CommandUDP: - return s.handler.NewPacketConnection(ctx, &PacketConn{conn}, metadata) + return s.handler.NewPacketConnection(ctx, &PacketConn{Conn: conn}, metadata) // case CommandMux: default: return HandleMuxConnection(ctx, conn, metadata, s.handler) @@ -122,6 +122,7 @@ func (s *Service[K]) fallback(ctx context.Context, conn net.Conn, metadata M.Met type PacketConn struct { net.Conn + readWaitOptions N.ReadWaitOptions } func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) { diff --git a/transport/trojan/service_wait.go b/transport/trojan/service_wait.go new file mode 100644 index 00000000..5ec082fe --- /dev/null +++ b/transport/trojan/service_wait.go @@ -0,0 +1,45 @@ +package trojan + +import ( + "encoding/binary" + + "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/sing/common/rw" +) + +var _ N.PacketReadWaiter = (*PacketConn)(nil) + +func (c *PacketConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) { + c.readWaitOptions = options + return false +} + +func (c *PacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) { + destination, err = M.SocksaddrSerializer.ReadAddrPort(c.Conn) + if err != nil { + return nil, M.Socksaddr{}, E.Cause(err, "read destination") + } + + var length uint16 + err = binary.Read(c.Conn, binary.BigEndian, &length) + if err != nil { + return nil, M.Socksaddr{}, E.Cause(err, "read chunk length") + } + + err = rw.SkipN(c.Conn, 2) + if err != nil { + return nil, M.Socksaddr{}, E.Cause(err, "skip crlf") + } + + buffer = c.readWaitOptions.NewPacketBuffer() + _, err = buffer.ReadFullFrom(c.Conn, int(length)) + if err != nil { + buffer.Release() + return + } + c.readWaitOptions.PostReturn(buffer) + return +} diff --git a/transport/wireguard/client_bind.go b/transport/wireguard/client_bind.go index 2b56f73a..a72432d3 100644 --- a/transport/wireguard/client_bind.go +++ b/transport/wireguard/client_bind.go @@ -76,11 +76,8 @@ func (c *ClientBind) connect() (*wireConn, error) { return nil, err } c.conn = &wireConn{ - PacketConn: &bufio.UnbindPacketConn{ - ExtendedConn: bufio.NewExtendedConn(udpConn), - Addr: c.connectAddr, - }, - done: make(chan struct{}), + PacketConn: bufio.NewUnbindPacketConn(udpConn), + done: make(chan struct{}), } } else { udpConn, err := c.dialer.ListenPacket(c.ctx, M.Socksaddr{Addr: netip.IPv4Unspecified()}) diff --git a/transport/wireguard/device_system.go b/transport/wireguard/device_system.go index 98626404..e70c3f35 100644 --- a/transport/wireguard/device_system.go +++ b/transport/wireguard/device_system.go @@ -2,6 +2,7 @@ package wireguard import ( "context" + "errors" "net" "net/netip" "os" @@ -19,16 +20,17 @@ import ( var _ Device = (*SystemDevice)(nil) type SystemDevice struct { - dialer N.Dialer - device tun.Tun - name string - mtu int - events chan wgTun.Event - addr4 netip.Addr - addr6 netip.Addr + dialer N.Dialer + device tun.Tun + frontHeadroom int + name string + mtu int + events chan wgTun.Event + addr4 netip.Addr + addr6 netip.Addr } -func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes []netip.Prefix, mtu uint32) (*SystemDevice, error) { +func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes []netip.Prefix, mtu uint32, gso bool, gsoMaxsize uint32) (*SystemDevice, error) { var inet4Addresses []netip.Prefix var inet6Addresses []netip.Prefix for _, prefixes := range localPrefixes { @@ -41,11 +43,16 @@ func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes if interfaceName == "" { interfaceName = tun.CalculateInterfaceName("wg") } + if gsoMaxsize == 0 { + gsoMaxsize = 65536 + } tunInterface, err := tun.New(tun.Options{ Name: interfaceName, Inet4Address: inet4Addresses, Inet6Address: inet6Addresses, MTU: mtu, + GSO: gso, + GSOMaxSize: gsoMaxsize, }) if err != nil { return nil, err @@ -62,12 +69,13 @@ func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{ BindInterface: interfaceName, })), - device: tunInterface, - name: interfaceName, - mtu: int(mtu), - events: make(chan wgTun.Event), - addr4: inet4Address, - addr6: inet6Address, + device: tunInterface, + frontHeadroom: tunInterface.FrontHeadroom(), + name: interfaceName, + mtu: int(mtu), + events: make(chan wgTun.Event), + addr4: inet4Address, + addr6: inet6Address, }, nil } @@ -97,16 +105,18 @@ func (w *SystemDevice) File() *os.File { } func (w *SystemDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) { - sizes[0], err = w.device.Read(bufs[0][offset-tun.PacketOffset:]) + sizes[0], err = w.device.Read(bufs[0][offset-w.frontHeadroom:]) if err == nil { count = 1 + } else if errors.Is(err, tun.ErrTooManySegments) { + err = wgTun.ErrTooManySegments } return } func (w *SystemDevice) Write(bufs [][]byte, offset int) (count int, err error) { for _, b := range bufs { - _, err = w.device.Write(b[offset:]) + _, err = w.device.Write(b[offset-w.frontHeadroom:]) if err != nil { return }