Browse Source

Improve read wait interface &
Refactor Authenticator interface to struct &
Update smux &
Update gVisor to 20231204.0 &
Update quic-go to v0.40.1 &
Update wireguard-go &
Add GSO support for TUN/WireGuard &
Fix router pre-start &
Fix bind forwarder to interface for systems stack

世界 1 year ago
parent
commit
89c723e3e4
48 changed files with 893 additions and 649 deletions
  1. 1 0
      adapter/router.go
  2. 5 4
      box.go
  3. 0 233
      common/badtls/badtls.go
  4. 0 14
      common/badtls/badtls_stub.go
  5. 0 22
      common/badtls/link.go
  6. 115 0
      common/badtls/read_wait.go
  7. 13 0
      common/badtls/read_wait_stub.go
  8. 20 7
      common/dialer/default.go
  9. 3 5
      common/dialer/dialer.go
  10. 9 0
      common/dialer/wireguard.go
  11. 11 0
      common/dialer/wireguard_control.go
  12. 9 0
      common/dialer/wiregurad_stub.go
  13. 12 1
      common/tls/client.go
  14. 13 1
      common/tls/server.go
  15. 1 0
      docs/clients/android/features.md
  16. 23 22
      docs/clients/apple/features.md
  17. 34 13
      docs/configuration/inbound/tun.md
  18. 32 10
      docs/configuration/inbound/tun.zh.md
  19. 22 3
      docs/configuration/outbound/wireguard.md
  20. 22 3
      docs/configuration/outbound/wireguard.zh.md
  21. 12 13
      docs/installation/build-from-source.md
  22. 14 16
      docs/installation/build-from-source.zh.md
  23. 11 12
      go.mod
  24. 24 27
      go.sum
  25. 1 1
      inbound/http.go
  26. 1 1
      inbound/mixed.go
  27. 1 1
      inbound/naive.go
  28. 1 1
      inbound/socks.go
  29. 2 4
      inbound/tun.go
  30. 15 14
      option/outbound.go
  31. 1 0
      option/tun.go
  32. 1 0
      option/wireguard.go
  33. 10 10
      outbound/dns.go
  34. 1 1
      outbound/proxy.go
  35. 93 121
      outbound/wireguard.go
  36. 34 21
      route/router.go
  37. 1 1
      route/rule_set_remote.go
  38. 6 6
      transport/fakeip/packet_wait.go
  39. 1 1
      transport/trojan/mux.go
  40. 4 3
      transport/trojan/protocol.go
  41. 45 0
      transport/trojan/protocol_wait.go
  42. 2 1
      transport/trojan/service.go
  43. 45 0
      transport/trojan/service_wait.go
  44. 21 28
      transport/wireguard/client_bind.go
  45. 1 1
      transport/wireguard/device_stack.go
  46. 48 22
      transport/wireguard/device_system.go
  47. 4 5
      transport/wireguard/endpoint.go
  48. 148 0
      transport/wireguard/resolve.go

+ 1 - 0
adapter/router.go

@@ -17,6 +17,7 @@ import (
 
 
 type Router interface {
 type Router interface {
 	Service
 	Service
+	PreStarter
 	PostStarter
 	PostStarter
 
 
 	Outbounds() []Outbound
 	Outbounds() []Outbound

+ 5 - 4
box.go

@@ -259,6 +259,10 @@ func (s *Box) preStart() error {
 			}
 			}
 		}
 		}
 	}
 	}
+	err = s.router.PreStart()
+	if err != nil {
+		return E.Cause(err, "pre-start router")
+	}
 	err = s.startOutbounds()
 	err = s.startOutbounds()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -313,10 +317,7 @@ func (s *Box) postStart() error {
 			}
 			}
 		}
 		}
 	}
 	}
-	err := s.router.PostStart()
-	if err != nil {
-		return E.Cause(err, "post-start router")
-	}
+
 	return s.router.PostStart()
 	return s.router.PostStart()
 }
 }
 
 

+ 0 - 233
common/badtls/badtls.go

@@ -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()
-}

+ 0 - 14
common/badtls/badtls_stub.go

@@ -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
-}

+ 0 - 22
common/badtls/link.go

@@ -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

+ 115 - 0
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

+ 13 - 0
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
+}

+ 20 - 7
common/dialer/default.go

@@ -15,14 +15,17 @@ import (
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
 )
 )
 
 
+var _ WireGuardListener = (*DefaultDialer)(nil)
+
 type DefaultDialer struct {
 type DefaultDialer struct {
-	dialer4     tcpDialer
-	dialer6     tcpDialer
-	udpDialer4  net.Dialer
-	udpDialer6  net.Dialer
-	udpListener net.ListenConfig
-	udpAddr4    string
-	udpAddr6    string
+	dialer4             tcpDialer
+	dialer6             tcpDialer
+	udpDialer4          net.Dialer
+	udpDialer6          net.Dialer
+	udpListener         net.ListenConfig
+	udpAddr4            string
+	udpAddr6            string
+	isWireGuardListener bool
 }
 }
 
 
 func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDialer, error) {
 func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDialer, error) {
@@ -98,6 +101,11 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
 		}
 		}
 		setMultiPathTCP(&dialer4)
 		setMultiPathTCP(&dialer4)
 	}
 	}
+	if options.IsWireGuardListener {
+		for _, controlFn := range wgControlFns {
+			listener.Control = control.Append(listener.Control, controlFn)
+		}
+	}
 	tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen)
 	tcpDialer4, err := newTCPDialer(dialer4, options.TCPFastOpen)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -114,6 +122,7 @@ func NewDefault(router adapter.Router, options option.DialerOptions) (*DefaultDi
 		listener,
 		listener,
 		udpAddr4,
 		udpAddr4,
 		udpAddr6,
 		udpAddr6,
+		options.IsWireGuardListener,
 	}, nil
 	}, nil
 }
 }
 
 
@@ -146,6 +155,10 @@ func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksadd
 	}
 	}
 }
 }
 
 
+func (d *DefaultDialer) ListenPacketCompat(network, address string) (net.PacketConn, error) {
+	return trackPacketConn(d.udpListener.ListenPacket(context.Background(), network, address))
+}
+
 func trackConn(conn net.Conn, err error) (net.Conn, error) {
 func trackConn(conn net.Conn, err error) (net.Conn, error) {
 	if !conntrack.Enabled || err != nil {
 	if !conntrack.Enabled || err != nil {
 		return conn, err
 		return conn, err

+ 3 - 5
common/dialer/dialer.go

@@ -6,15 +6,13 @@ import (
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-dns"
 	"github.com/sagernet/sing-dns"
-	"github.com/sagernet/sing/common"
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
 )
 )
 
 
-func MustNew(router adapter.Router, options option.DialerOptions) N.Dialer {
-	return common.Must1(New(router, options))
-}
-
 func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error) {
 func New(router adapter.Router, options option.DialerOptions) (N.Dialer, error) {
+	if options.IsWireGuardListener {
+		return NewDefault(router, options)
+	}
 	var (
 	var (
 		dialer N.Dialer
 		dialer N.Dialer
 		err    error
 		err    error

+ 9 - 0
common/dialer/wireguard.go

@@ -0,0 +1,9 @@
+package dialer
+
+import (
+	"net"
+)
+
+type WireGuardListener interface {
+	ListenPacketCompat(network, address string) (net.PacketConn, error)
+}

+ 11 - 0
common/dialer/wireguard_control.go

@@ -0,0 +1,11 @@
+//go:build with_wireguard
+
+package dialer
+
+import (
+	"github.com/sagernet/wireguard-go/conn"
+)
+
+var _ WireGuardListener = (conn.Listener)(nil)
+
+var wgControlFns = conn.ControlFns

+ 9 - 0
common/dialer/wiregurad_stub.go

@@ -0,0 +1,9 @@
+//go:build !with_wireguard
+
+package dialer
+
+import (
+	"github.com/sagernet/sing/common/control"
+)
+
+var wgControlFns []control.Func

+ 12 - 1
common/tls/client.go

@@ -6,6 +6,7 @@ import (
 	"os"
 	"os"
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/common/badtls"
 	C "github.com/sagernet/sing-box/constant"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 	M "github.com/sagernet/sing/common/metadata"
 	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) {
 func ClientHandshake(ctx context.Context, conn net.Conn, config Config) (Conn, error) {
 	ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
 	ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
 	defer cancel()
 	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 {
 type Dialer struct {

+ 13 - 1
common/tls/server.go

@@ -3,7 +3,9 @@ package tls
 import (
 import (
 	"context"
 	"context"
 	"net"
 	"net"
+	"os"
 
 
+	"github.com/sagernet/sing-box/common/badtls"
 	C "github.com/sagernet/sing-box/constant"
 	C "github.com/sagernet/sing-box/constant"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/log"
 	"github.com/sagernet/sing-box/option"
 	"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) {
 func ServerHandshake(ctx context.Context, conn net.Conn, config ServerConfig) (Conn, error) {
 	ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
 	ctx, cancel := context.WithTimeout(ctx, C.TCPTimeout)
 	defer cancel()
 	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
 }
 }

+ 1 - 0
docs/clients/android/features.md

@@ -18,6 +18,7 @@ SFA provides an unprivileged TUN implementation through Android VpnService.
 | `inet4_address`               | :material-check: | /                  |
 | `inet4_address`               | :material-check: | /                  |
 | `inet6_address`               | :material-check: | /                  |
 | `inet6_address`               | :material-check: | /                  |
 | `mtu`                         | :material-check: | /                  |
 | `mtu`                         | :material-check: | /                  |
+| `gso`                         | :material-close: | No permission      |
 | `auto_route`                  | :material-check: | /                  |
 | `auto_route`                  | :material-check: | /                  |
 | `strict_route`                | :material-close: | Not implemented    |
 | `strict_route`                | :material-close: | Not implemented    |
 | `inet4_route_address`         | :material-check: | /                  |
 | `inet4_route_address`         | :material-check: | /                  |

+ 23 - 22
docs/clients/apple/features.md

@@ -14,28 +14,29 @@ SFI/SFM/SFT allows you to run sing-box through NetworkExtension with Application
 
 
 SFI/SFM/SFT provides an unprivileged TUN implementation through NetworkExtension.
 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   |
+| `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                  |
 | Route/DNS rule option | Available        | Note                  |
 |-----------------------|------------------|-----------------------|
 |-----------------------|------------------|-----------------------|

+ 34 - 13
docs/configuration/inbound/tun.md

@@ -1,3 +1,12 @@
+---
+icon: material/alert-decagram
+---
+
+!!! quote "Changes in sing-box 1.8.0"
+
+    :material-plus: [gso](#gso)  
+    :material-alert-decagram: [stack](#stack)
+
 !!! quote ""
 !!! quote ""
 
 
     Only supported on Linux, Windows and macOS.
     Only supported on Linux, Windows and macOS.
@@ -12,6 +21,7 @@
   "inet4_address": "172.19.0.1/30",
   "inet4_address": "172.19.0.1/30",
   "inet6_address": "fdfe:dcba:9876::1/126",
   "inet6_address": "fdfe:dcba:9876::1/126",
   "mtu": 9000,
   "mtu": 9000,
+  "gso": false,
   "auto_route": true,
   "auto_route": true,
   "strict_route": true,
   "strict_route": true,
   "inet4_route_address": [
   "inet4_route_address": [
@@ -99,6 +109,16 @@ IPv6 prefix for the tun interface.
 
 
 The maximum transmission unit.
 The maximum transmission unit.
 
 
+#### gso
+
+!!! question "Since sing-box 1.8.0"
+
+!!! quote ""
+
+    Only supported on Linux.
+
+Enable generic segmentation offload.
+
 #### auto_route
 #### auto_route
 
 
 Set the default route to the Tun.
 Set the default route to the Tun.
@@ -161,18 +181,19 @@ UDP NAT expiration time in seconds, default is 300 (5 minutes).
 
 
 #### stack
 #### stack
 
 
-TCP/IP stack.
+!!! quote "Changes in sing-box 1.8.0"
 
 
-| 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 |
+    :material-delete-alert: The legacy LWIP stack has been deprecated and removed.
 
 
-!!! warning ""
+TCP/IP stack.
+
+| 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                                                       |
 
 
-    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
 #### include_interface
 
 
@@ -218,10 +239,10 @@ Exclude users in route, but in range.
 
 
 Limit android users in route.
 Limit android users in route.
 
 
-| Common user  | ID  |
-|--------------|-----|
-| Main         | 0   |
-| Work Profile | 10  |
+| Common user  | ID |
+|--------------|----|
+| Main         | 0  |
+| Work Profile | 10 |
 
 
 #### include_package
 #### include_package
 
 

+ 32 - 10
docs/configuration/inbound/tun.zh.md

@@ -1,3 +1,12 @@
+---
+icon: material/alert-decagram
+---
+
+!!! quote "sing-box 1.8.0 中的更改"
+
+    :material-plus: [gso](#gso)  
+    :material-alert-decagram: [stack](#stack)
+
 !!! quote ""
 !!! quote ""
 
 
     仅支持 Linux、Windows 和 macOS。
     仅支持 Linux、Windows 和 macOS。
@@ -12,6 +21,7 @@
   "inet4_address": "172.19.0.1/30",
   "inet4_address": "172.19.0.1/30",
   "inet6_address": "fdfe:dcba:9876::1/126",
   "inet6_address": "fdfe:dcba:9876::1/126",
   "mtu": 9000,
   "mtu": 9000,
+  "gso": false,
   "auto_route": true,
   "auto_route": true,
   "strict_route": true,
   "strict_route": true,
   "inet4_route_address": [
   "inet4_route_address": [
@@ -99,6 +109,16 @@ tun 接口的 IPv6 前缀。
 
 
 最大传输单元。
 最大传输单元。
 
 
+#### gso
+
+!!! question "自 sing-box 1.8.0 起"
+
+!!! quote ""
+
+    仅支持 Linux。
+
+启用通用分段卸载。
+
 #### auto_route
 #### auto_route
 
 
 设置到 Tun 的默认路由。
 设置到 Tun 的默认路由。
@@ -158,17 +178,19 @@ UDP NAT 过期时间,以秒为单位,默认为 300(5 分钟)。
 
 
 #### stack
 #### stack
 
 
-TCP/IP 栈。
+!!! quote "sing-box 1.8.0 中的更改"
 
 
-| 栈           | 描述                                                                       | 状态    |
-|-------------|--------------------------------------------------------------------------|-------|
-| system (默认) | 有时性能更好                                                                   | 推荐    |
-| gVisor      | 兼容性较好,基于 [google/gvisor](https://github.com/google/gvisor)               | 推荐    |
-| LWIP        | 基于 [eycorsican/go-tun2socks](https://github.com/eycorsican/go-tun2socks) | 上游已存档 |
+    :material-delete-alert: 旧的 LWIP 栈已被弃用并移除。
 
 
-!!! warning ""
+TCP/IP 栈。
+
+| 栈      | 描述                                                               |
+|--------|------------------------------------------------------------------|
+| system | 基于系统网络栈执行 L3 到 L4 转换                                             |
+| gVisor | 基于 [gVisor](https://github.com/google/gvisor) 虚拟网络栈执行 L3 到 L4 转换 |
+| mixed  | 混合 `system` TCP 栈与 `gvisor` UDP 栈                                |
 
 
-    默认安装不包含 LWIP 栈,参阅 [安装](/zh/installation/build-from-source/#_5)。
+默认使用 `mixed` 栈如果 gVisor 构建标记已启用,否则默认使用 `system` 栈
 
 
 #### include_interface
 #### include_interface
 
 
@@ -215,8 +237,8 @@ TCP/IP 栈。
 限制被路由的 Android 用户。
 限制被路由的 Android 用户。
 
 
 | 常用用户 | ID |
 | 常用用户 | ID |
-|--|-----|
-| 您 | 0 |
+|------|----|
+| 您    | 0  |
 | 工作资料 | 10 |
 | 工作资料 | 10 |
 
 
 #### include_package
 #### include_package

+ 22 - 3
docs/configuration/outbound/wireguard.md

@@ -1,3 +1,11 @@
+---
+icon: material/new-box
+---
+
+!!! quote "Changes in sing-box 1.8.0"
+    
+    :material-plus: [gso](#gso)  
+
 ### Structure
 ### Structure
 
 
 ```json
 ```json
@@ -8,6 +16,7 @@
   "server": "127.0.0.1",
   "server": "127.0.0.1",
   "server_port": 1080,
   "server_port": 1080,
   "system_interface": false,
   "system_interface": false,
+  "gso": false,
   "interface_name": "wg0",
   "interface_name": "wg0",
   "local_address": [
   "local_address": [
     "10.0.0.2/32"
     "10.0.0.2/32"
@@ -52,15 +61,25 @@ The server port.
 
 
 #### system_interface
 #### 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.
 Forced if gVisor not included in the build.
 
 
 #### interface_name
 #### 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.
+
+Try to enable generic segmentation offload.
 
 
 #### local_address
 #### local_address
 
 

+ 22 - 3
docs/configuration/outbound/wireguard.zh.md

@@ -1,3 +1,11 @@
+---
+icon: material/new-box
+---
+
+!!! quote "sing-box 1.8.0 中的更改"
+
+    :material-plus: [gso](#gso)  
+
 ### 结构
 ### 结构
 
 
 ```json
 ```json
@@ -8,6 +16,7 @@
   "server": "127.0.0.1",
   "server": "127.0.0.1",
   "server_port": 1080,
   "server_port": 1080,
   "system_interface": false,
   "system_interface": false,
+  "gso": false,
   "interface_name": "wg0",
   "interface_name": "wg0",
   "local_address": [
   "local_address": [
     "10.0.0.2/32"
     "10.0.0.2/32"
@@ -40,15 +49,25 @@
 
 
 #### system_interface
 #### system_interface
 
 
-使用系统 tun 支持
+使用系统设备
 
 
-需要特权且不能与系统接口冲突。
+需要特权且不能与已有系统接口冲突。
 
 
 如果 gVisor 未包含在构建中,则强制执行。
 如果 gVisor 未包含在构建中,则强制执行。
 
 
 #### interface_name
 #### interface_name
 
 
-启用 `system_interface` 时的自定义设备名称。
+为系统接口自定义设备名称。
+
+#### gso
+
+!!! question "自 sing-box 1.8.0 起"
+
+!!! quote ""
+
+    仅支持 Linux。
+
+尝试启用通用分段卸载。
 
 
 #### local_address
 #### local_address
 
 

+ 12 - 13
docs/installation/build-from-source.md

@@ -55,18 +55,17 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
 
 
 | Build Tag                          | Enabled by default | Description                                                                                                                                                                                                                                                                                                                    |
 | 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.
 It is not recommended to change the default build tag list unless you really know what you are adding.

+ 14 - 16
docs/installation/build-from-source.zh.md

@@ -53,21 +53,19 @@ go build -tags "tag_a tag_b" ./cmd/sing-box
 
 
 ## :material-folder-settings: 构建标记
 ## :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/).                                                                                                                                                                                                                                          |
 
 
 除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。
 除非您确实知道您正在启用什么,否则不建议更改默认构建标签列表。

+ 11 - 12
go.mod

@@ -23,22 +23,22 @@ require (
 	github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
 	github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
 	github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
 	github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1
 	github.com/sagernet/gomobile v0.1.1
 	github.com/sagernet/gomobile v0.1.1
-	github.com/sagernet/gvisor v0.0.0-20231119034329-07cfb6aaf930
-	github.com/sagernet/quic-go v0.40.0
+	github.com/sagernet/gvisor v0.0.0-20231209105102-8d27a30e436e
+	github.com/sagernet/quic-go v0.40.1-beta.2
 	github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
 	github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691
-	github.com/sagernet/sing v0.3.0-rc.4
+	github.com/sagernet/sing v0.3.0-rc.7
 	github.com/sagernet/sing-dns v0.1.12
 	github.com/sagernet/sing-dns v0.1.12
-	github.com/sagernet/sing-mux v0.1.7
-	github.com/sagernet/sing-quic v0.1.6
+	github.com/sagernet/sing-mux v0.1.8-rc.1
+	github.com/sagernet/sing-quic v0.1.7-rc.2
 	github.com/sagernet/sing-shadowsocks v0.2.6
 	github.com/sagernet/sing-shadowsocks v0.2.6
-	github.com/sagernet/sing-shadowsocks2 v0.1.5
+	github.com/sagernet/sing-shadowsocks2 v0.1.6-rc.1
 	github.com/sagernet/sing-shadowtls v0.1.4
 	github.com/sagernet/sing-shadowtls v0.1.4
-	github.com/sagernet/sing-tun v0.1.24
+	github.com/sagernet/sing-tun v0.2.0-rc.1
 	github.com/sagernet/sing-vmess v0.1.8
 	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-20231209031829-7b5343ac1dc6
 	github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
 	github.com/sagernet/utls v1.5.4
 	github.com/sagernet/utls v1.5.4
-	github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f
+	github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8
 	github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
 	github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854
 	github.com/spf13/cobra v1.8.0
 	github.com/spf13/cobra v1.8.0
 	github.com/stretchr/testify v1.8.4
 	github.com/stretchr/testify v1.8.4
@@ -79,7 +79,6 @@ require (
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/quic-go/qpack v0.4.0 // indirect
 	github.com/quic-go/qpack v0.4.0 // indirect
 	github.com/quic-go/qtls-go1-20 v0.4.1 // 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/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect
 	github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
 	github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
@@ -87,10 +86,10 @@ require (
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
 	github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect
 	github.com/zeebo/blake3 v0.2.3 // indirect
 	github.com/zeebo/blake3 v0.2.3 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
-	golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect
+	golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect
 	golang.org/x/mod v0.14.0 // indirect
 	golang.org/x/mod v0.14.0 // indirect
 	golang.org/x/text 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
 	golang.org/x/tools v0.16.0 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 // indirect
 	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
 	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect

+ 24 - 27
go.sum

@@ -98,46 +98,43 @@ 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/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
 github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
 github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 h1:YbmpqPQEMdlk9oFSKYWRqVuu9qzNiOayIonKmv1gCXY=
 github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
 github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1/go.mod h1:J2yAxTFPDjrDPhuAi9aWFz2L3ox9it4qAluBBbN0H5k=
-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.1.1 h1:3vihRGyUfFTToHMeeak0UK6/ldt2MV2bcWKFi2VyECU=
 github.com/sagernet/gomobile v0.1.1 h1:3vihRGyUfFTToHMeeak0UK6/ldt2MV2bcWKFi2VyECU=
 github.com/sagernet/gomobile v0.1.1/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
 github.com/sagernet/gomobile v0.1.1/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
-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 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=
 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
 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/quic-go v0.40.1-beta.2 h1:USRwm36XuAFdcrmv4vDRD+YUOO08DfvLNruXThrVHZU=
+github.com/sagernet/quic-go v0.40.1-beta.2/go.mod h1:CcKTpzTAISxrM4PA5M20/wYuz9Tj6Tx4DwGbNl9UQrU=
 github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
 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/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.3.0-rc.4 h1:1Til9jN0AnTPB9iiX/MbFrocbRCOXDsdZ/io1IjVWkg=
-github.com/sagernet/sing v0.3.0-rc.4/go.mod h1:Ce5LNojQOgOiWhiD8pPD6E9H7e2KgtOe3Zxx4Ou5u80=
+github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
+github.com/sagernet/sing v0.3.0-rc.7 h1:FmnzFRYC6usVgWf112cUxiexwvL+iAurKmCL4Axa9+A=
+github.com/sagernet/sing v0.3.0-rc.7/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
 github.com/sagernet/sing-dns v0.1.12 h1:1HqZ+ln+Rezx/aJMStaS0d7oPeX2EobSV1NT537kyj4=
 github.com/sagernet/sing-dns v0.1.12 h1:1HqZ+ln+Rezx/aJMStaS0d7oPeX2EobSV1NT537kyj4=
 github.com/sagernet/sing-dns v0.1.12/go.mod h1:rx/DTOisneQpCgNQ4jbFU/JNEtnz0lYcHXenlVzpjEU=
 github.com/sagernet/sing-dns v0.1.12/go.mod h1:rx/DTOisneQpCgNQ4jbFU/JNEtnz0lYcHXenlVzpjEU=
-github.com/sagernet/sing-mux v0.1.7 h1:+48spVReBwIrv6ZdUujiRFCCnblZFwxmbPgrs5zezlI=
-github.com/sagernet/sing-mux v0.1.7/go.mod h1:UmcVSPrVjsOGe95jDXmGgOyKKIXOcjz6FKbFy+0LeDU=
-github.com/sagernet/sing-quic v0.1.6 h1:yNkZiNOlmEGpS+A7I4/Zavhe/fRrLz7yCO/dVMZzt+k=
-github.com/sagernet/sing-quic v0.1.6/go.mod h1:g1Ogcy2KSwKvC7eDXEUu9AnHbjotC+2xsSP+A1i/VOA=
+github.com/sagernet/sing-mux v0.1.8-rc.1 h1:5dsZgWmNr9W6JzQj4fb3xX2pMP0OyJH6kVtlqc2kFKA=
+github.com/sagernet/sing-mux v0.1.8-rc.1/go.mod h1:KK5zCbNujj5kn36G+wLFROOXyJhaaXLyaZWY2w7kBNQ=
+github.com/sagernet/sing-quic v0.1.7-rc.2 h1:rCWhtvzQwgkWbX4sVHYdNwzyPweoUPEgBCBatywHjMs=
+github.com/sagernet/sing-quic v0.1.7-rc.2/go.mod h1:IbKCPWXP13zd3cdu0rirtYjkMlquc5zWtc3avfSUGAw=
 github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
 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-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-rc.1 h1:E+8OyyVg0YfFNUmxMx9jYBEhjLYMQSAMzJrUmE934bo=
+github.com/sagernet/sing-shadowsocks2 v0.1.6-rc.1/go.mod h1:wFkU7sKxyZADS/idtJqBhtc+QBf5iwX9nZO7ymcn6MM=
 github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
 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-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
-github.com/sagernet/sing-tun v0.1.24 h1:cxn8lr8uHMLB1tLU0SzBPE1Q04pG0Fb71GyeeCuic5Q=
-github.com/sagernet/sing-tun v0.1.24/go.mod h1:Mnd7+8iGNb9uGnMAh3bp0ZA+nPFBZNaMHZPMEGdAQJM=
+github.com/sagernet/sing-tun v0.2.0-rc.1 h1:CnlxRgrJKAMKYNuJOcKie6TjRz8wremEq1wndLup7cA=
+github.com/sagernet/sing-tun v0.2.0-rc.1/go.mod h1:hpbL9jNAbYT9G2EHCpCXVIgSrM/2Wgnrm/Hped+8zdY=
 github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
 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/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-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI=
 github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI=
 github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY=
 github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY=
 github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co=
 github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co=
 github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s=
 github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s=
-github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho=
-github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk=
+github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs=
+github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8/go.mod h1:K4J7/npM+VAMUeUmTa2JaA02JmyheP0GpRBOUvn3ecc=
 github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc=
 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/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA=
 github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
 github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
@@ -173,8 +170,8 @@ golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaE
 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
 golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
 golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
 golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
-golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE=
-golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
+golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4=
+golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
 golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
 golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
 golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -187,10 +184,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-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-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-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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.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.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 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
 golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 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=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -199,8 +196,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.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 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 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.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 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
 golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
 golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=

+ 1 - 1
inbound/http.go

@@ -26,7 +26,7 @@ var (
 
 
 type HTTP struct {
 type HTTP struct {
 	myInboundAdapter
 	myInboundAdapter
-	authenticator auth.Authenticator
+	authenticator *auth.Authenticator
 	tlsConfig     tls.ServerConfig
 	tlsConfig     tls.ServerConfig
 }
 }
 
 

+ 1 - 1
inbound/mixed.go

@@ -29,7 +29,7 @@ var (
 
 
 type Mixed struct {
 type Mixed struct {
 	myInboundAdapter
 	myInboundAdapter
-	authenticator auth.Authenticator
+	authenticator *auth.Authenticator
 }
 }
 
 
 func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) *Mixed {
 func NewMixed(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPMixedInboundOptions) *Mixed {

+ 1 - 1
inbound/naive.go

@@ -32,7 +32,7 @@ var _ adapter.Inbound = (*Naive)(nil)
 
 
 type Naive struct {
 type Naive struct {
 	myInboundAdapter
 	myInboundAdapter
-	authenticator auth.Authenticator
+	authenticator *auth.Authenticator
 	tlsConfig     tls.ServerConfig
 	tlsConfig     tls.ServerConfig
 	httpServer    *http.Server
 	httpServer    *http.Server
 	h3Server      any
 	h3Server      any

+ 1 - 1
inbound/socks.go

@@ -22,7 +22,7 @@ var (
 
 
 type Socks struct {
 type Socks struct {
 	myInboundAdapter
 	myInboundAdapter
-	authenticator auth.Authenticator
+	authenticator *auth.Authenticator
 }
 }
 
 
 func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) *Socks {
 func NewSocks(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SocksInboundOptions) *Socks {

+ 2 - 4
inbound/tun.go

@@ -75,6 +75,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
 		tunOptions: tun.Options{
 		tunOptions: tun.Options{
 			Name:                     options.InterfaceName,
 			Name:                     options.InterfaceName,
 			MTU:                      tunMTU,
 			MTU:                      tunMTU,
+			GSO:                      options.GSO,
 			Inet4Address:             options.Inet4Address,
 			Inet4Address:             options.Inet4Address,
 			Inet6Address:             options.Inet6Address,
 			Inet6Address:             options.Inet6Address,
 			AutoRoute:                options.AutoRoute,
 			AutoRoute:                options.AutoRoute,
@@ -168,10 +169,7 @@ func (t *Tun) Start() error {
 	t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
 	t.tunStack, err = tun.NewStack(t.stack, tun.StackOptions{
 		Context:                t.ctx,
 		Context:                t.ctx,
 		Tun:                    tunInterface,
 		Tun:                    tunInterface,
-		MTU:                    t.tunOptions.MTU,
-		Name:                   t.tunOptions.Name,
-		Inet4Address:           t.tunOptions.Inet4Address,
-		Inet6Address:           t.tunOptions.Inet6Address,
+		TunOptions:             t.tunOptions,
 		EndpointIndependentNat: t.endpointIndependentNat,
 		EndpointIndependentNat: t.endpointIndependentNat,
 		UDPTimeout:             t.udpTimeout,
 		UDPTimeout:             t.udpTimeout,
 		Handler:                t,
 		Handler:                t,

+ 15 - 14
option/outbound.go

@@ -108,20 +108,21 @@ type DialerOptionsWrapper interface {
 }
 }
 
 
 type DialerOptions struct {
 type DialerOptions struct {
-	Detour             string         `json:"detour,omitempty"`
-	BindInterface      string         `json:"bind_interface,omitempty"`
-	Inet4BindAddress   *ListenAddress `json:"inet4_bind_address,omitempty"`
-	Inet6BindAddress   *ListenAddress `json:"inet6_bind_address,omitempty"`
-	ProtectPath        string         `json:"protect_path,omitempty"`
-	RoutingMark        int            `json:"routing_mark,omitempty"`
-	ReuseAddr          bool           `json:"reuse_addr,omitempty"`
-	ConnectTimeout     Duration       `json:"connect_timeout,omitempty"`
-	TCPFastOpen        bool           `json:"tcp_fast_open,omitempty"`
-	TCPMultiPath       bool           `json:"tcp_multi_path,omitempty"`
-	UDPFragment        *bool          `json:"udp_fragment,omitempty"`
-	UDPFragmentDefault bool           `json:"-"`
-	DomainStrategy     DomainStrategy `json:"domain_strategy,omitempty"`
-	FallbackDelay      Duration       `json:"fallback_delay,omitempty"`
+	Detour              string         `json:"detour,omitempty"`
+	BindInterface       string         `json:"bind_interface,omitempty"`
+	Inet4BindAddress    *ListenAddress `json:"inet4_bind_address,omitempty"`
+	Inet6BindAddress    *ListenAddress `json:"inet6_bind_address,omitempty"`
+	ProtectPath         string         `json:"protect_path,omitempty"`
+	RoutingMark         int            `json:"routing_mark,omitempty"`
+	ReuseAddr           bool           `json:"reuse_addr,omitempty"`
+	ConnectTimeout      Duration       `json:"connect_timeout,omitempty"`
+	TCPFastOpen         bool           `json:"tcp_fast_open,omitempty"`
+	TCPMultiPath        bool           `json:"tcp_multi_path,omitempty"`
+	UDPFragment         *bool          `json:"udp_fragment,omitempty"`
+	UDPFragmentDefault  bool           `json:"-"`
+	DomainStrategy      DomainStrategy `json:"domain_strategy,omitempty"`
+	FallbackDelay       Duration       `json:"fallback_delay,omitempty"`
+	IsWireGuardListener bool           `json:"-"`
 }
 }
 
 
 func (o *DialerOptions) TakeDialerOptions() DialerOptions {
 func (o *DialerOptions) TakeDialerOptions() DialerOptions {

+ 1 - 0
option/tun.go

@@ -5,6 +5,7 @@ import "net/netip"
 type TunInboundOptions struct {
 type TunInboundOptions struct {
 	InterfaceName            string                 `json:"interface_name,omitempty"`
 	InterfaceName            string                 `json:"interface_name,omitempty"`
 	MTU                      uint32                 `json:"mtu,omitempty"`
 	MTU                      uint32                 `json:"mtu,omitempty"`
+	GSO                      bool                   `json:"gso,omitempty"`
 	Inet4Address             Listable[netip.Prefix] `json:"inet4_address,omitempty"`
 	Inet4Address             Listable[netip.Prefix] `json:"inet4_address,omitempty"`
 	Inet6Address             Listable[netip.Prefix] `json:"inet6_address,omitempty"`
 	Inet6Address             Listable[netip.Prefix] `json:"inet6_address,omitempty"`
 	AutoRoute                bool                   `json:"auto_route,omitempty"`
 	AutoRoute                bool                   `json:"auto_route,omitempty"`

+ 1 - 0
option/wireguard.go

@@ -5,6 +5,7 @@ import "net/netip"
 type WireGuardOutboundOptions struct {
 type WireGuardOutboundOptions struct {
 	DialerOptions
 	DialerOptions
 	SystemInterface bool                   `json:"system_interface,omitempty"`
 	SystemInterface bool                   `json:"system_interface,omitempty"`
+	GSO             bool                   `json:"gso,omitempty"`
 	InterfaceName   string                 `json:"interface_name,omitempty"`
 	InterfaceName   string                 `json:"interface_name,omitempty"`
 	LocalAddress    Listable[netip.Prefix] `json:"local_address"`
 	LocalAddress    Listable[netip.Prefix] `json:"local_address"`
 	PrivateKey      string                 `json:"private_key"`
 	PrivateKey      string                 `json:"private_key"`

+ 10 - 10
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 {
 		if readWaiter, created := bufio.CreatePacketReadWaiter(reader); created {
+			readWaiter.InitializeReadWaiter(N.ReadWaitOptions{
+				MTU: dns.FixedPacketSize,
+			})
 			return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata)
 			return d.newPacketConnection(ctx, conn, readWaiter, counters, cachedPackets, metadata)
 		}
 		}
 		break
 		break
@@ -193,15 +196,13 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
 	timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
 	timeout := canceler.New(fastClose, cancel, C.DNSTimeout)
 	var group task.Group
 	var group task.Group
 	group.Append0(func(ctx context.Context) error {
 	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 {
 		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 {
 			if len(cached) > 0 {
 				packet := cached[0]
 				packet := cached[0]
 				cached = cached[1:]
 				cached = cached[1:]
@@ -216,9 +217,8 @@ func (d *DNS) newPacketConnection(ctx context.Context, conn N.PacketConn, readWa
 				}
 				}
 				destination = packet.Destination
 				destination = packet.Destination
 			} else {
 			} else {
-				destination, err = readWaiter.WaitReadPacket()
+				buffer, destination, err = readWaiter.WaitReadPacket()
 				if err != nil {
 				if err != nil {
-					buffer.Release()
 					cancel(err)
 					cancel(err)
 					return err
 					return err
 				}
 				}

+ 1 - 1
outbound/proxy.go

@@ -30,7 +30,7 @@ type ProxyListener struct {
 	tcpListener   *net.TCPListener
 	tcpListener   *net.TCPListener
 	username      string
 	username      string
 	password      string
 	password      string
-	authenticator auth.Authenticator
+	authenticator *auth.Authenticator
 }
 }
 
 
 func NewProxyListener(ctx context.Context, logger log.ContextLogger, dialer N.Dialer) *ProxyListener {
 func NewProxyListener(ctx context.Context, logger log.ContextLogger, dialer N.Dialer) *ProxyListener {

+ 93 - 121
outbound/wireguard.go

@@ -8,6 +8,7 @@ import (
 	"encoding/hex"
 	"encoding/hex"
 	"fmt"
 	"fmt"
 	"net"
 	"net"
+	"net/netip"
 	"strings"
 	"strings"
 
 
 	"github.com/sagernet/sing-box/adapter"
 	"github.com/sagernet/sing-box/adapter"
@@ -18,10 +19,12 @@ import (
 	"github.com/sagernet/sing-box/transport/wireguard"
 	"github.com/sagernet/sing-box/transport/wireguard"
 	"github.com/sagernet/sing-dns"
 	"github.com/sagernet/sing-dns"
 	"github.com/sagernet/sing-tun"
 	"github.com/sagernet/sing-tun"
-	"github.com/sagernet/sing/common/debug"
 	E "github.com/sagernet/sing/common/exceptions"
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
+	"github.com/sagernet/sing/common/x/list"
+	"github.com/sagernet/sing/service/pause"
+	"github.com/sagernet/wireguard-go/conn"
 	"github.com/sagernet/wireguard-go/device"
 	"github.com/sagernet/wireguard-go/device"
 )
 )
 
 
@@ -32,9 +35,18 @@ var (
 
 
 type WireGuard struct {
 type WireGuard struct {
 	myOutboundAdapter
 	myOutboundAdapter
-	bind      *wireguard.ClientBind
-	device    *device.Device
-	tunDevice wireguard.Device
+	ctx           context.Context
+	workers       int
+	peers         []wireguard.PeerConfig
+	useStdNetBind bool
+	listener      N.Dialer
+	ipcConf       string
+
+	pauseManager  pause.Manager
+	pauseCallback *list.Element[pause.Callback]
+	bind          conn.Bind
+	device        *device.Device
+	tunDevice     wireguard.Device
 }
 }
 
 
 func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) {
 func NewWireGuard(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardOutboundOptions) (*WireGuard, error) {
@@ -47,32 +59,30 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
 			tag:          tag,
 			tag:          tag,
 			dependencies: withDialerDependency(options.DialerOptions),
 			dependencies: withDialerDependency(options.DialerOptions),
 		},
 		},
+		ctx:          ctx,
+		workers:      options.Workers,
+		pauseManager: pause.ManagerFromContext(ctx),
 	}
 	}
-	var reserved [3]uint8
-	if len(options.Reserved) > 0 {
-		if len(options.Reserved) != 3 {
-			return nil, E.New("invalid reserved value, required 3 bytes, got ", len(options.Reserved))
-		}
-		copy(reserved[:], options.Reserved)
-	}
-	var isConnect bool
-	var connectAddr M.Socksaddr
-	if len(options.Peers) < 2 {
-		isConnect = true
-		if len(options.Peers) == 1 {
-			connectAddr = options.Peers[0].ServerOptions.Build()
-		} else {
-			connectAddr = options.ServerOptions.Build()
-		}
-	}
-	outboundDialer, err := dialer.New(router, options.DialerOptions)
+	peers, err := wireguard.ParsePeers(options)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
-	outbound.bind = wireguard.NewClientBind(ctx, outbound, outboundDialer, isConnect, connectAddr, reserved)
+	outbound.peers = peers
 	if len(options.LocalAddress) == 0 {
 	if len(options.LocalAddress) == 0 {
 		return nil, E.New("missing local address")
 		return nil, E.New("missing local address")
 	}
 	}
+	if options.GSO {
+		if options.GSO && options.Detour != "" {
+			return nil, E.New("gso is conflict with detour")
+		}
+		options.IsWireGuardListener = true
+		outbound.useStdNetBind = true
+	}
+	listener, err := dialer.New(router, options.DialerOptions)
+	if err != nil {
+		return nil, err
+	}
+	outbound.listener = listener
 	var privateKey string
 	var privateKey string
 	{
 	{
 		bytes, err := base64.StdEncoding.DecodeString(options.PrivateKey)
 		bytes, err := base64.StdEncoding.DecodeString(options.PrivateKey)
@@ -81,80 +91,7 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
 		}
 		}
 		privateKey = hex.EncodeToString(bytes)
 		privateKey = hex.EncodeToString(bytes)
 	}
 	}
-	ipcConf := "private_key=" + privateKey
-	if len(options.Peers) > 0 {
-		for i, peer := range options.Peers {
-			var peerPublicKey, preSharedKey string
-			{
-				bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey)
-				if err != nil {
-					return nil, E.Cause(err, "decode public key for peer ", i)
-				}
-				peerPublicKey = hex.EncodeToString(bytes)
-			}
-			if peer.PreSharedKey != "" {
-				bytes, err := base64.StdEncoding.DecodeString(peer.PreSharedKey)
-				if err != nil {
-					return nil, E.Cause(err, "decode pre shared key for peer ", i)
-				}
-				preSharedKey = hex.EncodeToString(bytes)
-			}
-			destination := peer.ServerOptions.Build()
-			ipcConf += "\npublic_key=" + peerPublicKey
-			ipcConf += "\nendpoint=" + destination.String()
-			if preSharedKey != "" {
-				ipcConf += "\npreshared_key=" + preSharedKey
-			}
-			if len(peer.AllowedIPs) == 0 {
-				return nil, E.New("missing allowed_ips for peer ", i)
-			}
-			for _, allowedIP := range peer.AllowedIPs {
-				ipcConf += "\nallowed_ip=" + allowedIP
-			}
-			if len(peer.Reserved) > 0 {
-				if len(peer.Reserved) != 3 {
-					return nil, E.New("invalid reserved value for peer ", i, ", required 3 bytes, got ", len(peer.Reserved))
-				}
-				copy(reserved[:], options.Reserved)
-				outbound.bind.SetReservedForEndpoint(destination, reserved)
-			}
-		}
-	} else {
-		var peerPublicKey, preSharedKey string
-		{
-			bytes, err := base64.StdEncoding.DecodeString(options.PeerPublicKey)
-			if err != nil {
-				return nil, E.Cause(err, "decode peer public key")
-			}
-			peerPublicKey = hex.EncodeToString(bytes)
-		}
-		if options.PreSharedKey != "" {
-			bytes, err := base64.StdEncoding.DecodeString(options.PreSharedKey)
-			if err != nil {
-				return nil, E.Cause(err, "decode pre shared key")
-			}
-			preSharedKey = hex.EncodeToString(bytes)
-		}
-		ipcConf += "\npublic_key=" + peerPublicKey
-		ipcConf += "\nendpoint=" + options.ServerOptions.Build().String()
-		if preSharedKey != "" {
-			ipcConf += "\npreshared_key=" + preSharedKey
-		}
-		var has4, has6 bool
-		for _, address := range options.LocalAddress {
-			if address.Addr().Is4() {
-				has4 = true
-			} else {
-				has6 = true
-			}
-		}
-		if has4 {
-			ipcConf += "\nallowed_ip=0.0.0.0/0"
-		}
-		if has6 {
-			ipcConf += "\nallowed_ip=::/0"
-		}
-	}
+	outbound.ipcConf = "private_key=" + privateKey
 	mtu := options.MTU
 	mtu := options.MTU
 	if mtu == 0 {
 	if mtu == 0 {
 		mtu = 1408
 		mtu = 1408
@@ -163,36 +100,83 @@ func NewWireGuard(ctx context.Context, router adapter.Router, logger log.Context
 	if !options.SystemInterface && tun.WithGVisor {
 	if !options.SystemInterface && tun.WithGVisor {
 		wireTunDevice, err = wireguard.NewStackDevice(options.LocalAddress, mtu)
 		wireTunDevice, err = wireguard.NewStackDevice(options.LocalAddress, mtu)
 	} else {
 	} else {
-		wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, options.LocalAddress, mtu)
+		wireTunDevice, err = wireguard.NewSystemDevice(router, options.InterfaceName, options.LocalAddress, mtu, options.GSO)
 	}
 	}
 	if err != nil {
 	if err != nil {
 		return nil, E.Cause(err, "create WireGuard device")
 		return nil, E.Cause(err, "create WireGuard device")
 	}
 	}
-	wgDevice := device.NewDevice(ctx, wireTunDevice, outbound.bind, &device.Logger{
+	outbound.tunDevice = wireTunDevice
+	return outbound, nil
+}
+
+func (w *WireGuard) Start() error {
+	err := wireguard.ResolvePeers(w.ctx, w.router, w.peers)
+	if err != nil {
+		return err
+	}
+	var bind conn.Bind
+	if w.useStdNetBind {
+		bind = conn.NewStdNetBind(w.listener.(dialer.WireGuardListener))
+	} else {
+		var (
+			isConnect   bool
+			connectAddr netip.AddrPort
+			reserved    [3]uint8
+		)
+		peerLen := len(w.peers)
+		if peerLen == 1 {
+			isConnect = true
+			connectAddr = w.peers[0].Endpoint
+			reserved = w.peers[0].Reserved
+		}
+		bind = wireguard.NewClientBind(w.ctx, w, w.listener, isConnect, connectAddr, reserved)
+	}
+	wgDevice := device.NewDevice(w.tunDevice, bind, &device.Logger{
 		Verbosef: func(format string, args ...interface{}) {
 		Verbosef: func(format string, args ...interface{}) {
-			logger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
+			w.logger.Debug(fmt.Sprintf(strings.ToLower(format), args...))
 		},
 		},
 		Errorf: func(format string, args ...interface{}) {
 		Errorf: func(format string, args ...interface{}) {
-			logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
+			w.logger.Error(fmt.Sprintf(strings.ToLower(format), args...))
 		},
 		},
-	}, options.Workers)
-	if debug.Enabled {
-		logger.Trace("created wireguard ipc conf: \n", ipcConf)
+	}, w.workers)
+	ipcConf := w.ipcConf
+	for _, peer := range w.peers {
+		ipcConf += peer.GenerateIpcLines()
 	}
 	}
 	err = wgDevice.IpcSet(ipcConf)
 	err = wgDevice.IpcSet(ipcConf)
 	if err != nil {
 	if err != nil {
-		return nil, E.Cause(err, "setup wireguard")
+		return E.Cause(err, "setup wireguard: \n", ipcConf)
 	}
 	}
-	outbound.device = wgDevice
-	outbound.tunDevice = wireTunDevice
-	return outbound, nil
+	w.device = wgDevice
+	w.pauseCallback = w.pauseManager.RegisterCallback(w.onPauseUpdated)
+	return w.tunDevice.Start()
+}
+
+func (w *WireGuard) Close() error {
+	if w.device != nil {
+		w.device.Close()
+	}
+	if w.pauseCallback != nil {
+		w.pauseManager.UnregisterCallback(w.pauseCallback)
+	}
+	w.tunDevice.Close()
+	return nil
 }
 }
 
 
 func (w *WireGuard) InterfaceUpdated() {
 func (w *WireGuard) InterfaceUpdated() {
-	w.bind.Reset()
+	w.device.BindUpdate()
 	return
 	return
 }
 }
 
 
+func (w *WireGuard) onPauseUpdated(event int) {
+	switch event {
+	case pause.EventDevicePaused:
+		w.device.Down()
+	case pause.EventDeviceWake:
+		w.device.Up()
+	}
+}
+
 func (w *WireGuard) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
 func (w *WireGuard) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
 	switch network {
 	switch network {
 	case N.NetworkTCP:
 	case N.NetworkTCP:
@@ -233,15 +217,3 @@ func (w *WireGuard) NewConnection(ctx context.Context, conn net.Conn, metadata a
 func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
 func (w *WireGuard) NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
 	return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS)
 	return NewDirectPacketConnection(ctx, w.router, w, conn, metadata, dns.DomainStrategyAsIS)
 }
 }
-
-func (w *WireGuard) Start() error {
-	return w.tunDevice.Start()
-}
-
-func (w *WireGuard) Close() error {
-	if w.device != nil {
-		w.device.Close()
-	}
-	w.tunDevice.Close()
-	return nil
-}

+ 34 - 21
route/router.go

@@ -418,35 +418,48 @@ func (r *Router) Outbounds() []adapter.Outbound {
 	return r.outbounds
 	return r.outbounds
 }
 }
 
 
-func (r *Router) Start() error {
+func (r *Router) PreStart() error {
 	monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout)
 	monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout)
-	if r.needGeoIPDatabase {
-		monitor.Start("initialize geoip database")
-		err := r.prepareGeoIPDatabase()
+	if r.interfaceMonitor != nil {
+		monitor.Start("initialize interface monitor")
+		err := r.interfaceMonitor.Start()
 		monitor.Finish()
 		monitor.Finish()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	if r.needGeositeDatabase {
-		monitor.Start("initialize geosite database")
-		err := r.prepareGeositeDatabase()
+	if r.networkMonitor != nil {
+		monitor.Start("initialize network monitor")
+		err := r.networkMonitor.Start()
 		monitor.Finish()
 		monitor.Finish()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	if r.interfaceMonitor != nil {
-		monitor.Start("initialize interface monitor")
-		err := r.interfaceMonitor.Start()
+	if r.fakeIPStore != nil {
+		monitor.Start("initialize fakeip store")
+		err := r.fakeIPStore.Start()
 		monitor.Finish()
 		monitor.Finish()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	if r.networkMonitor != nil {
-		monitor.Start("initialize network monitor")
-		err := r.networkMonitor.Start()
+	return nil
+}
+
+func (r *Router) Start() error {
+	monitor := taskmonitor.New(r.logger, C.DefaultStartTimeout)
+	if r.needGeoIPDatabase {
+		monitor.Start("initialize geoip database")
+		err := r.prepareGeoIPDatabase()
+		monitor.Finish()
+		if err != nil {
+			return err
+		}
+	}
+	if r.needGeositeDatabase {
+		monitor.Start("initialize geosite database")
+		err := r.prepareGeositeDatabase()
 		monitor.Finish()
 		monitor.Finish()
 		if err != nil {
 		if err != nil {
 			return err
 			return err
@@ -472,14 +485,7 @@ func (r *Router) Start() error {
 		r.geositeCache = nil
 		r.geositeCache = nil
 		r.geositeReader = nil
 		r.geositeReader = nil
 	}
 	}
-	if r.fakeIPStore != nil {
-		monitor.Start("initialize fakeip store")
-		err := r.fakeIPStore.Start()
-		monitor.Finish()
-		if err != nil {
-			return err
-		}
-	}
+
 	if len(r.ruleSets) > 0 {
 	if len(r.ruleSets) > 0 {
 		monitor.Start("initialize rule-set")
 		monitor.Start("initialize rule-set")
 		ruleSetStartContext := NewRuleSetStartContext()
 		ruleSetStartContext := NewRuleSetStartContext()
@@ -708,6 +714,10 @@ func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) {
 }
 }
 
 
 func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
 func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata adapter.InboundContext) error {
+	if r.pauseManager.IsDevicePaused() {
+		return E.New("reject connection to ", metadata.Destination, " while device paused")
+	}
+
 	if metadata.InboundDetour != "" {
 	if metadata.InboundDetour != "" {
 		if metadata.LastInbound == metadata.InboundDetour {
 		if metadata.LastInbound == metadata.InboundDetour {
 			return E.New("routing loop on detour: ", metadata.InboundDetour)
 			return E.New("routing loop on detour: ", metadata.InboundDetour)
@@ -832,6 +842,9 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
 }
 }
 
 
 func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
 func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, metadata adapter.InboundContext) error {
+	if r.pauseManager.IsDevicePaused() {
+		return E.New("reject packet connection to ", metadata.Destination, " while device paused")
+	}
 	if metadata.InboundDetour != "" {
 	if metadata.InboundDetour != "" {
 		if metadata.LastInbound == metadata.InboundDetour {
 		if metadata.LastInbound == metadata.InboundDetour {
 			return E.New("routing loop on detour: ", metadata.InboundDetour)
 			return E.New("routing loop on detour: ", metadata.InboundDetour)

+ 1 - 1
route/rule_set_remote.go

@@ -126,7 +126,7 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
 		err          error
 		err          error
 	)
 	)
 	switch s.options.Format {
 	switch s.options.Format {
-	case C.RuleSetFormatSource, "":
+	case C.RuleSetFormatSource:
 		var compat option.PlainRuleSetCompat
 		var compat option.PlainRuleSetCompat
 		compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
 		compat, err = json.UnmarshalExtended[option.PlainRuleSetCompat](content)
 		if err != nil {
 		if err != nil {

+ 6 - 6
transport/fakeip/packet_wait.go

@@ -17,16 +17,16 @@ func (c *NATPacketConn) CreatePacketReadWaiter() (N.PacketReadWaiter, bool) {
 
 
 type waitNATPacketConn struct {
 type waitNATPacketConn struct {
 	*NATPacketConn
 	*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{
 		destination = M.Socksaddr{
 			Addr: c.destination.Addr,
 			Addr: c.destination.Addr,
 			Fqdn: c.destination.Fqdn,
 			Fqdn: c.destination.Fqdn,

+ 1 - 1
transport/trojan/mux.go

@@ -53,7 +53,7 @@ func newMuxConnection0(ctx context.Context, stream net.Conn, metadata M.Metadata
 	case CommandTCP:
 	case CommandTCP:
 		return handler.NewConnection(ctx, stream, metadata)
 		return handler.NewConnection(ctx, stream, metadata)
 	case CommandUDP:
 	case CommandUDP:
-		return handler.NewPacketConnection(ctx, &PacketConn{stream}, metadata)
+		return handler.NewPacketConnection(ctx, &PacketConn{Conn: stream}, metadata)
 	default:
 	default:
 		return E.New("unknown command ", command)
 		return E.New("unknown command ", command)
 	}
 	}

+ 4 - 3
transport/trojan/protocol.go

@@ -85,9 +85,10 @@ func (c *ClientConn) Upstream() any {
 
 
 type ClientPacketConn struct {
 type ClientPacketConn struct {
 	net.Conn
 	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 {
 func NewClientPacketConn(conn net.Conn, key [KeyLength]byte) *ClientPacketConn {

+ 45 - 0
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
+}

+ 2 - 1
transport/trojan/service.go

@@ -105,7 +105,7 @@ func (s *Service[K]) NewConnection(ctx context.Context, conn net.Conn, metadata
 	case CommandTCP:
 	case CommandTCP:
 		return s.handler.NewConnection(ctx, conn, metadata)
 		return s.handler.NewConnection(ctx, conn, metadata)
 	case CommandUDP:
 	case CommandUDP:
-		return s.handler.NewPacketConnection(ctx, &PacketConn{conn}, metadata)
+		return s.handler.NewPacketConnection(ctx, &PacketConn{Conn: conn}, metadata)
 	// case CommandMux:
 	// case CommandMux:
 	default:
 	default:
 		return HandleMuxConnection(ctx, conn, metadata, s.handler)
 		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 {
 type PacketConn struct {
 	net.Conn
 	net.Conn
+	readWaitOptions N.ReadWaitOptions
 }
 }
 
 
 func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {
 func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (M.Socksaddr, error) {

+ 45 - 0
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
+}

+ 21 - 28
transport/wireguard/client_bind.go

@@ -12,7 +12,6 @@ import (
 	E "github.com/sagernet/sing/common/exceptions"
 	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
-	"github.com/sagernet/sing/service/pause"
 	"github.com/sagernet/wireguard-go/conn"
 	"github.com/sagernet/wireguard-go/conn"
 )
 )
 
 
@@ -22,33 +21,27 @@ type ClientBind struct {
 	ctx                 context.Context
 	ctx                 context.Context
 	errorHandler        E.Handler
 	errorHandler        E.Handler
 	dialer              N.Dialer
 	dialer              N.Dialer
-	reservedForEndpoint map[M.Socksaddr][3]uint8
+	reservedForEndpoint map[netip.AddrPort][3]uint8
 	connAccess          sync.Mutex
 	connAccess          sync.Mutex
 	conn                *wireConn
 	conn                *wireConn
 	done                chan struct{}
 	done                chan struct{}
 	isConnect           bool
 	isConnect           bool
-	connectAddr         M.Socksaddr
+	connectAddr         netip.AddrPort
 	reserved            [3]uint8
 	reserved            [3]uint8
-	pauseManager        pause.Manager
 }
 }
 
 
-func NewClientBind(ctx context.Context, errorHandler E.Handler, dialer N.Dialer, isConnect bool, connectAddr M.Socksaddr, reserved [3]uint8) *ClientBind {
+func NewClientBind(ctx context.Context, errorHandler E.Handler, dialer N.Dialer, isConnect bool, connectAddr netip.AddrPort, reserved [3]uint8) *ClientBind {
 	return &ClientBind{
 	return &ClientBind{
 		ctx:                 ctx,
 		ctx:                 ctx,
 		errorHandler:        errorHandler,
 		errorHandler:        errorHandler,
 		dialer:              dialer,
 		dialer:              dialer,
-		reservedForEndpoint: make(map[M.Socksaddr][3]uint8),
+		reservedForEndpoint: make(map[netip.AddrPort][3]uint8),
 		isConnect:           isConnect,
 		isConnect:           isConnect,
 		connectAddr:         connectAddr,
 		connectAddr:         connectAddr,
 		reserved:            reserved,
 		reserved:            reserved,
-		pauseManager:        pause.ManagerFromContext(ctx),
 	}
 	}
 }
 }
 
 
-func (c *ClientBind) SetReservedForEndpoint(destination M.Socksaddr, reserved [3]byte) {
-	c.reservedForEndpoint[destination] = reserved
-}
-
 func (c *ClientBind) connect() (*wireConn, error) {
 func (c *ClientBind) connect() (*wireConn, error) {
 	serverConn := c.conn
 	serverConn := c.conn
 	if serverConn != nil {
 	if serverConn != nil {
@@ -71,16 +64,13 @@ func (c *ClientBind) connect() (*wireConn, error) {
 		}
 		}
 	}
 	}
 	if c.isConnect {
 	if c.isConnect {
-		udpConn, err := c.dialer.DialContext(c.ctx, N.NetworkUDP, c.connectAddr)
+		udpConn, err := c.dialer.DialContext(c.ctx, N.NetworkUDP, M.SocksaddrFromNetIP(c.connectAddr))
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
 		c.conn = &wireConn{
 		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 {
 	} else {
 		udpConn, err := c.dialer.ListenPacket(c.ctx, M.Socksaddr{Addr: netip.IPv4Unspecified()})
 		udpConn, err := c.dialer.ListenPacket(c.ctx, M.Socksaddr{Addr: netip.IPv4Unspecified()})
@@ -116,7 +106,6 @@ func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint)
 		c.errorHandler.NewError(context.Background(), E.Cause(err, "connect to server"))
 		c.errorHandler.NewError(context.Background(), E.Cause(err, "connect to server"))
 		err = nil
 		err = nil
 		time.Sleep(time.Second)
 		time.Sleep(time.Second)
-		c.pauseManager.WaitActive()
 		return
 		return
 	}
 	}
 	n, addr, err := udpConn.ReadFrom(packets[0])
 	n, addr, err := udpConn.ReadFrom(packets[0])
@@ -133,11 +122,9 @@ func (c *ClientBind) receive(packets [][]byte, sizes []int, eps []conn.Endpoint)
 	sizes[0] = n
 	sizes[0] = n
 	if n > 3 {
 	if n > 3 {
 		b := packets[0]
 		b := packets[0]
-		b[1] = 0
-		b[2] = 0
-		b[3] = 0
+		common.ClearArray(b[1:4])
 	}
 	}
-	eps[0] = Endpoint(M.SocksaddrFromNet(addr))
+	eps[0] = Endpoint(M.AddrPortFromNet(addr))
 	count = 1
 	count = 1
 	return
 	return
 }
 }
@@ -170,18 +157,16 @@ func (c *ClientBind) Send(bufs [][]byte, ep conn.Endpoint) error {
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	destination := M.Socksaddr(ep.(Endpoint))
+	destination := netip.AddrPort(ep.(Endpoint))
 	for _, b := range bufs {
 	for _, b := range bufs {
 		if len(b) > 3 {
 		if len(b) > 3 {
 			reserved, loaded := c.reservedForEndpoint[destination]
 			reserved, loaded := c.reservedForEndpoint[destination]
 			if !loaded {
 			if !loaded {
 				reserved = c.reserved
 				reserved = c.reserved
 			}
 			}
-			b[1] = reserved[0]
-			b[2] = reserved[1]
-			b[3] = reserved[2]
+			copy(b[1:4], reserved[:])
 		}
 		}
-		_, err = udpConn.WriteTo(b, destination)
+		_, err = udpConn.WriteTo(b, M.SocksaddrFromNetIP(destination))
 		if err != nil {
 		if err != nil {
 			udpConn.Close()
 			udpConn.Close()
 			return err
 			return err
@@ -191,13 +176,21 @@ func (c *ClientBind) Send(bufs [][]byte, ep conn.Endpoint) error {
 }
 }
 
 
 func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
 func (c *ClientBind) ParseEndpoint(s string) (conn.Endpoint, error) {
-	return Endpoint(M.ParseSocksaddr(s)), nil
+	ap, err := netip.ParseAddrPort(s)
+	if err != nil {
+		return nil, err
+	}
+	return Endpoint(ap), nil
 }
 }
 
 
 func (c *ClientBind) BatchSize() int {
 func (c *ClientBind) BatchSize() int {
 	return 1
 	return 1
 }
 }
 
 
+func (c *ClientBind) SetReservedForEndpoint(destination netip.AddrPort, reserved [3]byte) {
+	c.reservedForEndpoint[destination] = reserved
+}
+
 type wireConn struct {
 type wireConn struct {
 	net.PacketConn
 	net.PacketConn
 	access sync.Mutex
 	access sync.Mutex

+ 1 - 1
transport/wireguard/device_stack.go

@@ -265,7 +265,7 @@ func (ep *wireEndpoint) LinkAddress() tcpip.LinkAddress {
 }
 }
 
 
 func (ep *wireEndpoint) Capabilities() stack.LinkEndpointCapabilities {
 func (ep *wireEndpoint) Capabilities() stack.LinkEndpointCapabilities {
-	return stack.CapabilityNone
+	return stack.CapabilityRXChecksumOffload
 }
 }
 
 
 func (ep *wireEndpoint) Attach(dispatcher stack.NetworkDispatcher) {
 func (ep *wireEndpoint) Attach(dispatcher stack.NetworkDispatcher) {

+ 48 - 22
transport/wireguard/device_system.go

@@ -2,6 +2,7 @@ package wireguard
 
 
 import (
 import (
 	"context"
 	"context"
+	"errors"
 	"net"
 	"net"
 	"net/netip"
 	"net/netip"
 	"os"
 	"os"
@@ -11,6 +12,7 @@ import (
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-tun"
 	"github.com/sagernet/sing-tun"
 	"github.com/sagernet/sing/common"
 	"github.com/sagernet/sing/common"
+	E "github.com/sagernet/sing/common/exceptions"
 	M "github.com/sagernet/sing/common/metadata"
 	M "github.com/sagernet/sing/common/metadata"
 	N "github.com/sagernet/sing/common/network"
 	N "github.com/sagernet/sing/common/network"
 	wgTun "github.com/sagernet/wireguard-go/tun"
 	wgTun "github.com/sagernet/wireguard-go/tun"
@@ -19,16 +21,17 @@ import (
 var _ Device = (*SystemDevice)(nil)
 var _ Device = (*SystemDevice)(nil)
 
 
 type SystemDevice struct {
 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
+	batchDevice tun.LinuxTUN
+	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) (*SystemDevice, error) {
 	var inet4Addresses []netip.Prefix
 	var inet4Addresses []netip.Prefix
 	var inet6Addresses []netip.Prefix
 	var inet6Addresses []netip.Prefix
 	for _, prefixes := range localPrefixes {
 	for _, prefixes := range localPrefixes {
@@ -46,6 +49,7 @@ func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes
 		Inet4Address: inet4Addresses,
 		Inet4Address: inet4Addresses,
 		Inet6Address: inet6Addresses,
 		Inet6Address: inet6Addresses,
 		MTU:          mtu,
 		MTU:          mtu,
+		GSO:          gso,
 	})
 	})
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
@@ -58,16 +62,25 @@ func NewSystemDevice(router adapter.Router, interfaceName string, localPrefixes
 	if len(inet6Addresses) > 0 {
 	if len(inet6Addresses) > 0 {
 		inet6Address = inet6Addresses[0].Addr()
 		inet6Address = inet6Addresses[0].Addr()
 	}
 	}
+	var batchDevice tun.LinuxTUN
+	if gso {
+		batchTUN, isBatchTUN := tunInterface.(tun.LinuxTUN)
+		if !isBatchTUN {
+			return nil, E.New("GSO is not supported on current platform")
+		}
+		batchDevice = batchTUN
+	}
 	return &SystemDevice{
 	return &SystemDevice{
 		dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{
 		dialer: common.Must1(dialer.NewDefault(router, option.DialerOptions{
 			BindInterface: interfaceName,
 			BindInterface: interfaceName,
 		})),
 		})),
-		device: tunInterface,
-		name:   interfaceName,
-		mtu:    int(mtu),
-		events: make(chan wgTun.Event),
-		addr4:  inet4Address,
-		addr6:  inet6Address,
+		device:      tunInterface,
+		batchDevice: batchDevice,
+		name:        interfaceName,
+		mtu:         int(mtu),
+		events:      make(chan wgTun.Event),
+		addr4:       inet4Address,
+		addr6:       inet6Address,
 	}, nil
 	}, nil
 }
 }
 
 
@@ -97,21 +110,31 @@ func (w *SystemDevice) File() *os.File {
 }
 }
 
 
 func (w *SystemDevice) Read(bufs [][]byte, sizes []int, offset int) (count int, err error) {
 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:])
-	if err == nil {
-		count = 1
+	if w.batchDevice != nil {
+		count, err = w.batchDevice.BatchRead(bufs, offset, sizes)
+	} else {
+		sizes[0], err = w.device.Read(bufs[0][offset:])
+		if err == nil {
+			count = 1
+		} else if errors.Is(err, tun.ErrTooManySegments) {
+			err = wgTun.ErrTooManySegments
+		}
 	}
 	}
 	return
 	return
 }
 }
 
 
 func (w *SystemDevice) Write(bufs [][]byte, offset int) (count int, err error) {
 func (w *SystemDevice) Write(bufs [][]byte, offset int) (count int, err error) {
-	for _, b := range bufs {
-		_, err = w.device.Write(b[offset:])
-		if err != nil {
-			return
+	if w.batchDevice != nil {
+		return 0, w.batchDevice.BatchWrite(bufs, offset)
+	} else {
+		for _, b := range bufs {
+			_, err = w.device.Write(b[offset:])
+			if err != nil {
+				return
+			}
 		}
 		}
-		count++
 	}
 	}
+	// WireGuard will not read count
 	return
 	return
 }
 }
 
 
@@ -136,5 +159,8 @@ func (w *SystemDevice) Close() error {
 }
 }
 
 
 func (w *SystemDevice) BatchSize() int {
 func (w *SystemDevice) BatchSize() int {
+	if w.batchDevice != nil {
+		return w.batchDevice.BatchSize()
+	}
 	return 1
 	return 1
 }
 }

+ 4 - 5
transport/wireguard/endpoint.go

@@ -3,13 +3,12 @@ package wireguard
 import (
 import (
 	"net/netip"
 	"net/netip"
 
 
-	M "github.com/sagernet/sing/common/metadata"
 	"github.com/sagernet/wireguard-go/conn"
 	"github.com/sagernet/wireguard-go/conn"
 )
 )
 
 
 var _ conn.Endpoint = (*Endpoint)(nil)
 var _ conn.Endpoint = (*Endpoint)(nil)
 
 
-type Endpoint M.Socksaddr
+type Endpoint netip.AddrPort
 
 
 func (e Endpoint) ClearSrc() {
 func (e Endpoint) ClearSrc() {
 }
 }
@@ -19,16 +18,16 @@ func (e Endpoint) SrcToString() string {
 }
 }
 
 
 func (e Endpoint) DstToString() string {
 func (e Endpoint) DstToString() string {
-	return (M.Socksaddr)(e).String()
+	return (netip.AddrPort)(e).String()
 }
 }
 
 
 func (e Endpoint) DstToBytes() []byte {
 func (e Endpoint) DstToBytes() []byte {
-	b, _ := (M.Socksaddr)(e).AddrPort().MarshalBinary()
+	b, _ := (netip.AddrPort)(e).MarshalBinary()
 	return b
 	return b
 }
 }
 
 
 func (e Endpoint) DstIP() netip.Addr {
 func (e Endpoint) DstIP() netip.Addr {
-	return (M.Socksaddr)(e).Addr
+	return (netip.AddrPort)(e).Addr()
 }
 }
 
 
 func (e Endpoint) SrcIP() netip.Addr {
 func (e Endpoint) SrcIP() netip.Addr {

+ 148 - 0
transport/wireguard/resolve.go

@@ -0,0 +1,148 @@
+package wireguard
+
+import (
+	"context"
+	"encoding/base64"
+	"encoding/hex"
+	"net/netip"
+
+	"github.com/sagernet/sing-box/adapter"
+	"github.com/sagernet/sing-box/option"
+	dns "github.com/sagernet/sing-dns"
+	E "github.com/sagernet/sing/common/exceptions"
+	M "github.com/sagernet/sing/common/metadata"
+)
+
+type PeerConfig struct {
+	destination    M.Socksaddr
+	domainStrategy dns.DomainStrategy
+	Endpoint       netip.AddrPort
+	PublicKey      string
+	PreSharedKey   string
+	AllowedIPs     []string
+	Reserved       [3]uint8
+}
+
+func (c PeerConfig) GenerateIpcLines() string {
+	ipcLines := "\npublic_key=" + c.PublicKey
+	ipcLines += "\nendpoint=" + c.Endpoint.String()
+	if c.PreSharedKey != "" {
+		ipcLines += "\npreshared_key=" + c.PreSharedKey
+	}
+	for _, allowedIP := range c.AllowedIPs {
+		ipcLines += "\nallowed_ip=" + allowedIP
+	}
+	return ipcLines
+}
+
+func ParsePeers(options option.WireGuardOutboundOptions) ([]PeerConfig, error) {
+	var peers []PeerConfig
+	if len(options.Peers) > 0 {
+		for peerIndex, rawPeer := range options.Peers {
+			peer := PeerConfig{
+				AllowedIPs: rawPeer.AllowedIPs,
+			}
+			destination := rawPeer.ServerOptions.Build()
+			if destination.IsFqdn() {
+				peer.destination = destination
+				peer.domainStrategy = dns.DomainStrategy(options.DomainStrategy)
+			} else {
+				peer.Endpoint = destination.AddrPort()
+			}
+			{
+				bytes, err := base64.StdEncoding.DecodeString(rawPeer.PublicKey)
+				if err != nil {
+					return nil, E.Cause(err, "decode public key for peer ", peerIndex)
+				}
+				peer.PublicKey = hex.EncodeToString(bytes)
+			}
+			if rawPeer.PreSharedKey != "" {
+				bytes, err := base64.StdEncoding.DecodeString(rawPeer.PreSharedKey)
+				if err != nil {
+					return nil, E.Cause(err, "decode pre shared key for peer ", peerIndex)
+				}
+				peer.PreSharedKey = hex.EncodeToString(bytes)
+			}
+			if len(rawPeer.AllowedIPs) == 0 {
+				return nil, E.New("missing allowed_ips for peer ", peerIndex)
+			}
+			if len(rawPeer.Reserved) > 0 {
+				if len(rawPeer.Reserved) != 3 {
+					return nil, E.New("invalid reserved value for peer ", peerIndex, ", required 3 bytes, got ", len(peer.Reserved))
+				}
+				copy(peer.Reserved[:], options.Reserved)
+			}
+			peers = append(peers, peer)
+		}
+	} else {
+		peer := PeerConfig{}
+		var (
+			addressHas4 bool
+			addressHas6 bool
+		)
+		for _, localAddress := range options.LocalAddress {
+			if localAddress.Addr().Is4() {
+				addressHas4 = true
+			} else {
+				addressHas6 = true
+			}
+		}
+		if addressHas4 {
+			peer.AllowedIPs = append(peer.AllowedIPs, netip.PrefixFrom(netip.IPv4Unspecified(), 0).String())
+		}
+		if addressHas6 {
+			peer.AllowedIPs = append(peer.AllowedIPs, netip.PrefixFrom(netip.IPv6Unspecified(), 0).String())
+		}
+		destination := options.ServerOptions.Build()
+		if destination.IsFqdn() {
+			peer.destination = destination
+			peer.domainStrategy = dns.DomainStrategy(options.DomainStrategy)
+		} else {
+			peer.Endpoint = destination.AddrPort()
+		}
+		{
+			bytes, err := base64.StdEncoding.DecodeString(options.PeerPublicKey)
+			if err != nil {
+				return nil, E.Cause(err, "decode peer public key")
+			}
+			peer.PublicKey = hex.EncodeToString(bytes)
+		}
+		if options.PreSharedKey != "" {
+			bytes, err := base64.StdEncoding.DecodeString(options.PreSharedKey)
+			if err != nil {
+				return nil, E.Cause(err, "decode pre shared key")
+			}
+			peer.PreSharedKey = hex.EncodeToString(bytes)
+		}
+		if len(options.Reserved) > 0 {
+			if len(options.Reserved) != 3 {
+				return nil, E.New("invalid reserved value, required 3 bytes, got ", len(peer.Reserved))
+			}
+			copy(peer.Reserved[:], options.Reserved)
+		}
+		peers = append(peers, peer)
+	}
+	return peers, nil
+}
+
+func ResolvePeers(ctx context.Context, router adapter.Router, peers []PeerConfig) error {
+	for peerIndex, peer := range peers {
+		if peer.Endpoint.IsValid() {
+			continue
+		}
+		destinationAddresses, err := router.Lookup(ctx, peer.destination.Fqdn, peer.domainStrategy)
+		if err != nil {
+			if len(peers) == 1 {
+				return E.Cause(err, "resolve endpoint domain")
+			} else {
+				return E.Cause(err, "resolve endpoint domain for peer ", peerIndex)
+			}
+		}
+		if len(destinationAddresses) == 0 {
+			return E.New("no addresses found for endpoint domain: ", peer.destination.Fqdn)
+		}
+		peers[peerIndex].Endpoint = netip.AddrPortFrom(destinationAddresses[0], peer.destination.Port)
+
+	}
+	return nil
+}