diff --git a/Bdd/README.md b/Bdd/README.md index cae5206b..71f2883c 100644 --- a/Bdd/README.md +++ b/Bdd/README.md @@ -18,14 +18,19 @@ for the compose layout. ## Tags +Authoritative reference: [`docs/bdd.md`](../docs/bdd.md#feature-tags). Quick recap below. + | Tag | Meaning | |---|---| | `@udp` / `@tcp` / `@tls` / `@mtls` | Transport-specific scenario; CI jobs filter by these. | +| `@buffered` | Needs a buffered wiring (CircularBuffer / PosixMessageQueueBuffer / service thread) beyond single-task PassthroughBuffer. Carried by all three targets. | +| `@store` | Needs file-backed `SolidSyslogBlockStore` capability (write blocks, replay, threshold callbacks). Carried by all three targets. | +| `@requires_message_size_1500` | Needs `SOLIDSYSLOG_MAX_MESSAGE_SIZE >= 1500` — only the UDP path-MTU clipping feature. FreeRTOS keeps the default trimmed, so this scenario is also `@freertoswip`. | | `@windows_wip` | Skipped on the Windows runner (typically OS-specific behaviour the OTel oracle doesn't model). | | `@freertoswip` | Skipped on the FreeRTOS-on-QEMU runner. Per-scenario follow-up tag for capability gaps; removed scenario-by-scenario as each gap closes. The early bring-up reasons (hardcoded `TEST_*` values, missing SD wiring, getopt-only args) have all been closed out by S08.03 + S08.04 slices. | | `@rtc` | Scenario assumes the device has an RTC and synchronised wall-clock time. Run on Linux/Windows (which have both). Skipped on FreeRTOS, which models a no-RTC product per RFC 5424 §6.2.3.1. | | `@no_rtc` | Scenario asserts the no-RTC product behaviour over the wire (`tzKnown="0"`, `isSynced="0"`). Run on FreeRTOS. Skipped on Linux/Windows. The NILVALUE TIMESTAMP itself is not asserted via the oracle — syslog-ng silently substitutes receipt time for `${ISODATE}` / `${S_ISODATE}` when the wire timestamp is NILVALUE — so that case is covered by formatter unit tests. | -| `@wip` | Globally skipped on every runner. | +| `@wip` | Globally skipped on every runner. Not currently in use on any scenario. | ## Running locally @@ -51,7 +56,7 @@ once and run Behave from inside the container: ```bash cmake --preset freertos-cross cmake --build --preset freertos-cross --target SolidSyslogBddTarget -behave --tags='not @wip and not @freertoswip and not @rtc and not @windows_wip and (@udp or @tcp)' Bdd/features/ +behave --tags='not @wip and not @freertoswip and not @rtc and not @windows_wip and (@udp or @tcp or @tls or @mtls)' Bdd/features/ ``` `BDD_TARGET=freertos` and `EXAMPLE_BINARY=build/freertos-cross/...` are diff --git a/Bdd/Targets/FreeRtos/README.md b/Bdd/Targets/FreeRtos/README.md index b4ac5bec..540a744a 100644 --- a/Bdd/Targets/FreeRtos/README.md +++ b/Bdd/Targets/FreeRtos/README.md @@ -6,15 +6,17 @@ QEMU's stdio UART. SolidSyslog runs here with the portable `SolidSyslogCircularBuffer` + `SolidSyslogFreeRtosMutex` drained by a dedicated FreeRTOS Service task, -a `SolidSyslogSwitchingSender` wrapping a UDP sender (over -`SolidSyslogFreeRtosDatagram`) and a TCP sender (over -`SolidSyslogFreeRtosTcpStream` + `SolidSyslogStreamSender`), the -`SolidSyslogFreeRtosResolver` pinned to the QEMU slirp gateway -(`10.0.2.2`), and the `Bdd/Targets/Common/BddTargetInteractive` runner. -Drive identity / endpoint / PRIVAL fields over the UART command channel -with `set NAME VALUE`, flip the active transport at runtime with -`switch udp` / `switch tcp` (routed through `BddTargetSwitchConfig`), -emit N RFC 5424 messages with `send N`, exit cleanly with `quit`. See +a `SolidSyslogSwitchingSender` wrapping four inner senders — UDP over +`SolidSyslogFreeRtosDatagram`, TCP over `SolidSyslogFreeRtosTcpStream` + +`SolidSyslogStreamSender`, and TLS + mTLS via `SolidSyslogMbedTlsStream` +layered over the same `SolidSyslogFreeRtosTcpStream` byte transport. +`SolidSyslogFreeRtosResolver` is pinned to the QEMU slirp gateway +(`10.0.2.2`), and the `Bdd/Targets/Common/BddTargetInteractive` runner +drives the target. Drive identity / endpoint / PRIVAL fields over the +UART command channel with `set NAME VALUE`, flip the active transport at +runtime with `switch udp` / `switch tcp` / `switch tls` / `switch mtls` +(routed through `BddTargetSwitchConfig`), emit N RFC 5424 messages with +`send N`, exit cleanly with `quit`. See [`Bdd/README.md`](../../Bdd/README.md) for how Behave pipes these commands through `qemu-system-arm`'s stdio UART. @@ -163,10 +165,10 @@ static IPv4 of `10.0.2.15` on the QEMU slirp network, and starts the scheduler. On link-up the IP-task event hook spawns the interactive task (running `BddTargetInteractive_Run`) and the Service task (draining the `SolidSyslogCircularBuffer` and pushing through the -`SolidSyslogSwitchingSender`'s active inner — UDP by default, TCP after -`switch tcp`). Each `send N` line over the UART emits N RFC 5424 -messages to `{10.0.2.2, port=g_port}` over whichever transport is -currently selected. +`SolidSyslogSwitchingSender`'s active inner — UDP by default; TCP, TLS, +or mTLS after the matching `switch` command). Each `send N` line over +the UART emits N RFC 5424 messages to `{10.0.2.2, port=g_port}` over +whichever transport is currently selected. The FreeRTOS GCC ARM_CM3 port supplies `vPortSVCHandler`, `xPortPendSVHandler`, and `xPortSysTickHandler`. diff --git a/README.md b/README.md index 461488d0..0a73a9d7 100644 --- a/README.md +++ b/README.md @@ -23,20 +23,23 @@ block store-and-forward with CRC-16 integrity, and the full [IEC 62443 SL1–SL4 component set](docs/iec62443.md). FreeRTOS support is in active development on Cortex-M3 (mps2-an385 under QEMU): -UDP and TCP transports via FreeRTOS-Plus-TCP, persistent store-and-forward via -ChaN FatFs (semihosting-backed disk image in BDD; integrator-supplied -`diskio.c` in production), host-TDD'd adapters, an interactive BDD target -wired with the portable CircularBuffer + FreeRtosMutex behind a Service -task, and BDD scenarios driven through QEMU's UART +UDP, TCP, TLS, and mTLS transports — TCP and UDP via FreeRTOS-Plus-TCP, TLS +and mTLS via `SolidSyslogMbedTlsStream` (Mbed TLS) layered over the same +FreeRTOS-Plus-TCP byte stream. Persistent store-and-forward via ChaN FatFs +(semihosting-backed disk image in BDD; integrator-supplied `diskio.c` in +production), host-TDD'd adapters, an interactive BDD target wired with the +portable CircularBuffer + FreeRtosMutex behind a Service task, and BDD +scenarios driven through QEMU's UART ([epic E08 #10](https://github.com/DavidCozens/solid-syslog/issues/10)). **Not yet production-ready**, and no API stability guarantee yet. Known gaps: -- Public API may evolve — sender implementations currently use static - singleton state (`SolidSyslogUdpSender`, `SolidSyslogStreamSender`, - `SolidSyslogSwitchingSender`), so multiple concurrent instances per - process aren't yet supported. Additional platform backends (RTOS, - custom) are still to land. +- Public API may evolve. Multiple concurrent sender instances are now + pool-backed (E11) — `SOLIDSYSLOG_UDP_SENDER_POOL_SIZE`, + `SOLIDSYSLOG_STREAM_SENDER_POOL_SIZE`, + `SOLIDSYSLOG_SWITCHING_SENDER_POOL_SIZE` in + `Core/Interface/SolidSyslogTunablesDefaults.h` set the slot counts; the + matching pools live in `Core/Source/SolidSyslog*SenderStatic.c`. - At-rest integrity is CRC-16 only; HMAC + AES-at-rest are planned for SL4 ([E17 #105](https://github.com/DavidCozens/solid-syslog/issues/105)). - TLS revocation (CRL / OCSP) is deferred to the OS trust store; the library @@ -85,16 +88,16 @@ Public headers are split by audience (Interface Segregation Principle): - **`SolidSyslogTimeQualitySd.h`** — timeQuality structured data (RFC 5424 §7.1): tzKnown, isSynced, syncAccuracy - **`SolidSyslogOriginSd.h`** — origin structured data (RFC 5424 §7.2): software, swVersion, enterpriseId, ip - **`SolidSyslogPosixClock.h`** / **`SolidSyslogPosixHostname.h`** / **`SolidSyslogPosixProcessId.h`** / **`SolidSyslogPosixSysUpTime.h`** — POSIX helpers -- **`SolidSyslogFreeRtosDatagram.h`** / **`SolidSyslogFreeRtosTcpStream.h`** / **`SolidSyslogFreeRtosResolver.h`** / **`SolidSyslogFreeRtosMutex.h`** / **`SolidSyslogFreeRtosSysUpTime.h`** — FreeRTOS adapters: FreeRTOS-Plus-TCP UDP datagram and TCP stream (with ARP-prime on cold connect and bounded `SO_RCVTIMEO` connect), hardcoded-IPv4 resolver, `xSemaphoreCreateMutexStatic`-backed mutex for CircularBuffer, and a kernel-tick sysUpTime source +- **`SolidSyslogFreeRtosDatagram.h`** / **`SolidSyslogFreeRtosTcpStream.h`** / **`SolidSyslogFreeRtosResolver.h`** / **`SolidSyslogFreeRtosAddress.h`** / **`SolidSyslogFreeRtosMutex.h`** / **`SolidSyslogFreeRtosSysUpTime.h`** — FreeRTOS adapters: FreeRTOS-Plus-TCP UDP datagram and TCP stream (with ARP-prime on cold connect and bounded `SO_RCVTIMEO` connect), hardcoded-IPv4 resolver, `struct freertos_sockaddr` address adapter, `xSemaphoreCreateMutexStatic`-backed mutex for CircularBuffer, and a kernel-tick sysUpTime source - **`SolidSyslogFatFsFile.h`** — ChaN FatFs file adapter for the `SolidSyslogFile` extension point; `f_sync` per write for crash-safe store-and-forward. RTOS-agnostic; runs on bare-metal, FreeRTOS, Zephyr, NuttX Three BDD-driven target binaries exercise the library on each supported platform. They live under [`Bdd/Targets/`](Bdd/Targets/) — one binary per platform, all named `SolidSyslogBddTarget`: -- **`Bdd/Targets/Linux/`** — POSIX, PosixMessageQueueBuffer, two pthreads (logger + service), SwitchingSender over UDP + TCP + TLS + mTLS (TLS build required for the last two); `--transport` sets the initial transport, `switch ` flips it at runtime -- **`Bdd/Targets/Windows/`** — Windows, CircularBuffer + WindowsMutex, Win32 service thread (`_beginthreadex`) draining the buffer, Winsock UDP / TCP, with the Windows clock / hostname / process-id / sysUpTime helpers -- **`Bdd/Targets/FreeRtos/`** — FreeRTOS-on-QEMU (Cortex-M3, mps2-an385), CircularBuffer + FreeRtosMutex drained by a dedicated Service task, UDP + TCP via FreeRTOS-Plus-TCP, persistent store-and-forward via ChaN FatFs over a semihosting-backed disk image, interactive `set NAME VALUE` / `send N` / `set store file` / `set shutdown 1` / `quit` command channel over the CMSDK UART; BDD-driven against syslog-ng including `store_and_forward`, `power_cycle_replay`, and `store_capacity` scenarios. See [`Bdd/Targets/FreeRtos/README.md`](Bdd/Targets/FreeRtos/README.md) +- **`Bdd/Targets/Linux/`** — POSIX, PosixMessageQueueBuffer, two pthreads (logger + service), SwitchingSender over UDP + TCP + TLS + mTLS (OpenSSL); `--transport` sets the initial transport, `switch ` flips it at runtime +- **`Bdd/Targets/Windows/`** — Windows, CircularBuffer + WindowsMutex, Win32 service thread (`_beginthreadex`) draining the buffer, SwitchingSender over Winsock UDP / TCP + OpenSSL TLS / mTLS over Winsock TCP, file-backed `SolidSyslogBlockStore` over `SolidSyslogWindowsFile`, with the Windows clock / hostname / process-id / sysUpTime helpers +- **`Bdd/Targets/FreeRtos/`** — FreeRTOS-on-QEMU (Cortex-M3, mps2-an385), CircularBuffer + FreeRtosMutex drained by a dedicated Service task, SwitchingSender over UDP + TCP via FreeRTOS-Plus-TCP plus TLS + mTLS via `SolidSyslogMbedTlsStream` (Mbed TLS) layered over the same FreeRTOS-Plus-TCP byte stream, persistent store-and-forward via ChaN FatFs over a semihosting-backed disk image, interactive `set NAME VALUE` / `send N` / `set store file` / `set shutdown 1` / `quit` command channel over the CMSDK UART; BDD-driven against syslog-ng including `store_and_forward`, `power_cycle_replay`, `store_capacity`, `tls_transport`, and `mtls_transport` scenarios. See [`Bdd/Targets/FreeRtos/README.md`](Bdd/Targets/FreeRtos/README.md) ## Compliance diff --git a/docs/bdd.md b/docs/bdd.md index f67c06ee..de0af9cd 100644 --- a/docs/bdd.md +++ b/docs/bdd.md @@ -20,8 +20,8 @@ fail here. │ runs │ reads ▼ │ ┌──────────────┐│ - │ Example ││ - │ binary ││ + │ BDD target ││ + │ binary ││ └──────┬───────┘│ │ │ UDP 5514 / TCP 5514│ @@ -50,7 +50,7 @@ fault-finding tips. | FreeRTOS | inside `freertos-target` (cross image carries QEMU + Behave) | `syslog-ng-freertos` (shared netns; QEMU slirp `10.0.2.2` reaches it on loopback) | | Windows | `behave` on the runner | `otelcol-contrib` (no compose; runner-direct) | -The Linux example binary is built in the `gcc` container but executed by Behave via +The Linux BDD target binary is built in the `gcc` container but executed by Behave via `subprocess.run`. Both services share the workspace mount, so `Bdd/output/received.log` is visible to both syslog-ng (writer) and Behave (reader) without any network file transfer. The FreeRTOS ELF is built in the cross image and run under @@ -132,23 +132,27 @@ etc.) without rewriting feature tags. | `@tcp` | Needs TCP transport (RFC 6587 framing) | | `@tls` | Needs TLS transport (RFC 5425, server-auth) | | `@mtls` | Needs mutual TLS (client cert + key) | -| `@buffered` | Needs a Linux-only buffered example capability beyond a basic ring buffer + service thread — file-backed block store, switching sender between transports, or syslog-ng reload via the UNIX control socket. The cross-platform "single message via UDP/TCP through a real buffer" path is *not* `@buffered` post-S13.18; TLS and mTLS dropped `@buffered` in S13.19 once the OTel oracle gained TLS receivers (Windows otelcol-contrib listens on 6514 / 6515 with `client_ca_file` for mTLS). | +| `@buffered` | Needs a buffered wiring (CircularBuffer + service thread, PosixMessageQueueBuffer, etc.) beyond the single-task PassthroughBuffer path — e.g. file-backed block store, switching sender between transports, syslog-ng reload via the UNIX control socket. All three target binaries (Linux, Windows, FreeRTOS) carry a buffered wiring as of S13/S26, so `@buffered` is no longer excluded by any runner. TLS and mTLS dropped `@buffered` in S13.19 once the OTel oracle gained TLS receivers (Windows otelcol-contrib listens on 6514 / 6515 with `client_ca_file` for mTLS). | +| `@store` | Needs file-backed `SolidSyslogBlockStore` capability in the target (write blocks to disk, replay across restart, threshold callbacks). Carried alongside `@tcp @buffered` on the store-and-forward / capacity / power-cycle / block-lifecycle features. Run on all three targets — Linux + Windows over `SolidSyslogPosixFile` / `SolidSyslogWindowsFile`; FreeRTOS over `SolidSyslogFatFsFile`. | +| `@rtc` | Scenario assumes a real-time clock with synchronised wall-clock time — asserts a known absolute TIMESTAMP. Run on Linux and Windows; excluded on FreeRTOS, which models a no-RTC product per RFC 5424 §6.2.3.1. | +| `@no_rtc` | Scenario asserts the no-RTC product behaviour over the wire (`tzKnown="0"`, `isSynced="0"`). Run on FreeRTOS; excluded on Linux and Windows. The complementary pair of `@rtc`. | +| `@requires_message_size_1500` | Scenario requires `SOLIDSYSLOG_MAX_MESSAGE_SIZE` to be at least 1500 bytes — used by the UDP path-MTU clipping feature, which has to drive an oversized payload through `EMSGSIZE`. The Linux and Windows targets are built with the default 2048 max; FreeRTOS keeps the default trimmed to fit the embedded memory budget, so this scenario is also gated `@freertoswip` until the FreeRTOS target opts in. | Three rollout markers are also used (temporary; remove once the scenario passes): | Tag | Meaning | | --- | --- | -| `@wip` | Skip everywhere — work in progress | -| `@windows_wip` | Skip on Windows only — should work but not yet verified | -| `@freertoswip` | Skip on FreeRTOS only — scenario currently fails or errors on the FreeRTOS-on-QEMU target and is gated until the relevant capability lands. Each tagged scenario is a follow-up tied to a specific gap; the tag is removed scenario-by-scenario as the gap closes. The early bring-up reasons (hardcoded `TEST_*` values, missing SD elements, cmdline-only args) have all been closed out by S08.03 + S08.04 slices. | +| `@wip` | Skip everywhere — work in progress. Not currently in use on any scenario. | +| `@windows_wip` | Skip on Windows only — should work but not yet verified. Currently on `tcp_singletask.feature` (whole feature) and one scenario in `udp_mtu.feature`. | +| `@freertoswip` | Skip on FreeRTOS only — scenario currently fails or errors on the FreeRTOS-on-QEMU target and is gated until the relevant capability lands. Each tagged scenario is a follow-up tied to a specific gap; the tag is removed scenario-by-scenario as the gap closes. Currently on one scenario in `udp_mtu.feature` (paired with `@requires_message_size_1500`). | -Runner tag filters: +Runner tag filters (canonical source: `ci/docker-compose.bdd.yml` for Linux + FreeRTOS, `.github/workflows/ci.yml` for Windows): | Runner | Filter | | --- | --- | -| Linux (syslog-ng) | `not @wip` | -| FreeRTOS (syslog-ng-freertos via QEMU) | `not @wip and not @freertoswip and not @rtc and not @windows_wip and (@udp or @tcp)` | -| Windows (OTel Collector) | `not @wip and not @windows_wip and not @buffered` | +| Linux (syslog-ng) | `not @wip and not @no_rtc` | +| FreeRTOS (syslog-ng-freertos via QEMU) | `not @wip and not @freertoswip and not @rtc and not @windows_wip and (@udp or @tcp or @tls or @mtls)` | +| Windows (OTel Collector) | `not @wip and not @windows_wip and not @no_rtc` | ## Two oracles, one step file @@ -160,10 +164,10 @@ Step definitions read the active oracle from `ORACLE_FORMAT`: | OTel Collector Contrib (JSON Lines) | `otel-jsonl` | `Bdd/output/received.jsonl` | Windows native | `parse_oracle_line` dispatches to the right parser; both produce the same flat field dict -(`PRIORITY`, `TIMESTAMP`, `HOSTNAME`, `APP_NAME`, `PROCID`, ...) so the `Then` steps don't -need to know which oracle is in use. Walking-skeleton scope only — `STRUCTURED_DATA` -re-rendering for the OTel parser is added when the structured-data scenarios are -promoted out of `@windows_wip`. +(`PRIORITY`, `TIMESTAMP`, `HOSTNAME`, `APP_NAME`, `PROCID`, `STRUCTURED_DATA`, ...) so the +`Then` steps don't need to know which oracle is in use. The OTel parser re-renders the SD-ELEMENT +back into RFC 5424 wire form via `_render_otel_structured_data` so the structured-data scenarios +run cross-platform against the same assertions. ## Local Windows BDD setup diff --git a/docs/iec62443.md b/docs/iec62443.md index e4beefaf..42ee027c 100644 --- a/docs/iec62443.md +++ b/docs/iec62443.md @@ -37,7 +37,7 @@ at the end of this document for recommended starter component sets. | CR 2.11 | Timestamps | SL1+ | Caller-injected `SolidSyslogClockFunction` — `SolidSyslogPosixClock_GetTimestamp` (POSIX) or `SolidSyslogWindowsClock_GetTimestamp` (Windows). Quality metadata via `SolidSyslogTimeQualitySd` (`tzKnown` / `isSynced` / `syncAccuracy`) | None | | CR 2.12 | Non-repudiation | SL3+ | At the wire: `SolidSyslogTlsStream` mTLS cryptographically identifies the sender (SL4 deployments). End-to-end loss detection: `SolidSyslogMetaSd` sequenceId, gap detection at the SIEM. At rest: `SolidSyslogCrc16Policy` records integrity | At-rest CRC-16 is non-cryptographic — a local attacker can rewrite records and recompute the CRC. HMAC / AES-CMAC planned — [E17 #105](https://github.com/DavidCozens/solid-syslog/issues/105) | | CR 3.9 | Audit information protection | SL2+ | In transit: `SolidSyslogTlsStream` (TLS 1.2+, AEAD ciphers, hostname verification, optional mTLS). At rest: `SolidSyslogCrc16Policy` for tamper detection | At-rest cryptographic integrity / encryption planned — [E17 #105](https://github.com/DavidCozens/solid-syslog/issues/105) | -| SR 6.1 | Audit log accessibility | SL1+ | Two wirings, two cost models. **Single-task (`SolidSyslogPassthroughBuffer`):** `SolidSyslog_Log` performs the transport `Send` synchronously on the caller's thread — bounded by `SO_SNDTIMEO` (5 s — see [`SolidSyslogPosixTcpStream.c`](../Platform/Posix/Source/SolidSyslogPosixTcpStream.c) and [`SolidSyslogWinsockTcpStream.c`](../Platform/Windows/Source/SolidSyslogWinsockTcpStream.c)) but otherwise blocks the application until the bytes leave the socket. Suitable when the application has no real-time deadline or the transport latency is known to be sub-millisecond. **Buffered (`SolidSyslogCircularBuffer` with an injected `SolidSyslogMutex` — Posix / Windows / FreeRTOS / Null / caller-supplied RTOS primitive — or `SolidSyslogPosixMessageQueueBuffer`):** `SolidSyslog_Log` is non-blocking — it formats and enqueues, returning before any I/O. `SolidSyslog_Service`, called on the integrator's chosen thread (typically a dedicated service thread), performs the blocking transport I/O bounded by the same `SO_SNDTIMEO`. Caller picks the wiring that fits the deployment's audit-loss / latency trade-off | None | +| SR 6.1 | Audit log accessibility | SL1+ | Two wirings, two cost models. **Single-task (`SolidSyslogPassthroughBuffer`):** `SolidSyslog_Log` performs the transport `Send` synchronously on the caller's thread. The TCP socket is non-blocking from the moment it is opened (see [`SolidSyslogPosixTcpStream.c`](../Platform/Posix/Source/SolidSyslogPosixTcpStream.c) and [`SolidSyslogWinsockTcpStream.c`](../Platform/Windows/Source/SolidSyslogWinsockTcpStream.c)), so `Send` returns immediately on a wedged peer or a full kernel buffer; the bounded blocking surface is the initial `connect()`, sized by `SOLIDSYSLOG_TCP_CONNECT_TIMEOUT_MS` (default 200 ms) or runtime-overridable per Stream via `GetConnectTimeoutMs(ConnectTimeoutContext)` ([S12.17 #282](https://github.com/DavidCozens/solid-syslog/issues/282)). On the TLS path the same applies to the handshake — `SOLIDSYSLOG_TLS_HANDSHAKE_TIMEOUT_MS` (default 5000 ms) or `GetHandshakeTimeoutMs(HandshakeTimeoutContext)` for both OpenSSL and Mbed TLS adapters. Long-term silent peer death is detected out-of-band via kernel `TCP_KEEPALIVE` + `TCP_USER_TIMEOUT`. Suitable when the application has no real-time deadline or the transport latency is known to be sub-millisecond. **Buffered (`SolidSyslogCircularBuffer` with an injected `SolidSyslogMutex` — Posix / Windows / FreeRTOS / Null / caller-supplied RTOS primitive — or `SolidSyslogPosixMessageQueueBuffer`):** `SolidSyslog_Log` is non-blocking — it formats and enqueues, returning before any I/O. `SolidSyslog_Service`, called on the integrator's chosen thread (typically a dedicated service thread), performs the transport I/O against the same non-blocking socket; the bounded surface is the same connect / handshake budget as the single-task wiring. Caller picks the wiring that fits the deployment's audit-loss / latency trade-off | None | | SR 6.2 | Continuous monitoring | SL2+ | TCP / TLS delivery confirmation via `SolidSyslogStreamSender`. Replay across outages via `SolidSyslogBlockStore` store-and-forward. SIEM-side gap detection via `SolidSyslogMetaSd` sequenceId | None | CR 1.5 and CR 1.8 are listed only at SL4 because IEC 62443 requires them only @@ -174,7 +174,7 @@ For resource-constrained targets, SL1 or SL2 can operate with: - Single-task operation with `SolidSyslogPassthroughBuffer`, *or* multi-task on bare-metal / RTOS with `SolidSyslogCircularBuffer` driven by a `SolidSyslogMutex` you supply (the library ships `SolidSyslogPosixMutex`, - `SolidSyslogWindowsMutex`, and `SolidSyslogNullMutex`) + `SolidSyslogWindowsMutex`, `SolidSyslogFreeRtosMutex`, and `SolidSyslogNullMutex`) - Flash-based store-and-forward via an integrator-supplied `SolidSyslogBlockDevice` driving `SolidSyslogBlockStore` ([E18](https://github.com/DavidCozens/solid-syslog/issues/112)). diff --git a/docs/integrating-mbedtls.md b/docs/integrating-mbedtls.md index d18449c7..27a445f8 100644 --- a/docs/integrating-mbedtls.md +++ b/docs/integrating-mbedtls.md @@ -41,6 +41,7 @@ the per-context mbedTLS handles passed through |---|---|---| | `Transport` | You | A `SolidSyslogStream*` carrying TCP. The library ships `SolidSyslogPosixTcpStream` (POSIX), `SolidSyslogWinsockTcpStream` (Windows), and `SolidSyslogFreeRtosTcpStream` (FreeRTOS-Plus-TCP). If your TCP/IP stack is different (LwIP, NicheStack, vendor BSP), write your own `SolidSyslogStream` — see [`Platform/Posix/Source/SolidSyslogPosixTcpStream.c`](../Platform/Posix/Source/SolidSyslogPosixTcpStream.c) as a reference. | | `Sleep` | You | A `SolidSyslogSleepFunction`. Drives the bounded handshake retry between `WANT_READ` / `WANT_WRITE` polls. On FreeRTOS use a `vTaskDelay`-backed wrapper; on POSIX `SolidSyslogPosixSleep` is the natural fit. Required. | +| `GetHandshakeTimeoutMs` / `HandshakeTimeoutContext` | You (optional) | Per-instance accessor pair for the bounded handshake budget. `NULL` falls back to the `SOLIDSYSLOG_TLS_HANDSHAKE_TIMEOUT_MS` compile-time tunable (default 5000 ms). Install when you need to runtime-tune the handshake deadline — slow peers on a constrained link, or per-tenant policy from your existing configuration store. The accessor is called on every `Open`. See [S12.17 #282](https://github.com/DavidCozens/solid-syslog/issues/282). | | `Rng` | You | `mbedtls_ctr_drbg_context*` you seeded yourself. The adapter calls `mbedtls_ctr_drbg_random` against it. Required. | | `CaChain` | You | `mbedtls_x509_crt*` you parsed yourself (from filesystem, baked-in PEM, HSM, whatever fits your build). Required. | | `ServerName` | You | SNI + cert hostname check string. `NULL` skips the name check; only appropriate for closed networks where IP-pinning replaces hostname identity. | @@ -73,13 +74,15 @@ Concretely, on top of your existing setup: already have: ```c struct SolidSyslogMbedTlsStreamConfig cfg = { - .Transport = myTcpStream, /* from step 1 */ - .Sleep = MyVTaskDelayWrapper, /* or PosixSleep / similar */ - .Rng = &myAlreadySeededDrbg, - .CaChain = &myAlreadyParsedCaChain, - .ServerName = "syslog.example.com", - .ClientCertChain = &myClientCert, /* NULL for server-auth-only */ - .ClientKey = &myClientKey, /* paired with ClientCertChain */ + .Transport = myTcpStream, /* from step 1 */ + .Sleep = MyVTaskDelayWrapper, /* or PosixSleep / similar */ + .GetHandshakeTimeoutMs = NULL, /* defaults to SOLIDSYSLOG_TLS_HANDSHAKE_TIMEOUT_MS (5000 ms) */ + .HandshakeTimeoutContext = NULL, + .Rng = &myAlreadySeededDrbg, + .CaChain = &myAlreadyParsedCaChain, + .ServerName = "syslog.example.com", + .ClientCertChain = &myClientCert, /* NULL for server-auth-only */ + .ClientKey = &myClientKey, /* paired with ClientCertChain */ }; struct SolidSyslogStream* tlsStream = SolidSyslogMbedTlsStream_Create(&cfg); ``` diff --git a/docs/rfc-compliance.md b/docs/rfc-compliance.md index b1963c66..d8df20da 100644 --- a/docs/rfc-compliance.md +++ b/docs/rfc-compliance.md @@ -54,7 +54,7 @@ Status key: | 3.5 | Session closure handling | Supported | On send failure the stream is closed; the next Send transparently reconnects (S03.04) | | 3.5 | Handle receiver-initiated close | Supported | Detected via send failure path — same reconnect-on-next-Send mechanism (S03.04) | | 3.5 | Address rotation without app restart | Supported | App bumps `endpointVersion`; sender Disconnects and reconnects on next Send (S03.04) | -| — | Partial write handling (send returns short) | Supported | A short return from `send()` is treated as failure: `Send` returns false, the caller closes and reconnects on the next attempt, store-and-forward replays the message on the fresh socket. `SO_SNDTIMEO` is set at socket open (5 s, hard-coded — see `Platform/Posix/Source/SolidSyslogPosixTcpStream.c` and `Platform/Windows/Source/SolidSyslogWinsockTcpStream.c`) so a wedged peer can't make a single `SolidSyslog_Service` call hang indefinitely. On POSIX, `EINTR` is the only retried errno (portability shim for kernels without `SA_RESTART`); `EAGAIN`/`EWOULDBLOCK` (timeout) and any other error propagate as failure. Winsock has no signal-interruption semantics on `send()`, so the EINTR retry path is omitted; `WSAETIMEDOUT` (timeout) and any other error propagate as failure identically. | +| — | Partial write handling (send returns short) | Supported | A short return from `send()` is treated as failure: `Send` returns false, the caller closes and reconnects on the next attempt, store-and-forward replays the message on the fresh socket. The TCP socket is non-blocking from the moment it is created, so `Send` never blocks the service thread on a wedged peer or a full kernel send buffer — `EAGAIN`/`EWOULDBLOCK` (POSIX) and `WSAEWOULDBLOCK` (Winsock) propagate immediately as failure. On POSIX, `EINTR` is the only retried errno (portability shim for kernels without `SA_RESTART`); Winsock has no signal-interruption semantics on `send()`, so the EINTR retry path is omitted. Long-term wedge is detected out-of-band via kernel TCP keepalive (`TCP_KEEPIDLE` / `TCP_KEEPINTVL` / `TCP_KEEPCNT`) and `TCP_USER_TIMEOUT` for pending-write death; both are configured at socket open. Bounded connect is via non-blocking `connect()` + `select()`, with the timeout sourced from `SOLIDSYSLOG_TCP_CONNECT_TIMEOUT_MS` (default 200 ms) or runtime-overridable via the per-Stream `GetConnectTimeoutMs(ConnectTimeoutContext)` accessor — see `Platform/Posix/Source/SolidSyslogPosixTcpStream.c` and `Platform/Windows/Source/SolidSyslogWinsockTcpStream.c` ([S12.17 #282](https://github.com/DavidCozens/solid-syslog/issues/282)). | ## RFC 5425 — TLS Transport Mapping for Syslog