The channel formula's baseLow must not be masked — better fit, NOT a validated mechanism¶
Date: 2026-07-31 Status: an expression that matches the whole corpus and regresses nothing. NOT validated against the kernel carving, and the carving disagrees with the model it lives in. Read section 0 before trusting any of the vocabulary below.
Started from one odd frame in XMSG-APPEND-REMOTE-BATCH-CAPTURED-2026-07-31.md.
0. Honest status — read this first¶
An earlier revision of this document was titled "SOLVED" and said "root cause found". That overstated what was done. What was actually done is curve-fitting: an expression was found that reproduces the Protocol-ID byte on 1449 of 1449 captured data frames. That is good evidence the expression predicts the wire. It is not evidence that it is how SINTRAN computes the byte, and the two were conflated.
Three specific reasons to hold the mechanism claim loosely:
-
The kernel symbol file contradicts the field layout the formula is built on.
POFTABSmakes the transported header 7 words / 14 bytes ending afterXMCSM, andXMCSMa single word, commented "datagram checksum; if not checksum, then message size". Our layout reads a 32-bitXMCSMand the channel formula usesXMCSM >> 24— a shift that cannot exist on a one-word field. This conflict was already documented as OPEN inXMSG-SUBHEADER-NAMED-FROM-SYMBOLS-2026-07-29.mdsection 4, and it was not checked before the "root cause" claim was made. -
Flags2is a LENGTH on exactly the traffic where the bug shows up. Measured across the corpus,Flags2 - infoLenis-28on 492 frames — the file-server ones, matching the "length - 28" rule already noted inChannelOffsetDiagnosticTests.cs. But there are 54 distinct offsets overall, so it is not a length everywhere. That is precisely the overloaded field the kernel comment describes: checksum or size, depending on the message.This reframes the whole divergence. On TAD/routing traffic
Flags2is a small class word and the "class anchors the channel" story works. On file-server trafficFlags2is a message length, sobaseLow = seed - Flags2lowis subtracting a byte count — which is meaningless as an "epoch base". The formula did not fail because of an isolated masking slip; it failed because a length is being fed into a term that means "class". -
The likely real shape is a checksum with a borrow, not an "epoch". The seed identity
(Counter + Flags1 + Flags2low) & 0xFF == seedis a checksum relation. Removing the mask restores a borrow across the byte boundary, which is what an ordinary subtract with carry does. That is a plausible reason the corrected expression fits perfectly — but "epoch" and "class" remain model vocabulary the kernel does not support.
What is safe to rely on: the corrected expression reproduces every frame in the corpus and breaks nothing that previously passed. Use it to validate and to build frames. What is not settled: why. Do not cite this document as having explained the mechanism, and do not build new inference on the words "epoch" or "class word" without resolving section 4 of the sub-header doc first.
1. The change¶
(Subject to section 0: this is the better-fitting expression, not a derived mechanism.)
baseLow = (seed - F2low) & 0xFF <- WRONG
baseLow = seed - F2low <- correct (signed)
epoch = (Flags1 - baseLow + 0xFF) >> 8
Channel = 0xDE - (XMCSM >> 24) - epoch
That is the entire change: drop the & 0xFF.
When F2low > seed the true baseLow is negative. Masking it to 0..255 turns it into a
large positive, which moves the 256-boundary inside the epoch shift and loses a borrow. The
epoch then comes out one too low and the predicted channel one too high — which is
exactly the shape of every mismatch observed (0xDA predicted where the wire said 0xD9,
0xDD where the wire said 0xDC).
Validation over the whole corpus¶
Every .pcapng in E:\Dev\Ronny\X25Emulator\pcap\, every link, seed learned per frame from
the frame itself (so the 100-103 and 102-103 links and the relayed +1 lanes are all included
rather than silently dropped):
| Model | Data frames correct |
|---|---|
masked baseLow (old) |
1267 / 1449 |
signed baseLow (fix) |
1449 / 1449 |
Zero seed violations. Critically, the fix breaks nothing: every capture that previously passed still passes at 100%.
2. Why it hid for so long¶
Flags2 variety per capture:
| Family | distinct Flags2 |
old-model result |
|---|---|---|
| TAD / routing / connect | 5 | 0 mismatches |
| File-server / FA | 11 - 15 | mismatches everywhere |
TAD and routing traffic uses a handful of class words, and they are all below the seed,
so seed - F2low never goes negative and the mask never bites. File-server traffic swings
Flags2 across a wide range on consecutive frames, many values above the seed, and the
error fires immediately.
So the old "VERIFIED 753/753" was scope-limited, not false. It was measured only on the family that cannot exercise the bug. This is worth remembering as a general trap: a conformance figure is only as good as the variety in the corpus it was measured on.
The clearest single view, from claude-file-stat-102-to-100-2026-07-29 — Flags1 marching
+1 per frame, actual channel pinned at 0xDC, prediction flipping purely on Flags2:
Flags1 Flags2 base(masked) ep pred act
0x017F 0x0008 0x0C 2 0xDC 0xDC ok (F2low < seed)
0x0180 0x0062 0xB2 1 0xDD 0xDC WRONG (F2low > seed -> mask bites)
0x0181 0x0008 0x0C 2 0xDC 0xDC ok
0x0182 0x0046 0xCE 1 0xDD 0xDC WRONG
With baseLow signed, rows 2 and 4 become base = -0x4E / -0x32, epoch 2, channel 0xDC.
3. A wrong turn worth recording¶
An earlier version of this document claimed the Flags2 dependence itself was spurious
and that the channel depended on Flags1 alone. That was wrong, and the reasoning that
produced it was invalid.
The "decisive test" was: does any (direction, Flags1) pair map to two different epochs? It
returned zero conflicts everywhere, which looked like strong evidence. It was vacuous —
Flags1 increments by exactly 1 per data frame per direction, so a (direction, Flags1)
pair identifies exactly one frame. Single-valuedness was guaranteed by construction and
carried no information at all.
What actually broke it open was dumping frames in arrival order for one direction and
doing the arithmetic by hand, which immediately showed the mismatches all had the same
signature (predicted exactly one too high) and that the signature tracked F2low > seed.
Lesson: a test that cannot fail is not evidence. Check that a proposed invariant has a way of being violated before believing a clean result.
4. What changed in the repo¶
SINTRAN\Devices\HDLC\WireShark\hdlc_tcp.lua — the mask removed at the envelope-validation
site, with the reasoning recorded inline, and the file-header summary corrected from
753/753 to 1449/1449.
The installed copy has NOT been updated. The dissector Wireshark actually loads lives at
C:\Program Files\Wireshark\plugins\hdlc_tcp.lua, which needs administrator rights to
replace. Copy the repo file over it and then Analyze -> Reload Lua Plugins (Ctrl+Shift+L).
I verified the edited file compiles (tshark loads it and reaches the Proto call) and I
verified the arithmetic independently at 1449/1449 with a standalone scanner. I have
not run the patched dissector end to end, because that requires replacing the
admin-owned installed copy.
5. Knock-on¶
Fixed:
Xmsg.Protocol\Packet\XmsgEnvelope.cs—BaseLowreturnedbyte, so the truncation hit both consumers. AddedBaseLowSignedand pointedComputeEpochat it;BaseLownow delegates to it and keeps the byte truncation, which is correct for its only other consumer (ComputeCounteris a byte and is exact modulo 256 either way). Whole XMSG solution builds clean and all 459 tests pass, including the envelope tests that encode captured frames — so the fix does not disturb any previously-verified case.hdlc_tcp.lua— mask removed, reasoning recorded inline (repo copy only; see section 4).xmsg-decodeskill — envelope block corrected to the signed form and 1449/1449.
XMSG-PROTOCOL.mdsection 18.5 — formula corrected, with the reasoning and the corpus figures recorded inline. The ACK section header updated from 904/904 to 1671/1671.- The channel anomaly in
XMSG-APPEND-REMOTE-BATCH-CAPTURED-2026-07-31.mdsection 6 is explained by this and is no longer open.
The ACK closed form was re-scanned and needs NO change¶
It uses the same epoch machinery and the same masked baseLow, and its "904/904" came from
the same TAD-only corpus, so it deserved the same scrutiny. Re-scanned over every capture:
| Check | masked baseLow |
signed baseLow |
|---|---|---|
| trailing byte | 1671/1671 | 1671/1671 |
| channel | 1671/1671 | 1671/1671 |
Identical, because ACK Flags2 low is only ever 1, 2 or 3 — always far below
S_ack (~0x1F) — so baseLow never goes negative and the mask never bites. Frames where
F2low > S_ack: zero. Prediction confirmed rather than assumed.
Minor new observation: a Flags2 low byte of 0x0003 occurs once in the corpus. The
spec documents only 0x0001 and 0x0002 for ACKs, so receivers should tolerate 0x0003
too.
Outstanding — the real question¶
The empirical work is done; the mechanism is not. The next step is carving, not more captures — no amount of curve-fitting will settle what the kernel actually computes:
- Resolve
XMSG-SUBHEADER-NAMED-FROM-SYMBOLS-2026-07-29.mdsection 4 — isXMCSMone word or two? Everything else depends on it, including whetherXMCSM >> 24is even a real quantity. - Find the code that writes SINTRAN header offset 12 (the Protocol-ID byte). If it is a
checksum/borrow rather than a channel selector, the whole "channel lane" vocabulary in
XMSG-PROTOCOL.mdneeds rewriting, not patching. - Pin down when
XMCSMcarries a checksum and when it carries a size — the kernel comment says both, and the 54 distinctFlags2 - infoLenoffsets say the corpus contains both.
Until then the expression is a validated predictor and should be described as one.