Files
moby/cmd/docker-proxy/udp_proxy_linux_test.go
Albin Kerouanton 4276f330fc cmd/docker-proxy: do not eagerly GC one-sided UDP conns
The UDP proxy is setting a deadline of 90 seconds when reading from the
backend. If no data is received within this interval, it reclaims the
connection.

This means, the backend would see a different connection every 90
seconds if the backend never sends back any reply to a client.

This change prevents the proxy from eagerly GC'ing such connections by
taking into account the last time a datagram was proxyed to the backend.

Signed-off-by: Albin Kerouanton <albinker@gmail.com>
2025-03-17 17:51:50 +01:00

79 lines
1.9 KiB
Go

package main
import (
"net"
"testing"
"time"
"gotest.tools/v3/assert"
)
// TestUDPOneSided makes sure that the conntrack entry isn't GC'd if the
// backend never writes to the UDP client.
func TestUDPOneSided(t *testing.T) {
frontend, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
assert.NilError(t, err)
defer frontend.Close()
backend, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
assert.NilError(t, err)
defer backend.Close()
type udpMsg struct {
data []byte
saddr *net.UDPAddr
}
msgs := make(chan udpMsg)
go func() {
for {
buf := make([]byte, 1024)
n, saddr, err := backend.ReadFromUDP(buf)
if err != nil {
return
}
msgs <- udpMsg{data: buf[:n], saddr: saddr}
}
}()
proxy, err := NewUDPProxy(frontend, backend.LocalAddr().(*net.UDPAddr), ip4)
assert.NilError(t, err)
defer proxy.Close()
const connTrackTimeout = 1 * time.Second
proxy.connTrackTimeout = connTrackTimeout
go func() {
proxy.Run()
}()
client, err := net.DialUDP("udp", nil, frontend.LocalAddr().(*net.UDPAddr))
assert.NilError(t, err)
defer client.Close()
var expSaddr *net.UDPAddr
for i := range 15 {
_, err = client.Write([]byte("hello"))
assert.NilError(t, err)
time.Sleep(100 * time.Millisecond)
msg := <-msgs
assert.Equal(t, string(msg.data), "hello")
if i == 0 {
expSaddr = msg.saddr
} else {
assert.Equal(t, msg.saddr.Port, expSaddr.Port)
}
}
// The conntrack entry is checked every connTrackTimeout, but the latest
// write might be less than connTrackTimeout ago. So we need to wait for
// at least twice the conntrack timeout to make sure the entry is GC'd.
time.Sleep(2 * connTrackTimeout)
_, err = client.Write([]byte("hello"))
assert.NilError(t, err)
msg := <-msgs
assert.Equal(t, string(msg.data), "hello")
assert.Check(t, msg.saddr.Port != expSaddr.Port)
}