Say which syscall failed when a connected UDP socket cannot open

open_connected_fd returned a bare last_os_error() from both the bind
and the connect path, and its caller wrapped both identically, so a
field report of "Address already in use" could not be attributed to
either. The two have entirely different causes: on Linux a UDP
connect(2) to a 4-tuple another socket already holds returns
EADDRINUSE, which is not the same fault as bind refusing the local
address. A node at roughly 245 peers is emitting this three times a
second across nine peers and the report cannot be diagnosed as it
stands.

Name the syscall and the address it was operating on in each error,
the local address for bind and the peer address for connect. The
address is what makes the next step possible: the identity that can
collide is the resolved SocketAddr, not the configured transport
address, and nothing in the log carried it on the failure path.

This is diagnosis only and fixes nothing. The unbounded per-tick
retry, the process-global failure counter that hides it, and the
sockets that outlive their peers are separate changes.

Note that the resolved peer address now appears at warn level on the
failure path, alongside the peer's node address, where previously it
appeared only in the success-path debug line.

raw_os_error() on the returned error is now None, since wrapping
produces a custom error. No caller reads it: the sole consumer
flattens the error to a string, and the errno text survives in the
message. Recorded as a known gap rather than a defect.
This commit is contained in:
Johnathan Corgan
2026-07-29 17:10:47 +00:00
parent 83c4e800a5
commit 931bc5a9dd
+51 -2
View File
@@ -900,7 +900,7 @@ mod connected {
)
};
if bind_r < 0 {
return Err(io::Error::last_os_error());
return Err(syscall_err("bind", local_addr));
}
// Connect to the peer — locks in the per-packet kernel route.
@@ -913,12 +913,17 @@ mod connected {
)
};
if conn_r < 0 {
return Err(io::Error::last_os_error());
return Err(syscall_err("connect", peer_addr));
}
Ok(owned)
}
fn syscall_err(syscall: &str, addr: SocketAddr) -> io::Error {
let err = io::Error::last_os_error();
io::Error::new(err.kind(), format!("{syscall} {addr}: {err}"))
}
#[cfg(not(target_os = "linux"))]
fn set_nonblocking_cloexec(fd: RawFd) -> io::Result<()> {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
@@ -1005,6 +1010,50 @@ mod connected {
)
};
}
#[cfg(all(test, target_os = "linux"))]
mod tests {
use super::*;
use std::net::UdpSocket;
const BUF: usize = 1 << 20;
#[test]
fn bind_failure_names_bind_and_local_addr() {
// A plain socket without SO_REUSEPORT holds the address, so the
// SO_REUSEPORT bind below is refused with EADDRINUSE.
let holder = UdpSocket::bind("127.0.0.1:0").expect("holder bind");
let holder_addr = holder.local_addr().expect("holder addr");
let err = open_connected_fd(holder_addr, "127.0.0.1:9".parse().unwrap(), BUF, BUF)
.expect_err("bind must fail against a non-reuseport holder");
assert_eq!(err.kind(), io::ErrorKind::AddrInUse, "{err}");
let msg = err.to_string();
assert!(msg.starts_with("bind "), "{msg}");
assert!(msg.contains(&holder_addr.to_string()), "{msg}");
assert!(!msg.contains("connect"), "{msg}");
}
#[test]
fn connect_failure_names_connect_and_peer_addr() {
// connect(2) to the broadcast address without SO_BROADCAST fails
// synchronously with EACCES; the bind before it succeeds.
let err = open_connected_fd(
"127.0.0.1:0".parse().unwrap(),
"255.255.255.255:9999".parse().unwrap(),
BUF,
BUF,
)
.expect_err("connect to broadcast without SO_BROADCAST must fail");
assert_eq!(err.kind(), io::ErrorKind::PermissionDenied, "{err}");
let msg = err.to_string();
assert!(msg.starts_with("connect "), "{msg}");
assert!(msg.contains("255.255.255.255:9999"), "{msg}");
assert!(!msg.contains("bind"), "{msg}");
}
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]