From a0c46bb4b7703ab3128beb58c07cf0447ffdb373 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 24 May 2025 15:57:49 +0800 Subject: [PATCH] chore: remove the redundant layer of udpnat in sing-tun to reduce resource usage when processing udp --- go.mod | 2 +- go.sum | 4 +- listener/sing/sing.go | 74 ++++++++++++++++++++-------------- listener/sing_tun/dns.go | 85 ++++++++++++++++++++++++---------------- 4 files changed, 100 insertions(+), 65 deletions(-) diff --git a/go.mod b/go.mod index cf9ce2c7..c9b9b252 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/metacubex/sing-shadowsocks v0.2.9 github.com/metacubex/sing-shadowsocks2 v0.2.3 github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 - github.com/metacubex/sing-tun v0.4.6-0.20250523121712-972bc1c2b1e7 + github.com/metacubex/sing-tun v0.4.6-0.20250524070426-1a615b3de4c5 github.com/metacubex/sing-vmess v0.2.1 github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f github.com/metacubex/smux v0.0.0-20250503055512-501391591dee diff --git a/go.sum b/go.sum index 6b0c9373..0ce934ae 100644 --- a/go.sum +++ b/go.sum @@ -128,8 +128,8 @@ github.com/metacubex/sing-shadowsocks2 v0.2.3 h1:v3rNS/5Ywh0NIZ6VU/NmdERQIN5RePz github.com/metacubex/sing-shadowsocks2 v0.2.3/go.mod h1:/WNy/Q8ahLCoPRriWuFZFD0Jy+JNp1MEQl28Zw6SaF8= github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI= github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E= -github.com/metacubex/sing-tun v0.4.6-0.20250523121712-972bc1c2b1e7 h1:vxkBCkZocH2de2tqeeCZxWvT1VjSJPFkE/8hGqvcLCQ= -github.com/metacubex/sing-tun v0.4.6-0.20250523121712-972bc1c2b1e7/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U= +github.com/metacubex/sing-tun v0.4.6-0.20250524070426-1a615b3de4c5 h1:i+aNFgMMcGFl0vxh7xf4LwAWhc7OzOBdd9SKsWx6vdQ= +github.com/metacubex/sing-tun v0.4.6-0.20250524070426-1a615b3de4c5/go.mod h1:HDaHDL6onAX2ZGbAGUXKp++PohRdNb7Nzt6zxzhox+U= github.com/metacubex/sing-vmess v0.2.1 h1:I6gM3VUjtvJ15D805EUbNH+SRBuqzJeFnuIbKYUsWZ0= github.com/metacubex/sing-vmess v0.2.1/go.mod h1:DsODWItJtOMZNna8Qhheg8r3tUivrcO3vWgaTYKnfTo= github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU= diff --git a/listener/sing/sing.go b/listener/sing/sing.go index a2ce9b0a..741fa963 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -146,11 +146,11 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error { defer func() { _ = conn.Close() }() mutex := sync.Mutex{} - conn2 := bufio.NewNetPacketConn(conn) // a new interface to set nil in defer + writer := bufio.NewNetPacketWriter(conn) // a new interface to set nil in defer defer func() { mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running defer mutex.Unlock() - conn2 = nil + writer = nil }() rwOptions := network.ReadWaitOptions{} readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn) @@ -180,32 +180,48 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. return err } cPacket := &packet{ - conn: &conn2, - mutex: &mutex, - rAddr: metadata.Source.UDPAddr(), - lAddr: conn.LocalAddr(), - buff: buff, + writer: &writer, + mutex: &mutex, + rAddr: metadata.Source.UDPAddr(), + lAddr: conn.LocalAddr(), + buff: buff, } - - cMetadata := &C.Metadata{ - NetWork: C.UDP, - Type: h.Type, - } - if metadata.Source.IsIP() && metadata.Source.Fqdn == "" { - cMetadata.RawSrcAddr = metadata.Source.Unwrap().UDPAddr() - } - if dest.IsIP() && dest.Fqdn == "" { - cMetadata.RawDstAddr = dest.Unwrap().UDPAddr() - } - inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(dest), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr())) - inbound.ApplyAdditions(cMetadata, h.Additions...) - inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...) - - h.Tunnel.HandleUDPPacket(cPacket, cMetadata) + h.handlePacket(ctx, cPacket, metadata.Source, dest) } return nil } +func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) { + writer := bufio.NewNetPacketWriter(init(nil)) + mutex := sync.Mutex{} + cPacket := &packet{ + writer: &writer, + mutex: &mutex, + rAddr: metadata.Source.UDPAddr(), + lAddr: metadata.Source.UDPAddr(), // tun does not have real inAddr + buff: buffer, + } + h.handlePacket(ctx, cPacket, metadata.Source, metadata.Destination) +} + +func (h *ListenerHandler) handlePacket(ctx context.Context, cPacket *packet, source M.Socksaddr, destination M.Socksaddr) { + cMetadata := &C.Metadata{ + NetWork: C.UDP, + Type: h.Type, + } + if source.IsIP() && source.Fqdn == "" { + cMetadata.RawSrcAddr = source.Unwrap().UDPAddr() + } + if destination.IsIP() && destination.Fqdn == "" { + cMetadata.RawDstAddr = destination.Unwrap().UDPAddr() + } + inbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(destination), inbound.WithSrcAddr(source), inbound.WithInAddr(cPacket.InAddr())) + inbound.ApplyAdditions(cMetadata, h.Additions...) + inbound.ApplyAdditions(cMetadata, getAdditions(ctx)...) + + h.Tunnel.HandleUDPPacket(cPacket, cMetadata) +} + func (h *ListenerHandler) NewError(ctx context.Context, err error) { log.Warnln("%s listener get error: %+v", h.Type.String(), err) } @@ -225,11 +241,11 @@ func ShouldIgnorePacketError(err error) bool { } type packet struct { - conn *network.NetPacketConn - mutex *sync.Mutex - rAddr net.Addr - lAddr net.Addr - buff *buf.Buffer + writer *network.NetPacketWriter + mutex *sync.Mutex + rAddr net.Addr + lAddr net.Addr + buff *buf.Buffer } func (c *packet) Data() []byte { @@ -245,7 +261,7 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { c.mutex.Lock() defer c.mutex.Unlock() - conn := *c.conn + conn := *c.writer if conn == nil { err = errors.New("writeBack to closed connection") return diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index ab565848..0b8a3ebe 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -43,16 +43,31 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta return h.ListenerHandler.NewConnection(ctx, conn, metadata) } +func (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) { + if h.ShouldHijackDns(metadata.Destination.AddrPort()) { + log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String()) + writer := init(nil) + rwOptions := network.ReadWaitOptions{ + FrontHeadroom: network.CalculateFrontHeadroom(writer), + RearHeadroom: network.CalculateRearHeadroom(writer), + MTU: resolver.SafeDnsPacketSize, + } + go relayDnsPacket(ctx, buffer, rwOptions, metadata.Destination, nil, &writer) + return + } + h.ListenerHandler.NewPacket(ctx, key, buffer, metadata, init) +} + func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error { if h.ShouldHijackDns(metadata.Destination.AddrPort()) { log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String()) defer func() { _ = conn.Close() }() mutex := sync.Mutex{} - conn2 := conn // a new interface to set nil in defer + var writer network.PacketWriter = conn // a new interface to set nil in defer defer func() { mutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running defer mutex.Unlock() - conn2 = nil + writer = nil }() rwOptions := network.ReadWaitOptions{ FrontHeadroom: network.CalculateFrontHeadroom(conn), @@ -89,43 +104,47 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. } return err } - go func() { - ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout) - defer cancel() - inData := readBuff.Bytes() - writeBuff := readBuff - writeBuff.Resize(writeBuff.Start(), 0) - if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough - writeBuff = rwOptions.NewPacketBuffer() - } - msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes()) - if writeBuff != readBuff { - readBuff.Release() - } - if err != nil { - writeBuff.Release() - return - } - writeBuff.Truncate(len(msg)) - mutex.Lock() - defer mutex.Unlock() - conn := conn2 - if conn == nil { - writeBuff.Release() - return - } - err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff - if err != nil { - writeBuff.Release() - return - } - }() + go relayDnsPacket(ctx, readBuff, rwOptions, dest, &mutex, &writer) } return nil } return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata) } +func relayDnsPacket(ctx context.Context, readBuff *buf.Buffer, rwOptions network.ReadWaitOptions, dest M.Socksaddr, mutex *sync.Mutex, writer *network.PacketWriter) { + ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout) + defer cancel() + inData := readBuff.Bytes() + writeBuff := readBuff + writeBuff.Resize(writeBuff.Start(), 0) + if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough + writeBuff = rwOptions.NewPacketBuffer() + } + msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes()) + if writeBuff != readBuff { + readBuff.Release() + } + if err != nil { + writeBuff.Release() + return + } + writeBuff.Truncate(len(msg)) + if mutex != nil { + mutex.Lock() + defer mutex.Unlock() + } + conn := *writer + if conn == nil { + writeBuff.Release() + return + } + err = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff + if err != nil { + writeBuff.Release() + return + } +} + func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler { handle := *h handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ)