Browse code

docker-proxy: Retry UDP write on ECONNREFUSED from stale ICMP errors

When the proxy forwards a UDP datagram to a backend that isn't listening,
the kernel queues an ICMP port-unreachable error on the connected socket.
This error is returned on the *next* Write() call, not the one that
triggered it. As a result, a subsequent write carrying new data silently
fails and the datagram is never sent to the backend.

The replyLoop already handles this for reads (goto again on ECONNREFUSED).
Apply the same treatment to writes in Run() and retry the write when the
error is ECONNREFUSED.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>

Paweł Gronowski authored on 2026/04/30 03:18:55
Showing 1 changed files
... ...
@@ -199,6 +199,13 @@ func (proxy *UDPProxy) Run() {
199 199
 		for i := 0; i != read; {
200 200
 			written, err := cte.conn.Write(readBuf[i:read])
201 201
 			if err != nil {
202
+				if opErr, ok := err.(*net.OpError); ok && errors.Is(opErr.Err, syscall.ECONNREFUSED) {
203
+					// A previous write to the backend may have resulted in an
204
+					// ICMP port-unreachable. The kernel queues this as an error
205
+					// on the socket, which is returned on the next Write. Retry
206
+					// so the current datagram is not silently dropped.
207
+					continue
208
+				}
202 209
 				log.Printf("Can't proxy a datagram to udp/%s: %s\n", proxy.backendAddr, err)
203 210
 				break
204 211
 			}