From 4deba9bacaca860ca7633104dffd6b22f3bd3968 Mon Sep 17 00:00:00 2001 From: Ruihan Li Date: Thu, 25 Jul 2024 21:29:12 +0800 Subject: [PATCH] Rewrite the whole `net/addr.rs` file --- kernel/aster-nix/src/net/socket/unix/addr.rs | 5 +- .../src/net/socket/unix/stream/init.rs | 1 + .../src/net/socket/unix/stream/socket.rs | 1 + .../src/net/socket/util/socket_addr.rs | 1 - kernel/aster-nix/src/util/net/addr.rs | 372 ------------------ kernel/aster-nix/src/util/net/addr/family.rs | 270 +++++++++++++ kernel/aster-nix/src/util/net/addr/ip.rs | 84 ++++ kernel/aster-nix/src/util/net/addr/mod.rs | 11 + kernel/aster-nix/src/util/net/addr/unix.rs | 106 +++++ kernel/aster-nix/src/util/net/addr/vsock.rs | 41 ++ test/apps/network/tcp_err.c | 7 +- test/apps/network/udp_err.c | 7 +- test/apps/network/unix_err.c | 78 ++++ test/apps/scripts/network.sh | 1 + 14 files changed, 608 insertions(+), 377 deletions(-) delete mode 100644 kernel/aster-nix/src/util/net/addr.rs create mode 100644 kernel/aster-nix/src/util/net/addr/family.rs create mode 100644 kernel/aster-nix/src/util/net/addr/ip.rs create mode 100644 kernel/aster-nix/src/util/net/addr/mod.rs create mode 100644 kernel/aster-nix/src/util/net/addr/unix.rs create mode 100644 kernel/aster-nix/src/util/net/addr/vsock.rs create mode 100644 test/apps/network/unix_err.c diff --git a/kernel/aster-nix/src/net/socket/unix/addr.rs b/kernel/aster-nix/src/net/socket/unix/addr.rs index 60e75547..c45c1f63 100644 --- a/kernel/aster-nix/src/net/socket/unix/addr.rs +++ b/kernel/aster-nix/src/net/socket/unix/addr.rs @@ -4,14 +4,15 @@ use crate::{fs::path::Dentry, net::socket::util::socket_addr::SocketAddr, prelud #[derive(Clone, Debug, PartialEq, Eq)] pub enum UnixSocketAddr { + Unnamed, Path(String), - Abstract(String), + Abstract(Vec), } #[derive(Clone)] pub(super) enum UnixSocketAddrBound { Path(Arc), - Abstract(String), + Abstract(Vec), } impl PartialEq for UnixSocketAddrBound { diff --git a/kernel/aster-nix/src/net/socket/unix/stream/init.rs b/kernel/aster-nix/src/net/socket/unix/stream/init.rs index 197b44a3..e5378f96 100644 --- a/kernel/aster-nix/src/net/socket/unix/stream/init.rs +++ b/kernel/aster-nix/src/net/socket/unix/stream/init.rs @@ -33,6 +33,7 @@ impl Init { } let bound_addr = match addr_to_bind { + UnixSocketAddr::Unnamed => todo!(), UnixSocketAddr::Abstract(_) => todo!(), UnixSocketAddr::Path(path) => { let dentry = create_socket_file(path)?; diff --git a/kernel/aster-nix/src/net/socket/unix/stream/socket.rs b/kernel/aster-nix/src/net/socket/unix/stream/socket.rs index df93af0f..19341140 100644 --- a/kernel/aster-nix/src/net/socket/unix/stream/socket.rs +++ b/kernel/aster-nix/src/net/socket/unix/stream/socket.rs @@ -211,6 +211,7 @@ impl Socket for UnixStreamSocket { let remote_addr = { let unix_socket_addr = UnixSocketAddr::try_from(socket_addr)?; match unix_socket_addr { + UnixSocketAddr::Unnamed => todo!(), UnixSocketAddr::Abstract(abstract_name) => { UnixSocketAddrBound::Abstract(abstract_name) } diff --git a/kernel/aster-nix/src/net/socket/util/socket_addr.rs b/kernel/aster-nix/src/net/socket/util/socket_addr.rs index 9dd8aca0..445fd6b2 100644 --- a/kernel/aster-nix/src/net/socket/util/socket_addr.rs +++ b/kernel/aster-nix/src/net/socket/util/socket_addr.rs @@ -13,6 +13,5 @@ use crate::{ pub enum SocketAddr { Unix(UnixSocketAddr), IPv4(Ipv4Address, PortNum), - IPv6, Vsock(VsockSocketAddr), } diff --git a/kernel/aster-nix/src/util/net/addr.rs b/kernel/aster-nix/src/util/net/addr.rs deleted file mode 100644 index becadedf..00000000 --- a/kernel/aster-nix/src/util/net/addr.rs +++ /dev/null @@ -1,372 +0,0 @@ -// SPDX-License-Identifier: MPL-2.0 - -#![allow(dead_code)] -#![allow(unused_variables)] - -use crate::{ - net::socket::{ip::Ipv4Address, unix::UnixSocketAddr, vsock::VsockSocketAddr, SocketAddr}, - prelude::*, - util::{read_bytes_from_user, read_val_from_user, write_val_to_user}, -}; - -pub fn read_socket_addr_from_user(addr: Vaddr, addr_len: usize) -> Result { - debug_assert!(addr_len >= core::mem::size_of::()); - let sockaddr: CSocketAddr = read_val_from_user(addr)?; - let socket_addr = match sockaddr.sa_family()? { - CSocketAddrFamily::AF_UNSPEC => { - return_errno_with_message!(Errno::EINVAL, "the socket addr family is unspecified") - } - CSocketAddrFamily::AF_UNIX => { - debug_assert!(addr_len >= core::mem::size_of::()); - let sa_family: u16 = read_val_from_user(addr)?; - debug_assert!(sa_family == CSocketAddrFamily::AF_UNIX as u16); - - let bytes = { - let bytes_len = addr_len - core::mem::size_of::(); - let mut bytes = vec![0u8; bytes_len]; - read_bytes_from_user( - addr + core::mem::size_of::(), - &mut VmWriter::from(bytes.as_mut_slice()), - )?; - bytes - }; - - let unix_socket_addr = if bytes.starts_with(&[0]) { - // Abstract unix socket addr - let cstr = CStr::from_bytes_until_nul(&bytes[1..])?; - let abstract_path = cstr.to_string_lossy().to_string(); - UnixSocketAddr::Abstract(abstract_path) - } else { - // Normal unix sockket addr - let cstr = CStr::from_bytes_until_nul(&bytes)?; - let path = cstr.to_string_lossy().to_string(); - UnixSocketAddr::Path(path) - }; - - SocketAddr::Unix(unix_socket_addr) - } - CSocketAddrFamily::AF_INET => { - debug_assert!(addr_len >= core::mem::size_of::()); - let sock_addr_in: CSocketAddrInet = read_val_from_user(addr)?; - SocketAddr::from(sock_addr_in) - } - CSocketAddrFamily::AF_INET6 => { - debug_assert!(addr_len >= core::mem::size_of::()); - let sock_addr_in6: CSocketAddrInet6 = read_val_from_user(addr)?; - todo!() - } - CSocketAddrFamily::AF_VSOCK => { - debug_assert!(addr_len >= core::mem::size_of::()); - let sock_addr_vm: CSocketAddrVm = read_val_from_user(addr)?; - SocketAddr::Vsock(VsockSocketAddr::new( - sock_addr_vm.svm_cid, - sock_addr_vm.svm_port, - )) - } - _ => { - return_errno_with_message!(Errno::EAFNOSUPPORT, "cannot support address for the family") - } - }; - Ok(socket_addr) -} - -pub fn write_socket_addr_to_user( - socket_addr: &SocketAddr, - dest: Vaddr, - addrlen_ptr: Vaddr, -) -> Result<()> { - debug_assert!(addrlen_ptr != 0); - if addrlen_ptr == 0 { - return_errno_with_message!(Errno::EINVAL, "must provide the addrlen ptr"); - } - - let write_size = { - let max_len = read_val_from_user::(addrlen_ptr)?; - write_socket_addr_with_max_len(socket_addr, dest, max_len)? - }; - - if addrlen_ptr != 0 { - write_val_to_user(addrlen_ptr, &write_size)?; - } - Ok(()) -} - -pub fn write_socket_addr_with_max_len( - socket_addr: &SocketAddr, - dest: Vaddr, - max_len: i32, -) -> Result { - let max_len = max_len as usize; - - let write_size = match socket_addr { - SocketAddr::Unix(path) => { - let sock_addr_unix = CSocketAddrUnix::try_from(path)?; - let write_size = core::mem::size_of::(); - debug_assert!(max_len >= write_size); - write_val_to_user(dest, &sock_addr_unix)?; - write_size as i32 - } - SocketAddr::IPv4(addr, port) => { - let in_addr = CInetAddr::from(*addr); - let sock_addr_in = CSocketAddrInet::new(*port, in_addr); - let write_size = core::mem::size_of::(); - debug_assert!(max_len >= write_size); - write_val_to_user(dest, &sock_addr_in)?; - write_size as i32 - } - SocketAddr::IPv6 => todo!(), - SocketAddr::Vsock(addr) => { - let vm_addr = CSocketAddrVm::new(addr.cid, addr.port); - let write_size = core::mem::size_of::(); - write_val_to_user(dest, &vm_addr)?; - write_size as i32 - } - }; - - Ok(write_size) -} - -/// PlaceHolder -#[derive(Debug, Clone, Copy, Pod)] -#[repr(C)] -pub struct CSocketAddr { - sa_family: u16, // SaFamily - sa_data: [u8; 14], -} - -impl CSocketAddr { - pub fn sa_family(&self) -> Result { - Ok(CSocketAddrFamily::try_from(self.sa_family as i32)?) - } -} - -const SOCKET_ADDR_UNIX_LEN: usize = 108; - -#[repr(C)] -#[derive(Debug, Clone, Copy, Pod)] -pub struct CSocketAddrUnix { - sun_family: u16, // Always SaFamily::AF_UNIX - sun_path: [u8; SOCKET_ADDR_UNIX_LEN], -} - -/// IPv4 4-byte address -#[repr(C)] -#[derive(Debug, Clone, Copy, Pod)] -pub struct CInetAddr { - s_addr: [u8; 4], -} - -impl CInetAddr { - pub fn as_bytes(&self) -> &[u8] { - &self.s_addr - } - - pub fn from_bytes(bytes: &[u8]) -> Self { - debug_assert!(bytes.len() == 4); - let mut s_addr = [0u8; 4]; - s_addr.copy_from_slice(bytes); - Self { s_addr } - } -} - -#[derive(Debug, Clone, Copy, Pod)] -#[repr(C)] -pub struct CPortNum { - port: [u8; 2], -} - -impl CPortNum { - pub fn as_u16(&self) -> u16 { - u16::from_be_bytes(self.port) - } - - pub fn from_u16(value: u16) -> Self { - let bytes = value.to_be_bytes(); - Self { port: bytes } - } -} - -/// IPv4 socket address -#[repr(C)] -#[derive(Debug, Clone, Copy, Pod)] -pub struct CSocketAddrInet { - /// always SaFamily::AF_INET - sin_family: u16, - /// Port number - sin_port_t: CPortNum, - /// IPv4 address - sin_addr: CInetAddr, - /// Pad to size of 'SockAddr' structure (16 bytes) - _pad: [u8; 8], -} - -impl CSocketAddrInet { - pub fn new(port: u16, addr: CInetAddr) -> Self { - let port = CPortNum::from_u16(port); - Self { - sin_family: CSocketAddrFamily::AF_INET as _, - sin_port_t: port, - sin_addr: addr, - _pad: [0u8; 8], - } - } -} - -#[repr(C)] -#[derive(Debug, Clone, Copy, Pod)] -pub struct CInet6Addr { - s6_addr: [u8; 16], -} - -impl CInet6Addr { - pub fn as_bytes(&self) -> &[u8] { - &self.s6_addr - } -} - -/// IPv6 socket address -#[repr(C)] -#[derive(Debug, Clone, Copy, Pod)] -pub struct CSocketAddrInet6 { - /// always SaFamily::AF_INET6 - sin6_family: u16, - /// Port number - sin6_port: CPortNum, - /// IPv6 flow information - sin6_flowinfo: u32, - /// IPv6 address - sin6_addr: CInet6Addr, - // Scope ID - sin6_scope_id: u32, -} - -/// vm socket address -#[repr(C)] -#[derive(Debug, Clone, Copy, Pod)] -pub struct CSocketAddrVm { - /// always [SaFamily::AF_VSOCK] - svm_family: u16, - /// always 0 - svm_reserved1: u16, - /// Port number in host byte order. - svm_port: u32, - /// Address in host byte order. - svm_cid: u32, - /// Pad to size of [SockAddr] structure (16 bytes), must be zero-filled - svm_zero: [u8; 4], -} - -impl CSocketAddrVm { - pub fn new(cid: u32, port: u32) -> Self { - Self { - svm_family: CSocketAddrFamily::AF_VSOCK as _, - svm_reserved1: 0, - svm_port: port, - svm_cid: cid, - svm_zero: [0u8; 4], - } - } -} - -/// Address family. The definition is from https://elixir.bootlin.com/linux/v6.0.9/source/include/linux/socket.h. -#[repr(i32)] -#[derive(Debug, Clone, Copy, TryFromInt, PartialEq, Eq)] -#[allow(non_camel_case_types)] -pub enum CSocketAddrFamily { - AF_UNSPEC = 0, - AF_UNIX = 1, /* Unix domain sockets */ - //AF_LOCAL 1 /* POSIX name for AF_UNIX */ - AF_INET = 2, /* Internet IP Protocol */ - AF_AX25 = 3, /* Amateur Radio AX.25 */ - AF_IPX = 4, /* Novell IPX */ - AF_APPLETALK = 5, /* AppleTalk DDP */ - AF_NETROM = 6, /* Amateur Radio NET/ROM */ - AF_BRIDGE = 7, /* Multiprotocol bridge */ - AF_ATMPVC = 8, /* ATM PVCs */ - AF_X25 = 9, /* Reserved for X.25 project */ - AF_INET6 = 10, /* IP version 6 */ - AF_ROSE = 11, /* Amateur Radio X.25 PLP */ - AF_DECnet = 12, /* Reserved for DECnet project */ - AF_NETBEUI = 13, /* Reserved for 802.2LLC project*/ - AF_SECURITY = 14, /* Security callback pseudo AF */ - AF_KEY = 15, /* PF_KEY key management API */ - AF_NETLINK = 16, - //AF_ROUTE = AF_NETLINK /* Alias to emulate 4.4BSD */ - AF_PACKET = 17, /* Packet family */ - AF_ASH = 18, /* Ash */ - AF_ECONET = 19, /* Acorn Econet */ - AF_ATMSVC = 20, /* ATM SVCs */ - AF_RDS = 21, /* RDS sockets */ - AF_SNA = 22, /* Linux SNA Project (nutters!) */ - AF_IRDA = 23, /* IRDA sockets */ - AF_PPPOX = 24, /* PPPoX sockets */ - AF_WANPIPE = 25, /* Wanpipe API Sockets */ - AF_LLC = 26, /* Linux LLC */ - AF_IB = 27, /* Native InfiniBand address */ - AF_MPLS = 28, /* MPLS */ - AF_CAN = 29, /* Controller Area Network */ - AF_TIPC = 30, /* TIPC sockets */ - AF_BLUETOOTH = 31, /* Bluetooth sockets */ - AF_IUCV = 32, /* IUCV sockets */ - AF_RXRPC = 33, /* RxRPC sockets */ - AF_ISDN = 34, /* mISDN sockets */ - AF_PHONET = 35, /* Phonet sockets */ - AF_IEEE802154 = 36, /* IEEE802154 sockets */ - AF_CAIF = 37, /* CAIF sockets */ - AF_ALG = 38, /* Algorithm sockets */ - AF_NFC = 39, /* NFC sockets */ - AF_VSOCK = 40, /* vSockets */ - AF_KCM = 41, /* Kernel Connection Multiplexor*/ - AF_QIPCRTR = 42, /* Qualcomm IPC Router */ - AF_SMC = 43, /* smc sockets: reserve number for - * PF_SMC protocol family that - * reuses AF_INET address family - */ - AF_XDP = 44, /* XDP sockets */ - AF_MCTP = 45, /* Management component - * transport protocol - */ - AF_MAX = 46, /* For now.. */ -} - -impl From for Ipv4Address { - fn from(value: CInetAddr) -> Self { - let addr = value.as_bytes(); - Ipv4Address::from_bytes(addr) - } -} - -impl From for CInetAddr { - fn from(value: Ipv4Address) -> Self { - let bytes = value.as_bytes(); - CInetAddr::from_bytes(bytes) - } -} - -impl From for SocketAddr { - fn from(value: CSocketAddrInet) -> Self { - let port = value.sin_port_t.as_u16(); - let addr = Ipv4Address::from(value.sin_addr); - SocketAddr::IPv4(addr, port) - } -} - -impl TryFrom<&UnixSocketAddr> for CSocketAddrUnix { - type Error = Error; - - fn try_from(value: &UnixSocketAddr) -> Result { - let mut sun_path = [0u8; SOCKET_ADDR_UNIX_LEN]; - match value { - UnixSocketAddr::Path(path) => { - let bytes = path.as_bytes(); - let copy_len = bytes.len().min(SOCKET_ADDR_UNIX_LEN - 1); - sun_path[..copy_len].copy_from_slice(&bytes[..copy_len]); - Ok(CSocketAddrUnix { - sun_family: CSocketAddrFamily::AF_UNIX as u16, - sun_path, - }) - } - UnixSocketAddr::Abstract(_) => todo!(), - } - } -} diff --git a/kernel/aster-nix/src/util/net/addr/family.rs b/kernel/aster-nix/src/util/net/addr/family.rs new file mode 100644 index 00000000..f3455ac6 --- /dev/null +++ b/kernel/aster-nix/src/util/net/addr/family.rs @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: MPL-2.0 + +use core::cmp::min; + +use super::{ip::CSocketAddrInet, unix, vsock::CSocketAddrVm}; +use crate::{ + net::socket::SocketAddr, + prelude::*, + util::{read_bytes_from_user, read_val_from_user, write_bytes_to_user, write_val_to_user}, +}; + +/// Address family. +/// +/// See . +#[repr(i32)] +#[derive(Debug, Clone, Copy, TryFromInt, PartialEq, Eq)] +#[allow(non_camel_case_types)] +#[allow(dead_code)] +pub enum CSocketAddrFamily { + AF_UNSPEC = 0, + /// Unix domain sockets + AF_UNIX = 1, + // POSIX name for AF_UNIX + // AF_LOCAL = 1, + /// Internet IP Protocol + AF_INET = 2, + /// Amateur Radio AX.25 + AF_AX25 = 3, + /// Novell IPX + AF_IPX = 4, + /// AppleTalk DDP + AF_APPLETALK = 5, + /// Amateur Radio NET/ROM + AF_NETROM = 6, + /// Multiprotocol bridge + AF_BRIDGE = 7, + /// ATM PVCs + AF_ATMPVC = 8, + /// Reserved for X.25 project + AF_X25 = 9, + /// IP version 6, + AF_INET6 = 10, + /// Amateur Radio X.25 PLP + AF_ROSE = 11, + /// Reserved for DECnet project + AF_DECnet = 12, + /// Reserved for 802.2LLC project + AF_NETBEUI = 13, + /// Security callback pseudo AF + AF_SECURITY = 14, + /// PF_KEY key management API + AF_KEY = 15, + AF_NETLINK = 16, + // Alias to emulate 4.4BSD + // AF_ROUTE = AF_NETLINK + /// Packet family + AF_PACKET = 17, + /// Ash + AF_ASH = 18, + /// Acorn Econet + AF_ECONET = 19, + /// ATM SVCs + AF_ATMSVC = 20, + /// RDS sockets + AF_RDS = 21, + /// Linux SNA Project (nutters!) + AF_SNA = 22, + /// IRDA sockets + AF_IRDA = 23, + /// PPPoX sockets + AF_PPPOX = 24, + /// Wanpipe API Sockets + AF_WANPIPE = 25, + /// Linux LLC + AF_LLC = 26, + /// Native InfiniBand address + AF_IB = 27, + /// MPLS + AF_MPLS = 28, + /// Controller Area Network + AF_CAN = 29, + /// TIPC sockets + AF_TIPC = 30, + /// Bluetooth sockets + AF_BLUETOOTH = 31, + /// IUCV sockets + AF_IUCV = 32, + /// RxRPC sockets + AF_RXRPC = 33, + /// mISDN sockets + AF_ISDN = 34, + /// Phonet sockets + AF_PHONET = 35, + /// IEEE802154 sockets + AF_IEEE802154 = 36, + /// CAIF sockets + AF_CAIF = 37, + /// Algorithm sockets + AF_ALG = 38, + /// NFC sockets + AF_NFC = 39, + /// vSockets + AF_VSOCK = 40, + /// Kernel Connection Multiplexor + AF_KCM = 41, + /// Qualcomm IPC Router + AF_QIPCRTR = 42, + /// smc sockets: reserve number for + /// PF_SMC protocol family that + /// reuses AF_INET address family + AF_SMC = 43, + /// XDP sockets + AF_XDP = 44, + /// Management component transport protocol + AF_MCTP = 45, +} + +const ADDR_MAX_LEN: usize = 128; + +/// Storage that can contain _any_ socket addresses. +/// +/// The size and layout of this structure is specified by RFC 3493. For details, see +/// . +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] +struct Storage { + sa_family: u16, + bytes: [u8; ADDR_MAX_LEN - 2], + _align: [u64; 0], +} + +/// Reads a socket address from userspace. +/// +/// This method returns `Err(EINVAL)` for invalid socket address lengths and `Err(EAFNOSUPPORT)` +/// for unsupported address families. +/// +/// These error codes may be different from the Linux ones, but the difference is tricky and hard +/// to fix. The main reason for this is that in Linux it's up to each protocol to decide how to +/// intercept the bytes that represent socket addresses, but here this method is designed to parse +/// socket addresses before diving deep into protocol-specific code. +pub fn read_socket_addr_from_user(addr: Vaddr, addr_len: usize) -> Result { + if addr_len > ADDR_MAX_LEN { + return_errno_with_message!(Errno::EINVAL, "the socket address length is too large"); + } + + if addr_len < 2 { + return_errno_with_message!(Errno::EINVAL, "the socket address length is too small"); + } + + let mut storage = Storage::new_zeroed(); + read_bytes_from_user( + addr, + &mut VmWriter::from(&mut storage.as_bytes_mut()[..addr_len]), + )?; + + let result = match CSocketAddrFamily::try_from(storage.sa_family as i32) { + Ok(CSocketAddrFamily::AF_INET) => { + if addr_len < size_of::() { + return_errno_with_message!(Errno::EINVAL, "the socket address length is too small"); + } + let (addr, port) = CSocketAddrInet::from_bytes(storage.as_bytes()).into(); + SocketAddr::IPv4(addr, port) + } + Ok(CSocketAddrFamily::AF_UNIX) => { + let addr = unix::from_c_bytes(&storage.as_bytes()[..addr_len])?; + SocketAddr::Unix(addr) + } + Ok(CSocketAddrFamily::AF_VSOCK) => { + if addr_len < size_of::() { + return_errno_with_message!(Errno::EINVAL, "the socket address length is too small"); + } + let addr = CSocketAddrVm::from_bytes(storage.as_bytes()); + SocketAddr::Vsock(addr.into()) + } + _ => { + return_errno_with_message!( + Errno::EAFNOSUPPORT, + "the specified address family is not supported" + ) + } + }; + + Ok(result) +} + +/// Writes a socket address and its length to userspace. +/// +/// Similar to [`write_socket_addr_with_max_len`], the socket address may be truncated if the +/// buffer is not long enough. Even if truncation occurs, the actual length of the socket address +/// is written to userspace. See for +/// details on this behavior. +/// +/// # Panics +/// +/// This method will panic if the socket address cannot be validly mapped to the corresponding +/// Linux C structures. Currently, the only possible example is that the pathname in the UNIX +/// domain socket address is too long. +/// +/// It is guaranteed that all socket addresses returned by [`read_socket_addr_from_user`] have +/// valid representations for the corresponding C structures, so passing them to this method will +/// not cause panic. +pub fn write_socket_addr_to_user( + socket_addr: &SocketAddr, + dest: Vaddr, + max_len_ptr: Vaddr, +) -> Result<()> { + let max_len = read_val_from_user::(max_len_ptr)?; + + let actual_len = write_socket_addr_with_max_len(socket_addr, dest, max_len)?; + + write_val_to_user(max_len_ptr, &actual_len) +} + +/// Writes a socket address to the user space. +/// +/// If the specified maximum length for the socket address is not enough, the socket address is +/// truncated to the specified maximum length. This method returns the _actual_ length of the +/// socket address, regardless of whether the truncation occurs or not. +/// +/// # Panics +/// +/// This method will panic if the socket address cannot be validly mapped to the corresponding +/// Linux C structures. Currently, the only possible example is that the pathname in the UNIX +/// domain socket address is too long. +/// +/// It is guaranteed that all socket addresses returned by [`read_socket_addr_from_user`] have +/// valid representations for the corresponding C structures, so passing them to this method will +/// not cause panic. +pub fn write_socket_addr_with_max_len( + socket_addr: &SocketAddr, + dest: Vaddr, + max_len: i32, +) -> Result { + if max_len < 0 { + return_errno_with_message!( + Errno::EINVAL, + "the socket address length cannot be negative" + ); + } + + let actual_len = match socket_addr { + SocketAddr::IPv4(addr, port) => { + let socket_addr = CSocketAddrInet::from((*addr, *port)); + let actual_len = size_of::(); + let written_len = min(actual_len, max_len as _); + write_bytes_to_user( + dest, + &mut VmReader::from(&socket_addr.as_bytes()[..written_len]), + )?; + actual_len + } + SocketAddr::Unix(addr) => unix::into_c_bytes_and(addr, |bytes| { + let written_len = min(bytes.len(), max_len as _); + write_bytes_to_user(dest, &mut VmReader::from(&bytes[..written_len]))?; + Ok::(bytes.len()) + })?, + SocketAddr::Vsock(addr) => { + let socket_addr = CSocketAddrVm::from(*addr); + let actual_len = size_of::(); + let written_len = min(actual_len, max_len as _); + write_bytes_to_user( + dest, + &mut VmReader::from(&socket_addr.as_bytes()[..written_len]), + )?; + actual_len + } + }; + + Ok(actual_len as i32) +} diff --git a/kernel/aster-nix/src/util/net/addr/ip.rs b/kernel/aster-nix/src/util/net/addr/ip.rs new file mode 100644 index 00000000..c43dc83b --- /dev/null +++ b/kernel/aster-nix/src/util/net/addr/ip.rs @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: MPL-2.0 + +use super::family::CSocketAddrFamily; +use crate::{ + net::socket::ip::{Ipv4Address, PortNum}, + prelude::*, +}; + +/// IPv4 socket address. +/// +/// See . +/// +/// The pad bytes (namely `sin_zero`) do not appear in the man pages, but are actually required by +/// the Linux implementation. See +/// . +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] +pub(super) struct CSocketAddrInet { + /// Address family (AF_INET). + sin_family: u16, + /// Port number. + sin_port: CPortNum, + /// IPv4 address. + sin_addr: CInetAddr, + /// Pad bytes to 16-byte `struct sockaddr`. + sin_zero: [u8; 8], +} + +impl From<(Ipv4Address, PortNum)> for CSocketAddrInet { + fn from(value: (Ipv4Address, PortNum)) -> Self { + Self { + sin_family: CSocketAddrFamily::AF_INET as u16, + sin_port: value.1.into(), + sin_addr: value.0.into(), + sin_zero: [0; 8], + } + } +} + +impl From for (Ipv4Address, PortNum) { + fn from(value: CSocketAddrInet) -> Self { + (value.sin_addr.into(), value.sin_port.into()) + } +} + +/// IPv4 4-byte address. +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] +struct CInetAddr { + s_addr: [u8; 4], +} + +impl From for CInetAddr { + fn from(value: Ipv4Address) -> Self { + Self { s_addr: value.0 } + } +} + +impl From for Ipv4Address { + fn from(value: CInetAddr) -> Self { + Self(value.s_addr) + } +} + +/// TCP/UDP port number. +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] +struct CPortNum { + port: [u8; 2], +} + +impl From for CPortNum { + fn from(value: PortNum) -> Self { + Self { + port: value.to_be_bytes(), + } + } +} + +impl From for PortNum { + fn from(value: CPortNum) -> Self { + Self::from_be_bytes(value.port) + } +} diff --git a/kernel/aster-nix/src/util/net/addr/mod.rs b/kernel/aster-nix/src/util/net/addr/mod.rs new file mode 100644 index 00000000..f4db56cf --- /dev/null +++ b/kernel/aster-nix/src/util/net/addr/mod.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MPL-2.0 + +pub use family::{ + read_socket_addr_from_user, write_socket_addr_to_user, write_socket_addr_with_max_len, + CSocketAddrFamily, +}; + +mod family; +mod ip; +mod unix; +mod vsock; diff --git a/kernel/aster-nix/src/util/net/addr/unix.rs b/kernel/aster-nix/src/util/net/addr/unix.rs new file mode 100644 index 00000000..e5146bca --- /dev/null +++ b/kernel/aster-nix/src/util/net/addr/unix.rs @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: MPL-2.0 + +use core::{ffi::CStr, mem::offset_of}; + +use super::family::CSocketAddrFamily; +use crate::{net::socket::unix::UnixSocketAddr, prelude::*}; + +/// UNIX domain socket address. +/// +/// See . +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] +pub(super) struct CSocketAddrUnix { + /// Address family (AF_UNIX). + sun_family: u16, + /// Pathname. + sun_path: [u8; Self::PATH_MAX_LEN], +} + +impl CSocketAddrUnix { + const PATH_MAX_LEN: usize = 108; + + const PATH_OFFSET: usize = offset_of!(Self, sun_path); + + const MIN_LEN: usize = Self::PATH_OFFSET; + const MAX_LEN: usize = size_of::(); +} + +/// Converts a [`UnixSocketAddr`] to bytes representing a [`CSocketAddrUnix`]. +/// +/// We don't actually create a [`CSocketAddrUnix`]. Instead, we create its byte representation +/// directly for ease of operation. +/// +/// # Panics +/// +/// This method will panic if the pathname in [`UnixSocketAddr`] is too long to be stored in +/// the `sun_path` field of `CUnixSocketAddr`. +pub(super) fn into_c_bytes_and(value: &UnixSocketAddr, f: F) -> R +where + F: FnOnce(&[u8]) -> R, +{ + // We need to reserve one byte for the null terminator. Because of this, the number of + // bytes may exceed the size of `CSocketAddrUnix`. This is to match the Linux + // implementation. See the "BUGS" section at + // . + let mut bytes: [u8; CSocketAddrUnix::MAX_LEN + 1] = Pod::new_zeroed(); + + bytes[..2].copy_from_slice(&(CSocketAddrFamily::AF_UNIX as u16).to_ne_bytes()); + #[allow(clippy::assertions_on_constants)] + const { assert!(CSocketAddrUnix::PATH_OFFSET == 2) }; + + let sun_path = &mut bytes[CSocketAddrUnix::PATH_OFFSET..]; + + let copied = match value { + UnixSocketAddr::Unnamed => 0, + UnixSocketAddr::Path(path) => { + let bytes = path.as_bytes(); + let len = bytes.len(); + sun_path[..len].copy_from_slice(bytes); + sun_path[len] = 0; + len + 1 + } + UnixSocketAddr::Abstract(name) => { + let len = name.len(); + sun_path[0] = 0; + sun_path[1..len + 1].copy_from_slice(&name[..]); + len + 1 + } + }; + + f(&bytes[..CSocketAddrUnix::PATH_OFFSET + copied]) +} + +/// Converts bytes representing a [`CSocketAddrUnix`] to a [`UnixSocketAddr`]. +/// +/// We accept the byte representation of a [`CSocketAddrUnix`] directly, instead of +/// [`CSocketAddrUnix`] itself, for ease of operation. +pub(super) fn from_c_bytes(bytes: &[u8]) -> Result { + if bytes.len() < CSocketAddrUnix::MIN_LEN { + return_errno_with_message!(Errno::EINVAL, "the socket address length is too small"); + } + + if bytes.len() > CSocketAddrUnix::MAX_LEN { + return_errno_with_message!(Errno::EINVAL, "the socket address length is too small"); + } + + let sun_path = &bytes[CSocketAddrUnix::PATH_OFFSET..]; + + if sun_path.is_empty() { + return Ok(UnixSocketAddr::Unnamed); + } + + if sun_path[0] == 0 { + return Ok(UnixSocketAddr::Abstract(Vec::from(&sun_path[1..]))); + } + + // Again, Linux always appends a null terminator to the pathname if none is supplied. So we + // need to deal with the case where `CStr::from_bytes_until_nul` fails. + if let Ok(c_str) = CStr::from_bytes_until_nul(sun_path) { + Ok(UnixSocketAddr::Path(c_str.to_string_lossy().to_string())) + } else { + Ok(UnixSocketAddr::Path( + String::from_utf8_lossy(sun_path).to_string(), + )) + } +} diff --git a/kernel/aster-nix/src/util/net/addr/vsock.rs b/kernel/aster-nix/src/util/net/addr/vsock.rs new file mode 100644 index 00000000..8e1dfe88 --- /dev/null +++ b/kernel/aster-nix/src/util/net/addr/vsock.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MPL-2.0 + +use super::family::CSocketAddrFamily; +use crate::{net::socket::vsock::VsockSocketAddr, prelude::*}; + +/// VSOCK socket address. +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] +pub(super) struct CSocketAddrVm { + /// Address family (AF_VSOCK). + svm_family: u16, + /// Reserved (always zero). + svm_reserved1: u16, + /// Port number in host byte order. + svm_port: u32, + /// Address in host byte order. + svm_cid: u32, + /// Pad bytes to 16-byte `struct sockaddr` (always zero). + svm_zero: [u8; 4], +} + +impl From for CSocketAddrVm { + fn from(value: VsockSocketAddr) -> Self { + Self { + svm_family: CSocketAddrFamily::AF_VSOCK as u16, + svm_reserved1: 0, + svm_port: value.port, + svm_cid: value.cid, + svm_zero: [0; 4], + } + } +} + +impl From for VsockSocketAddr { + fn from(value: CSocketAddrVm) -> Self { + Self { + cid: value.svm_cid, + port: value.svm_port, + } + } +} diff --git a/test/apps/network/tcp_err.c b/test/apps/network/tcp_err.c index d8247cec..8229ddba 100644 --- a/test/apps/network/tcp_err.c +++ b/test/apps/network/tcp_err.c @@ -85,7 +85,10 @@ FN_TEST(getsockname) { struct sockaddr_in saddr = { .sin_port = 0xbeef }; struct sockaddr *psaddr = (struct sockaddr *)&saddr; - socklen_t addrlen = sizeof(saddr); + socklen_t addrlen = 0; + + TEST_RES(getsockname(sk_unbound, psaddr, &addrlen), + addrlen == sizeof(saddr) && saddr.sin_port == 0xbeef); TEST_RES(getsockname(sk_unbound, psaddr, &addrlen), addrlen == sizeof(saddr) && saddr.sin_port == 0); @@ -190,6 +193,8 @@ FN_TEST(bind) struct sockaddr *psaddr = (struct sockaddr *)&sk_addr; socklen_t addrlen = sizeof(sk_addr); + TEST_ERRNO(bind(sk_unbound, psaddr, addrlen - 1), EINVAL); + TEST_ERRNO(bind(sk_bound, psaddr, addrlen), EINVAL); TEST_ERRNO(bind(sk_listen, psaddr, addrlen), EINVAL); diff --git a/test/apps/network/udp_err.c b/test/apps/network/udp_err.c index 03b778e5..2ded0f99 100644 --- a/test/apps/network/udp_err.c +++ b/test/apps/network/udp_err.c @@ -57,7 +57,10 @@ FN_TEST(getsockname) { struct sockaddr_in saddr = { .sin_port = 0xbeef }; struct sockaddr *psaddr = (struct sockaddr *)&saddr; - socklen_t addrlen = sizeof(saddr); + socklen_t addrlen = 0; + + TEST_RES(getsockname(sk_unbound, psaddr, &addrlen), + addrlen == sizeof(saddr) && saddr.sin_port == 0xbeef); TEST_RES(getsockname(sk_unbound, psaddr, &addrlen), addrlen == sizeof(saddr) && saddr.sin_port == 0); @@ -143,6 +146,8 @@ FN_TEST(bind) struct sockaddr *psaddr = (struct sockaddr *)&sk_addr; socklen_t addrlen = sizeof(sk_addr); + TEST_ERRNO(bind(sk_unbound, psaddr, addrlen - 1), EINVAL); + TEST_ERRNO(bind(sk_bound, psaddr, addrlen), EINVAL); TEST_ERRNO(bind(sk_connected, psaddr, addrlen), EINVAL); diff --git a/test/apps/network/unix_err.c b/test/apps/network/unix_err.c new file mode 100644 index 00000000..351e6b10 --- /dev/null +++ b/test/apps/network/unix_err.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: MPL-2.0 + +#include +#include +#include +#include +#include + +#include "test.h" + +#define PATH_OFFSET offsetof(struct sockaddr_un, sun_path) + +FN_TEST(socket_addresses) +{ + int sk; + socklen_t addrlen; + struct sockaddr_un addr; + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define MAKE_TEST(path, path_copy_len, path_len_to_kernel, path_buf_len, \ + path_len_from_kernel, path_from_kernel) \ + sk = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0)); \ + \ + memset(&addr, 0, sizeof(addr)); \ + addr.sun_family = AF_UNIX; \ + memcpy(addr.sun_path, path, path_copy_len); \ + \ + TEST_SUCC(bind(sk, (struct sockaddr *)&addr, \ + PATH_OFFSET + path_len_to_kernel)); \ + \ + memset(&addr, 0, sizeof(addr)); \ + \ + addrlen = path_buf_len + PATH_OFFSET; \ + TEST_RES( \ + getsockname(sk, (struct sockaddr *)&addr, &addrlen), \ + addrlen == PATH_OFFSET + path_len_from_kernel && \ + 0 == memcmp(addr.sun_path, path_from_kernel, \ + MIN(path_buf_len, path_len_from_kernel))); \ + \ + TEST_SUCC(close(sk)); + +#define LONG_PATH \ + "/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + _Static_assert(sizeof(LONG_PATH) == sizeof(addr.sun_path), + "LONG_PATH has a wrong length"); + + MAKE_TEST("/tmp/R0", 8, 8, 8, 8, "/tmp/R0"); + + MAKE_TEST("/tmp/R1", 8, 9, 8, 8, "/tmp/R1"); + + MAKE_TEST("/tmp/R2", 6, 6, 8, 7, "/tmp/R"); + + MAKE_TEST("/tmp/R3", 7, 7, 8, 8, "/tmp/R3"); + + MAKE_TEST("/tmp/R4", 7, 7, 7, 8, "/tmp/R4"); + + MAKE_TEST("/tmp/R5", 7, 7, 6, 8, "/tmp/R"); + + MAKE_TEST("/tmp/R6", 7, 7, 0, 8, ""); + + MAKE_TEST(LONG_PATH, 107, 107, 108, 108, LONG_PATH); + + MAKE_TEST(LONG_PATH "a", 108, 108, 108, 109, LONG_PATH "a"); + +#undef LONG_PATH +#undef MAKE_TEST + + sk = TEST_SUCC(socket(PF_UNIX, SOCK_STREAM, 0)); + + TEST_ERRNO(bind(sk, (struct sockaddr *)&addr, -1), EINVAL); + TEST_ERRNO(bind(sk, (struct sockaddr *)&addr, PATH_OFFSET - 1), EINVAL); + TEST_ERRNO(bind(sk, (struct sockaddr *)&addr, sizeof(addr) + 1), + EINVAL); + + TEST_SUCC(close(sk)); +} +END_TEST() diff --git a/test/apps/scripts/network.sh b/test/apps/scripts/network.sh index 9ece2b11..5b1035ee 100755 --- a/test/apps/scripts/network.sh +++ b/test/apps/scripts/network.sh @@ -23,5 +23,6 @@ echo "Start network test......" ./http_client ./tcp_err ./udp_err +./unix_err echo "All network test passed"