From cbab86ae380efef08463e2e5bd23218ce5d3e9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 25 Sep 2022 17:13:42 +0800 Subject: [PATCH] Refine tproxy write back --- common/redir/tproxy_linux.go | 93 +++++------------------------------- common/redir/tproxy_other.go | 11 +++-- inbound/tproxy.go | 42 +++++++++------- 3 files changed, 43 insertions(+), 103 deletions(-) diff --git a/common/redir/tproxy_linux.go b/common/redir/tproxy_linux.go index 5458ee3c..ccf94037 100644 --- a/common/redir/tproxy_linux.go +++ b/common/redir/tproxy_linux.go @@ -2,14 +2,11 @@ package redir import ( "encoding/binary" - "net" "net/netip" - "os" - "strconv" "syscall" + "github.com/sagernet/sing/common/control" E "github.com/sagernet/sing/common/exceptions" - F "github.com/sagernet/sing/common/format" M "github.com/sagernet/sing/common/metadata" "golang.org/x/sys/unix" @@ -32,6 +29,18 @@ func TProxy(fd uintptr, isIPv6 bool) error { return err } +func TProxyWriteBack() control.Func { + return func(network, address string, conn syscall.RawConn) error { + return control.Raw(conn, func(fd uintptr) error { + if M.ParseSocksaddr(address).Addr.Is6() { + return syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, unix.IPV6_TRANSPARENT, 1) + } else { + return syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1) + } + }) + } +} + func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) { controlMessages, err := unix.ParseSocketControlMessage(oob) if err != nil { @@ -46,79 +55,3 @@ func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) { } return netip.AddrPort{}, E.New("not found") } - -func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) { - rSockAddr, err := udpAddrToSockAddr(rAddr) - if err != nil { - return nil, err - } - - lSockAddr, err := udpAddrToSockAddr(lAddr) - if err != nil { - return nil, err - } - - fd, err := syscall.Socket(udpAddrFamily(lAddr, rAddr), syscall.SOCK_DGRAM, 0) - if err != nil { - return nil, err - } - - if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { - syscall.Close(fd) - return nil, err - } - - if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { - syscall.Close(fd) - return nil, err - } - - if err = syscall.Bind(fd, lSockAddr); err != nil { - syscall.Close(fd) - return nil, err - } - - if err = syscall.Connect(fd, rSockAddr); err != nil { - syscall.Close(fd) - return nil, err - } - - fdFile := os.NewFile(uintptr(fd), F.ToString("net-udp-dial-", rAddr)) - defer fdFile.Close() - - c, err := net.FileConn(fdFile) - if err != nil { - syscall.Close(fd) - return nil, err - } - - return c.(*net.UDPConn), nil -} - -func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) { - switch { - case addr.IP.To4() != nil: - ip := [4]byte{} - copy(ip[:], addr.IP.To4()) - - return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil - - default: - ip := [16]byte{} - copy(ip[:], addr.IP.To16()) - - zoneID, err := strconv.ParseUint(addr.Zone, 10, 32) - if err != nil { - zoneID = 0 - } - - return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil - } -} - -func udpAddrFamily(lAddr, rAddr *net.UDPAddr) int { - if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) { - return syscall.AF_INET - } - return syscall.AF_INET6 -} diff --git a/common/redir/tproxy_other.go b/common/redir/tproxy_other.go index 12e575d8..5869a195 100644 --- a/common/redir/tproxy_other.go +++ b/common/redir/tproxy_other.go @@ -3,19 +3,20 @@ package redir import ( - "net" "net/netip" "os" + + "github.com/sagernet/sing/common/control" ) func TProxy(fd uintptr, isIPv6 bool) error { return os.ErrInvalid } +func TProxyWriteBack() control.Func { + return nil +} + func GetOriginalDestinationFromOOB(oob []byte) (netip.AddrPort, error) { return netip.AddrPort{}, os.ErrInvalid } - -func DialUDP(lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) { - return nil, os.ErrInvalid -} diff --git a/inbound/tproxy.go b/inbound/tproxy.go index a322f6cf..46162737 100644 --- a/inbound/tproxy.go +++ b/inbound/tproxy.go @@ -86,12 +86,13 @@ func (t *TProxy) NewPacket(ctx context.Context, conn N.PacketConn, buffer *buf.B } metadata.Destination = M.SocksaddrFromNetIP(destination) t.udpNat.NewContextPacket(ctx, metadata.Source.AddrPort(), buffer, adapter.UpstreamMetadata(metadata), func(natConn N.PacketConn) (context.Context, N.PacketWriter) { - return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{source: natConn} + return adapter.WithContext(log.ContextWithNewID(ctx), &metadata), &tproxyPacketWriter{ctx: ctx, source: natConn} }) return nil } type tproxyPacketWriter struct { + ctx context.Context source N.PacketConn destination M.Socksaddr conn *net.UDPConn @@ -99,25 +100,30 @@ type tproxyPacketWriter struct { func (w *tproxyPacketWriter) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { defer buffer.Release() - var udpConn *net.UDPConn + destination = destination.Unwrap() + if !w.destination.Addr.IsValid() { + w.destination = destination + } else if w.destination == destination && w.conn != nil { + _, err := w.conn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr())) + if err == nil { + w.conn = nil + } + return err + } + var listener net.ListenConfig + listener.Control = control.Append(listener.Control, control.ReuseAddr()) + listener.Control = control.Append(listener.Control, redir.TProxyWriteBack()) + packetConn, err := listener.ListenPacket(w.ctx, "udp", destination.String()) + if err != nil { + return err + } + udpConn := packetConn.(*net.UDPConn) if w.destination == destination { - if w.conn != nil { - udpConn = w.conn - } + w.conn = udpConn + } else { + defer udpConn.Close() } - if udpConn == nil { - var err error - udpConn, err = redir.DialUDP(destination.UDPAddr(), M.SocksaddrFromNet(w.source.LocalAddr()).UDPAddr()) - if err != nil { - return E.Cause(err, "tproxy udp write back") - } - if w.destination == destination { - w.conn = udpConn - } else { - defer udpConn.Close() - } - } - return common.Error(udpConn.Write(buffer.Bytes())) + return common.Error(udpConn.WriteToUDPAddrPort(buffer.Bytes(), M.AddrPortFromNet(w.source.LocalAddr()))) } func (w *tproxyPacketWriter) Close() error {