Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions userland/capsule_linux/src/linux/abi/errno.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

//! Linux errno values, and the convention for returning them.

pub use super::errno_sock::*;

pub const EPERM: i64 = 1;
pub const ENOENT: i64 = 2;
pub const EINTR: i64 = 4;
Expand Down
33 changes: 33 additions & 0 deletions userland/capsule_linux/src/linux/abi/errno_sock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Linux errno values the socket calls answer with, from
//! include/uapi/asm-generic/errno-base.h and errno.h.

pub const EDOM: i64 = 33;
pub const EDESTADDRREQ: i64 = 89;
pub const EMSGSIZE: i64 = 90;
pub const EPROTOTYPE: i64 = 91;
pub const ENOPROTOOPT: i64 = 92;
pub const EPROTONOSUPPORT: i64 = 93;
pub const ESOCKTNOSUPPORT: i64 = 94;
pub const EOPNOTSUPP: i64 = 95;
pub const EADDRINUSE: i64 = 98;
pub const EADDRNOTAVAIL: i64 = 99;
pub const ENETUNREACH: i64 = 101;
pub const EISCONN: i64 = 106;
pub const ECONNABORTED: i64 = 103;
pub const EALREADY: i64 = 114;
4 changes: 3 additions & 1 deletion userland/capsule_linux/src/linux/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
#![allow(dead_code)]

pub mod errno;
pub mod errno_sock;
pub mod name;
pub mod nr;
pub mod nr_path;
pub mod nr_high;
pub mod nr_path;
pub mod nr_sched;
pub mod nr_sock;
2 changes: 1 addition & 1 deletion userland/capsule_linux/src/linux/abi/nr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.


//! Linux x86_64 syscall numbers, by family.

pub use super::nr_high::*;
pub use super::nr_sched::*;
pub use super::nr_sock::*;

pub const READ: u64 = 0;
pub const WRITE: u64 = 1;
Expand Down
28 changes: 28 additions & 0 deletions userland/capsule_linux/src/linux/abi/nr_sock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! Linux x86_64 syscall numbers for sockets, from
//! arch/x86/entry/syscalls/syscall_64.tbl. Same contract as `nr`.

pub const BIND: u64 = 49;
pub const LISTEN: u64 = 50;
pub const GETSOCKNAME: u64 = 51;
pub const GETPEERNAME: u64 = 52;
pub const SOCKETPAIR: u64 = 53;
pub const SETSOCKOPT: u64 = 54;
pub const GETSOCKOPT: u64 = 55;
pub const RECVMMSG: u64 = 299;
pub const SENDMMSG: u64 = 307;
4 changes: 1 addition & 3 deletions userland/capsule_linux/src/linux/call/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,6 @@ pub fn read(guest: &mut Guest, fd: u64, buf: u64, len: u64) -> u64 {
}

pub fn close(guest: &mut Guest, fd: u64) -> u64 {
if let Some(h) = guest.socket_handle(fd) {
net::close(h);
}
net::close(guest, fd);
file::close(guest, fd)
}
1 change: 1 addition & 0 deletions userland/capsule_linux/src/linux/guest/fork_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ impl Guest {
g.sid = self.sid;
g.umask = self.umask;
g.links = self.links.clone();
self.sockets.fork(child, &self.fds);
g
}
}
2 changes: 2 additions & 0 deletions userland/capsule_linux/src/linux/guest/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,6 @@ pub struct Guest {
pub blocked: Vec<super::Blocked>,
/// The image's symbolic links, read once and shared by the family.
pub links: alloc::rc::Rc<super::Links>,
/// Lets go of this process's family sockets when it is dropped (net::sock).
pub sockets: crate::linux::net::sock::Holder,
}
1 change: 1 addition & 0 deletions userland/capsule_linux/src/linux/guest/handle_new.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ impl Guest {
sleepers: Vec::new(),
blocked: Vec::new(),
links: Default::default(),
sockets: crate::linux::net::sock::Holder::new(pid),
}
}
}
9 changes: 8 additions & 1 deletion userland/capsule_linux/src/linux/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ use nonos_libc::{heap_init, heap_init_sized, mk_args};

/// An install holds a distribution's index while it resolves a closure.
/// Kali's main is 21 MB fetched and 85 MB inflated, parsed into records
/// beside it; Alpine's is a few. A run takes the default.
/// beside it; Alpine's is a few.
const INSTALL_HEAP: usize = 320 << 20;

/// A run holds the program it loads, read whole from the store, beside the
/// family's own state. A 6 MB Go program outgrew the 16 MiB default while it
/// was read; this is the most a program may be (`source::MAX_IMAGE`).
const RUN_HEAP: usize = 64 << 20;

pub fn init() {
let mut buf = [0u8; 256];
let n = mk_args(buf.as_mut_ptr(), buf.len());
Expand All @@ -35,6 +40,8 @@ pub fn init() {
// it is read, with that reason, instead of here without one.
let line = b"[LINUX] no room for a large index, installing in the default heap\n";
let _ = nonos_libc::mk_debug(line.as_ptr(), line.len());
} else if heap_init_sized(RUN_HEAP).is_ok() {
return;
}
let _ = heap_init();
}
68 changes: 68 additions & 0 deletions userland/capsule_linux/src/linux/net/accept.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! `accept` and `accept4`: the oldest connection a listener has queued, as
//! a new descriptor held by the caller alone.

use crate::linux::abi::errno;
use crate::linux::guest::Guest;

use super::close::discard;
use super::fd::{install, sock_of, SOCK_CLOEXEC, SOCK_NONBLOCK};
use super::sock::{self, Domain, Peer, Proto};

pub fn accept4(guest: &mut Guest, fd: u64, at: u64, lenp: u64, flags: u64) -> u64 {
if flags & !(SOCK_NONBLOCK | SOCK_CLOEXEC) != 0 {
return errno::fail(errno::EINVAL);
}
let id = match sock_of(guest, fd) {
Ok(id) => id,
Err(e) => return e,
};
let pid = guest.pid;
let taken = sock::with(|t| {
let s = t.get_mut(id).ok_or(errno::EBADF)?;
if s.proto == Proto::Dgram {
return Err(errno::EOPNOTSUPP);
}
if !s.listening {
return Err(errno::EINVAL);
}
let child = s.pending.pop_front().ok_or(errno::EAGAIN)?;
t.make_room(id);
let c = t.get_mut(child).ok_or(errno::ECONNABORTED)?;
c.holders.push(pid);
let from = match c.domain {
Domain::Inet => Peer::Inet(c.remote.unwrap_or_default()),
Domain::Unix => Peer::Unix(c.upeer.clone()),
};
Ok((child, from))
});
let (child, from) = match taken {
Ok(v) => v,
Err(e) => return errno::fail(e),
};
let n = install(guest, child, flags);
let Some(slot) = errno::slot(n) else {
return n;
};
let wrote = super::sockaddr_out::write(guest, at, lenp, &from);
if errno::slot(wrote).is_none() {
discard(guest, slot as u64);
return wrote;
}
n
}
42 changes: 42 additions & 0 deletions userland/capsule_linux/src/linux/net/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! What the rest of the personality calls on sockets.

pub use super::accept::accept4;
pub use super::bind::bind;
pub use super::call_kind::{flags as call_flags, wants_all};
pub use super::close::close;
pub use super::connect::connect;
pub use super::dgram::sendto;
pub use super::fd::{is_stream, sock_id};
pub use super::listen::listen;
pub use super::mmsg::{recvmmsg, sendmmsg};
pub use super::msg::sendmsg;
pub use super::msg_recv::recvmsg;
pub use super::name::{getpeername, getsockname};
pub use super::opt::{getsockopt, limit_ms, setsockopt};
pub use super::pair::socketpair;
pub use super::poll::{ready, POLLERR, POLLHUP};
pub use super::poll_set::poll;
pub use super::poll_socket::outside;
pub use super::recvfrom::recvfrom;
pub use super::select::{clear as select_clear, select};
pub use super::shutdown::shutdown;
pub use super::socket::socket;
pub use super::try_call::try_call;
pub use super::xfer_in::read as recv;
pub use super::xfer_out::write as send;
65 changes: 65 additions & 0 deletions userland/capsule_linux/src/linux/net/bind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! `bind` and `listen`, on 127.0.0.0/8 only (`policy`).

use crate::linux::abi::errno;
use crate::linux::guest::Guest;

use super::fd::sock_of;
use super::policy::not_loopback;
use super::sock::{self, Domain};
use super::sockaddr::{self, is_loopback, AF_INET};

pub fn bind(guest: &mut Guest, fd: u64, at: u64, len: u64) -> u64 {
let id = match sock_of(guest, fd) {
Ok(id) => id,
Err(e) => return e,
};
if sock::with(|t| t.get(id).is_some_and(|s| s.domain == Domain::Unix)) {
return super::named::bind(guest, id, at, len);
}
let (family, mut want) = match sockaddr::read(guest, at, len) {
Ok(v) => v,
Err(e) => return e,
};
if family != AF_INET {
return errno::fail(errno::EAFNOSUPPORT);
}
if !is_loopback(want.ip) {
return not_loopback("bind", want);
}
sock::with(|t| {
let Some(s) = t.get(id) else {
return errno::fail(errno::EBADF);
};
if s.domain != Domain::Inet || s.local.is_some() || s.svc.is_some() {
return errno::fail(errno::EINVAL);
}
if want.port == 0 {
match t.ephemeral(s.proto, want.ip) {
Some(port) => want.port = port,
None => return errno::fail(errno::EADDRINUSE),
}
} else if t.in_use(id, want) {
return errno::fail(errno::EADDRINUSE);
}
if let Some(s) = t.get_mut(id) {
s.local = Some(want);
}
errno::ok(0)
})
}
61 changes: 61 additions & 0 deletions userland/capsule_linux/src/linux/net/call_kind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// NONOS Operating System
// Copyright (C) 2026 NONOS Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

//! What kind of wait a socket call makes: its flags, and whether a
//! blocking one waits until it has moved everything.

use crate::linux::abi::{errno, nr};
use crate::linux::guest::Guest;

use super::flags::{MSG_DONTWAIT, MSG_WAITALL};
use super::{iov, mmsg};

/// The call's flags, where it has them.
pub fn flags(n: u64, a: [u64; 6]) -> u64 {
match n {
nr::RECVFROM | nr::SENDTO | nr::RECVMMSG | nr::SENDMMSG | nr::ACCEPT4 => a[3],
nr::RECVMSG | nr::SENDMSG => a[2],
_ => 0,
}
}

/// True when a blocking call waits until it has moved everything it asked
/// for: a send on a stream, a receive with MSG_WAITALL, and recvmmsg
/// without MSG_WAITFORONE.
pub fn wants_all(stream: bool, n: u64, flags: u64) -> bool {
match n {
nr::WRITE | nr::WRITEV | nr::SENDTO | nr::SENDMSG => stream,
nr::RECVFROM | nr::RECVMSG => stream && flags & MSG_WAITALL != 0,
nr::RECVMMSG => flags & (mmsg::MSG_WAITFORONE | MSG_DONTWAIT) == 0,
_ => false,
}
}

/// A receive's answer: the count, or the errno.
pub(super) fn bytes_in(guest: &Guest, id: u32, v: &iov::Iov, done: usize, flags: u64) -> u64 {
match super::xfer_in::recv(guest, id, v, done, flags) {
Ok(got) => errno::ok(got.n as u64),
Err(e) => e,
}
}

/// The bytes a msghdr's iovecs ask for.
pub(super) fn msg_len(guest: &Guest, msg: u64) -> usize {
let word = |at: u64| {
guest.read(at, 8).map_or(0, |b| u64::from_le_bytes(b.try_into().unwrap_or([0; 8])))
};
iov::read(guest, word(msg + 16), word(msg + 24)).map_or(0, |v| iov::total(&v))
}
Loading
Loading