| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Re-key each encrypted flow's batch root periodically so a long-lived
flow never exhausts or over-uses a single root. The IRMd re-runs the
OAP exchange with the peer IRMd over the flow-update relay. The
per-flow re-keying state is tracked in the registry (reg_flow).
A re-key delivers one root seed from the OAP exchange. keyrot
immediately HKDF-expands it into 128 node keys (KR_NODES_SZ = 128 × 32
B) and wipes the root. Then each of the 128 node keys is itself a root
→ HKDF-expanded into 64 (2^KEY_NODE_BITS) leaf keys, forked per
direction; each leaf key is the actual AEAD key, good for 2^20 packets
(the low counter bits are its nonce/seq). If the number of keys runs
low, a re-key will be triggered (KEY_REKEY_WATERMARK).
The rekey is signalled out of band to the application. The rbuff ACL
is generalized into a flags word, so an RB_REKEY bit rides alongside
the access RB_RD/RB_WR and FLOWDOWN/FLOWPEER bits. The RD and WR bits
are revised ditching the fcntl historical weirdness. The seed is
pulled via flow_read/flow_write, installed with crypt_rekey(). TX
holds the old epoch until the peer is observed on the new one (or a
grace deadline elapses), promoted from both the read and write paths
so a recv-mostly flow still advances.
Also fix the FLOW_ACCEPT and FLOW_ALLOC handlers, which on a key-buffer
allocation failure returned from inside the cleanup-push region: that
leaked the reply message and skipped both the stack-key scrub and the
cleanup pop.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Flow crypto signalled rotation with a single phase-parity bit, so a
loss burst that hid an even number of rotations went unnoticed and
wedged the flow for good.
Each packet now carries a small cleartext selector naming its key
directly, so a receiver that falls behind recovers on the next packet
instead of getting stuck.
The selector also serves as the AEAD nonce and is authenticated as
associated data (AAD). Key rotation moves into a new backend-agnostic
keyrot module that rotates sub-keys to bound AEAD usage while
preserving forward secrecy.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Replace the linked-list cache with three timestamp-generation hash
buckets, each capped at OAP_REPLAY_MAX entries.
A bucket is an open-addressed hash set whose slots count as live only
while slot.gen equals the bucket generation, so a stale bucket clears
in O(1) by bumping its generation instead of being scanned and pruned.
On overflow the cache fails closed - it rejects the header rather than
evicting the oldest entry. Under flood, dropping a genuine entry would
let that header be replayed, so refusing new entries is the safer
degradation than evict-oldest (fail-open) behaviour.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
OAP accepted requests and responses without a certificate even when
the peer was expected to authenticate. An on-path attacker could
strip the certificate and signature from a flow allocation response
and substitute its own key exchange, silently downgrading the
handshake to unauthenticated.
Add an auth=required|optional policy to enc.conf, enforced per role: a
client config requires the server to present a valid certificate, a
server config requires the same from the client. Default is required
for client side (https), optional server side. The client side default
can be changed via OAP_CLIENT_AUTH_DEFAULT for testing.
Replace the bare 'none' keyword with encryption=none, which disables
encryption only: the digest and the authentication policy are kept, so
authenticated but unencrypted flows can be configured. Configs using
bare 'none' are now rejected.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
| |
The reclaim_pid_from_sc() function reaped any block allocated with
refcount from a dead PID, but cross-process hand-offs can leave a
block briefly allocated by the producer while a live consumer still
holds it. This skips reclaim within SSM_POOL_RECLAIM_AGE_S (default
60s) so in-flight hand-offs survive a producer crash.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
| |
The rbuff size was at one point coupled to RQ size for debugging,
reverting.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
The previous shape registered no-op _rib_read / _rib_readdir /
_rib_getattr handlers when IPCP_FLOW_STATS was off, and there was a
bug where *buf was not set in the no-op causing free on an
uninitialized pointer.
IPCP_FLOW_STATS was referenced by config.h.in but the declaration was
lost during the CMake refactor.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The Flow and Retransmission Control Protocol (FRCP) runs end-to-end
between two peers over a flow. It provides reliability, in-order
delivery, flow control, and liveness. Note that congestion avoidance
is orthogonal to FRCP and handled in the IPCP.
A fixed 16-octet header, network byte order, is prefixed to every FRCP
packet:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| flags | hcs |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| window |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| seqno |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ackno |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| payload (variable) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
hcs is a CRC-16-CCITT-FALSE checksum over the PCI (and the stream
extension when present), verified before any flag-driven dispatch. A
single packet can simultaneously carry DATA + ACK + FC + RXM by OR-ing
flag bits. An optional CRC trailer covers the body on DATA when qs.ber
== 0, and on every SACK packet; an optional AEAD wrap (per-flow keys)
sits outermost.
Flag bits (MSB-first; bits 13..15 reserved, MUST be zero):
+------+--------+--------+----------------------------------------+
| Bit | Mask | Name | Meaning |
+------+--------+--------+----------------------------------------+
| 0 | 0x8000 | DATA | Carries caller payload |
| 1 | 0x4000 | DRF | Start of a fresh data run |
| 2 | 0x2000 | ACK | ackno field valid |
| 3 | 0x1000 | NACK | Pre-DRF nudge (seqno informational) |
| 4 | 0x0800 | FC | window field valid (rwe advertisement) |
| 5 | 0x0400 | RDVS | Rendezvous probe (window-closed) |
| 6 | 0x0200 | FFGM | First Fragment of a multi-fragment SDU |
| 7 | 0x0100 | LFGM | Last Fragment of a multi-fragment SDU |
| 8 | 0x0080 | RXM | Retransmission |
| 9 | 0x0040 | SACK | Block list follows in payload |
| 10 | 0x0020 | RTTP | RTT probe / echo (payload follows) |
| 11 | 0x0010 | KA | Keepalive |
| 12 | 0x0008 | FIN | End of stream marker |
| 13-15| -- | -- | Reserved (MUST be zero) |
+------+--------+--------+----------------------------------------+
(FFGM, LFGM) encodes the fragment role of a DATA packet (SCTP-style
B/E): 11=SOLE, 10=FIRST, 00=MID, 01=LAST. Each fragment carries its
own seqno; Retransmission recovers fragments individually, reassembly
runs at consume time. In stream mode FFGM/LFGM are unused; per-byte
position is carried by the stream extension below and end-of-stream is
signalled by FIN on a 0-byte DATA packet.
SACK payload (FRCT_ACK | FRCT_FC | FRCT_SACK):
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| n_blocks | padding (2 octets) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| start[0] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| end[0] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
... n_blocks pairs total ...
Each block describes a *present* (received) range strictly above the
cumulative ACK in the PCI ackno. D-SACK (RFC 2883) is signalled
in-band as block[0] - no flag bit, no extra framing - and consumed by
the RACK reo_wnd_mult scaler (RFC 8985 sec. 7.2).
RTTP payload (FRCT_RTTP only; 24 octets):
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| probe_id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| echo_id |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ nonce (16 octets, echoed verbatim) +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Stream PCI extension (in_order == STREAM only; 8 octets after the base
PCI on every DATA packet):
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| start |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| end |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
start, end are monotonic 32-bit byte offsets; end - start equals the
on-wire payload length. Stream mode is negotiated at flow allocation;
the extension is present iff stream mode is in use, never on a
per-packet basis.
Service modes are an orthogonal (in_order, loss, ber) vector selected
at flow_alloc; the cubes above map to the axes:
+----------------+---------+------+-----+-----------------------+
| Cube | in_order| loss | ber | Engaged |
+----------------+---------+------+-----+-----------------------+
| qos_raw | 0 | 1 | 1 | Raw passthrough |
| qos_raw_safe | 0 | 1 | 0 | Raw + CRC trailer |
| qos_rt | 1 | 1 | 1 | FRCP, no FRTX, no CRC |
| qos_rt_safe | 1 | 1 | 0 | FRCP, no FRTX, CRC |
| qos_msg | 1 | 0 | 0 | FRCP + FRTX |
| qos_stream | 2 | 0 | 0 | FRCP + FRTX, stream |
+----------------+---------+------+-----+-----------------------+
in_order=0 sends raw datagrams with no PCI (UDP-equivalent);
in_order=1 engages FRCP with SDU framing; in_order=2 (stream) requires
loss=0 and is rejected otherwise. loss=0 engages the FRTX retransmit
machinery. ber=0 appends the CRC-32 trailer; QOS_DISABLE_CRC at build
time forces ber=1 for development. Encryption is a separate per-flow
attribute layered as an AEAD wrap outside the FRCP packet.
Heritage: delta-t (Watson 1981) supplies timer-based connection
management - no SYN/FIN handshake, the DRF marker, the t_mpl / t_a /
t_r timers. RINA (Day 2008) supplies the unified flow_alloc(name, qos,
...) primitive and the orthogonal QoS-cube axes. Loss detection
follows TCP/QUIC practice (RFCs 2018, 2883, 6582, 6298, 8985); RTT
probing is nonce-authenticated like QUIC PATH_CHALLENGE.
Adds oftp, a minimal file-transfer tool over an FRCP stream flow. The
client reads from stdin or --in FILE and writes through a
flow_alloc(qos_stream); the server (--listen) calls flow_accept and
writes to stdout or --out FILE. Both sides compute a CRC-64/NVMe over
the bytes they handle and print the result. The server rejects flows
whose negotiated qs.in_order != STREAM.
Two FRCP knobs are exposed via env vars on either side:
OFTP_FRCT_RTO_MIN fccntl FRCTSRTOMIN (ns)
OFTP_FRCT_STREAM_RING_SZ fccntl FRCTSRRINGSZ (octets)
The ocbr_client gains an OCBR_QOS env var to pick the cube the client
uses for flow_alloc; recognised values are raw, safe, rt, rt_safe,
msg, stream. Unknown values fall back to raw with a warning on
stderr. Without the env set behaviour is unchanged.
Removes the deprecated lib/timerwheel.c
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
| |
RXM_BUFFER_ON_HEAP and SSM_POOL_BLOCKS were no longer used.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Add IPCP_ETH_SNDBUF/RCVBUF cmake build options so deployments can size
the AF_PACKET socket buffers without patching code. Drop the
O_NONBLOCK fcntl on the raw socket in favour of per-recvfrom
MSG_DONTWAIT so race-loser reader threads exit with EAGAIN without
spamming the log.
Track frame send failures and the SO_SNDBUF size in the eth/summary
RIB; log a one-shot warning when the kernel reports AF_PACKET drops.
Retry name queries up to NAME_QUERY_RETRIES times within
NAME_QUERY_TIMEO; a single lost ARP-style mgmt frame no longer fails
the query. Bump IRMd QUERY_TIMEOUT from 200 ms to 2200 ms so the IRMd
budget exceeds the new shim retry window.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
| |
FRCT needs to know the MTU for fragmentation. The MTU is now passed
from the layer serving the flow to the process as part of flow
allocation.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
| |
Per-process flow / fd / fqueue limits are properties of a process, not
a program; align the naming. Mechanical rename of PROG_MAX_FLOWS,
PROG_RES_FDS, and PROG_MAX_FQUEUES to PROC_*.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
Adds an IPCP_ETH_FLOW_STATS cmake option (gated on HAVE_FUSE; default
off) exposing per-flow and aggregate frame counters at
/<ipcp>/eth/{summary,<fd>}.
Counters use RELAXED atomics; the macros expand to ((void) 0) when the
option is off. Per-flow and global counters live in nested stat
structs.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Add cmake/utils/CPUUtils.cmake providing detect_cpu_feature() plus
detect_pclmul() and detect_pmull() that compile-test for x86
PCLMULQDQ+SSE4.1 and aarch64 FEAT_PMULL respectively.
This will be useful for hardware accelerated CRC64/NVMe integrity
checks.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
This allows bypassing the IPCP for local processes that share the same
packet pool, lowering latency between processes to comparable levels
as Unix sockets (RTT in the order of a microsecond).
For local processes, no IPCPs are needed:
$ irm b prog oping n oping
$ oping -l
Ouroboros ping server started.
New flow 64.
Received 64 bytes on fd 64.
The direct IPC can be disabled with the DISABLE_DIRECT_IPC build
flag. Note that this is needed for rumba 'local' experiments to
emulate network topologies. Without this flag all processes will just
communicate directly.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
| |
The FUSE_PREFIX was set in global.cmake, but before HAVE_FUSE was
defined. The FUSE_PREFIX should be set in fuse.cmake.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The IPCP_*_TARGET variables (e.g., set(IPCP_LOCAL_TARGET ipcpd-local))
were defined locally in each IPCP's CMakeLists.txt (e.g.,
CMakeLists.txt), but the configure_file() that substitutes
@IPCP_LOCAL_TARGET@ into config.h.in runs in a sibling scope that is
processed before ipcpd. Since CMake variables don't propagate between
sibling directory scopes, all @IPCP_*_TARGET@ substituted to empty
strings, resulting in IPCP_LOCAL_EXEC "".
Moved the IPCP_*_TARGET definitions into the cmake/config/ipcp/*.cmake
files so they are known when generating config.h.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
| |
|
|
|
|
|
|
| |
The message for enabled/disabled state of the assertion tests core
dumping was reversed.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|
|
|
This moves the build definitions back to src/ subdirectories
(CMakeLists.txt per component). Configuration and dependencies are
kept out of tree. Configuration options are bundled into cmake/config/
modules. Dependencies are grouped by component (system/, crypt/, eth/,
coverage/, etc.). It now consistently uses target-based commands
(target_include_directories, target_link_libraries) instead of global
include_directories(). Proper PRIVATE/PUBLIC visibility for executable
link libraries. CONFIG_OUROBOROS_DEBUG now properly set based on being
a valid debug config (not just checking the string name).
It also adds OuroborosTargets export for find_package() support and
CMake package config files (OuroborosConfig.cmake) for easier
integration with CMake projects.
The build logic now follows more idiomatic CMake practices with
configuration separated from target definitions.
Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks>
Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
|