refactor: udp

This commit is contained in:
世界 2022-06-09 21:16:42 +08:00
parent cd466f05d3
commit 637a8b6ed5
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
23 changed files with 231 additions and 127 deletions

View File

@ -2,7 +2,7 @@ package inbound
import ( import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5" M "github.com/sagernet/sing/common/metadata"
) )
// PacketAdapter is a UDP Packet adapter for socks/redir/tun // PacketAdapter is a UDP Packet adapter for socks/redir/tun
@ -17,8 +17,8 @@ func (s *PacketAdapter) Metadata() *C.Metadata {
} }
// NewPacket is PacketAdapter generator // NewPacket is PacketAdapter generator
func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type) *PacketAdapter { func NewPacket(target M.Socksaddr, packet C.UDPPacket, source C.Type) *PacketAdapter {
metadata := parseSocksAddr(target) metadata := socksAddrToMetadata(target)
metadata.NetWork = C.UDP metadata.NetWork = C.UDP
metadata.Type = source metadata.Type = source
if ip, port, err := parseAddr(packet.LocalAddr().String()); err == nil { if ip, port, err := parseAddr(packet.LocalAddr().String()); err == nil {

View File

@ -10,8 +10,26 @@ import (
"github.com/Dreamacro/clash/common/nnip" "github.com/Dreamacro/clash/common/nnip"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/socks5"
M "github.com/sagernet/sing/common/metadata"
) )
func socksAddrToMetadata(addr M.Socksaddr) *C.Metadata {
metadata := &C.Metadata{}
switch addr.Family() {
case M.AddressFamilyIPv4:
metadata.AddrType = C.AtypIPv4
metadata.DstIP = addr.Addr
case M.AddressFamilyIPv6:
metadata.AddrType = C.AtypIPv6
metadata.DstIP = addr.Addr
case M.AddressFamilyFqdn:
metadata.AddrType = C.AtypDomainName
metadata.Host = addr.Fqdn
}
metadata.DstPort = strconv.Itoa(int(addr.Port))
return metadata
}
func parseSocksAddr(target socks5.Addr) *C.Metadata { func parseSocksAddr(target socks5.Addr) *C.Metadata {
metadata := &C.Metadata{ metadata := &C.Metadata{
AddrType: int(target[0]), AddrType: int(target[0]),

View File

@ -10,6 +10,10 @@ import (
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
) )
type Base struct { type Base struct {
@ -157,6 +161,7 @@ func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {
type packetConn struct { type packetConn struct {
net.PacketConn net.PacketConn
nc N.PacketConn
chain C.Chain chain C.Chain
actualRemoteDestination string actualRemoteDestination string
} }
@ -175,8 +180,22 @@ func (c *packetConn) AppendToChains(a C.ProxyAdapter) {
c.chain = append(c.chain, a.Name()) c.chain = append(c.chain, a.Name())
} }
func (c *packetConn) ReadPacket(buffer *buf.Buffer) (addr M.Socksaddr, err error) {
return c.nc.ReadPacket(buffer)
}
func (c *packetConn) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error {
return c.nc.WritePacket(buffer, addr)
}
func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn { func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn {
return &packetConn{pc, []string{a.Name()}, parseRemoteDestination(a.Addr())} var nc N.PacketConn
if n, isNc := pc.(N.PacketConn); isNc {
nc = n
} else {
nc = &bufio.PacketConnWrapper{PacketConn: pc}
}
return &packetConn{pc, nc, []string{a.Name()}, parseRemoteDestination(a.Addr())}
} }
func parseRemoteDestination(addr string) string { func parseRemoteDestination(addr string) string {

View File

@ -7,7 +7,6 @@ import (
"net" "net"
"strconv" "strconv"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/common/structure"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
@ -16,15 +15,10 @@ import (
v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin"
"github.com/sagernet/sing-shadowsocks" "github.com/sagernet/sing-shadowsocks"
"github.com/sagernet/sing-shadowsocks/shadowimpl" "github.com/sagernet/sing-shadowsocks/shadowimpl"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio" "github.com/sagernet/sing/common/bufio"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
) )
func init() {
buf.DefaultAllocator = pool.DefaultAllocator
}
type ShadowSocks struct { type ShadowSocks struct {
*Base *Base
method shadowsocks.Method method shadowsocks.Method

View File

@ -6,9 +6,15 @@ import (
"errors" "errors"
"math/bits" "math/bits"
"sync" "sync"
"github.com/sagernet/sing/common/buf"
) )
var DefaultAllocator = NewAllocator() var defaultAllocator = NewAllocator()
func init() {
buf.DefaultAllocator = defaultAllocator
}
// Allocator for incoming frames, optimized to prevent overwriting after zeroing // Allocator for incoming frames, optimized to prevent overwriting after zeroing
type Allocator struct { type Allocator struct {

View File

@ -13,9 +13,9 @@ const (
) )
func Get(size int) []byte { func Get(size int) []byte {
return DefaultAllocator.Get(size) return defaultAllocator.Get(size)
} }
func Put(buf []byte) error { func Put(buf []byte) error {
return DefaultAllocator.Put(buf) return defaultAllocator.Put(buf)
} }

View File

@ -26,8 +26,8 @@ import (
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
providerTypes "github.com/Dreamacro/clash/constant/provider" providerTypes "github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/constant/sniffer"
snifferTypes "github.com/Dreamacro/clash/constant/sniffer" snifferTypes "github.com/Dreamacro/clash/constant/sniffer"
"github.com/Dreamacro/clash/constant/sniffer"
"github.com/Dreamacro/clash/dns" "github.com/Dreamacro/clash/dns"
"github.com/Dreamacro/clash/listener/tun/ipstack/commons" "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"

View File

@ -7,6 +7,8 @@ import (
"time" "time"
"github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/dialer"
"github.com/sagernet/sing/common/buf"
N "github.com/sagernet/sing/common/network"
) )
// Adapter Type // Adapter Type
@ -75,6 +77,7 @@ type Conn interface {
type PacketConn interface { type PacketConn interface {
net.PacketConn net.PacketConn
Connection Connection
N.PacketConn
// Deprecate WriteWithMetadata because of remote resolve DNS cause TURN failed // Deprecate WriteWithMetadata because of remote resolve DNS cause TURN failed
// WriteWithMetadata(p []byte, metadata *Metadata) (n int, err error) // WriteWithMetadata(p []byte, metadata *Metadata) (n int, err error)
} }
@ -184,7 +187,7 @@ func (at AdapterType) String() string {
// UDPPacket contains the data of UDP packet, and offers control/info of UDP packet's source // UDPPacket contains the data of UDP packet, and offers control/info of UDP packet's source
type UDPPacket interface { type UDPPacket interface {
// Data get the payload of UDP Packet // Data get the payload of UDP Packet
Data() []byte Data() *buf.Buffer
// WriteBack writes the payload with source IP/Port equals addr // WriteBack writes the payload with source IP/Port equals addr
// - variable source IP/Port is important to STUN // - variable source IP/Port is important to STUN
@ -192,8 +195,7 @@ type UDPPacket interface {
// this is important when using Fake-IP. // this is important when using Fake-IP.
WriteBack(b []byte, addr net.Addr) (n int, err error) WriteBack(b []byte, addr net.Addr) (n int, err error)
// Drop call after packet is used, could recycle buffer in this function. N.PacketWriter
Drop()
// LocalAddr returns the source IP/Port of packet // LocalAddr returns the source IP/Port of packet
LocalAddr() net.Addr LocalAddr() net.Addr

View File

@ -6,6 +6,8 @@ import (
"net" "net"
"net/netip" "net/netip"
"strconv" "strconv"
M "github.com/sagernet/sing/common/metadata"
) )
// Socks addr type // Socks addr type
@ -170,6 +172,18 @@ func (m *Metadata) UDPAddr() *net.UDPAddr {
} }
} }
func (m *Metadata) Socksaddr() M.Socksaddr {
port, _ := strconv.ParseUint(m.DstPort, 10, 16)
if m.Host != "" {
return M.Socksaddr{
Fqdn: m.Host,
Port: uint16(port),
}
} else {
return M.SocksaddrFromAddrPort(m.DstIP, uint16(port))
}
}
func (m *Metadata) String() string { func (m *Metadata) String() string {
if m.Host != "" { if m.Host != "" {
return m.Host return m.Host

2
go.mod
View File

@ -13,7 +13,7 @@ require (
github.com/lucas-clemente/quic-go v0.27.2 github.com/lucas-clemente/quic-go v0.27.2
github.com/miekg/dns v1.1.49 github.com/miekg/dns v1.1.49
github.com/oschwald/geoip2-golang v1.7.0 github.com/oschwald/geoip2-golang v1.7.0
github.com/sagernet/sing v0.0.0-20220609091055-86d0144940e7 github.com/sagernet/sing v0.0.0-20220609123159-a93588755159
github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13 github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.2 github.com/stretchr/testify v1.7.2

4
go.sum
View File

@ -305,8 +305,8 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sagernet/sing v0.0.0-20220609091055-86d0144940e7 h1:Q+uNKLNSKqpx+p96qcBTVFh8RUKiQFr4IrNVi5Q5yl0= github.com/sagernet/sing v0.0.0-20220609123159-a93588755159 h1:G3fww5jjADkHWi6yDOEzkZbQ6lnrytv0mKesBtEslxo=
github.com/sagernet/sing v0.0.0-20220609091055-86d0144940e7/go.mod h1:w2HnJzXKHpD6F5Z/9XlSD4qbcpHY2RSZuQnFzqgELMg= github.com/sagernet/sing v0.0.0-20220609123159-a93588755159/go.mod h1:w2HnJzXKHpD6F5Z/9XlSD4qbcpHY2RSZuQnFzqgELMg=
github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13 h1:bQN0hjTHdB7SyaD9yjEYAl+bDl/kXW9zC0xNa+LMTrA= github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13 h1:bQN0hjTHdB7SyaD9yjEYAl+bDl/kXW9zC0xNa+LMTrA=
github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13/go.mod h1:Fp/9+odJhtgDmiHbZClMLnxaVvmDRJxwA7u/+uXWDiQ= github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13/go.mod h1:Fp/9+odJhtgDmiHbZClMLnxaVvmDRJxwA7u/+uXWDiQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=

View File

@ -4,11 +4,11 @@ import (
"net" "net"
"github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/common/sockopt" "github.com/Dreamacro/clash/common/sockopt"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5" "github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
) )
type UDPListener struct { type UDPListener struct {
@ -49,34 +49,35 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
} }
go func() { go func() {
for { for {
buf := pool.Get(pool.UDPBufferSize) buffer := buf.NewPacket()
n, remoteAddr, err := l.ReadFrom(buf) n, remoteAddr, err := l.ReadFrom(buffer.FreeBytes())
if err != nil { if err != nil {
pool.Put(buf) buffer.Release()
if sl.closed { if sl.closed {
break break
} }
continue continue
} }
handleSocksUDP(l, in, buf[:n], remoteAddr) buffer.Extend(n)
handleSocksUDP(l, in, buffer, remoteAddr)
} }
}() }()
return sl, nil return sl, nil
} }
func handleSocksUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, addr net.Addr) { func handleSocksUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buffer *buf.Buffer, addr net.Addr) {
target, payload, err := socks5.DecodeUDPPacket(buf) buffer.Advance(3)
target, err := M.SocksaddrSerializer.ReadAddrPort(buffer)
if err != nil { if err != nil {
// Unresolved UDP packet, return buffer to the pool // Unresolved UDP packet, return buffer to the pool
pool.Put(buf) buffer.Release()
return return
} }
packet := &packet{ packet := &packet{
pc: pc, pc: pc,
rAddr: addr, rAddr: addr,
payload: payload, payload: buffer,
bufRef: buf,
} }
select { select {
case in <- inbound.NewPacket(target, packet, C.SOCKS5): case in <- inbound.NewPacket(target, packet, C.SOCKS5):

View File

@ -3,18 +3,19 @@ package socks
import ( import (
"net" "net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/socks5"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
) )
type packet struct { type packet struct {
pc net.PacketConn pc net.PacketConn
rAddr net.Addr rAddr net.Addr
payload []byte payload *buf.Buffer
bufRef []byte
} }
func (c *packet) Data() []byte { func (c *packet) Data() *buf.Buffer {
return c.payload return c.payload
} }
@ -27,11 +28,15 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
return c.pc.WriteTo(packet, c.rAddr) return c.pc.WriteTo(packet, c.rAddr)
} }
func (c *packet) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
defer buffer.Release()
header := buf.With(buffer.ExtendHeader(3 + M.SocksaddrSerializer.AddrPortLen(destination)))
common.Must(header.WriteZeroN(3))
common.Must(M.SocksaddrSerializer.WriteAddrPort(header, destination))
return common.Error(c.pc.WriteTo(buffer.Bytes(), c.rAddr))
}
// LocalAddr returns the source IP/Port of UDP Packet // LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr { func (c *packet) LocalAddr() net.Addr {
return c.rAddr return c.rAddr
} }
func (c *packet) Drop() {
pool.Put(c.bufRef)
}

View File

@ -3,15 +3,16 @@ package tproxy
import ( import (
"net" "net"
"github.com/Dreamacro/clash/common/pool" "github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
) )
type packet struct { type packet struct {
lAddr *net.UDPAddr lAddr *net.UDPAddr
buf []byte buf *buf.Buffer
} }
func (c *packet) Data() []byte { func (c *packet) Data() *buf.Buffer {
return c.buf return c.buf
} }
@ -27,11 +28,18 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
return return
} }
func (c *packet) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error {
defer buffer.Release()
tc, err := dialUDP("udp", addr.UDPAddr(), c.lAddr)
defer tc.Close()
if err != nil {
return err
}
_, err = tc.Write(buffer.Bytes())
return nil
}
// LocalAddr returns the source IP/Port of UDP Packet // LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr { func (c *packet) LocalAddr() net.Addr {
return c.lAddr return c.lAddr
} }
func (c *packet) Drop() {
pool.Put(c.buf)
}

View File

@ -4,9 +4,9 @@ import (
"net" "net"
"github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/adapter/inbound"
"github.com/Dreamacro/clash/common/pool"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5" "github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
) )
type UDPListener struct { type UDPListener struct {
@ -57,10 +57,10 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
go func() { go func() {
oob := make([]byte, 1024) oob := make([]byte, 1024)
for { for {
buf := pool.Get(pool.UDPBufferSize) buffer := buf.NewPacket()
n, oobn, _, lAddr, err := c.ReadMsgUDP(buf, oob) n, oobn, _, lAddr, err := c.ReadMsgUDP(buffer.FreeBytes(), oob)
if err != nil { if err != nil {
pool.Put(buf) buffer.Release()
if rl.closed { if rl.closed {
break break
} }
@ -71,21 +71,21 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error)
if err != nil { if err != nil {
continue continue
} }
handlePacketConn(l, in, buf[:n], lAddr, rAddr) buffer.Extend(n)
handlePacketConn(l, in, buffer, lAddr, rAddr)
} }
}() }()
return rl, nil return rl, nil
} }
func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) { func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf *buf.Buffer, lAddr *net.UDPAddr, rAddr *net.UDPAddr) {
target := socks5.ParseAddrToSocksAddr(rAddr)
pkt := &packet{ pkt := &packet{
lAddr: lAddr, lAddr: lAddr,
buf: buf, buf: buf,
} }
select { select {
case in <- inbound.NewPacket(target, pkt, C.TPROXY): case in <- inbound.NewPacket(M.SocksaddrFromNet(rAddr), pkt, C.TPROXY):
default: default:
} }
} }

View File

@ -15,6 +15,8 @@ import (
D "github.com/Dreamacro/clash/listener/tun/ipstack/commons" D "github.com/Dreamacro/clash/listener/tun/ipstack/commons"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter" "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/socks5"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
) )
var _ adapter.Handler = (*gvHandler)(nil) var _ adapter.Handler = (*gvHandler)(nil)
@ -96,27 +98,24 @@ func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
return return
} }
target := socks5.ParseAddrToSocksAddr(rAddr) target := M.SocksaddrFromNet(rAddr)
go func() { go func() {
for { for {
buf := pool.Get(pool.UDPBufferSize) buffer := buf.NewPacket()
n, addr, err := tunConn.ReadFrom(buf) n, addr, err := tunConn.ReadFrom(buffer.FreeBytes())
if err != nil { if err != nil {
_ = pool.Put(buf) buffer.Release()
break break
} }
buffer.Truncate(n)
payload := buf[:n]
if D.ShouldHijackDns(gh.dnsHijack, rAddrPort) { if D.ShouldHijackDns(gh.dnsHijack, rAddrPort) {
go func() { go func() {
defer func() { defer buffer.Release()
_ = pool.Put(buf)
}()
msg, err1 := D.RelayDnsPacket(payload) msg, err1 := D.RelayDnsPacket(buffer.Bytes())
if err1 != nil { if err1 != nil {
return return
} }
@ -130,7 +129,7 @@ func (gh *gvHandler) HandleUDP(tunConn adapter.UDPConn) {
gvPacket := &packet{ gvPacket := &packet{
pc: tunConn, pc: tunConn,
rAddr: addr, rAddr: addr,
payload: payload, payload: buffer,
} }
select { select {

View File

@ -5,10 +5,12 @@ package gvisor
import ( import (
"net" "net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter" "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/adapter"
"github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option" "github.com/Dreamacro/clash/listener/tun/ipstack/gvisor/option"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet" "gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
"gvisor.dev/gvisor/pkg/tcpip/stack" "gvisor.dev/gvisor/pkg/tcpip/stack"
"gvisor.dev/gvisor/pkg/tcpip/transport/udp" "gvisor.dev/gvisor/pkg/tcpip/transport/udp"
@ -51,10 +53,10 @@ func (c *udpConn) ID() *stack.TransportEndpointID {
type packet struct { type packet struct {
pc adapter.UDPConn pc adapter.UDPConn
rAddr net.Addr rAddr net.Addr
payload []byte payload *buf.Buffer
} }
func (c *packet) Data() []byte { func (c *packet) Data() *buf.Buffer {
return c.payload return c.payload
} }
@ -63,11 +65,11 @@ func (c *packet) WriteBack(b []byte, _ net.Addr) (n int, err error) {
return c.pc.WriteTo(b, c.rAddr) return c.pc.WriteTo(b, c.rAddr)
} }
func (c *packet) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error {
return common.Error(c.pc.WriteTo(buffer.Bytes(), c.rAddr))
}
// LocalAddr returns the source IP/Port of UDP Packet // LocalAddr returns the source IP/Port of UDP Packet
func (c *packet) LocalAddr() net.Addr { func (c *packet) LocalAddr() net.Addr {
return c.rAddr return c.rAddr
} }
func (c *packet) Drop() {
_ = pool.Put(c.payload)
}

View File

@ -21,7 +21,8 @@ import (
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars" "github.com/Dreamacro/clash/listener/tun/ipstack/system/mars"
"github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/nat" "github.com/Dreamacro/clash/listener/tun/ipstack/system/mars/nat"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/transport/socks5" "github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
) )
type sysStack struct { type sysStack struct {
@ -153,37 +154,37 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
}(stack.UDP()) }(stack.UDP())
for !ipStack.closed { for !ipStack.closed {
buf := pool.Get(pool.UDPBufferSize) buffer := buf.NewPacket()
n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf) n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buffer.FreeBytes())
if err != nil { if err != nil {
_ = pool.Put(buf) buffer.Release()
break break
} }
buffer.Truncate(n)
raw := buf[:n]
lAddr := lRAddr.(*net.UDPAddr) lAddr := lRAddr.(*net.UDPAddr)
rAddr := rRAddr.(*net.UDPAddr) rAddr := rRAddr.(*net.UDPAddr)
rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port)) rAddrPort := netip.AddrPortFrom(nnip.IpToAddr(rAddr.IP), uint16(rAddr.Port))
if rAddrPort.Addr().IsLoopback() || rAddrPort.Addr() == gateway { if rAddrPort.Addr().IsLoopback() || rAddrPort.Addr() == gateway {
_ = pool.Put(buf) buffer.Release()
continue continue
} }
if D.ShouldHijackDns(dnsAddr, rAddrPort) { if D.ShouldHijackDns(dnsAddr, rAddrPort) {
go func() { go func() {
msg, err := D.RelayDnsPacket(raw) msg, err := D.RelayDnsPacket(buffer.Bytes())
if err != nil { if err != nil {
_ = pool.Put(buf) buffer.Release()
return return
} }
_, _ = stack.UDP().WriteTo(msg, rAddr, lAddr) _, _ = stack.UDP().WriteTo(msg, rAddr, lAddr)
_ = pool.Put(buf) buffer.Release()
}() }()
continue continue
@ -191,17 +192,14 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
pkt := &packet{ pkt := &packet{
local: lAddr, local: lAddr,
data: raw, data: buffer,
writeBack: func(b []byte, addr net.Addr) (int, error) { writeBack: func(b []byte, addr net.Addr) (int, error) {
return stack.UDP().WriteTo(b, rAddr, lAddr) return stack.UDP().WriteTo(b, rAddr, lAddr)
}, },
drop: func() {
_ = pool.Put(buf)
},
} }
select { select {
case udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(rAddr), pkt, C.TUN): case udpIn <- inbound.NewPacket(M.SocksaddrFromNet(rAddr), pkt, C.TUN):
default: default:
} }
} }

View File

@ -1,15 +1,20 @@
package system package system
import "net" import (
"net"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
)
type packet struct { type packet struct {
local *net.UDPAddr local *net.UDPAddr
data []byte data *buf.Buffer
writeBack func(b []byte, addr net.Addr) (int, error) writeBack func(b []byte, addr net.Addr) (int, error)
drop func()
} }
func (pkt *packet) Data() []byte { func (pkt *packet) Data() *buf.Buffer {
return pkt.data return pkt.data
} }
@ -17,8 +22,9 @@ func (pkt *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {
return pkt.writeBack(b, addr) return pkt.writeBack(b, addr)
} }
func (pkt *packet) Drop() { func (pkt *packet) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error {
pkt.drop() defer buffer.Release()
return common.Error(pkt.writeBack(buffer.Bytes(), addr.UDPAddr()))
} }
func (pkt *packet) LocalAddr() net.Addr { func (pkt *packet) LocalAddr() net.Addr {

View File

@ -61,7 +61,7 @@ require (
github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect github.com/prometheus/procfs v0.7.3 // indirect
github.com/sagernet/sing v0.0.0-20220609091055-86d0144940e7 // indirect github.com/sagernet/sing v0.0.0-20220609123159-a93588755159 // indirect
github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13 // indirect github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect github.com/sirupsen/logrus v1.8.1 // indirect
github.com/tobyxdd/hysteria v1.0.4 // indirect github.com/tobyxdd/hysteria v1.0.4 // indirect

View File

@ -323,8 +323,8 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sagernet/sing v0.0.0-20220609091055-86d0144940e7 h1:Q+uNKLNSKqpx+p96qcBTVFh8RUKiQFr4IrNVi5Q5yl0= github.com/sagernet/sing v0.0.0-20220609123159-a93588755159 h1:G3fww5jjADkHWi6yDOEzkZbQ6lnrytv0mKesBtEslxo=
github.com/sagernet/sing v0.0.0-20220609091055-86d0144940e7/go.mod h1:w2HnJzXKHpD6F5Z/9XlSD4qbcpHY2RSZuQnFzqgELMg= github.com/sagernet/sing v0.0.0-20220609123159-a93588755159/go.mod h1:w2HnJzXKHpD6F5Z/9XlSD4qbcpHY2RSZuQnFzqgELMg=
github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13 h1:bQN0hjTHdB7SyaD9yjEYAl+bDl/kXW9zC0xNa+LMTrA= github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13 h1:bQN0hjTHdB7SyaD9yjEYAl+bDl/kXW9zC0xNa+LMTrA=
github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13/go.mod h1:Fp/9+odJhtgDmiHbZClMLnxaVvmDRJxwA7u/+uXWDiQ= github.com/sagernet/sing-shadowsocks v0.0.0-20220609092835-699292971c13/go.mod h1:Fp/9+odJhtgDmiHbZClMLnxaVvmDRJxwA7u/+uXWDiQ=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=

View File

@ -1,19 +1,20 @@
package tunnel package tunnel
import ( import (
"errors"
"net" "net"
"time" "time"
N "github.com/Dreamacro/clash/common/net" cN "github.com/Dreamacro/clash/common/net"
"github.com/Dreamacro/clash/common/pool"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
) )
func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error {
defer packet.Drop() defer packet.Data().Release()
// local resolve UDP dns // local resolve UDP dns
if !metadata.Resolved() { if !metadata.Resolved() {
ip, err := resolver.ResolveIP(metadata.Host) ip, err := resolver.ResolveIP(metadata.Host)
@ -23,12 +24,7 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata
metadata.DstIP = ip metadata.DstIP = ip
} }
addr := metadata.UDPAddr() if err := pc.WritePacket(packet.Data(), metadata.Socksaddr()); err != nil {
if addr == nil {
return errors.New("udp addr invalid")
}
if _, err := pc.WriteTo(packet.Data(), addr); err != nil {
return err return err
} }
// reset timeout // reset timeout
@ -37,30 +33,45 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata
return nil return nil
} }
func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr net.Addr) { func handleUDPToLocal(packet C.UDPPacket, pc C.PacketConn, key string, fAddr net.Addr) {
buf := pool.Get(pool.UDPBufferSize)
defer pool.Put(buf)
defer natTable.Delete(key) defer natTable.Delete(key)
defer pc.Close() defer pc.Close()
var fSocksaddr M.Socksaddr
for {
pc.SetReadDeadline(time.Now().Add(udpTimeout))
n, from, err := pc.ReadFrom(buf)
if err != nil {
return
}
if fAddr != nil { if fAddr != nil {
from = fAddr fSocksaddr = M.SocksaddrFromNet(fAddr)
}
_, err = packet.WriteBack(buf[:n], from)
if err != nil {
return
}
} }
_, _ = copyPacketTimeout(packet, pc, udpTimeout, fSocksaddr, fAddr != nil)
} }
func handleSocket(ctx C.ConnContext, outbound net.Conn) { func handleSocket(ctx C.ConnContext, outbound net.Conn) {
N.Relay(ctx.Conn(), outbound) cN.Relay(ctx.Conn(), outbound)
}
func copyPacketTimeout(dst N.PacketWriter, src N.TimeoutPacketReader, timeout time.Duration, fAddr M.Socksaddr, fOverride bool) (n int64, err error) {
_buffer := buf.StackNewPacket()
defer common.KeepAlive(_buffer)
buffer := common.Dup(_buffer)
buffer.IncRef()
defer buffer.DecRef()
var destination M.Socksaddr
for {
buffer.Reset()
err = src.SetReadDeadline(time.Now().Add(timeout))
if err != nil {
return
}
destination, err = src.ReadPacket(buffer)
if err != nil {
return
}
if fOverride {
destination = fAddr
}
dataLen := buffer.Len()
err = dst.WritePacket(buffer, destination)
if err != nil {
return
}
n += int64(dataLen)
}
} }

View File

@ -6,6 +6,8 @@ import (
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
"github.com/sagernet/sing/common/buf"
M "github.com/sagernet/sing/common/metadata"
"go.uber.org/atomic" "go.uber.org/atomic"
) )
@ -115,6 +117,25 @@ func (ut *udpTracker) WriteTo(b []byte, addr net.Addr) (int, error) {
return n, err return n, err
} }
func (ut *udpTracker) ReadPacket(buffer *buf.Buffer) (addr M.Socksaddr, err error) {
addr, err = ut.PacketConn.ReadPacket(buffer)
download := int64(buffer.Len())
ut.manager.PushDownloaded(download)
ut.DownloadTotal.Add(download)
return
}
func (ut *udpTracker) WritePacket(buffer *buf.Buffer, addr M.Socksaddr) error {
dataLen := buffer.Len()
err := ut.PacketConn.WritePacket(buffer, addr)
if err == nil {
upload := int64(dataLen)
ut.manager.PushUploaded(upload)
ut.UploadTotal.Add(upload)
}
return err
}
func (ut *udpTracker) Close() error { func (ut *udpTracker) Close() error {
ut.manager.Leave(ut) ut.manager.Leave(ut)
return ut.PacketConn.Close() return ut.PacketConn.Close()