fix(udp): датаграммы до 2048 байт на всём пути

This commit is contained in:
samosvalishe
2026-09-22 20:04:44 +03:00
parent 6f81528a34
commit 5b175bf4be
4 changed files with 102 additions and 10 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ type Packet struct {
// packetPool переиспользует буферы датаграмм.
var packetPool = sync.Pool{
New: func() any { return &Packet{Data: make([]byte, 2048)} },
New: func() any { return &Packet{Data: make([]byte, maxDatagramLen)} },
}
func runListener(ctx context.Context, listenConn net.PacketConn, activeLocalPeer *atomic.Value, inboundChan chan<- *Packet) error {
+10 -8
View File
@@ -24,6 +24,10 @@ var errPairRecycled = errors.New("udprelay: stream pair recycled")
const (
pipeBufLimit = 256 << 10
// maxDatagramLen - бюджет локальной датаграммы (WG/AWG) по всей цепочке, как у сервера.
maxDatagramLen = 2048
// maxRecordLen - та же датаграмма в DTLS-записи: заголовок с CID, явный nonce, тег GCM.
maxRecordLen = maxDatagramLen + 64
)
// streamPair связывает DTLS-сессию с аллокацией, поверх которой она поднята. Смерть
@@ -188,7 +192,7 @@ func dtlsSession(dtlsctx context.Context, dtlscancel context.CancelFunc, deps *D
forwardDone := make(chan struct{})
go func() {
defer close(forwardDone)
var buf [2048]byte
var buf [maxDatagramLen]byte
for {
n, err := dtlsConn.Read(buf[:])
if err != nil {
@@ -284,8 +288,6 @@ func oneTURN(ctx context.Context, deps *Deps, params *Params, peer *net.UDPAddr,
return
}
const maxPayload = 1600
wg.Go(func() {
select {
case <-turnctx.Done():
@@ -304,10 +306,10 @@ func oneTURN(ctx context.Context, deps *Deps, params *Params, peer *net.UDPAddr,
defer turncancel()
var buf, readSlot []byte
if obfConn != nil {
buf = make([]byte, obfConn.MaxWire(maxPayload))
readSlot = buf[obfConn.HeaderLen() : obfConn.HeaderLen()+maxPayload]
buf = make([]byte, obfConn.MaxWire(maxRecordLen))
readSlot = buf[obfConn.HeaderLen() : obfConn.HeaderLen()+maxRecordLen]
} else {
buf = make([]byte, maxPayload)
buf = make([]byte, maxRecordLen)
readSlot = buf
}
addrStored := false
@@ -350,9 +352,9 @@ func oneTURN(ctx context.Context, deps *Deps, params *Params, peer *net.UDPAddr,
wg.Go(func() {
defer turncancel()
readBufLen := maxPayload
readBufLen := maxRecordLen
if obfConn != nil {
readBufLen = obfConn.MaxWire(maxPayload)
readBufLen = obfConn.MaxWire(maxRecordLen)
}
buf := make([]byte, readBufLen)
for {
+1 -1
View File
@@ -11,7 +11,7 @@ import (
)
const (
udpRelayBufSize = 1600
udpRelayBufSize = 2048
udpIdleTimeout = 30 * time.Minute
udpDeadlineRefresh = time.Minute
)
@@ -0,0 +1,90 @@
package udpserver
import (
"bytes"
"context"
"net"
"testing"
"time"
"github.com/pion/dtls/v3"
"github.com/samosvalishe/free-turn-proxy/internal/logx"
"github.com/samosvalishe/free-turn-proxy/internal/transport/dtlsdial"
)
func echoBackend(t *testing.T) string {
t.Helper()
pc, err := (&net.ListenConfig{}).ListenPacket(t.Context(), "udp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = pc.Close() })
go func() {
buf := make([]byte, 64<<10)
for {
n, addr, err := pc.ReadFrom(buf)
if err != nil {
return
}
_, _ = pc.WriteTo(buf[:n], addr)
}
}()
return pc.LocalAddr().String()
}
func dtlsPair(t *testing.T, ctx context.Context, backend string) net.Conn {
t.Helper()
cert, err := dtlsdial.GenerateSelfSignedCert()
if err != nil {
t.Fatal(err)
}
ln, err := dtls.ListenWithOptions("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}, dtls.WithCertificates(cert))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = ln.Close() })
go func() {
conn, aerr := ln.Accept()
if aerr != nil {
return
}
Handle(ctx, logx.Nop(), conn, backend)
}()
pc, err := (&net.ListenConfig{}).ListenPacket(ctx, "udp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
serverAddr, ok := ln.Addr().(*net.UDPAddr)
if !ok {
t.Fatalf("listener addr %T", ln.Addr())
}
dialer := &dtlsdial.Dialer{HandshakeTimeout: 5 * time.Second}
conn, err := dialer.Dial(ctx, pc, serverAddr)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = conn.Close() })
return conn
}
// Датаграмма в пределах клиентского буфера 2048 проходит сервер в обе стороны целиком.
func TestHandleRelaysLargeDatagram(t *testing.T) {
ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second)
defer cancel()
conn := dtlsPair(t, ctx, echoBackend(t))
for _, size := range []int{1200, 1800, 2048} {
payload := bytes.Repeat([]byte{0x5a}, size)
if _, err := conn.Write(payload); err != nil {
t.Fatalf("size %d: write: %v", size, err)
}
_ = conn.SetReadDeadline(time.Now().Add(2 * time.Second))
buf := make([]byte, 4096)
n, err := conn.Read(buf)
if err != nil || !bytes.Equal(buf[:n], payload) {
t.Fatalf("size %d: echo n=%d err=%v", size, n, err)
}
}
}