TAD connect-to — field analysis (what is solved vs. uncertain)¶
Analysis of every connect-to capture (decoded to fields) to separate the fields whose
values we can reproduce from the ones whose derivation is still unknown. Goal: a live node
that answers connect-to d103 with a terminal, without crashing 100's XMSG.
Captures analysed (all under pcap/ in the X25Emulator repo, decoded in
SINTRAN/XMSG/SRC/pcap-decode-report.txt):
conn-to-102-from103-via100.pcapng— 103 connects to 102 (connect channel DA), 102 responds. 102 frames each way.conn-to-d102-from-100.pcapng— 100 connects to 102 (channel D9), 102 responds.new-conn-to-102-from-100.pcapng— 100 connects to 102 (channel D9), 58 frames each direction (a full session).li-syst-tad-103.pcapng— 103 connecting outward (asker side only).
1. SOLVED — the responder MIRRORS the sender [VERIFIED across captures]¶
For every data frame the asker sends, the responder replies with a frame that echoes the
sender's transport envelope and fills in its own payload. Verified frame-for-frame in
conn-to-102-from103 (asker 103→102 vs responder 102→103, aligned by Flags1):
| asker Flags1 | asker proto | asker counter | responder proto | responder counter |
|---|---|---|---|---|
| 0x0004 | DA | 0x0D | DA | 0x0D |
| 0x0005 | DA | 0x0C | DA | 0x0C |
| 0x0006 | DD | 0x03 | DD | 0x03 |
| 0x0007 | DE | 0x02 | DE | 0x02 |
| 0x0008 | DD | 0x01 | DD | 0x01 |
| 0x000A | DC | 0xFF | DC | 0xFF |
So these fields are not chosen by the responder — they are copied from the sender:
| Field | Rule | Confidence |
|---|---|---|
| Flags1 (datagram seq) | = sender's Flags1 | VERIFIED |
| Protocol ID (channel) | = sender's Protocol ID | VERIFIED |
| Sub-header Counter | = sender's Counter | VERIFIED |
| Dest/Src node, XMDSY/XMDPT/XMSSY/XMSPT | swapped (reply to sender), our port in XMSPT | VERIFIED |
| Role byte | 0x40 in the XROUT/setup phase (accept, port-assign); 0x00 in the data phase (session frames) |
VERIFIED |
| XMCSM | 0x04000041 connect, 0x04000000 port-assign/setup, 0x01080000 terminal data, 0x00080000 control |
VERIFIED |
| Connect-accept params | 01 02 0000 02 02 000A — constant in all captures (the 000A is fixed, not derived) |
VERIFIED |
Consequence: we cannot initiate session frames with invented channels/counters — that is
exactly what crashed 100 (XXPER). We must mirror each frame 100 sends. And 100 does drive
a full session when talking to a real 102 (new-conn = 58 frames each way); it stalled for us
only because we stopped ACKing its frames (it retransmits the connect/setup instead of
advancing).
2. UNCERTAIN — fields whose derivation I cannot yet prove¶
These are the values I am NOT sure how to compute. Observed across captures so a pattern can be reasoned about.
2.1 Frame-flags byte (sub-header offset 3)¶
Response value sometimes equals the sender's, sometimes not — so it is not a pure echo:
| Flags1 | asker frame-flags | responder frame-flags |
|---|---|---|
| 0x0004 | 0x86 | 0x86 |
| 0x0005 | 0x86 | 0x86 |
| 0x0006 | 0x86 | 0x92 |
| 0x0007 | 0x82 | 0x86 |
| 0x0008 | 0x96 | 0x96 |
| 0x0009 | 0x96 | 0x92 |
| 0x000A | 0x96 | 0x96 |
Observed set: 0x82, 0x86, 0x92, 0x96. Bit view (bit7 always set, bit1 always set):
| value | b7 | b4(0x10) | b2(0x04) | b1(0x02) |
|---|---|---|---|---|
| 0x82 | 1 | 0 | 0 | 1 |
| 0x86 | 1 | 0 | 1 | 1 |
| 0x92 | 1 | 1 | 0 | 1 |
| 0x96 | 1 | 1 | 1 | 1 |
Open question: what do bit 4 (0x10) and bit 2 (0x04) encode? (message class? a
per-frame toggle? first/last-of-a-group?) This is the field I am least sure of.
2.2 Role byte — high nibble¶
Low nibble is known (4 = asker, 0 = responder). High nibble varies and is unexplained:
| Seen (asker) | Seen (responder) |
|---|---|
0xC4, 0xE4, 0x84, 0x94 |
0x40, 0x00 |
Open question: meaning of the high nibble (C/E/8/9 on the asker, 4/0 on the responder)?
Does it encode the message/opcode class? Our node uses 0x40/0x00 and that has been accepted,
so this may not be blocking — but it is not understood.
2.3 Session port the responder assigns (port-assign TAD 0x07)¶
The responder allocates a session port and advertises it in 07 05 00 00 <sys> <portHi> <portLo>.
Observed:
| Capture | asker | responder session port | logical (>>7) | low-7 (random?) |
|---|---|---|---|---|
| conn-to-102-from103 | 103 | 0x0313 | 6 | 0x13 |
| conn-to-d102-from-100 | 100 | 0x04C2 | 9 | 0x42 |
| new-conn-to-102-from-100 | 100 | 0x0341 | 6 | 0x41 |
Same asker (100) yields low-7 0x42 then 0x41 in two sessions → looks per-session (an
incarnation / the magic-number "random part"). Open question (your hypothesis): is the
low-7 actually derived from the sender's info (its port/system/magic) rather than random? If so,
what is the formula? (This matters: an invented session port may be why 100 never validated us.)
2.4 Port-assign 0x0B option — second data byte¶
The port-assign trailer is identical across captures EXCEPT one byte in the 0B 02 03 ?? option:
| Capture | asker | 0x0B data |
session port |
|---|---|---|---|
| conn-to-102-from103 | 103 | 03 00 |
0x0313 |
| conn-to-d102-from-100 | 100 | 03 04 |
0x04C2 |
| new-conn-to-102-from-100 | 100 | 03 02 |
0x0341 |
Open question: the second byte is 00 / 04 / 02 — what does it encode? (It does not obviously
track the asker system or port; may track the session port / a link index / a count.)
2.5 The counter's absolute base (secondary — we echo, so not blocking)¶
The sender's own counter runs two linear regimes: XROUT phase ctr = 0x11 - Flags1
(f1=4→0x0D, f1=5→0x0C), then session phase ctr = 0x09 - Flags1 (f1=6→0x03, f1=7→0x02 …), a
-8 base drop (a -9 jump with the f1 increment). Because the responder echoes the counter
we do not need to originate it — but the base values (0x11, 0x09) are unexplained.
Open question: where do the per-session base values come from?
3. The one flow question that actually blocks us¶
When 100 connects to a real 102 it drives a full session (new-conn = 58 frames). When it
connects to our node it sends only connect + session-setup, then stalls ("waiting for
feedback from 103" / "Unable to communicate with this TAD"). The most likely cause is that we
stopped sending the 0x03 delivery ACKs (so 100 retransmits instead of advancing). Next
experiment (no guessing at content): ACK every 100 frame (subtype 0x03, echo its Flags1,
Flags2=0x0001, echo its Protocol ID, our decrementing counter) and MIRROR each data frame it
sends, and see whether 100 then drives the session the way it does with a real 102.
Fields to keep in mind while reasoning: 2.1 frame-flags bits, 2.3 session-port low-7, 2.4 the
0x0B byte. If any of these must be derived from the sender (your hypothesis) rather than
free/echoed, that is what to work out.