From 8c8b10ba4c9d9ad977bf43914ec660b630a695cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Sun, 31 May 2026 19:35:50 +0200 Subject: [PATCH 1/8] fix(fd): dead_code --- src/fd/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 548bcac736..a2fb5d6f21 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -43,7 +43,7 @@ pub(crate) enum ListenEndpoint { Vsock(socket::vsock::VsockListenEndpoint), } -#[allow(dead_code)] +#[cfg(any(feature = "net", feature = "virtio-vsock"))] #[derive(Debug, PartialEq)] pub(crate) enum SocketOption { TcpNoDelay, From 9eab4c6c9f4dabac9f703397d6a4605ec5a9ade5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Sun, 31 May 2026 18:27:34 +0200 Subject: [PATCH 2/8] style: rename `SocketOption::TcpNoDelay` to `TcpNodelay` to match C --- src/fd/mod.rs | 2 +- src/fd/socket/tcp.rs | 4 ++-- src/syscalls/socket/mod.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index a2fb5d6f21..97999d47b1 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -46,7 +46,7 @@ pub(crate) enum ListenEndpoint { #[cfg(any(feature = "net", feature = "virtio-vsock"))] #[derive(Debug, PartialEq)] pub(crate) enum SocketOption { - TcpNoDelay, + TcpNodelay, } pub(crate) type RawFd = i32; diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index db71556afe..0975c2fb85 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -418,7 +418,7 @@ impl ObjectInterface for Socket { } async fn setsockopt(&self, opt: SocketOption, optval: bool) -> io::Result<()> { - if opt == SocketOption::TcpNoDelay { + if opt == SocketOption::TcpNodelay { let mut guard = NIC.lock(); let nic = guard.as_nic_mut().unwrap(); @@ -434,7 +434,7 @@ impl ObjectInterface for Socket { } async fn getsockopt(&self, opt: SocketOption) -> io::Result { - if opt == SocketOption::TcpNoDelay { + if opt == SocketOption::TcpNodelay { let mut guard = NIC.lock(); let nic = guard.as_nic_mut().unwrap(); let socket = nic.get_mut_socket::>(*self.handle.first().unwrap()); diff --git a/src/syscalls/socket/mod.rs b/src/syscalls/socket/mod.rs index b0b6e47616..8b4f44e9d6 100644 --- a/src/syscalls/socket/mod.rs +++ b/src/syscalls/socket/mod.rs @@ -951,7 +951,7 @@ pub unsafe extern "C" fn sys_setsockopt( async { v.read() .await - .setsockopt(SocketOption::TcpNoDelay, value != 0) + .setsockopt(SocketOption::TcpNodelay, value != 0) .await }, None, @@ -991,7 +991,7 @@ pub unsafe extern "C" fn sys_getsockopt( |e| -i32::from(e), |v| { block_on( - async { v.read().await.getsockopt(SocketOption::TcpNoDelay).await }, + async { v.read().await.getsockopt(SocketOption::TcpNodelay).await }, None, ) .map_or_else( From 17e2b4f5265b4b9b8fce33776c1a1589b96aa37b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Sun, 31 May 2026 19:41:23 +0200 Subject: [PATCH 3/8] refactor(fd): define TCP_NODELAY in enum --- src/fd/mod.rs | 7 +++++-- src/syscalls/socket/mod.rs | 30 ++++++++++++++---------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 97999d47b1..bed4661b77 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -5,6 +5,8 @@ use core::pin::pin; use core::task::Poll::{Pending, Ready}; use core::time::Duration; +#[cfg(any(feature = "net", feature = "virtio-vsock"))] +use num_enum::TryFromPrimitive; #[cfg(feature = "net")] use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; @@ -44,9 +46,10 @@ pub(crate) enum ListenEndpoint { } #[cfg(any(feature = "net", feature = "virtio-vsock"))] -#[derive(Debug, PartialEq)] +#[derive(TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] +#[repr(i32)] pub(crate) enum SocketOption { - TcpNodelay, + TcpNodelay = 1, } pub(crate) type RawFd = i32; diff --git a/src/syscalls/socket/mod.rs b/src/syscalls/socket/mod.rs index 8b4f44e9d6..84194e17f5 100644 --- a/src/syscalls/socket/mod.rs +++ b/src/syscalls/socket/mod.rs @@ -78,7 +78,6 @@ pub const SO_RCVBUF: i32 = 0x1002; pub const SO_SNDTIMEO: i32 = 0x1005; pub const SO_RCVTIMEO: i32 = 0x1006; pub const SO_ERROR: i32 = 0x1007; -pub const TCP_NODELAY: i32 = 1; pub const MSG_PEEK: i32 = 1; pub type sa_family_t = u8; pub type socklen_t = u32; @@ -932,10 +931,14 @@ pub unsafe extern "C" fn sys_setsockopt( return -i32::from(Errno::Inval); }; - debug!("sys_setsockopt: {fd}, level {level:?}, optname {optname}"); + let Ok(optname) = SocketOption::try_from(optname) else { + return -i32::from(Errno::Inval); + }; + + debug!("sys_setsockopt: {fd}, level {level:?}, optname {optname:?}"); if level == Ipproto::Tcp - && optname == TCP_NODELAY + && optname == SocketOption::TcpNodelay && optlen == u32::try_from(size_of::()).unwrap() { if optval.is_null() { @@ -948,12 +951,7 @@ pub unsafe extern "C" fn sys_setsockopt( |e| -i32::from(e), |v| { block_on( - async { - v.read() - .await - .setsockopt(SocketOption::TcpNodelay, value != 0) - .await - }, + async { v.read().await.setsockopt(optname, value != 0).await }, None, ) .map_or_else(|e| -i32::from(e), |()| 0) @@ -977,9 +975,13 @@ pub unsafe extern "C" fn sys_getsockopt( return -i32::from(Errno::Inval); }; - debug!("sys_getsockopt: {fd}, level {level:?}, optname {optname}"); + let Ok(optname) = SocketOption::try_from(optname) else { + return -i32::from(Errno::Inval); + }; + + debug!("sys_getsockopt: {fd}, level {level:?}, optname {optname:?}"); - if level == Ipproto::Tcp && optname == TCP_NODELAY { + if level == Ipproto::Tcp && optname == SocketOption::TcpNodelay { if optval.is_null() || optlen.is_null() { return -i32::from(Errno::Inval); } @@ -990,11 +992,7 @@ pub unsafe extern "C" fn sys_getsockopt( obj.map_or_else( |e| -i32::from(e), |v| { - block_on( - async { v.read().await.getsockopt(SocketOption::TcpNodelay).await }, - None, - ) - .map_or_else( + block_on(async { v.read().await.getsockopt(optname).await }, None).map_or_else( |e| -i32::from(e), |value| { if value { From 52ff7309c1aae51d2f179e72fba2c2bf85a249df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20Yi=C4=9Fit=20=C5=9Eahin?= Date: Sun, 31 May 2026 18:15:17 +0200 Subject: [PATCH 4/8] fix(tcp): dead_code in getsockopt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin Kröning --- src/fd/socket/tcp.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 0975c2fb85..6e37ad2259 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -434,14 +434,12 @@ impl ObjectInterface for Socket { } async fn getsockopt(&self, opt: SocketOption) -> io::Result { - if opt == SocketOption::TcpNodelay { - let mut guard = NIC.lock(); - let nic = guard.as_nic_mut().unwrap(); - let socket = nic.get_mut_socket::>(*self.handle.first().unwrap()); + let mut guard = NIC.lock(); + let nic = guard.as_nic_mut().unwrap(); + let socket = nic.get_mut_socket::>(*self.handle.first().unwrap()); - Ok(socket.nagle_enabled()) - } else { - Err(Errno::Inval) + match opt { + SocketOption::TcpNodelay => Ok(socket.nagle_enabled()), } } From b82361fa4fa1a7382dd0ef418dd39c7018c71456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20Yi=C4=9Fit=20=C5=9Eahin?= Date: Sun, 31 May 2026 18:19:13 +0200 Subject: [PATCH 5/8] refactor(socket): make getsockopt return c_int MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin Kröning --- src/fd/delegate.rs | 4 +++- src/fd/mod.rs | 4 +++- src/fd/socket/tcp.rs | 5 +++-- src/syscalls/socket/mod.rs | 6 +----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/fd/delegate.rs b/src/fd/delegate.rs index 64dd792769..f7f3dbd083 100644 --- a/src/fd/delegate.rs +++ b/src/fd/delegate.rs @@ -1,5 +1,7 @@ #[cfg(any(feature = "net", feature = "virtio-vsock"))] use alloc::sync::Arc; +#[cfg(any(feature = "net", feature = "virtio-vsock"))] +use core::ffi::c_int; use core::mem::MaybeUninit; use delegate::delegate; @@ -141,7 +143,7 @@ impl ObjectInterface for Fd { #[cfg(any(feature = "net", feature = "virtio-vsock"))] async fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> io::Result<()>; #[cfg(any(feature = "net", feature = "virtio-vsock"))] - async fn getsockopt(&self, _opt: SocketOption) -> io::Result; + async fn getsockopt(&self, _opt: SocketOption) -> io::Result; #[cfg(any(feature = "net", feature = "virtio-vsock"))] async fn getsockname(&self) -> io::Result>; #[cfg(any(feature = "net", feature = "virtio-vsock"))] diff --git a/src/fd/mod.rs b/src/fd/mod.rs index bed4661b77..45c953c575 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -1,4 +1,6 @@ use alloc::sync::Arc; +#[cfg(any(feature = "net", feature = "virtio-vsock"))] +use core::ffi::c_int; use core::future; use core::mem::MaybeUninit; use core::pin::pin; @@ -266,7 +268,7 @@ pub(crate) trait ObjectInterface: Sync + Send { /// `getsockopt` gets options on sockets #[cfg(any(feature = "net", feature = "virtio-vsock"))] - async fn getsockopt(&self, _opt: SocketOption) -> io::Result { + async fn getsockopt(&self, _opt: SocketOption) -> io::Result { Err(Errno::Notsock) } diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 6e37ad2259..b481322013 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -1,5 +1,6 @@ use alloc::collections::BTreeSet; use alloc::sync::Arc; +use core::ffi::c_int; use core::future; use core::sync::atomic::{AtomicU16, Ordering}; use core::task::Poll; @@ -433,13 +434,13 @@ impl ObjectInterface for Socket { } } - async fn getsockopt(&self, opt: SocketOption) -> io::Result { + async fn getsockopt(&self, opt: SocketOption) -> io::Result { let mut guard = NIC.lock(); let nic = guard.as_nic_mut().unwrap(); let socket = nic.get_mut_socket::>(*self.handle.first().unwrap()); match opt { - SocketOption::TcpNodelay => Ok(socket.nagle_enabled()), + SocketOption::TcpNodelay => Ok(socket.nagle_enabled().into()), } } diff --git a/src/syscalls/socket/mod.rs b/src/syscalls/socket/mod.rs index 84194e17f5..6977129f52 100644 --- a/src/syscalls/socket/mod.rs +++ b/src/syscalls/socket/mod.rs @@ -995,11 +995,7 @@ pub unsafe extern "C" fn sys_getsockopt( block_on(async { v.read().await.getsockopt(optname).await }, None).map_or_else( |e| -i32::from(e), |value| { - if value { - *optval = 1; - } else { - *optval = 0; - } + *optval = value; *optlen = size_of::().try_into().unwrap(); 0 From f69c921fdbb279ebc16a716c4697321453c08c55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20Yi=C4=9Fit=20=C5=9Eahin?= Date: Wed, 6 May 2026 19:25:11 +0200 Subject: [PATCH 6/8] feat(getsockopt): allow reading buffer sizes --- src/fd/mod.rs | 2 ++ src/fd/socket/tcp.rs | 2 ++ src/fd/socket/udp.rs | 17 ++++++++++++++++- src/syscalls/socket/mod.rs | 13 +++++-------- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 45c953c575..7be9165536 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -52,6 +52,8 @@ pub(crate) enum ListenEndpoint { #[repr(i32)] pub(crate) enum SocketOption { TcpNodelay = 1, + SoSndbuf = 0x1001, + SoRcvbuf = 0x1002, } pub(crate) type RawFd = i32; diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index b481322013..3b8e28067e 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -441,6 +441,8 @@ impl ObjectInterface for Socket { match opt { SocketOption::TcpNodelay => Ok(socket.nagle_enabled().into()), + SocketOption::SoSndbuf => Ok(c_int::try_from(socket.send_capacity()).unwrap()), + SocketOption::SoRcvbuf => Ok(c_int::try_from(socket.recv_capacity()).unwrap()), } } diff --git a/src/fd/socket/udp.rs b/src/fd/socket/udp.rs index 369b2f09b7..bb1aa72b1c 100644 --- a/src/fd/socket/udp.rs +++ b/src/fd/socket/udp.rs @@ -1,3 +1,4 @@ +use core::ffi::c_int; use core::future; use core::mem::MaybeUninit; use core::task::Poll; @@ -9,7 +10,7 @@ use smoltcp::wire::{IpEndpoint, Ipv4Address, Ipv6Address}; use crate::errno::Errno; use crate::executor::block_on; use crate::executor::network::{Handle, NIC, wake_network_waker}; -use crate::fd::{self, Endpoint, ListenEndpoint, ObjectInterface, PollEvent}; +use crate::fd::{self, Endpoint, ListenEndpoint, ObjectInterface, PollEvent, SocketOption}; use crate::io; use crate::syscalls::socket::Af; @@ -242,6 +243,20 @@ impl ObjectInterface for Socket { async fn getsockname(&self) -> io::Result> { Ok(Some(Endpoint::Ip(self.local_endpoint))) } + + async fn getsockopt(&self, opt: SocketOption) -> io::Result { + let mut guard = NIC.lock(); + let socket = guard + .as_nic_mut() + .unwrap() + .get_mut_socket::>(self.handle); + + match opt { + SocketOption::TcpNodelay => Err(Errno::Inval), + SocketOption::SoSndbuf => Ok(c_int::try_from(socket.payload_send_capacity()).unwrap()), + SocketOption::SoRcvbuf => Ok(c_int::try_from(socket.payload_recv_capacity()).unwrap()), + } + } } impl Drop for Socket { diff --git a/src/syscalls/socket/mod.rs b/src/syscalls/socket/mod.rs index 6977129f52..1806f35d2e 100644 --- a/src/syscalls/socket/mod.rs +++ b/src/syscalls/socket/mod.rs @@ -73,8 +73,6 @@ pub const SO_REUSEADDR: i32 = 0x0004; pub const SO_KEEPALIVE: i32 = 0x0008; pub const SO_BROADCAST: i32 = 0x0020; pub const SO_LINGER: i32 = 0x0080; -pub const SO_SNDBUF: i32 = 0x1001; -pub const SO_RCVBUF: i32 = 0x1002; pub const SO_SNDTIMEO: i32 = 0x1005; pub const SO_RCVTIMEO: i32 = 0x1006; pub const SO_ERROR: i32 = 0x1007; @@ -971,17 +969,16 @@ pub unsafe extern "C" fn sys_getsockopt( optval: *mut c_void, optlen: *mut socklen_t, ) -> i32 { - let Ok(Ok(level)) = u8::try_from(level).map(Ipproto::try_from) else { - return -i32::from(Errno::Inval); - }; - let Ok(optname) = SocketOption::try_from(optname) else { return -i32::from(Errno::Inval); }; - debug!("sys_getsockopt: {fd}, level {level:?}, optname {optname:?}"); + debug!("sys_getsockopt: {fd}, level {level}, optname {optname:?}"); - if level == Ipproto::Tcp && optname == SocketOption::TcpNodelay { + if level == Ipproto::Tcp as i32 && optname == SocketOption::TcpNodelay + || level == SOL_SOCKET + && (optname == SocketOption::SoSndbuf || optname == SocketOption::SoRcvbuf) + { if optval.is_null() || optlen.is_null() { return -i32::from(Errno::Inval); } From c0e00456b3c7bd4da9c1de8d09dbb826bdf7f527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20Yi=C4=9Fit=20=C5=9Eahin?= Date: Mon, 4 May 2026 16:20:18 +0200 Subject: [PATCH 7/8] fix(socket): return error for IPv6 sockets We currently do not support IPv6, so return error to allow the caller to handle the error. --- src/syscalls/socket/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/syscalls/socket/mod.rs b/src/syscalls/socket/mod.rs index 1806f35d2e..6bfb169239 100644 --- a/src/syscalls/socket/mod.rs +++ b/src/syscalls/socket/mod.rs @@ -622,8 +622,7 @@ pub extern "C" fn sys_socket(domain: i32, type_: i32, protocol: i32) -> i32 { } #[cfg(feature = "net")] - if (domain == Af::Inet || domain == Af::Inet6) && (sock == Sock::Stream || sock == Sock::Dgram) - { + if domain == Af::Inet && matches!(sock, Sock::Stream | Sock::Dgram) { let mut guard = NIC.lock(); let NetworkState::Initialized(nic) = &mut *guard else { From df4e6ff5b7953a625a7562b5b5ac92fe4f374df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=87a=C4=9Fatay=20Yi=C4=9Fit=20=C5=9Eahin?= Date: Wed, 6 May 2026 13:14:50 +0200 Subject: [PATCH 8/8] fix(socket): limit the queue size for incoming connections Excessively large queue sizes can cause us to exhaust memory. Set a maximum value that is based on the one used by the Linux kernel by default. --- src/fd/socket/tcp.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 3b8e28067e..6e72e8c5ff 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -25,6 +25,9 @@ pub const SHUT_WR: i32 = 1; pub const SHUT_RDWR: i32 = 2; /// The default queue size for incoming connections pub const DEFAULT_BACKLOG: i32 = 128; +/// The maximum queue size for incoming connections, +/// based on the default maximum used by modern Linux. +pub const SOMAXCONN: i32 = 4096; fn get_ephemeral_port() -> u16 { static LOCAL_ENDPOINT: AtomicU16 = AtomicU16::new(49152); @@ -405,7 +408,7 @@ impl ObjectInterface for Socket { self.is_listen = true; - for _ in 1..backlog { + for _ in 1..backlog.min(SOMAXCONN) { let handle = nic.create_tcp_handle().unwrap(); let s = nic.get_mut_socket::>(handle);