19 KiB
DEC MOP Remote Console (console carrier) — protocol notes and VT1200 DWT results
Status: the protocol works against the MAME VT1200. HELP and SHOW were
answered by the firmware; TEST and INIT were accepted but do not return a
usable console reply (details in §6). The client is dwt/mop_console.py.
Everything below is either read from a cited source or was observed on the emulator. Where the two disagree, the observation is called out as such.
1. Sources
| source | what it establishes |
|---|---|
DEC, DECnet Digital Network Architecture — Maintenance Operations Protocol Functional Specification, v3.0.0, Dec 1983, order AA-X436A-TK. PDF: http://decnet.ipv7.net/docs/dundas/aa-x436a-tk.pdf (text extracted locally with pdftotext -layout) |
MOP message codes and bodies, §5.3 “Remote Console” pp. 79–87; Ethernet protocol types and multicast addresses, Appendix B.3 pp. B-1/B-2 |
| DEC, MOP Functional Specification, v4.0.0, Jun 1992, EK-DNA11-FS-001. PDF: https://bitsavers.org/pdf/dec/decnet/EK-DNA11-FS-001_MOP_Functional_Specification_V4.0.0_Jun92.pdf | later revision of the same protocol (linked from the revision table at https://gunkies.org/wiki/Maintenance_Operations_Protocol) |
Mats O Jansson, mopd 2.5.3, common/mopdef.h and common/rc.c: https://sources.debian.org/src/mopd/1:2.5.3-21/common/mopdef.h/ |
independent constants: MOP_K_PROTO_RC 0x6002; codes 5/7/9/11/13/15/17/19; multicast ab:00:00:02:00:00 |
Christine Caulfield, latd's moprc.cc / moprc.h: https://github.com/cguldenschuh/latd/blob/main/moprc.cc |
a working console requester: the on-wire header (2-byte little-endian length, then code), the Reserve + Request ID handshake, the System ID function-bit check, CCP/CRA flag handling, Release |
libpcap ethertype.h: https://github.com/the-tcpdump-group/libpcap/blob/master/ethertype.h |
all four ethertypes: MOPDL 0x6001, MOPRC 0x6002, DN 0x6003, LAT 0x6004 |
| VSI, DECnet-Plus for OpenVMS Network Management Guide, §10.2.2.1 and Appendix F: https://docs.vmssoftware.com/vsi-decnet-plus-network-management-guide/ | the 8-byte Reserve-Console verification value is the MOP service password; byte order for NCL vs NCP; ccr is the DECnet-Plus console client |
docs/EK-V1000-SV-002_service-guide.txt §2.2 (this repo) |
the VT1200 side: NCP CONNECT VIA <circuit> PHYSICAL ADDRESS <MAC>, DWT>>> prompt, INIT/SHOW/TEST/HELP, TEST parameters and P3 mode bits |
Ethertypes were not taken on trust:
0x6001MOP Dump/Load — libpcap; also60-01in MOP §B.3.0x6002MOP Remote Console — libpcap,mopdef.h, MOP §B.3 (“60-02 Remote Console”), and observed on the wire.0x6003DECnet (DNA Routing) — libpcap; the MOP spec does not claim it.0x6004LAT — libpcap.
Only 0x6002 is used here.
2. Frame layout
Ethernet II, protocol type 0x6002, destination = the terminal’s unicast MAC
(or the protocol’s multicast ab:00:00:02:00:00; the VT1200 answers unicast,
which is all we needed):
offset size field
0 6 destination MAC
6 6 source MAC
12 2 ethertype 0x6002 (big-endian on the wire)
14 2 MOP length, little-endian
16 1 MOP function code
17 ... code-specific body
end ... zero padding to a 46-byte Ethernet payload minimum
The length counts the bytes after the length field (code + body), and
excludes the length itself and any padding. moprc.cc builds every message
that way (buf[0] = len+2; buf[1] = 0; buf[2] = code; …) and the VT1200 does
the same: a SHOW reply is a 527-byte frame with length 0x01FF = 511 =
1 code + 1 flags + 509 data.
The length field is authoritative, not the frame size. This is not
theoretical: when polled for a second response the VT1200 emits a 60-byte frame
whose length field is 2 but whose bytes after the CRA flags are stale text
from the previous response buffer (observed: SHOW leftovers after the
TEST reply). A client that trusts the frame size will print garbage.
dwt/evidence/mop-script3.pcap contains both cases.
There is no version byte, no session id and no checksum on this path. The MOP spec’s Remote Console messages are additions to MOP 2.1 (§5.3); the fields above are the whole header.
Function codes used
From MOP §5.3 and mopdef.h:
| code | message | body |
|---|---|---|
| 5 | Request ID | reserved (1, = 0) + receipt number (2, LE) |
| 6 | Boot | verification + processor/control/software id (not used) |
| 7 | System ID | reserved (1, = 0) + receipt (2, LE) + TLVs |
| 9 | Request Counters | receipt (2) |
| 11 | Counters | receipt (2) + counter block |
| 13 | Reserve Console | verification (8) — the MOP service password |
| 15 | Release Console | empty |
| 17 | Console Command and Poll | control flags (1) + console data (*) |
| 19 | Console Response and Acknowledge | control flags (1) + console data (*) |
Codes 16 and 18 are explicitly reserved by MOP §5.
System ID TLVs
Body after the reserved byte and receipt is a sequence of
type (2, LE) | length (1) | value (length) entries. The VT1200’s reply
(dwt/evidence/mop-hello.pcap) decodes as:
37 00 length = 55
07 code = System ID
00 reserved
00 00 receipt
01 00 03 03 00 00 maintenance version 3.0.0
02 00 02 e1 00 functions = 0x00E1
03 00 06 c6 fc cc 16 f5 66 console user = host MAC
04 00 02 0c 00 reservation timer = 12 s
05 00 02 20 00 console command buffer = 32 bytes
06 00 02 58 02 console response buffer = 600 bytes
07 00 06 08 00 2b 16 4b 5a hardware address
64 00 01 72 communication device = 0x72
00 00 00 padding (parsed as an empty type-0 entry)
Function bitmap (type 2, bit 5 = console carrier, 0x20):
bit 0 loop, 1 dump, 2 primary loader, 3 multi-block loader, 4 boot,
bit 5 console carrier, 6 data link counters, 7 console reservation
0xE1 therefore means loop + console carrier + counters + reservation.
moprc refuses to proceed unless functions & 0x20 (and prints “target does
not support remote console” otherwise).
3. Session and sequence handling
moprc’s sequence is the reference implementation and was followed exactly:
- Reserve Console (13) with an 8-byte verification value.
- Request ID (5) with reserved
0and a 2-byte receipt number. - Wait for System ID (7). This is the “connected” condition; check function bit 5.
- Console Command and Poll (17) carries console input. The control-flags
byte has:
- bit 0 — message number, a one-bit sequence number toggled for each command;
- bit 1 — break, prepend a serial break to the command data.
- Console Response and Acknowledge (19) carries console output. Its flags:
- bit 0 — message number being acknowledged;
- bit 1 — command data lost (resend);
- bit 2 — response data lost (overrun).
- Release Console (15) with no body.
There is no connection id beyond the 48-bit MAC pair, and no per-message
acknowledgement other than the poll/response alternation. The protocol is
half-duplex and polled: the terminal buffers console output and sends it in a
CRA when it is polled. Do not keep polling after data arrives unless you
want the response repeated — the VT1200 re-sends its whole response buffer on
every poll, and an empty response is a CRA with length 2 whose frame may still
carry stale bytes (§2). mop_console.py polls only until the first data, then
drains, treating an exact repeat as a duplicate and a superset as a buffer
that grew.
Data is a raw byte stream. A command line is the ASCII text plus \r (the
DWT parser and the real terminal use CR; moprc translates LF to CR). The
terminal echoes the input and answers with CRLF text and the DWT>>> prompt.
4. Password / verification
Reserve Console’s 8-byte VERIFICATION is the MOP service password
(MOP §5.3.6: “a verification code that must match before the receiving system
can honor the request”). VSI §10.2.2.1 says a server that requires a password
ignores the request if it is wrong — there is no negative reply, so the
symptom is exactly “Target does not respond”. The default is
%X0000000000000000.
Byte order (VSI §10.2.2.1):
- NCL / DECnet-Plus: the value is an octet string; the leftmost hex pair is the first byte on the wire.
- NCP (Phase IV): the value is an unsigned integer; the rightmost
digits are the low byte, so bytes go out reversed. NCP
123456789ABCDbecomes NCL%XCDAB896745230100, transmittedCD AB 89 67 45 23 01 00.
The client supports both: --password HEX (NCL order, zero-padded to 8) and
--password-int HEX (NCP integer order, little-endian).
The VT1200 did not challenge us. Reserve with eight zero bytes works, and
Reserve with the arbitrary non-zero value 0102030405060708 also works
(dwt/evidence/wrong-password.log): the firmware accepted the reservation and
answered HELP. So in this configuration the verification field is not
validated (or no password is configured). There is no challenge/response
handshake on the MOP path.
The ROM string dwt_prf: challenge failed - size error (expecting %d, got %d)
is not this path. Per dwt/notes/rdebug-static.md §6, dwt_prf
(0x07EB3FB8) is the 16-byte challenge of the TCP remote-debug listener
(DEBUG TCP Port, 771), and that listener is separately gated closed by the
configuration word at bit 0xAECD2. MOP never calls it.
5. What the emulator actually replied
Setup: VT1200_SKIP_LEVEL1=1, -cfg_directory cfg-net -networkprovider taptun,
private NVRAM, OS booted to the Terminal Manager. Capture:
dwt/evidence/mop-hello.pcap (tcpdump filter ether proto 0x6002), log:
dwt/evidence/hello.log.
Sequence (timestamps from the pcap):
19.715674 host -> terminal Reserve Console payload 0900 0d00 0000000000000000
19.715796 host -> terminal Request ID payload 0400 0500 0000
19.716371 term -> host System ID 71-byte frame, length 0x37
19.716620 host -> terminal CCP "HELP\r" payload 0700 1100 48454c500d
19.716674 host -> terminal CCP poll (flags 1)
19.719929 term -> host CRA 443-byte frame, length 0x01AB = 427
19.720308 term -> host CRA (empty ack, stale bytes, length 2)
...
20.822062 term -> host CRA SHOW 527-byte frame, length 0x01FF = 511
The first CRA data is the whole 425-byte HELP response in one frame:
ab01 1300 48454c50 0d0a 0d0a
2020 2044 4543 7769 6e64 6f77 7320 5465 726d 696e 616c ...
...
0d0a 0a0a 0d44 5754 3e3e 3e20
i.e. HELP\r\n\r\n DECwindows Terminal Remote Console\r\n…DWT>>> .
SHOW is 509 bytes (dwt/evidence/dwt-show.txt):
SHOW
DECwindows Terminal System Configuration
Configuration Parameter Parameter Value
--------------------------------------------------------
Firmware Revision V2.2
Option Memory Present
SIMM 1 installed
SIMM 2 installed
SIMM 3 installed
Image Option Present
Display Type Monochrome
Monitor Type VR150
DWT>>>
HELP (dwt/evidence/dwt-help.txt) lists exactly the four documented
commands and their syntax; it matches the ROM strings and the static analysis
(rdebug-static.md §9).
6. TEST and INIT
The service guide says remote diagnostics requires a reset and no active work (no open windows). DWT lives inside the running session manager, so with the Terminal Manager open we could not give it that state. The observed behaviour is consistent with that precondition, but note what is and is not established:
TEST 0 0 and TEST 12 (two different tests, two fresh boots, private
NVRAM): the terminal accepts the line and replies
TEST 0 0
Testing...
(22 bytes, CRA length 24; dwt/evidence/test00b.log, test12.log), and then
no further console output is produced, even with continued polling for
10 s. Worse, the MOP server then stops answering: a following Reserve gets
no System ID (help-after-test.log, help-after-test12.log). We tried a
longer wait (test 0: more than a minute after the command) with the same
result. So the documented TEST is reachable and parses, but does not return
the OK / DWT>>> the service guide shows in this OS-up configuration. We
cannot distinguish “test is running and has taken over the machine” from “the
session manager was stopped by the test” from the console alone. This is an
honest gap, not a protocol failure: the frame carrying Testing... has correct
length, code and flags.
INIT: accepted, no reply at all, and the terminal stops answering
MOP for >90 s afterwards (dwt/evidence/init.log, help-after-init2.log);
consistent with a reset/reboot. Do not run this against the real terminal.
Why not run TEST in the pre-OS menu? DWT does not exist there — see §7.
7. Does DWT answer before the OS boots? No.
Two configurations were tested.
OS-up (VT1200_SKIP_LEVEL1=1, -cfg_directory cfg-net, private NVRAM):
System ID in 0.5 ms, HELP/SHOW as above.
Bare boot (no VT1200_SKIP_LEVEL1), which is the repo’s level-0
diagnostics-menu baseline PC=3FDCE400 (harness/mame/README.md). A Lua probe
printed the PC and a client sent Reserve/RequestID at the same time:
PC_PROBE frame=100 PC=3FDCE400
PC_PROBE frame=200 PC=3FDCE400
PC_PROBE frame=400 PC=3FDCE400
PC_PROBE frame=600 PC=3FDCE400
PC_PROBE frame=1200 PC=3FDCE400
PC_PROBE frame=2400 PC=3FDCE400
PC_PROBE frame=3600 PC=3FDCE400
while 8 Reserve/RequestID pairs over 8 s got zero frames back
(dwt/evidence/noos-attempt3.log, mop-noos.pcap, mame-noos-probe.log).
Earlier without the probe, two 8 s attempts (16 pairs) were also silent
(noos-attempt1.log, noos-attempt2.log).
So the DWT console is part of the booted OS/session manager, not the power-on
diagnostics code. That matches the ROM string
Session Manager : Cannot create MOP-Server and the static analysis showing
the TEXT parser and the four-command table live in the OS image.
Caveat: the OS-up runs use VT1200_SKIP_LEVEL1=1, a deliberate harness
divergence (modelled board input; the real machine reads IP3 low). These
results are evidence about the OS/DWT path, not about the real terminal’s POST.
8. Client
dwt/mop_console.py, stdlib only (socket.AF_PACKET, select, struct,
fcntl). No scapy.
--iface(defaultvt1200),--mac(default08:00:2b:16:4b:5a),--src-mac(default: the interface MAC).connect= Reserve + Request ID; prints the System ID decode; requires function bit 5.- one command:
… HELPor… --raw HEX. --script [CMDS]defaults toHELP,SHOW,TEST 0 0;--script HELP,SHOWruns a subset. Every TX/RX frame is hexdumped and decoded.--raw HEXsends arbitrary console data inside a CCP.--raw-frame HEXsends a complete MOP payload (length + code + body) for protocol experiments.--password HEX/--password-int HEX,--ethertype(default 0x6002),--dst(unicast/broadcast/multicast override),--no-connect,--no-release,--quiet,--connect-timeout,--dry-run.- Timeout diagnosis prints the phase, the last frames sent (full hex and payload-only hex), whether anything was received, and the common causes (wrong password = silent ignore; pre-OS menu; wrong ethertype).
--dry-runneeds no socket and prints the exact frames.
Privileges: AF_PACKET send needs CAP_NET_RAW. Run with sudo, or grant
the interpreter the capability once:
sudo setcap cap_net_raw+ep "$(readlink -f "$(which python3)")"
tcpdump on this host works for user arti via the wireshark group; the
client itself does not.
Reproduce
# once per boot of the host
./harness/net-setup.sh
# terminal: OS-up headless run (private NVRAM, ~20 s wall to the Terminal Manager)
cd harness/mame
VT1200_SKIP_LEVEL1=1 /home/arti/code/k-space/mame0289/vt1200 vt1200 \
-rompath roms -sound none -skip_gameinfo -lightgun -lightgun_device mouse \
-cfg_directory cfg-net -networkprovider taptun \
-video none -nothrottle -seconds_to_run 600 \
-nvram_directory /home/arti/code/k-space/vt1200/.vt1200-scratch/dwt-nv
# capture (second shell)
sudo tcpdump -i vt1200 -U -w dwt/evidence/mop-hello.pcap 'ether proto 0x6002'
# client (third shell, after the Terminal Manager is up)
sudo python3 dwt/mop_console.py --iface vt1200 --mac 08:00:2b:16:4b:5a \
--script HELP,SHOW
Expected: 425 bytes for HELP, 509 for SHOW, each in one CRA frame.
For the pre-OS check, drop VT1200_SKIP_LEVEL1=1 (and add
-debug -debugger none -autoboot_script dwt/evidence/pc_probe.lua if you want
the PC=3FDCE400 confirmation). For TEST/INIT:
sudo python3 dwt/mop_console.py --iface vt1200 --mac 08:00:2b:16:4b:5a \
--quiet 10 --raw "$(printf 'TEST 0 0\r' | xxd -p | tr -d '\n')"
--dry-run version of the whole script (no privileges):
python3 dwt/mop_console.py --dry-run --script HELP,SHOW
9. What changed between attempts
- The first attempt used the
moprcframing verbatim and worked on the first OS-up try; no ethertype or addressing fallback was needed. (0x6001is Dump/Load and was not tried because0x6002answered.) - The first collector polled continuously. The VT1200 answers every poll by
re-sending its buffer, so the first HELP capture had 1235 frames for one
HELP (not kept; the behaviour is visible in the duplicate 60-byte CRAs in
mop-script3.pcap). The client now stops polling once data has arrived, and treats a repeated chunk as a duplicate and a superset as a grown buffer. The clean run ismop-hello.pcap— 135 packets because the terminal emits an empty CRA for every poll, but only three carry data (System ID, HELP, SHOW). - A 0.8–1.0 s quiet window cut
TESToff afterTesting...; extending it to 10 s and then polling for over a minute confirmed there is no further data, so theTESTresult is not a client timeout. - The “no DWT before OS” claim was first one silent attempt (not enough), then
repeated 16 silent handshakes, then tied to
PC=3FDCE400with a frame probe.
10. Open points
- Whether
TESTwould complete if the terminal were reset with no active windows (the service guide’s precondition). DWT lives in the session manager, so “no session manager” and “DWT reachable” appear mutually exclusive in this firmware; a real terminal may behave differently. - Whether the VT1200 has a non-empty maintenance password setting in NVR that would make the 8-byte verification meaningful. Our fresh private NVR is all-zero and the firmware ignored even a wrong non-zero value.
dwt_prf/ TCP port 771 is the other agent’s path, not this one.