mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-09 00:04:54 +00:00
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:
+51
-2
@@ -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"))]
|
||||
|
||||
Reference in New Issue
Block a user