diff --git a/Cargo.lock b/Cargo.lock index f18fa66c26..ab5fa38b3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1596,7 +1596,7 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.10" +version = "0.5.11" dependencies = [ "cfg_aliases", "criterion", diff --git a/perf/src/lib.rs b/perf/src/lib.rs index 46d29acd08..7e10fcf40b 100644 --- a/perf/src/lib.rs +++ b/perf/src/lib.rs @@ -1,6 +1,7 @@ use std::net::SocketAddr; use anyhow::{Context, Result}; +use quinn::udp::UdpSocketState; use rustls::crypto::ring::cipher_suite; use socket2::{Domain, Protocol, Socket, Type}; use tracing::warn; @@ -25,14 +26,18 @@ pub fn bind_socket( socket .bind(&socket2::SockAddr::from(addr)) .context("binding endpoint")?; - socket - .set_send_buffer_size(send_buffer_size) + + let socket_state = UdpSocketState::new((&socket).into())?; + socket_state + .set_send_buffer_size((&socket).into(), send_buffer_size) .context("send buffer size")?; - socket - .set_recv_buffer_size(recv_buffer_size) + socket_state + .set_recv_buffer_size((&socket).into(), recv_buffer_size) .context("recv buffer size")?; - let buf_size = socket.send_buffer_size().context("send buffer size")?; + let buf_size = socket_state + .send_buffer_size((&socket).into()) + .context("send buffer size")?; if buf_size < send_buffer_size { warn!( "Unable to set desired send buffer size. Desired: {}, Actual: {}", @@ -40,7 +45,9 @@ pub fn bind_socket( ); } - let buf_size = socket.recv_buffer_size().context("recv buffer size")?; + let buf_size = socket_state + .recv_buffer_size((&socket).into()) + .context("recv buffer size")?; if buf_size < recv_buffer_size { warn!( "Unable to set desired recv buffer size. Desired: {}, Actual: {}", diff --git a/quinn-udp/Cargo.toml b/quinn-udp/Cargo.toml index 53d3ee7826..70789607c9 100644 --- a/quinn-udp/Cargo.toml +++ b/quinn-udp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "quinn-udp" -version = "0.5.10" +version = "0.5.11" edition.workspace = true rust-version.workspace = true license.workspace = true diff --git a/quinn-udp/src/fallback.rs b/quinn-udp/src/fallback.rs index b35ab343be..4444bed814 100644 --- a/quinn-udp/src/fallback.rs +++ b/quinn-udp/src/fallback.rs @@ -86,6 +86,30 @@ impl UdpSocketState { 1 } + /// Resize the send buffer of `socket` to `bytes` + #[inline] + pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> { + socket.0.set_send_buffer_size(bytes) + } + + /// Resize the receive buffer of `socket` to `bytes` + #[inline] + pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> { + socket.0.set_recv_buffer_size(bytes) + } + + /// Get the size of the `socket` send buffer + #[inline] + pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result { + socket.0.send_buffer_size() + } + + /// Get the size of the `socket` receive buffer + #[inline] + pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result { + socket.0.recv_buffer_size() + } + #[inline] pub fn may_fragment(&self) -> bool { true diff --git a/quinn-udp/src/unix.rs b/quinn-udp/src/unix.rs index 0dce8aad90..d6a0345dcf 100644 --- a/quinn-udp/src/unix.rs +++ b/quinn-udp/src/unix.rs @@ -249,6 +249,30 @@ impl UdpSocketState { self.gro_segments } + /// Resize the send buffer of `socket` to `bytes` + #[inline] + pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> { + socket.0.set_send_buffer_size(bytes) + } + + /// Resize the receive buffer of `socket` to `bytes` + #[inline] + pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> { + socket.0.set_recv_buffer_size(bytes) + } + + /// Get the size of the `socket` send buffer + #[inline] + pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result { + socket.0.send_buffer_size() + } + + /// Get the size of the `socket` receive buffer + #[inline] + pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result { + socket.0.recv_buffer_size() + } + /// Whether transmitted datagrams might get fragmented by the IP layer /// /// Returns `false` on targets which employ e.g. the `IPV6_DONTFRAG` socket option. diff --git a/quinn-udp/src/windows.rs b/quinn-udp/src/windows.rs index 81b28e1358..1e0410722d 100644 --- a/quinn-udp/src/windows.rs +++ b/quinn-udp/src/windows.rs @@ -292,6 +292,30 @@ impl UdpSocketState { 64 } + /// Resize the send buffer of `socket` to `bytes` + #[inline] + pub fn set_send_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> { + socket.0.set_send_buffer_size(bytes) + } + + /// Resize the receive buffer of `socket` to `bytes` + #[inline] + pub fn set_recv_buffer_size(&self, socket: UdpSockRef<'_>, bytes: usize) -> io::Result<()> { + socket.0.set_recv_buffer_size(bytes) + } + + /// Get the size of the `socket` send buffer + #[inline] + pub fn send_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result { + socket.0.send_buffer_size() + } + + /// Get the size of the `socket` receive buffer + #[inline] + pub fn recv_buffer_size(&self, socket: UdpSockRef<'_>) -> io::Result { + socket.0.recv_buffer_size() + } + #[inline] pub fn may_fragment(&self) -> bool { false diff --git a/quinn-udp/tests/tests.rs b/quinn-udp/tests/tests.rs index 25de5cfc5b..96f8415b4a 100644 --- a/quinn-udp/tests/tests.rs +++ b/quinn-udp/tests/tests.rs @@ -1,8 +1,8 @@ #[cfg(not(any(target_os = "openbsd", target_os = "netbsd", solarish)))] -use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::net::{SocketAddr, SocketAddrV6}; use std::{ io::IoSliceMut, - net::{IpAddr, Ipv4Addr, Ipv6Addr, UdpSocket}, + net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, UdpSocket}, slice, }; @@ -186,6 +186,79 @@ fn gso() { ); } +#[test] +fn socket_buffers() { + const BUFFER_SIZE: usize = 123456; + const FACTOR: usize = if cfg!(any(target_os = "linux", target_os = "android")) { + 2 // Linux and Android set the buffer to double the requested size + } else { + 1 // Everyone else is sane. + }; + + let send = socket2::Socket::new( + socket2::Domain::IPV4, + socket2::Type::DGRAM, + Some(socket2::Protocol::UDP), + ) + .unwrap(); + let recv = socket2::Socket::new( + socket2::Domain::IPV4, + socket2::Type::DGRAM, + Some(socket2::Protocol::UDP), + ) + .unwrap(); + for sock in [&send, &recv] { + sock.bind(&socket2::SockAddr::from(SocketAddrV4::new( + Ipv4Addr::LOCALHOST, + 0, + ))) + .unwrap(); + + let socket_state = UdpSocketState::new(sock.into()).expect("created socket state"); + + // Change the send buffer size. + let buffer_before = socket_state.send_buffer_size(sock.into()).unwrap(); + assert_ne!( + buffer_before, + BUFFER_SIZE * FACTOR, + "make sure buffer is not already desired size" + ); + socket_state + .set_send_buffer_size(sock.into(), BUFFER_SIZE) + .expect("set send buffer size {buffer_before} -> {BUFFER_SIZE}"); + let buffer_after = socket_state.send_buffer_size(sock.into()).unwrap(); + assert_eq!( + buffer_after, + BUFFER_SIZE * FACTOR, + "setting send buffer size to {BUFFER_SIZE} resulted in {buffer_before} -> {buffer_after}", + ); + + // Change the receive buffer size. + let buffer_before = socket_state.recv_buffer_size(sock.into()).unwrap(); + socket_state + .set_recv_buffer_size(sock.into(), BUFFER_SIZE) + .expect("set recv buffer size {buffer_before} -> {BUFFER_SIZE}"); + let buffer_after = socket_state.recv_buffer_size(sock.into()).unwrap(); + assert_eq!( + buffer_after, + BUFFER_SIZE * FACTOR, + "setting recv buffer size to {BUFFER_SIZE} resulted in {buffer_before} -> {buffer_after}", + ); + } + + test_send_recv( + &send, + &recv, + Transmit { + destination: recv.local_addr().unwrap().as_socket().unwrap(), + ecn: None, + contents: b"hello", + segment_size: None, + src_ip: None, + }, + ); +} + fn test_send_recv(send: &Socket, recv: &Socket, transmit: Transmit) { let send_state = UdpSocketState::new(send.into()).unwrap(); let recv_state = UdpSocketState::new(recv.into()).unwrap();