summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/ouroboros/atomics.h39
-rw-r--r--include/ouroboros/crc16.h43
-rw-r--r--include/ouroboros/crc64.h44
-rw-r--r--include/ouroboros/crc8.h43
-rw-r--r--include/ouroboros/crypt.h5
-rw-r--r--include/ouroboros/errno.h1
-rw-r--r--include/ouroboros/fccntl.h13
-rw-r--r--include/ouroboros/flow.h3
-rw-r--r--include/ouroboros/hash.h3
-rw-r--r--include/ouroboros/ipcp-dev.h4
-rw-r--r--include/ouroboros/irm.h6
-rw-r--r--include/ouroboros/np1_flow.h2
-rw-r--r--include/ouroboros/qos.h57
-rw-r--r--include/ouroboros/ssm_pk_buff.h24
-rw-r--r--include/ouroboros/ssm_pool.h12
-rw-r--r--include/ouroboros/ssm_rbuff.h4
-rw-r--r--include/ouroboros/time.h6
-rw-r--r--include/ouroboros/tpm.h1
-rw-r--r--include/ouroboros/tw.h77
-rw-r--r--include/test/test.h6
20 files changed, 344 insertions, 49 deletions
diff --git a/include/ouroboros/atomics.h b/include/ouroboros/atomics.h
new file mode 100644
index 00000000..8e667522
--- /dev/null
+++ b/include/ouroboros/atomics.h
@@ -0,0 +1,39 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Atomic helpers
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_LIB_ATOMICS_H
+#define OUROBOROS_LIB_ATOMICS_H
+
+#define LOAD_RELAXED(p) (__atomic_load_n(p, __ATOMIC_RELAXED))
+#define LOAD_ACQUIRE(p) (__atomic_load_n(p, __ATOMIC_ACQUIRE))
+#define LOAD(p) (__atomic_load_n(p, __ATOMIC_SEQ_CST))
+
+#define STORE_RELAXED(p, v) (__atomic_store_n(p, v, __ATOMIC_RELAXED))
+#define STORE_RELEASE(p, v) (__atomic_store_n(p, v, __ATOMIC_RELEASE))
+#define STORE(p, v) (__atomic_store_n(p, v, __ATOMIC_SEQ_CST))
+
+#define FETCH_ADD_RELAXED(p, v) (__atomic_fetch_add(p, v, __ATOMIC_RELAXED))
+#define FETCH_SUB_RELAXED(p, v) (__atomic_fetch_sub(p, v, __ATOMIC_RELAXED))
+#define FETCH_ADD(p, v) (__atomic_fetch_add(p, v, __ATOMIC_SEQ_CST))
+#define FETCH_SUB(p, v) (__atomic_fetch_sub(p, v, __ATOMIC_SEQ_CST))
+
+#endif /* OUROBOROS_LIB_ATOMICS_H */
diff --git a/include/ouroboros/crc16.h b/include/ouroboros/crc16.h
new file mode 100644
index 00000000..df4d4f57
--- /dev/null
+++ b/include/ouroboros/crc16.h
@@ -0,0 +1,43 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * 16-bit Cyclic Redundancy Check (CCITT-FALSE variant)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+/*
+ * Polynomial: ITU-T V.41 / CCITT-FALSE, CRC-16/IBM-3740.
+ * reveng catalog: https://reveng.sourceforge.io/crc-catalogue
+ *
+ * Intended for medium-size header check sequences (typ. <= 4 KiB).
+ * Hamming distance HD=4 up to 32751 message bits.
+ */
+
+#ifndef OUROBOROS_LIB_CRC16_H
+#define OUROBOROS_LIB_CRC16_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define CRC16_HASH_LEN 2
+
+void crc16_ccitt_false(uint16_t * crc,
+ const void * buf,
+ size_t len);
+
+#endif /* OUROBOROS_LIB_CRC16_H */
diff --git a/include/ouroboros/crc64.h b/include/ouroboros/crc64.h
new file mode 100644
index 00000000..f6e407a0
--- /dev/null
+++ b/include/ouroboros/crc64.h
@@ -0,0 +1,44 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * 64-bit Cyclic Redundancy Check (NVMe variant)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+/*
+ * Polynomial: NVM Express Base Spec, CRC-64/NVMe.
+ * reveng catalog: https://reveng.sourceforge.io/crc-catalogue
+ *
+ * Fold-by-N (PCLMUL/PMULL) algorithm:
+ * V. Gopal et al., "Fast CRC Computation for Generic Polynomials
+ * Using PCLMULQDQ", Intel white paper, 2009.
+ */
+
+#ifndef OUROBOROS_LIB_CRC64_H
+#define OUROBOROS_LIB_CRC64_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define CRC64_HASH_LEN 8
+
+void crc64_nvme(uint64_t * crc,
+ const void * buf,
+ size_t len);
+
+#endif /* OUROBOROS_LIB_CRC64_H */
diff --git a/include/ouroboros/crc8.h b/include/ouroboros/crc8.h
new file mode 100644
index 00000000..97502a25
--- /dev/null
+++ b/include/ouroboros/crc8.h
@@ -0,0 +1,43 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * 8-bit Cyclic Redundancy Check (AUTOSAR variant)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+/*
+ * Polynomial: AUTOSAR SWS_CRC, CRC-8/AUTOSAR.
+ * reveng catalog: https://reveng.sourceforge.io/crc-catalogue
+ *
+ * Intended for short header check sequences (typ. <= 32 bytes).
+ * Hamming distance HD=4 up to 119 message bits, HD=3 up to 247.
+ */
+
+#ifndef OUROBOROS_LIB_CRC8_H
+#define OUROBOROS_LIB_CRC8_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#define CRC8_HASH_LEN 1
+
+void crc8_autosar(uint8_t * crc,
+ const void * buf,
+ size_t len);
+
+#endif /* OUROBOROS_LIB_CRC8_H */
diff --git a/include/ouroboros/crypt.h b/include/ouroboros/crypt.h
index 806d39ab..5e082bb9 100644
--- a/include/ouroboros/crypt.h
+++ b/include/ouroboros/crypt.h
@@ -33,7 +33,6 @@
#define MAX_HASH_SIZE 64 /* SHA-512/BLAKE2b max */
#define KEX_ALGO_BUFSZ 32
#define KEX_CIPHER_BUFSZ 32
-#define MSGBUFSZ 2048
/*
* On OSX the OpenSSL NIDs are automatically loaded with evp.h.
@@ -95,6 +94,8 @@
#define X448MLKEM1024_PKSZ 1624 /* 56 + 1568 */
#define X448MLKEM1024_SKSZ 3224 /* 56 + 3168 */
+#define CRYPT_KEY_BUFSZ 4096 /* Safe buffer for key material */
+
#define KEM_MODE_SERVER_ENCAP 0 /* Server encapsulates (default) */
#define KEM_MODE_CLIENT_ENCAP 1 /* Client encapsulates */
#define IS_KEX_ALGO_SET(cfg) ((cfg)->x.nid != NID_undef)
@@ -358,6 +359,8 @@ int crypt_check_crt_name(void * crt,
int crypt_get_crt_name(void * crt,
char * name);
+void crypt_cleanup(void);
+
/* Secure memory allocation for sensitive data (keys, secrets) */
int crypt_secure_malloc_init(size_t max);
diff --git a/include/ouroboros/errno.h b/include/ouroboros/errno.h
index 9d84df88..eedd978f 100644
--- a/include/ouroboros/errno.h
+++ b/include/ouroboros/errno.h
@@ -37,5 +37,6 @@
#ifndef EAUTH /* Exists on BSD */
#define EAUTH 1009 /* Authentication error */
#endif
+#define EREPLAY 1010 /* OAP replay detected */
#endif /* OUROBOROS_ERRNO_H */
diff --git a/include/ouroboros/fccntl.h b/include/ouroboros/fccntl.h
index d3baea8f..e91e91dd 100644
--- a/include/ouroboros/fccntl.h
+++ b/include/ouroboros/fccntl.h
@@ -50,6 +50,12 @@
#define FRCTFRESCNTL 00000002 /* Feedback from receiver */
#define FRCTFLINGER 00000004 /* Send unsent data */
+/* All user-visible bits (readable via FRCTGFLAGS). */
+#define FRCTFMASK (FRCTFRTX | FRCTFRESCNTL | FRCTFLINGER)
+
+/* Subset writable via FRCTSFLAGS; FRCTFRTX is fixed at flow_alloc. */
+#define FRCTFSETMASK (FRCTFRESCNTL | FRCTFLINGER)
+
/* Flow operations */
#define FLOWSRCVTIMEO 00000001 /* Set read timeout */
#define FLOWGRCVTIMEO 00000002 /* Get read timeout */
@@ -60,10 +66,17 @@
#define FLOWGFLAGS 00000007 /* Get flags for flow */
#define FLOWGRXQLEN 00000010 /* Get queue length on rx */
#define FLOWGTXQLEN 00000011 /* Get queue length on tx */
+#define FLOWGMTU 00000012 /* Get per-packet MTU */
/* FRCT operations */
#define FRCTSFLAGS 00001000 /* Set flags for FRCT */
#define FRCTGFLAGS 00002000 /* Get flags for FRCT */
+#define FRCTSMAXSDU 00003000 /* Set max recv SDU size */
+#define FRCTGMAXSDU 00004000 /* Get max recv SDU size */
+#define FRCTSRRINGSZ 00005000 /* Set stream rcv ring sz */
+#define FRCTGRRINGSZ 00006000 /* Get stream rcv ring sz */
+#define FRCTSRTOMIN 00007000 /* Set RTO floor (ns) */
+#define FRCTGRTOMIN 00010000 /* Get RTO floor (ns) */
__BEGIN_DECLS
diff --git a/include/ouroboros/flow.h b/include/ouroboros/flow.h
index fe4582e7..8b096410 100644
--- a/include/ouroboros/flow.h
+++ b/include/ouroboros/flow.h
@@ -25,6 +25,7 @@
#include <ouroboros/qos.h>
+#include <stdint.h>
#include <sys/types.h>
#define SYMMKEYSZ 32
@@ -50,6 +51,8 @@ struct flow_info {
time_t mpl;
+ uint32_t mtu; /* n-1 layer MTU in bytes, 0 = unknown */
+
struct qos_spec qs;
enum flow_state state;
diff --git a/include/ouroboros/hash.h b/include/ouroboros/hash.h
index 0838df97..17ab98ac 100644
--- a/include/ouroboros/hash.h
+++ b/include/ouroboros/hash.h
@@ -38,6 +38,9 @@ enum hash_algo {
HASH_SHA3_512 = DIR_HASH_SHA3_512,
HASH_CRC32,
HASH_MD5,
+ HASH_CRC64,
+ HASH_CRC8,
+ HASH_CRC16,
};
#define HASH_FMT32 "%02x%02x%02x%02x"
diff --git a/include/ouroboros/ipcp-dev.h b/include/ouroboros/ipcp-dev.h
index 93236271..d23f757e 100644
--- a/include/ouroboros/ipcp-dev.h
+++ b/include/ouroboros/ipcp-dev.h
@@ -28,16 +28,20 @@
#include <ouroboros/ssm_pool.h>
#include <ouroboros/utils.h>
+#include <stdint.h>
+
int ipcp_create_r(const struct ipcp_info * info);
int ipcp_flow_req_arr(const buffer_t * dst,
qosspec_t qs,
time_t mpl,
+ uint32_t mtu,
const buffer_t * data);
int ipcp_flow_alloc_reply(int fd,
int response,
time_t mpl,
+ uint32_t mtu,
const buffer_t * data);
int ipcp_flow_read(int fd,
diff --git a/include/ouroboros/irm.h b/include/ouroboros/irm.h
index d5e4f1ab..7cb71c21 100644
--- a/include/ouroboros/irm.h
+++ b/include/ouroboros/irm.h
@@ -53,13 +53,13 @@ int irm_bootstrap_ipcp(pid_t pid,
const struct ipcp_config * conf);
int irm_connect_ipcp(pid_t pid,
- const char * component,
const char * dst,
+ const char * component,
qosspec_t qs);
int irm_disconnect_ipcp(pid_t pid,
- const char * component,
- const char * dst);
+ const char * dst,
+ const char * component);
int irm_bind_program(const char * prog,
const char * name,
diff --git a/include/ouroboros/np1_flow.h b/include/ouroboros/np1_flow.h
index 6f341cfc..309d01c2 100644
--- a/include/ouroboros/np1_flow.h
+++ b/include/ouroboros/np1_flow.h
@@ -37,12 +37,12 @@ int np1_flow_dealloc(int flow_id,
time_t timeo);
static const qosspec_t qos_np1 = {
+ .service = SVC_RAW,
.delay = UINT32_MAX,
.bandwidth = 0,
.availability = 0,
.loss = UINT32_MAX,
.ber = UINT32_MAX,
- .in_order = 0,
.max_gap = UINT32_MAX,
.timeout = 0
};
diff --git a/include/ouroboros/qos.h b/include/ouroboros/qos.h
index 6b0bbc17..7980ad00 100644
--- a/include/ouroboros/qos.h
+++ b/include/ouroboros/qos.h
@@ -28,79 +28,88 @@
#define DEFAULT_PEER_TIMEOUT 120000
+/* qos_spec.service: framing / reliability class. */
+enum qos_service {
+ SVC_RAW = 0, /* No FRCT; best-effort raw messages */
+ SVC_MESSAGE = 1, /* FRCT, reliable ordered messages */
+ SVC_STREAM = 2, /* FRCT, reliable ordered byte stream */
+};
+
typedef struct qos_spec {
+ uint8_t service; /* enum qos_service; gates FRCT (>0). */
uint32_t delay; /* In ms. */
uint64_t bandwidth; /* In bits/s. */
uint8_t availability; /* Class of 9s. */
uint32_t loss; /* Packet loss. */
uint32_t ber; /* Bit error rate, errors per billion bits. */
- uint8_t in_order; /* In-order delivery, enables FRCT. */
uint32_t max_gap; /* In ms. */
uint32_t timeout; /* Peer timeout time, in ms, 0 = no timeout. */
} qosspec_t;
+/* "_safe" = integrity check (ber=0). "rt" = latency over reliability. */
+
static const qosspec_t qos_raw = {
+ .service = SVC_RAW,
.delay = UINT32_MAX,
.bandwidth = 0,
.availability = 0,
.loss = 1,
.ber = 1,
- .in_order = 0,
.max_gap = UINT32_MAX,
- .timeout = DEFAULT_PEER_TIMEOUT
+ .timeout = 0
};
-static const qosspec_t qos_raw_no_errors = {
+static const qosspec_t qos_raw_safe = {
+ .service = SVC_RAW,
.delay = UINT32_MAX,
.bandwidth = 0,
.availability = 0,
.loss = 1,
.ber = 0,
- .in_order = 0,
.max_gap = UINT32_MAX,
- .timeout = DEFAULT_PEER_TIMEOUT
+ .timeout = 0
};
-static const qosspec_t qos_best_effort = {
- .delay = UINT32_MAX,
- .bandwidth = 0,
- .availability = 0,
+static const qosspec_t qos_rt = {
+ .service = SVC_MESSAGE,
+ .delay = 100,
+ .bandwidth = UINT64_MAX,
+ .availability = 3,
.loss = 1,
- .ber = 0,
- .in_order = 1,
- .max_gap = UINT32_MAX,
+ .ber = 1,
+ .max_gap = 100,
.timeout = DEFAULT_PEER_TIMEOUT
};
-static const qosspec_t qos_video = {
+static const qosspec_t qos_rt_safe = {
+ .service = SVC_MESSAGE,
.delay = 100,
.bandwidth = UINT64_MAX,
.availability = 3,
.loss = 1,
.ber = 0,
- .in_order = 1,
.max_gap = 100,
.timeout = DEFAULT_PEER_TIMEOUT
};
-static const qosspec_t qos_voice = {
- .delay = 50,
- .bandwidth = 100000,
- .availability = 5,
- .loss = 1,
+static const qosspec_t qos_msg = {
+ .service = SVC_MESSAGE,
+ .delay = 1000,
+ .bandwidth = 0,
+ .availability = 0,
+ .loss = 0,
.ber = 0,
- .in_order = 1,
- .max_gap = 50,
+ .max_gap = 2000,
.timeout = DEFAULT_PEER_TIMEOUT
};
-static const qosspec_t qos_data = {
+static const qosspec_t qos_stream = {
+ .service = SVC_STREAM,
.delay = 1000,
.bandwidth = 0,
.availability = 0,
.loss = 0,
.ber = 0,
- .in_order = 1,
.max_gap = 2000,
.timeout = DEFAULT_PEER_TIMEOUT
};
diff --git a/include/ouroboros/ssm_pk_buff.h b/include/ouroboros/ssm_pk_buff.h
index 1b779ad1..1d5597c7 100644
--- a/include/ouroboros/ssm_pk_buff.h
+++ b/include/ouroboros/ssm_pk_buff.h
@@ -28,25 +28,25 @@
struct ssm_pk_buff;
-size_t ssm_pk_buff_get_idx(struct ssm_pk_buff * spb);
+size_t ssm_pk_buff_get_off(const struct ssm_pk_buff * spb);
-uint8_t * ssm_pk_buff_head(struct ssm_pk_buff * spb);
+uint8_t * ssm_pk_buff_head(const struct ssm_pk_buff * spb);
-uint8_t * ssm_pk_buff_tail(struct ssm_pk_buff * spb);
+uint8_t * ssm_pk_buff_tail(const struct ssm_pk_buff * spb);
-size_t ssm_pk_buff_len(struct ssm_pk_buff * spb);
+size_t ssm_pk_buff_len(const struct ssm_pk_buff * spb);
-uint8_t * ssm_pk_buff_head_alloc(struct ssm_pk_buff * spb,
- size_t size);
+uint8_t * ssm_pk_buff_push(struct ssm_pk_buff * spb,
+ size_t size);
-uint8_t * ssm_pk_buff_tail_alloc(struct ssm_pk_buff * spb,
- size_t size);
+uint8_t * ssm_pk_buff_push_tail(struct ssm_pk_buff * spb,
+ size_t size);
-uint8_t * ssm_pk_buff_head_release(struct ssm_pk_buff * spb,
- size_t size);
+uint8_t * ssm_pk_buff_pop(struct ssm_pk_buff * spb,
+ size_t size);
-uint8_t * ssm_pk_buff_tail_release(struct ssm_pk_buff * spb,
- size_t size);
+uint8_t * ssm_pk_buff_pop_tail(struct ssm_pk_buff * spb,
+ size_t size);
void ssm_pk_buff_truncate(struct ssm_pk_buff * spb,
size_t len);
diff --git a/include/ouroboros/ssm_pool.h b/include/ouroboros/ssm_pool.h
index 89eff8eb..bba76798 100644
--- a/include/ouroboros/ssm_pool.h
+++ b/include/ouroboros/ssm_pool.h
@@ -32,7 +32,7 @@
struct ssm_pool;
-/* Pool API: uid = 0 for GSPP (privileged), uid > 0 for PUP (per-user) */
+/* Pool API: uid = 0 for GSPP (privileged), uid > 0 for PUP (per-user). */
struct ssm_pool * ssm_pool_create(uid_t uid,
gid_t gid);
@@ -46,13 +46,13 @@ int ssm_pool_mlock(struct ssm_pool * pool);
void ssm_pool_gspp_purge(void);
-/* Alloc count bytes, returns block index, a ptr and pk_buff. */
+/* Alloc count bytes, returns block offset, a ptr and pk_buff. */
ssize_t ssm_pool_alloc(struct ssm_pool * pool,
size_t count,
uint8_t ** ptr,
struct ssm_pk_buff ** spb);
-ssize_t ssm_pool_alloc_b(struct ssm_pool * pool,
+ssize_t ssm_pool_alloc_b(struct ssm_pool * pool,
size_t count,
uint8_t ** ptr,
struct ssm_pk_buff ** spb,
@@ -60,13 +60,13 @@ ssize_t ssm_pool_alloc_b(struct ssm_pool * pool,
ssize_t ssm_pool_read(uint8_t ** dst,
struct ssm_pool * pool,
- size_t idx);
+ size_t off);
struct ssm_pk_buff * ssm_pool_get(struct ssm_pool * pool,
- size_t idx);
+ size_t off);
int ssm_pool_remove(struct ssm_pool * pool,
- size_t idx);
+ size_t off);
void ssm_pool_reclaim_orphans(struct ssm_pool * pool,
pid_t pid);
diff --git a/include/ouroboros/ssm_rbuff.h b/include/ouroboros/ssm_rbuff.h
index ffa10b8e..2443b63d 100644
--- a/include/ouroboros/ssm_rbuff.h
+++ b/include/ouroboros/ssm_rbuff.h
@@ -55,10 +55,10 @@ void ssm_rbuff_fini(struct ssm_rbuff * rb);
int ssm_rbuff_mlock(struct ssm_rbuff * rb);
int ssm_rbuff_write(struct ssm_rbuff * rb,
- size_t idx);
+ size_t off);
int ssm_rbuff_write_b(struct ssm_rbuff * rb,
- size_t idx,
+ size_t off,
const struct timespec * abstime);
ssize_t ssm_rbuff_read(struct ssm_rbuff * rb);
diff --git a/include/ouroboros/time.h b/include/ouroboros/time.h
index 3d037a3c..a4136e8e 100644
--- a/include/ouroboros/time.h
+++ b/include/ouroboros/time.h
@@ -46,6 +46,12 @@
#define TS_TO_UINT64(ts) \
((uint64_t)(ts).tv_sec * BILLION + (uint64_t)(ts).tv_nsec)
+#define UINT64_TO_TS(ns, ts) \
+ do { \
+ (ts)->tv_sec = (time_t)((ns) / BILLION); \
+ (ts)->tv_nsec = (long)((ns) % BILLION); \
+ } while (0)
+
#define TIMEVAL_INIT_S(s) {(s), 0}
#define TIMEVAL_INIT_MS(ms) {(ms) / 1000, ((ms) % 1000) * 1000}
#define TIMEVAL_INIT_US(us) {(us) / MILLION, ((us) % MILLION)}
diff --git a/include/ouroboros/tpm.h b/include/ouroboros/tpm.h
index c01a235c..56c04701 100644
--- a/include/ouroboros/tpm.h
+++ b/include/ouroboros/tpm.h
@@ -24,6 +24,7 @@
#define OUROBOROS_LIB_TPM_H
#include <stdbool.h>
+#include <sys/types.h>
struct tpm;
diff --git a/include/ouroboros/tw.h b/include/ouroboros/tw.h
new file mode 100644
index 00000000..156f99db
--- /dev/null
+++ b/include/ouroboros/tw.h
@@ -0,0 +1,77 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Generic deadline-ordered callback queue (timing wheel)
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., http://www.fsf.org/about/contact/.
+ */
+
+#ifndef OUROBOROS_TW_H
+#define OUROBOROS_TW_H
+
+#include <ouroboros/cdefs.h>
+#include <ouroboros/list.h>
+
+#include <stddef.h>
+#include <stdint.h>
+#include <time.h>
+
+typedef void (*tw_fire_fn_t)(void * arg);
+
+struct tw_entry {
+ struct list_head next;
+ uint64_t deadline_ns;
+ tw_fire_fn_t fire;
+ void * arg;
+ size_t lvl;
+};
+
+__BEGIN_DECLS
+
+int tw_init(void);
+
+void tw_fini(void);
+
+void tw_init_entry(struct tw_entry * e);
+
+/*
+ * Schedule e to fire at deadline_ns. If e is already posted,
+ * the previous schedule is cancelled and replaced.
+ */
+void tw_post(struct tw_entry * e,
+ uint64_t deadline_ns,
+ tw_fire_fn_t fire,
+ void * arg);
+
+void tw_cancel(struct tw_entry * e);
+
+/*
+ * Advance the wheel and fire due callbacks. Callbacks run with the wheel
+ * unlocked and may call tw_post / tw_cancel on any entry, including the one
+ * currently firing. Concurrent tw_move from a second thread is a no-op.
+ */
+void tw_move(void);
+
+/*
+ * Write the absolute deadline of the earliest pending entry to *out.
+ * Empty wheel is signalled by out->tv_nsec == -1.
+ */
+void tw_next_expiry(struct timespec * out);
+
+__END_DECLS
+
+#endif /* OUROBOROS_TW_H */
diff --git a/include/test/test.h b/include/test/test.h
index 99681384..a76fe62a 100644
--- a/include/test/test.h
+++ b/include/test/test.h
@@ -30,6 +30,9 @@
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/resource.h>
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif
#define TEST_RC_SUCCESS 0
#define TEST_RC_SKIP 1
@@ -86,6 +89,9 @@ static int __attribute__((unused)) test_assert_fail(int(* testfunc)(void))
#ifdef DISABLE_TESTS_CORE_DUMPS
struct rlimit rl = { .rlim_cur = 0, .rlim_max = 0 };
setrlimit(RLIMIT_CORE, &rl);
+#ifdef __linux__
+ prctl(PR_SET_DUMPABLE, 0);
+#endif
#endif
return testfunc(); /* should abort */
}