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
17 changes: 14 additions & 3 deletions userland/capsule_linux/abi/disclosure.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,19 @@ nanosleep | elapsed time at 1 ms, on the family's clock | the family's clock sta
getpid | the family's own number for the process | family numbering hides the machine's process count
socket | a descriptor number | its own table
connect | success or refusal; names resolve to addresses invented here | no DNS leaves the machine; the remote sees the network service's egress, not this machine
accept | a descriptor number and the address of a peer in its own family | its own table; the peer is one of its own processes
sendto | a count | as write
recvfrom | bytes the remote sent | the guest asked for them
sendmsg | a count, on the display socket | the family's own display
recvmsg | display events for its own surfaces | input only while focused, through the router
recvfrom | bytes and the sender's address, from its own family or the remote it connected to | the guest asked for them; a family address is 127.0.0.0/8 or a name the family bound
sendmsg | a count | as write
recvmsg | bytes and the sender's address, or display events for its own surfaces | as recvfrom; display input only while focused, through the router
shutdown | nothing | none
bind | success or refusal; anything outside 127.0.0.0/8 is refused | the ports are the family's own; nothing is bound on the machine's network
listen | success or refusal | its own state
getsockname | the address it bound, or one chosen here on 127.0.0.0/8 | its own state
getpeername | the address of a peer in its own family, or the remote it connected to | its own processes, or the address it named
socketpair | two descriptor numbers | its own table
setsockopt | success or refusal of an option kept on its own socket | its own state
getsockopt | options it set, Linux's defaults for the rest, and its socket's pending error | its own state; the defaults are Linux's constants, not the machine's
clone | a thread number in the family's numbering | as getpid
fork | a child number in the family's numbering | as getpid
vfork | as fork | as getpid
Expand Down Expand Up @@ -103,10 +111,13 @@ set_robust_list | 0 | a constant
epoll_pwait | as epoll_wait | as poll
timerfd_create | a descriptor number | its own table
timerfd_settime | expirations on the family's clock | as nanosleep
accept4 | as accept | as accept
epoll_create1 | a descriptor number | its own table
dup3 | a descriptor number | its own table
pipe2 | as pipe | its own table
recvmmsg | as recvmsg | as recvmsg
prlimit64 | fixed limits; changes refused | constants
sendmmsg | as sendmsg | as sendmsg
getrandom | bytes from the kernel's generator, up to 256 a call | fresh per call and never shared between guests
memfd_create | a descriptor number | its own table
statx | as stat | as stat
Expand Down
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)
})
}
Loading
Loading