ADR 0015: Protocol Codec Conformance Policy
Status
Accepted
Date
2026-06-11
Context
The SDK ships wire codecs for 3GPP protocols (GTP-U, PFCP, NAS-5GS, with NGAP planned). Codec bugs are uniquely dangerous: an encoder and decoder written by the same hand are internally consistent, so round-trip tests pass perfectly while every byte on the wire is wrong for a real peer. This failure mode occurred twice during development — a scrambled PFCP header flag layout and a byte-swapped Outer Header Creation description field — and in both cases the existing test suite was green because the fixtures had been derived from the codec's own output.
Decision
Every protocol codec crate (opc-proto-*) MUST satisfy all of the
following before it is merged, and CONFORMANCE.md must claim nothing the
tests do not prove:
- Spec-authored fixtures. Conformance tests include byte fixtures hand-authored from the 3GPP specification (or captured from an independent implementation), with octet-level comments citing the spec section. Fixtures derived from this codec's own encoder do not count as conformance evidence — they detect regressions, not wire-format errors.
- Byte-exact round-trips.
decode → encodemust reproduce the input bytes exactly for every fixture, including unknown/vendor-extension elements, which must be preserved raw. - Declared canonicalization. Where a typed view legitimately normalizes (zeroing spare bits, dropping forward-compatibility trailing octets that the spec requires receivers to ignore), CONFORMANCE.md must say so explicitly, and a raw byte-preserving layer must remain available for forwarding paths.
- Hostile-input safety. No panics on any input: checked arithmetic on all length/offset math, enforced decode limits (message length, element count, recursion depth), and negative tests for truncation, overflow, and depth bombs.
- Fuzzing. A fuzz target over the decode surface with a seed corpus of spec-valid messages, registered in the fuzz CI workflow. The fuzz crate must compile in CI even when fuzzing is not executed.
- Framework fit. Codecs implement the
opc-protocoltraits (BorrowDecode/OwnedDecode/Encode) and carry@spec/@reqtraceability tags so RFC 006 evidence tooling can index them. - CONFORMANCE.md enumerates exactly which messages, elements, and fields are covered, at which 3GPP release, and what belongs outside the codec boundary.
Consequences
Fixed NWu GRE profile clarification (SDK #789)
The TS 24.502 NWu profile requires transmitted Protocol Type zero while explicitly requiring receivers to ignore nonzero values. Its direction also comes from the caller's transport context, not from a wire field. For this profile, items 2, 3, and 6 above therefore have the following narrow scope:
- Golden tests assert byte-exact canonical transmit against independently authored expected bytes, including accepted inputs that require normalization. Raw-preserving NWu transmission is unsupported and must fail before writing. Callers may retain the original input for inspection, but forwarding arbitrary received headers is outside this fixed-profile codec.
- Decode entry points must require direction explicitly. They reuse
DecodeContext,DecodeError,Encode, andToOwnedPdu; the directionlessBorrowDecodeandOwnedDecodetraits must not silently choose a direction. - CONFORMANCE.md must identify every ignored received field, canonical byte change, unsupported outcome, and caller-owned limit or mapping choice.
This clarification does not relax independent wire evidence, adversarial tests, fuzz registration, bounded parsing, or value-free diagnostics. General GRE forwarding would require a separate declared contract and qualification.
Caller-selected Diameter routing errors
RFC 6733 §6.1 and §7.1.3 routing outcomes depend on the caller's routing
policy. The SDK exposes only the closed UnableToDeliver (3002) and
RealmNotServed (3003) choices, bound to an inspected request after existing
framing and dictionary failures take precedence. If no earlier failure is
selected, the binding requires P set and fails locally with a typed reason
when P is cleared, preserving RFC 6733 §3's local-processing requirement.
This does not certify a routing decision or replace command-specific
validation. Independent request and answer fixtures must prove the E-bit
grammar, identifiers, permitted reflection and size bounds through the
existing error-answer builder; no arbitrary numeric Result-Code constructor
is introduced.
General consequences
- Writing a codec costs more up front: authoring fixtures from the spec is slower than round-tripping the encoder. That cost is the point — it is the only test construction that catches self-consistent wire errors.
- Reviews of codec changes start from the fixtures: a reviewer verifies bytes against the cited spec section before reading the implementation.
opc-proto-gtpu,opc-proto-pfcp, andopc-proto-nasconform today and serve as the templates; future codecs (NGAP per ADR 0013) inherit the same bar.
N2 numeric fixture claims — 2026-09-17
A well-formed, digest-consistent N2 metadata vector encoded 38428 while its manifest claimed the registered service port 38412. Correct the bytes and require independent numeric claim checks, including every metadata tuple and DATA length. A passing disposition or writer reproduction is insufficient when an asserted field was never compared with the bytes. The amended fixture publication records the correction without granting runtime or interoperability claims; consumer qualification requires its review and merge (Refs #784, #788).