Skip to content

fix(kiss): decode frames sharing a single FEND delimiter#444

Open
chrissnell wants to merge 1 commit into
mainfrom
fix/kiss-fend-shared-delimiter-rx
Open

fix(kiss): decode frames sharing a single FEND delimiter#444
chrissnell wants to merge 1 commit into
mainfrom
fix/kiss-fend-shared-delimiter-rx

Conversation

@chrissnell

Copy link
Copy Markdown
Owner

Problem

A user running Graywolf as a KISS TNC client (tcp-client), dialing a real hardware TNC over the network, reported receiving no packets — while other apps connected to the same TNC saw traffic fine. This is a recurring "connects fine but no packets" report.

Root cause

kiss.Decoder.Next() ran a "skip bytes until the next FEND" resync at the top of every call. But FEND (0xC0) is a frame delimiter, not a per-frame wrapper — the byte immediately after a frame's closing FEND is the start of the next frame.

The decoder only worked for TNCs that wrap every frame C0 <frame> C0 (a leading FEND per frame). TNCs that instead:

  • share a single FEND between back-to-back frames (C0 f1 C0 f2 C0), or
  • emit only a trailing FEND per frame (no leading FEND),

had their frames thrown away: after emitting one frame (and consuming its trailing FEND), the next Next() discarded the following frame's bytes while hunting for a leading FEND that never comes. Result: every-other frame lost, or effectively all frames when packets arrive with idle gaps between them.

The server-listen and serial paths share the same decoder and had the same latent bug; it just surfaced most visibly on the newer tcp-client-to-real-TNC path.

Fix

Sync on the first FEND once per stream (still discards a TNC text banner or a mid-stream-connect partial frame), then never discard frame bytes again. The read loop's existing empty-frame skip (len(buf)==0 → continue) already absorbs leading FENDs, empty frames, and shared delimiters. Only the first pre-sync frame in trailing-FEND-only framing is unavoidably lost (standard KISS sync behavior).

Tests

  • TestDecodeSharedDelimiter — three frames sharing one FEND each → all decode.
  • TestDecodeTrailingFendOnly — trailing-FEND-only stream → first (pre-sync) frame lost, rest decode.
  • Existing TestDecodeMultipleFrames, TestDecodeSkipsLeadingFends, round-trip and escape tests still pass.

go test ./pkg/kiss/... and ./pkg/app/... green; go build ./... and go vet ./pkg/kiss/... clean.

Wiki: added invariant #58 documenting the FEND-delimiter contract so this isn't reintroduced.

The KISS decoder ran its 'skip until FEND' resync on every Next() call.
FEND is a frame delimiter, not a per-frame wrapper: the byte right after
a frame's closing FEND begins the next frame. TNCs that share one FEND
between back-to-back frames, or emit only a trailing FEND per frame,
had their packets silently discarded (every-other frame, or effectively
all frames when packets arrive with idle gaps) — the 'connects fine but
receives no packets' reports on the tcp-client path. The server and
serial paths share the same decoder and had the same latent bug.

Sync on the first FEND once per stream, then never discard frame bytes
again; the read loop's empty-frame skip already absorbs leading/shared
delimiters. Only the first pre-sync frame in trailing-FEND-only framing
is (unavoidably) lost.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant