Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This file records each release. The format follows [Keep a Changelog](https://ke

- The eBPF verifier gives the result of kernel/bpf/verifier.c of Linux for all programs of bpf/test_verifier of the socket filter and cgroup program types. It prunes the paths with equal states at the jump targets and refuses a loop that does not end, it refines the bounds of a register after `BPF_JSET` and after a `BPF_JMP32` that compares as 64 bits, and it checks the alignment of the pointers and the size of each context field as Linux, with the errors and the log messages of Linux in the same order. `bpf(BPF_PROG_TEST_RUN)` runs a program on a packet or a context from user space. The kselftest boot test runs bpf/test_verifier, and tools/kselftest/bpf.md gives the result: 281 passed and 0 failed (#537).
- The stateful expressions of the sets of nf_tables, as `nft_set_elem_expr_alloc` of Linux: `NFTA_SET_EXPR` and `NFTA_SET_EXPRESSIONS` of a set, `NFTA_SET_ELEM_EXPR` and `NFTA_SET_ELEM_EXPRESSIONS` of an element, and `NFTA_DYNSET_EXPR` and `NFTA_DYNSET_EXPRESSIONS` of `dynset`. The expressions are `counter`, `quota`, `limit` and `connlimit`, two at most. Each element has its own state: a copy of the expressions of the set, or the ones that it gives with the same types. A `lookup` and a `dynset` run the expressions of the element that they find. The `connlimit` expression of nft_connlimit.c counts the connections that connection tracking still has (#542).
- The `AF_ALG` sockets with the type `hash`, as crypto/af_alg.c and crypto/algif_hash.c of Linux. A socket binds to a hash of `rk-crypto` by its name, `setsockopt(SOL_ALG, ALG_SET_KEY)` sets the key of an HMAC, and `accept` gives a socket for one computation. A `send` with `MSG_MORE` adds data, and a `recv` gives the digest. An `accept` on the socket of a computation copies its state. As with `crypto.af_alg_restrict=1` of Linux, a name that is not in the allow list gives `ENOENT`, and a name that needs a capability gives `ENOENT` without `CAP_SYS_ADMIN` or `CAP_NET_ADMIN` (#541).

## [0.5.22] - 2026-11-12

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/rk-syscall/src/net.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ fn family_socket(
let protocol = u32::try_from(protocol).map_err(|_| Errno::EINVAL)?;
Ok(rk_net::packet::PacketSocket::new(kind, protocol)?)
}
rk_net::alg::AF_ALG => Ok(rk_net::alg::AlgSocket::new(kind, protocol)?),
AF_NETLINK => {
let protocol = u32::try_from(protocol as i32).map_err(|_| Errno::EPROTONOSUPPORT)?;
Ok(rk_net::netlink::NetlinkSocket::new(kind, protocol)?)
Expand Down
1 change: 1 addition & 0 deletions net/rk-net/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ publish.workspace = true

[dependencies]
rk-abi.workspace = true
rk-crypto.workspace = true
rk-frame.workspace = true
rk-vfs.workspace = true

Expand Down
202 changes: 202 additions & 0 deletions net/rk-net/src/alg/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
//! The operation sockets of the type `hash`, as crypto/algif_hash.c of Linux.
//!
//! A send adds its data to the digest. A send with `MSG_MORE` keeps the digest open for more
//! data. A send without it ends the digest, and the next send starts a new one. A receive gives
//! the digest and ends it if it is open. A receive with no data before gives the digest of no
//! data. A receive shorter than the digest gives its start, with `MSG_TRUNC`. `accept` on an
//! operation socket gives a new one with a copy of the open digest.

use super::{AF_ALG, AllowList, Bound, Child, Tfm, get_socket_option};
use crate::consts::{MSG_MORE, MSG_TRUNC, SOCK_SEQPACKET};
use crate::options::Options;
use crate::{Message, PathOps, Received, Socket, next_ino};
use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::vec;
use alloc::vec::Vec;
use core::any::Any;
use rk_abi::Errno;
use rk_abi::native::socket::SOL_SOCKET;
use rk_crypto::Hash;
use rk_frame::sync::SpinLock;
use rk_vfs::inode::Result;

/// The hashes that a program can bind, as `hash_allowlist` of Linux.
const ALLOW: AllowList = &[
("cmac(aes)", false),
("hmac(md5)", false),
("hmac(sha1)", false),
("hmac(sha224)", false),
("hmac(sha256)", false),
("hmac(sha384)", false),
("hmac(sha512)", true),
("md4", false),
("md5", false),
("sha1", true),
("sha224", false),
("sha256", false),
("sha384", false),
("sha512", false),
];

/// Finds a hash, as `hash_bind` of Linux.
pub(super) fn bind(name: &str) -> Result<Box<dyn Hash>> {
super::check_restriction(name, ALLOW)?;
rk_crypto::alloc_hash(name)
}

/// The state of an operation socket, as `struct hash_ctx` of Linux.
struct Ctx {
hash: Box<dyn Hash>,
/// The digest is open: the last send had `MSG_MORE`.
more: bool,
/// The digest that the last send ended.
result: Option<Vec<u8>>,
/// The socket has no key yet.
nokey: bool,
}

/// An operation socket of the type `hash`.
pub(super) struct HashSocket {
ino: u64,
bound: Arc<Bound>,
ctx: SpinLock<Ctx>,
options: SpinLock<Options>,
}

impl HashSocket {
pub(super) fn new(bound: Arc<Bound>, hash: Box<dyn Hash>, nokey: bool) -> Arc<Self> {
Arc::new(Self {
ino: next_ino(),
bound,
ctx: SpinLock::new(Ctx { hash, more: false, result: None, nokey }),
options: SpinLock::new(Options::default()),
})
}

/// A socket with no key takes the key of the bound socket, as `hash_check_key` of Linux.
fn check_key(&self, ctx: &mut Ctx) -> Result<()> {
if !ctx.nokey {
return Ok(());
}
self.bound.check_key(|tfm| {
let Tfm::Hash(h) = tfm;
ctx.hash = h.box_clone();
})?;
ctx.nokey = false;
Ok(())
}
}

impl Drop for HashSocket {
fn drop(&mut self) {
let nokey = self.ctx.lock().nokey;
self.bound.release(nokey);
}
}

/// Ends the digest of `ctx` and gives it.
fn finish(ctx: &mut Ctx) -> Vec<u8> {
let mut out = vec![0u8; ctx.hash.digest_size()];
ctx.hash.finish(&mut out);
out
}

impl Socket for HashSocket {
fn family(&self) -> u32 {
AF_ALG
}

fn kind(&self) -> u32 {
SOCK_SEQPACKET
}

fn ino(&self) -> u64 {
self.ino
}

/// Gives a new operation socket with a copy of the open digest, as `hash_accept` of Linux.
fn accept(&self, _nonblock: bool) -> Result<Arc<dyn Socket>> {
let state = {
let mut ctx = self.ctx.lock();
self.check_key(&mut ctx)?;
ctx.more.then(|| ctx.hash.box_clone())
};
let Child::Hash(child) = self.bound.accept()?;
if let Some(hash) = state {
let mut c = child.ctx.lock();
c.hash = hash;
c.more = true;
}
Ok(child)
}

/// Adds the data to the digest, as `hash_sendmsg` of Linux.
fn send(&self, _: &dyn PathOps, msg: Message, flags: u32, _: bool) -> Result<usize> {
let mut ctx = self.ctx.lock();
self.check_key(&mut ctx)?;
let more = flags & MSG_MORE != 0;
let data = &msg.data;
if !ctx.more {
// A send that does not go on from an open digest drops the last digest.
ctx.result = None;
if data.is_empty() {
return Ok(0);
}
ctx.hash.init();
}
ctx.hash.update(data);
if !more {
let out = finish(&mut ctx);
ctx.result = Some(out);
}
ctx.more = more;
Ok(data.len())
}

/// Gives the digest, as `hash_recvmsg` of Linux.
fn recv(&self, len: usize, _flags: u32, _nonblock: bool) -> Result<Received> {
let mut ctx = self.ctx.lock();
self.check_key(&mut ctx)?;
let size = ctx.hash.digest_size();
let n = len.min(size);
let result = match ctx.result.take() {
Some(r) if !ctx.more => r,
r => {
if r.is_none() && !ctx.more {
ctx.hash.init();
}
ctx.more = false;
finish(&mut ctx)
}
};
let flags = if len < size { MSG_TRUNC } else { 0 };
Ok(Received { data: result[..n].to_vec(), len: n, flags, ..Received::default() })
}

fn name(&self) -> Vec<u8> {
(AF_ALG as u16).to_ne_bytes().to_vec()
}

fn get_option(&self, level: u32, name: u32) -> Result<Vec<u8>> {
if level != SOL_SOCKET {
return Err(Errno::EOPNOTSUPP);
}
get_socket_option(&self.options, name)
}

fn set_option(&self, level: u32, name: u32, value: &[u8]) -> Result<()> {
if level != SOL_SOCKET {
return Err(Errno::EOPNOTSUPP);
}
self.options.lock().set(name, value)
}

fn poll(&self) -> u32 {
0
}

fn as_any(&self) -> &dyn Any {
self
}
}
Loading
Loading