From a0559c361e879464b81284749c2794dfa8b84251 Mon Sep 17 00:00:00 2001 From: jasg Date: Mon, 6 Jul 2026 21:57:10 -0700 Subject: [PATCH] libnetwork: skip storing ingress proxy listener on bind failure When plumbIngressPortsProxy fails to bind a port, it stored a typed-nil listener in ingressProxyTbl. closeIngressPortsProxy then panicked on Close() during ingress port removal. Add a regression test and continue on bind error instead of recording the failed listener. Signed-off-by: Jason Green --- daemon/libnetwork/service_linux.go | 1 + daemon/libnetwork/service_linux_test.go | 43 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 daemon/libnetwork/service_linux_test.go diff --git a/daemon/libnetwork/service_linux.go b/daemon/libnetwork/service_linux.go index 5a830233cc..1002a06369 100644 --- a/daemon/libnetwork/service_linux.go +++ b/daemon/libnetwork/service_linux.go @@ -552,6 +552,7 @@ func plumbIngressPortsProxy(ingressPorts []*PortConfig) { if err != nil { log.G(context.TODO()).Warnf("failed to create proxy for port %s: %v", iPort, err) + continue } ingressProxyTbl[portSpec] = l diff --git a/daemon/libnetwork/service_linux_test.go b/daemon/libnetwork/service_linux_test.go new file mode 100644 index 0000000000..09561538fb --- /dev/null +++ b/daemon/libnetwork/service_linux_test.go @@ -0,0 +1,43 @@ +//go:build linux + +package libnetwork + +import ( + "fmt" + "io" + "net" + "strings" + "testing" + + "gotest.tools/v3/assert" +) + +func TestCloseIngressPortsProxyAfterBindFailure(t *testing.T) { + ingressMu.Lock() + defer ingressMu.Unlock() + + origTbl := ingressProxyTbl + t.Cleanup(func() { + ingressProxyTbl = origTbl + }) + ingressProxyTbl = make(map[string]io.Closer) + + blocker, err := net.ListenUDP("udp", &net.UDPAddr{Port: 0}) + assert.NilError(t, err) + defer blocker.Close() + + port := uint32(blocker.LocalAddr().(*net.UDPAddr).Port) + ingressPort := &PortConfig{ + Protocol: ProtocolUDP, + PublishedPort: port, + } + + plumbIngressPortsProxy([]*PortConfig{ingressPort}) + + portSpec := fmt.Sprintf("%d/%s", ingressPort.PublishedPort, strings.ToLower(ingressPort.Protocol.String())) + if _, ok := ingressProxyTbl[portSpec]; ok { + t.Fatal("bind failure must not store a listener in ingressProxyTbl") + } + + closeIngressPortsProxy([]*PortConfig{ingressPort}) +}