Add UDPEgressPool

This commit is contained in:
世界 2026-07-17 10:40:08 +08:00
parent 95bc107a1c
commit 1ba7d79118
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
4 changed files with 414 additions and 3 deletions

124
udp_egress_conn.go Normal file
View file

@ -0,0 +1,124 @@
package tun
import (
"net"
"net/netip"
"sync"
"time"
"github.com/sagernet/sing/common/buf"
E "github.com/sagernet/sing/common/exceptions"
)
type UDPEgressConn struct {
anchor *net.UDPConn
pool *UDPEgressPool
packetChan chan udpEgressConnPacket
doneChan chan struct{}
closeOnce sync.Once
readWait sync.WaitGroup
}
type udpEgressConnPacket struct {
buffer *buf.Buffer
source netip.AddrPort
err error
}
func NewUDPEgressConn(anchor *net.UDPConn, pool *UDPEgressPool) *UDPEgressConn {
conn := &UDPEgressConn{
anchor: anchor,
pool: pool,
packetChan: make(chan udpEgressConnPacket, 64),
doneChan: make(chan struct{}),
}
conn.readWait.Add(2)
go conn.read(anchor.ReadFromUDPAddrPort)
go conn.read(pool.ReceiveEgress)
return conn
}
func (c *UDPEgressConn) read(readPacket func([]byte) (int, netip.AddrPort, error)) {
defer c.readWait.Done()
for {
buffer := buf.NewSize(udpEgressBufferSize)
dataLength, source, err := readPacket(buffer.FreeBytes())
if err != nil {
buffer.Release()
if E.IsClosed(err) {
return
}
select {
case c.packetChan <- udpEgressConnPacket{err: err}:
case <-c.doneChan:
return
}
continue
}
buffer.Extend(dataLength)
select {
case c.packetChan <- udpEgressConnPacket{buffer: buffer, source: source}:
case <-c.doneChan:
buffer.Release()
return
}
}
}
func (c *UDPEgressConn) ReadFromUDPAddrPort(buffer []byte) (int, netip.AddrPort, error) {
select {
case packet := <-c.packetChan:
if packet.err != nil {
return 0, netip.AddrPort{}, packet.err
}
copied := copy(buffer, packet.buffer.Bytes())
packet.buffer.Release()
return copied, packet.source, nil
case <-c.doneChan:
return 0, netip.AddrPort{}, net.ErrClosed
}
}
func (c *UDPEgressConn) WriteToUDPAddrPort(buffer []byte, destination netip.AddrPort) (int, error) {
memberConn := c.pool.LookupEgress(destination)
if memberConn != nil {
return memberConn.WriteToUDPAddrPort(buffer, destination)
}
return c.anchor.WriteToUDPAddrPort(buffer, destination)
}
func (c *UDPEgressConn) LocalAddr() net.Addr {
return c.anchor.LocalAddr()
}
func (c *UDPEgressConn) SetDeadline(t time.Time) error {
return c.anchor.SetDeadline(t)
}
func (c *UDPEgressConn) SetReadDeadline(t time.Time) error {
return c.anchor.SetReadDeadline(t)
}
func (c *UDPEgressConn) SetWriteDeadline(t time.Time) error {
return c.anchor.SetWriteDeadline(t)
}
func (c *UDPEgressConn) Close() error {
c.closeOnce.Do(func() {
close(c.doneChan)
c.anchor.Close()
c.pool.Close()
c.readWait.Wait()
for {
select {
case packet := <-c.packetChan:
if packet.buffer != nil {
packet.buffer.Release()
}
default:
return
}
}
})
return nil
}