diff --git a/Cargo.toml b/Cargo.toml index 260506aa..cf693232 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ linux-raw-sys = { version = "0.11", default-features = false, features = [ "net", "prctl", "system", + "netlink", ] } memory_addr = "0.4" scope-local = "0.1" @@ -98,6 +99,9 @@ qemu = [ "axfeat/vsock", "starry-api/vsock", + "axfeat/netlink", + "starry-api/netlink", + "axfeat/defplat", "axfeat/bus-pci", diff --git a/api/Cargo.toml b/api/Cargo.toml index d9d74f76..405f8e46 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -11,6 +11,7 @@ repository.workspace = true input = ["dep:axinput"] memtrack = ["axfeat/dwarf", "axalloc/tracking", "dep:gimli"] vsock = ["axnet/vsock"] +netlink = ["axnet/netlink"] dev-log = [] [dependencies] diff --git a/api/src/file/mod.rs b/api/src/file/mod.rs index f6d6e4e5..5df95f23 100644 --- a/api/src/file/mod.rs +++ b/api/src/file/mod.rs @@ -320,6 +320,22 @@ pub fn close_file_like(fd: c_int) -> AxResult { Ok(()) } +pub fn get_cloexec(fd: c_int) -> AxResult { + FD_TABLE + .read() + .get(fd as usize) + .ok_or(AxError::BadFileDescriptor) + .map(|fd| fd.cloexec) +} + +pub fn set_cloexec(fd: c_int, cloexec: bool) -> AxResult { + FD_TABLE + .write() + .get_mut(fd as usize) + .ok_or(AxError::BadFileDescriptor) + .map(|fd| fd.cloexec = cloexec) +} + pub fn add_stdio(fd_table: &mut FlattenObjects) -> AxResult<()> { assert_eq!(fd_table.count(), 0); let cx = FS_CONTEXT.lock(); diff --git a/api/src/socket.rs b/api/src/socket.rs index 6c502fa7..380bb7a5 100644 --- a/api/src/socket.rs +++ b/api/src/socket.rs @@ -8,10 +8,14 @@ use core::{ }; use axerrno::{AxError, AxResult, LinuxError}; +#[cfg(feature = "netlink")] +use axnet::netlink::{GroupIdSet, NetlinkSocketAddr}; #[cfg(feature = "vsock")] use axnet::vsock::VsockAddr; use axnet::{SocketAddrEx, unix::UnixSocketAddr}; use linux_raw_sys::net::*; +#[cfg(feature = "netlink")] +use linux_raw_sys::netlink::sockaddr_nl; use crate::mm::{UserConstPtr, UserPtr}; @@ -241,11 +245,45 @@ impl SocketAddrExt for VsockAddr { } } +#[cfg(feature = "netlink")] +impl SocketAddrExt for NetlinkSocketAddr { + fn read_from_user(addr: UserConstPtr, addrlen: socklen_t) -> AxResult { + if addrlen != size_of::() as socklen_t { + return Err(AxError::InvalidInput); + } + let addr_nl = addr.cast::().get_as_ref()?; + if addr_nl.nl_family as u32 != AF_NETLINK { + return Err(AxError::from(LinuxError::EAFNOSUPPORT)); + } + + Ok(NetlinkSocketAddr::new( + addr_nl.nl_pid, + GroupIdSet::new(addr_nl.nl_groups), + )) + } + + fn write_to_user(&self, addr: UserPtr, addrlen: &mut socklen_t) -> AxResult<()> { + let socknl_addr = sockaddr_nl { + nl_family: AF_NETLINK as _, + nl_pad: 0, + nl_pid: self.port(), + nl_groups: self.groups().as_u32(), + }; + fill_addr(addr, addrlen, unsafe { cast_to_slice(&socknl_addr) }) + } + + fn family(&self) -> u16 { + AF_NETLINK as u16 + } +} + impl SocketAddrExt for SocketAddrEx { fn read_from_user(addr: UserConstPtr, addrlen: socklen_t) -> AxResult { match read_family(addr, addrlen)? as u32 { AF_INET | AF_INET6 => SocketAddr::read_from_user(addr, addrlen).map(Self::Ip), AF_UNIX => UnixSocketAddr::read_from_user(addr, addrlen).map(Self::Unix), + #[cfg(feature = "netlink")] + AF_NETLINK => NetlinkSocketAddr::read_from_user(addr, addrlen).map(Self::Netlink), #[cfg(feature = "vsock")] AF_VSOCK => VsockAddr::read_from_user(addr, addrlen).map(Self::Vsock), _ => Err(AxError::from(LinuxError::EAFNOSUPPORT)), @@ -256,6 +294,8 @@ impl SocketAddrExt for SocketAddrEx { match self { SocketAddrEx::Ip(ip_addr) => ip_addr.write_to_user(addr, addrlen), SocketAddrEx::Unix(unix_addr) => unix_addr.write_to_user(addr, addrlen), + #[cfg(feature = "netlink")] + SocketAddrEx::Netlink(netlink_addr) => netlink_addr.write_to_user(addr, addrlen), #[cfg(feature = "vsock")] SocketAddrEx::Vsock(vsock_addr) => vsock_addr.write_to_user(addr, addrlen), } diff --git a/api/src/syscall/fs/ctl.rs b/api/src/syscall/fs/ctl.rs index 4dac60c1..d6107995 100644 --- a/api/src/syscall/fs/ctl.rs +++ b/api/src/syscall/fs/ctl.rs @@ -12,13 +12,13 @@ use axhal::time::wall_time; use axtask::current; use linux_raw_sys::{ general::*, - ioctl::{FIONBIO, TIOCGWINSZ}, + ioctl::{FIOCLEX, FIONBIO, FIONCLEX, TCGETS, TIOCGWINSZ}, }; use starry_core::task::AsThread; use starry_vm::{VmPtr, vm_write_slice}; use crate::{ - file::{Directory, FileLike, get_file_like, resolve_at, with_fs}, + file::{Directory, FileLike, get_file_like, resolve_at, set_cloexec, with_fs}, mm::vm_load_string, time::TimeValueLike, }; @@ -26,28 +26,38 @@ use crate::{ /// The ioctl() system call manipulates the underlying device parameters /// of special files. pub fn sys_ioctl(fd: i32, cmd: u32, arg: usize) -> AxResult { - debug!("sys_ioctl <= fd: {fd}, cmd: {cmd}, arg: {arg}"); - let f = get_file_like(fd)?; - if cmd == FIONBIO { - let val = (arg as *const u8).vm_read()?; - if val != 0 && val != 1 { - return Err(AxError::InvalidInput); + debug!("sys_ioctl <= fd: {}, cmd: {}, arg: {}", fd, cmd, arg); + match cmd { + FIONBIO => { + let val = (arg as *const u8).vm_read()?; + if val > 1 { + return Err(AxError::InvalidInput); + } + get_file_like(fd)?.set_nonblocking(val != 0)?; + Ok(0) } - f.set_nonblocking(val != 0)?; - return Ok(0); - } - f.ioctl(cmd, arg) - .map(|result| result as isize) - .inspect_err(|err| { - if *err == AxError::NotATty { - // glibc likes to call TIOCGWINSZ on non-terminal files, just - // ignore it - if cmd == TIOCGWINSZ { - return; + FIOCLEX => { + set_cloexec(fd, true)?; + Ok(0) + } + FIONCLEX => { + set_cloexec(fd, false)?; + Ok(0) + } + _ => get_file_like(fd)? + .ioctl(cmd, arg) + .map(|result| result as isize) + .inspect_err(|err| { + if *err == AxError::NotATty { + // glibc likes to call TIOCGWINSZ and TCGETS on non-terminal files, just + // ignore it + if cmd == TIOCGWINSZ || cmd == TCGETS { + return; + } + warn!("Unsupported ioctl command: {cmd} for fd: {fd}"); } - warn!("Unsupported ioctl command: {cmd} for fd: {fd}"); - } - }) + }), + } } pub fn sys_chdir(path: *const c_char) -> AxResult { diff --git a/api/src/syscall/fs/fd_ops.rs b/api/src/syscall/fs/fd_ops.rs index 825ee48b..6203b625 100644 --- a/api/src/syscall/fs/fd_ops.rs +++ b/api/src/syscall/fs/fd_ops.rs @@ -15,8 +15,8 @@ use starry_core::{task::AsThread, vfs::Device}; use crate::{ file::{ - Directory, FD_TABLE, File, FileLike, Pipe, add_file_like, close_file_like, get_file_like, - with_fs, + Directory, FD_TABLE, File, FileLike, Pipe, add_file_like, close_file_like, get_cloexec, + get_file_like, set_cloexec, with_fs, }, mm::{UserPtr, vm_load_string}, syscall::sys::{sys_getegid, sys_geteuid}, @@ -273,20 +273,12 @@ pub fn sys_fcntl(fd: c_int, cmd: c_int, arg: usize) -> AxResult { Ok(ret as _) } F_GETFD => { - let cloexec = FD_TABLE - .read() - .get(fd as _) - .ok_or(AxError::BadFileDescriptor)? - .cloexec; + let cloexec = get_cloexec(fd)?; Ok(if cloexec { FD_CLOEXEC as _ } else { 0 }) } F_SETFD => { let cloexec = arg & FD_CLOEXEC as usize != 0; - FD_TABLE - .write() - .get_mut(fd as _) - .ok_or(AxError::BadFileDescriptor)? - .cloexec = cloexec; + set_cloexec(fd, cloexec)?; Ok(0) } F_GETPIPE_SZ => { diff --git a/api/src/syscall/net/socket.rs b/api/src/syscall/net/socket.rs index b8ebc904..61a60556 100644 --- a/api/src/syscall/net/socket.rs +++ b/api/src/syscall/net/socket.rs @@ -1,4 +1,6 @@ use axerrno::{AxError, AxResult, LinuxError}; +#[cfg(feature = "netlink")] +use axnet::netlink::RouteTransport; #[cfg(feature = "vsock")] use axnet::vsock::{VsockSocket, VsockStreamTransport}; use axnet::{ @@ -15,6 +17,11 @@ use linux_raw_sys::{ SOCK_DGRAM, SOCK_SEQPACKET, SOCK_STREAM, sockaddr, socklen_t, }, }; +#[cfg(feature = "netlink")] +use linux_raw_sys::{ + net::{AF_NETLINK, SOCK_RAW}, + netlink::NETLINK_ROUTE, +}; use starry_core::task::AsThread; use crate::{ @@ -47,6 +54,13 @@ pub fn sys_socket(domain: u32, raw_ty: u32, proto: u32) -> AxResult { (AF_VSOCK, SOCK_STREAM) => { axnet::Socket::Vsock(VsockSocket::new(VsockStreamTransport::new())) } + #[cfg(feature = "netlink")] + (AF_NETLINK, SOCK_RAW | SOCK_DGRAM) => { + if proto != NETLINK_ROUTE as _ { + return Err(AxError::from(LinuxError::EPROTONOSUPPORT)); + } + axnet::Socket::Netlink(axnet::netlink::NetlinkSocket::new(RouteTransport::new())) + } (AF_INET, _) | (AF_UNIX, _) | (AF_VSOCK, _) => { warn!("Unsupported socket type: domain: {domain}, ty: {ty}"); return Err(AxError::from(LinuxError::ESOCKTNOSUPPORT)); diff --git a/api/src/syscall/task/job.rs b/api/src/syscall/task/job.rs index bf267a85..bb7954a4 100644 --- a/api/src/syscall/task/job.rs +++ b/api/src/syscall/task/job.rs @@ -28,7 +28,7 @@ pub fn sys_getpgid(pid: Pid) -> AxResult { pub fn sys_setpgid(pid: Pid, pgid: Pid) -> AxResult { let proc = &get_process_data(pid)?.proc; - if pgid == 0 { + if pgid == 0 || pgid == proc.pid() { proc.create_group(); } else if !proc.move_to_group(&get_process_group(pgid)?) { return Err(AxError::OperationNotPermitted);