From 346887ace5f14437f308830e19d7da10a12372b9 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Tue, 9 Jan 2024 23:26:24 +0800 Subject: [PATCH] Fix error codes in UDP `sendto` and `recvfrom` --- .../src/net/socket/ip/datagram/bound.rs | 38 +++++++++---- .../src/net/socket/ip/datagram/mod.rs | 49 ++++++++++------- regression/apps/network/udp_err.c | 53 +++++++++++++++++++ 3 files changed, 109 insertions(+), 31 deletions(-) diff --git a/kernel/aster-nix/src/net/socket/ip/datagram/bound.rs b/kernel/aster-nix/src/net/socket/ip/datagram/bound.rs index 317a93f57..3e42420c0 100644 --- a/kernel/aster-nix/src/net/socket/ip/datagram/bound.rs +++ b/kernel/aster-nix/src/net/socket/ip/datagram/bound.rs @@ -1,4 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 +use smoltcp::socket::udp::{RecvError, SendError}; use crate::{ events::IoEvents, @@ -40,24 +41,39 @@ impl BoundDatagram { buf: &mut [u8], flags: SendRecvFlags, ) -> Result<(usize, IpEndpoint)> { - self.bound_socket - .raw_with(|socket: &mut RawUdpSocket| socket.recv_slice(buf)) - .map_err(|_| Error::with_message(Errno::EAGAIN, "recv buf is empty")) + let result = self + .bound_socket + .raw_with(|socket: &mut RawUdpSocket| socket.recv_slice(buf)); + match result { + Ok((recv_len, endpoint)) => Ok((recv_len, endpoint)), + Err(RecvError::Exhausted) => { + return_errno_with_message!(Errno::EAGAIN, "the receive buffer is empty") + } + } } pub fn try_sendto( &self, buf: &[u8], - remote: Option, + remote: &IpEndpoint, flags: SendRecvFlags, ) -> Result { - let remote_endpoint = remote - .or(self.remote_endpoint) - .ok_or_else(|| Error::with_message(Errno::EINVAL, "udp should provide remote addr"))?; - self.bound_socket - .raw_with(|socket: &mut RawUdpSocket| socket.send_slice(buf, remote_endpoint)) - .map(|_| buf.len()) - .map_err(|_| Error::with_message(Errno::EAGAIN, "send udp packet fails")) + let result = self.bound_socket.raw_with(|socket: &mut RawUdpSocket| { + if socket.payload_send_capacity() < buf.len() { + return None; + } + Some(socket.send_slice(buf, *remote)) + }); + match result { + Some(Ok(())) => Ok(buf.len()), + Some(Err(SendError::BufferFull)) => { + return_errno_with_message!(Errno::EAGAIN, "the send buffer is full") + } + Some(Err(SendError::Unaddressable)) => { + return_errno_with_message!(Errno::EINVAL, "the destionation address is invalid") + } + None => return_errno_with_message!(Errno::EMSGSIZE, "the message is too large"), + } } pub(super) fn init_pollee(&self, pollee: &Pollee) { diff --git a/kernel/aster-nix/src/net/socket/ip/datagram/mod.rs b/kernel/aster-nix/src/net/socket/ip/datagram/mod.rs index b05611fe4..991f5668a 100644 --- a/kernel/aster-nix/src/net/socket/ip/datagram/mod.rs +++ b/kernel/aster-nix/src/net/socket/ip/datagram/mod.rs @@ -88,6 +88,15 @@ impl DatagramSocket { self.nonblocking.store(nonblocking, Ordering::SeqCst); } + fn remote_endpoint(&self) -> Option { + let inner = self.inner.read(); + + match inner.as_ref() { + Inner::Bound(bound_datagram) => bound_datagram.remote_endpoint(), + Inner::Unbound(_) => None, + } + } + fn try_bind_empheral(&self, remote_endpoint: &IpEndpoint) -> Result<()> { // Fast path if let Inner::Bound(_) = self.inner.read().as_ref() { @@ -110,24 +119,23 @@ impl DatagramSocket { fn try_recvfrom(&self, buf: &mut [u8], flags: SendRecvFlags) -> Result<(usize, SocketAddr)> { let inner = self.inner.read(); + let Inner::Bound(bound_datagram) = inner.as_ref() else { - return_errno_with_message!(Errno::EINVAL, "the socket is not bound"); + return_errno_with_message!(Errno::EAGAIN, "the socket is not bound"); }; + let (recv_bytes, remote_endpoint) = bound_datagram.try_recvfrom(buf, flags)?; bound_datagram.update_io_events(&self.pollee); Ok((recv_bytes, remote_endpoint.into())) } - fn try_sendto( - &self, - buf: &[u8], - remote: Option, - flags: SendRecvFlags, - ) -> Result { + fn try_sendto(&self, buf: &[u8], remote: &IpEndpoint, flags: SendRecvFlags) -> Result { let inner = self.inner.read(); + let Inner::Bound(bound_datagram) = inner.as_ref() else { - return_errno_with_message!(Errno::EINVAL, "the socket is not bound"); + return_errno_with_message!(Errno::EAGAIN, "the socket is not bound") }; + let sent_bytes = bound_datagram.try_sendto(buf, remote, flags)?; bound_datagram.update_io_events(&self.pollee); Ok(sent_bytes) @@ -244,12 +252,7 @@ impl Socket for DatagramSocket { } fn peer_addr(&self) -> Result { - let inner = self.inner.read(); - let Inner::Bound(bound_datagram) = inner.as_ref() else { - return_errno_with_message!(Errno::ENOTCONN, "the socket is not connected") - }; - bound_datagram - .remote_endpoint() + self.remote_endpoint() .map(|endpoint| endpoint.into()) .ok_or_else(|| Error::with_message(Errno::ENOTCONN, "the socket is not connected")) } @@ -275,15 +278,21 @@ impl Socket for DatagramSocket { debug_assert!(flags.is_all_supported()); let remote_endpoint = match remote { - Some(remote_addr) => Some(remote_addr.try_into()?), - None => None, + Some(remote_addr) => { + let endpoint = remote_addr.try_into()?; + self.try_bind_empheral(&endpoint)?; + endpoint + } + None => self.remote_endpoint().ok_or_else(|| { + Error::with_message( + Errno::EDESTADDRREQ, + "the destination address is not specified", + ) + })?, }; - if let Some(endpoint) = remote_endpoint { - self.try_bind_empheral(&endpoint)?; - } // TODO: Block if the send buffer is full - let sent_bytes = self.try_sendto(buf, remote_endpoint, flags)?; + let sent_bytes = self.try_sendto(buf, &remote_endpoint, flags)?; poll_ifaces(); Ok(sent_bytes) } diff --git a/regression/apps/network/udp_err.c b/regression/apps/network/udp_err.c index 7bdb4bb1b..31a89d70e 100644 --- a/regression/apps/network/udp_err.c +++ b/regression/apps/network/udp_err.c @@ -82,3 +82,56 @@ FN_TEST(getpeername) addrlen == sizeof(saddr) && saddr.sin_port == C_PORT); } END_TEST() + +FN_TEST(send) +{ + char buf[1] = { 'z' }; + + TEST_ERRNO(send(sk_unbound, buf, 1, 0), EDESTADDRREQ); + + TEST_ERRNO(send(sk_bound, buf, 1, 0), EDESTADDRREQ); +} +END_TEST() + +FN_TEST(recv) +{ + char buf[1] = { 'z' }; + + TEST_ERRNO(recv(sk_unbound, buf, 1, 0), EAGAIN); + + TEST_ERRNO(recv(sk_bound, buf, 1, 0), EAGAIN); + + TEST_ERRNO(recv(sk_connected, buf, 1, 0), EAGAIN); +} +END_TEST() + +FN_TEST(send_and_recv) +{ + char buf[1]; + struct sockaddr_in saddr; + socklen_t addrlen = sizeof(saddr); + + sk_addr.sin_port = C_PORT; + buf[0] = 'a'; + TEST_RES(sendto(sk_bound, buf, 1, 0, (struct sockaddr *)&sk_addr, + sizeof(sk_addr)), + _ret == 1); + + buf[0] = 'b'; + TEST_RES(send(sk_connected, buf, 1, 0), _ret == 1); + + saddr.sin_port = 0; + buf[0] = 0; + TEST_RES(recvfrom(sk_bound, buf, 1, 0, (struct sockaddr *)&saddr, + &addrlen), + _ret == 1 && addrlen == sizeof(sk_addr) && + saddr.sin_port == C_PORT && buf[0] == 'a'); + + saddr.sin_port = 0; + buf[0] = 0; + TEST_RES(recvfrom(sk_bound, buf, 1, 0, (struct sockaddr *)&saddr, + &addrlen), + _ret == 1 && addrlen == sizeof(sk_addr) && + saddr.sin_port != C_PORT && buf[0] == 'b'); +} +END_TEST()