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
58 changes: 29 additions & 29 deletions .fusa-reqs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1186,64 +1186,64 @@
},
{
"id": "REQ-RL-001",
"title": "Token bucket with configurable rate and burst",
"text": "ratelimit::Controller shall implement a token bucket with configurable rate (tokens/s) and burst capacity.",
"title": "Manager::admit admits while the domain's bucket has tokens",
"text": "rcp::ratelimit::Manager::admit shall return a success (falsy) std::error_code for a target EndpointKey whose token bucket has at least one token available.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B"
},
{
"id": "REQ-RL-002",
"title": "Tokens refilled based on elapsed time",
"text": "The token bucket shall refill tokens proportionally to elapsed wall-clock time since the last take().",
"title": "Tokens refill proportionally to elapsed now_ms",
"text": "rcp::ratelimit::TokenBucket::take shall refill tokens proportionally to the elapsed now_ms since the previous take() call on the same bucket, before evaluating admission, capped at Config::burst.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B"
},
{
"id": "REQ-RL-003",
"title": "ErrBusy when bucket exhausted",
"text": "send() shall return ErrBusy immediately, without calling inner send(), when fewer than 1 token remains.",
"title": "admit() returns ErrAdmissionDenied once the bucket is exhausted",
"text": "rcp::ratelimit::Manager::admit shall return ratelimit::ErrAdmissionDenied, unchanged, once fewer than one token remains in the target EndpointKey's bucket.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B"
},
{
"id": "REQ-RL-004",
"title": "Critical commands exempt when configured",
"text": "When Config::exempt_critical is true, Priority::Critical commands shall bypass the token bucket.",
"title": "Safety-tagged requests bypass the bucket when exempted",
"text": "When Config::exempt_safety_tagged is true, rcp::ratelimit::Manager::admit shall return success for an is_safety_tagged=true request without consuming a token, regardless of the target bucket's remaining token count.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B"
},
{
"id": "REQ-RL-005",
"title": "Non-critical commands not exempt",
"text": "Priority::Normal and Priority::High commands shall always be subject to the token bucket.",
"title": "Safety-tagged requests are throttled when not exempted",
"text": "When Config::exempt_safety_tagged is false, rcp::ratelimit::Manager::admit shall subject an is_safety_tagged=true request to the same token-bucket admission check as any other request.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B"
},
{
"id": "REQ-RL-006",
"title": "Zone mismatch rejected",
"text": "send() shall return ErrZoneMismatch if cmd.zone differs from the inner controller's zone.",
"title": "Distinct (stream, endpoint) domains have independent buckets",
"text": "rcp::ratelimit::Manager shall maintain an independent token bucket per distinct EndpointKey, such that exhausting one EndpointKey's bucket shall not affect admission for any other EndpointKey.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B"
},
{
"id": "REQ-RL-007",
"title": "Close stops sends",
"text": "close() shall cause subsequent send() calls to return ErrClosed.",
"title": "reset() rebuilds a domain's bucket at full burst",
"text": "rcp::ratelimit::Manager::reset shall discard the target EndpointKey's bucket state, such that its next admit() call is evaluated against a freshly constructed bucket at full Config::burst capacity.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B"
},
{
"id": "REQ-RL-008",
"title": "send() after close returns ErrClosed",
"text": "Any send() call after close() shall return ErrClosed.",
"title": "ErrAdmissionDenied is a distinct error code",
"text": "ratelimit::ErrAdmissionDenied shall be a non-zero std::error_code in a category distinct from every other rcp error category.",
"standard": "iso26262",
"level": "ASIL-B",
"asil": "ASIL-B"
Expand Down Expand Up @@ -1682,24 +1682,24 @@
},
{
"id": "REQ-AUTH-001",
"title": "AuthController enforces AccessPolicy on every send",
"text": "AuthController.send() shall check the presented Identity against the AccessPolicy before forwarding any Command. If the identity is not permitted, ErrForbidden shall be returned without invoking the inner controller.",
"title": "AccessPolicy::permit grants access only on a full (identity, stream, endpoint, kind) match",
"text": "rcp::authz::AccessPolicy::permit shall return true only when at least one PolicyEntry for the given identity matches the given target stream, target endpoint, and request kind simultaneously; it shall return false if any one axis does not match.",
"standard": "iec62443",
"level": "SL-2",
"asil": "ASIL-B"
},
{
"id": "REQ-AUTH-002",
"title": "Empty zone/type set means all allowed",
"text": "A PolicyEntry with an empty zones set shall permit any Zone. A PolicyEntry with an empty cmd_types set shall permit any CommandType.",
"title": "Empty streams/endpoints/kinds sets mean unrestricted on that axis",
"text": "A PolicyEntry with an empty streams set shall match any target stream. A PolicyEntry with an empty endpoints set shall match any target endpoint. A PolicyEntry with an empty kinds set shall match any request kind.",
"standard": "iec62443",
"level": "SL-2",
"asil": "ASIL-B"
},
{
"id": "REQ-AUTH-003",
"title": "Identity resolver takes precedence over set_identity",
"text": "If an IdentityFn is supplied to AuthController, it shall be called on each send() and its result shall be used as the caller identity, superseding any value set via set_identity().",
"title": "Identity not covered by any PolicyEntry is denied",
"text": "rcp::authz::AccessPolicy::permit shall return false for any identity with no PolicyEntry present in the policy, regardless of the requested stream, endpoint, or kind.",
"standard": "iec62443",
"level": "SL-2",
"asil": "ASIL-B"
Expand All @@ -1714,32 +1714,32 @@
},
{
"id": "REQ-AUTH-005",
"title": "AuthController::zone() delegates to inner",
"text": "AuthController::zone() shall return the same zone as the wrapped inner controller.",
"title": "check() returns ErrForbidden when permit() would deny",
"text": "rcp::authz::check shall return authz::ErrForbidden, unchanged, whenever the equivalent AccessPolicy::permit() call would return false.",
"standard": "iec62443",
"level": "SL-2",
"asil": "ASIL-A"
},
{
"id": "REQ-AUTH-006",
"title": "AuthController::subscribe() delegates to inner",
"text": "AuthController::subscribe() shall forward the call to the inner controller unchanged.",
"title": "check() returns success when permit() would allow",
"text": "rcp::authz::check shall return a default-constructed (falsy) std::error_code whenever the equivalent AccessPolicy::permit() call would return true.",
"standard": "iec62443",
"level": "SL-2",
"asil": "ASIL-A"
},
{
"id": "REQ-AUTH-007",
"title": "AuthController::close() delegates to inner",
"text": "AuthController::close() shall forward the call to the inner controller and return its error code.",
"title": "An identity with zero policy entries is denied by default",
"text": "rcp::authz::AccessPolicy::permit shall return false for any identity for which AccessPolicy::allow() has never been called, i.e. the policy defaults to deny, not allow.",
"standard": "iec62443",
"level": "SL-2",
"asil": "ASIL-A"
},
{
"id": "REQ-AUTH-008",
"title": "ErrForbidden is a distinct error code",
"text": "authz::ErrForbidden shall be a non-zero std::error_code distinct from all other rcp error codes.",
"text": "authz::ErrForbidden shall be a non-zero std::error_code in a category distinct from every other rcp error category.",
"standard": "iec62443",
"level": "SL-2",
"asil": "ASIL-B"
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.21)

project(cpp-rcp
VERSION 2.10.0
VERSION 2.11.0
DESCRIPTION "Remote Control Protocol for automotive zonal architecture (C++)"
HOMEPAGE_URL "https://github.com/SoundMatt/cpp-RCP"
LANGUAGES CXX
Expand Down
51 changes: 51 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,57 @@ file's own disclaimer pattern in `rcp/e2e.hpp`/`rcp/regmap.hpp`/
already does one layer up
- Depends on: RC Server lifecycle & register map (v2.1.0)

**Done (v2.11.0):** Both `rcp/authz.hpp` and `rcp/ratelimit.hpp` are ADAPTed
in place per the Satellite Package Disposition table's entries for these
two files — the `AccessPolicy`/`PolicyEntry`/`AuthzErrc` and
`Config`/token-bucket shapes survive; only their keying axes change.
`authz.hpp`'s `PolicyEntry` and `AccessPolicy::permit` now key on
(`Identity`, target stream, target endpoint, request kind) instead of
(`Identity`, `Zone`, `CommandType`): a target stream is the same opaque
`uint64_t` per-connection key `rcp/regmap.hpp`'s `Ep0` and
`rcp/watchdog.hpp`'s `Manager` already use (typically an
`avtp::StreamId::to_u64()`), a target endpoint is an `avtp::ByteBusId`, and
a request kind is `rcp::request::RequestCategory` (v2.5.0) — already the
single taxonomy spanning the mandatory standard kind and every
conditional/cancellation kind. Per the roadmap's own framing, this package
does not reimplement `regmap.hpp`'s root-client/per-endpoint-owner access
model (v2.1.0) — it has no dependency on `regmap.hpp` at all — it adds an
independent policy gate a dispatch call site consults *in addition to*
that model. The pre-replacement `AuthController` wrapper is dropped
outright rather than rebound: there is no longer a single unified
`Controller::send()` chokepoint to wrap (that unification, if any, does
not land until the CLI/capi/adapt rebuilds at v2.16.0), so `permit`/`check`
are standalone primitives a dispatch call site invokes directly, the same
"primitives driven by the embedding application" pattern
`rcp/e2e.hpp`/`rcp/watchdog.hpp`/`rcp/request.hpp` already established.

`ratelimit.hpp`'s token bucket is now one per (target stream, target
endpoint) admission-control domain (`ratelimit::EndpointKey`), keyed the
same stream/`byte_bus_id` way, instead of one per `Controller`
instance/`Zone`; `ratelimit::Manager` multiplexes domains lazily, mirroring
`rcp/watchdog.hpp`'s `Manager` multiplexing one `StreamWatchdog` per
stream. The pre-replacement `Priority::Critical` bypass — `Priority` being
`rcp/prioqueue.hpp`'s whole client-side-priority-wrapper concept, marked
DEPRECATE outright per the disposition table, with no TC18 analog — is
replaced by an explicit `is_safety_tagged` argument callers derive from
`rcp::request::is_safety_variant` (v2.6.0): the traffic class that
ultimately drives an endpoint through its configured safe state once it
executes is the closest real analog to "must not be dropped by an
admission-control layer" here. Like every other Phase 14 primitive header,
`TokenBucket::take`/`Manager::admit` take an explicit `now_ms` rather than
reading a clock internally, the same convention `rcp/e2e.hpp`'s
`RxWatchdog` and `rcp/watchdog.hpp`'s `StreamWatchdog` already use.

Nothing else in this tree depended on the old `AuthController`/
`ratelimit::Controller` APIs (only their own tests did), so no legacy shim
was needed, same as every other Phase 14 rebuild. New/rewritten coverage
lives in `tests/test_authz.cpp` (`REQ-AUTH-001..008`, entirely rewritten)
and `tests/test_ratelimit.cpp` (`REQ-RL-001..008`, entirely rewritten),
traced in `.fusa-reqs.json`. Full bit-for-bit conformance against other
TC18 implementations is not claimed — see this file's own disclaimer
pattern in `rcp/regmap.hpp`/`rcp/request.hpp`/`rcp/e2e.hpp`/
`rcp/watchdog.hpp`.

### 56. Test & Simulation Harness Rebuild (v2.12.0)

- Rebuild `mock.hpp` as an in-process RC Server simulator implementing the
Expand Down
Loading
Loading