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:

  1. 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.
  2. Byte-exact round-trips. decode → encode must reproduce the input bytes exactly for every fixture, including unknown/vendor-extension elements, which must be preserved raw.
  3. 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.
  4. 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.
  5. 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.
  6. Framework fit. Codecs implement the opc-protocol traits (BorrowDecode/OwnedDecode/Encode) and carry @spec/@req traceability tags so RFC 006 evidence tooling can index them.
  7. 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, and ToOwnedPdu; the directionless BorrowDecode and OwnedDecode traits 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, and opc-proto-nas conform 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).