mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-31 12:05:05 +02:00
235 lines
9.0 KiB
Markdown
235 lines
9.0 KiB
Markdown
# latd ↔ VT1200 — status, instructions & handoff
|
|
|
|
**Current state: latd is STOPPED.** No `latd`, `login`, `osc-filter.py` or
|
|
`tcpdump` processes remain; `lat/local/run/` is empty. Everything is inside
|
|
`lat/` — nothing was installed system-wide.
|
|
|
|
Root of this directory = `lat/` (referred to as `$LAT` below).
|
|
|
|
```
|
|
$LAT = /home/arti/code/k-space/vt1200/lat
|
|
```
|
|
|
|
---
|
|
|
|
## 1. Endpoint facts (needed to reproduce)
|
|
|
|
| Thing | Value |
|
|
|---|---|
|
|
| VT1200 LAT/Ethernet MAC | `08:00:2b:16:4b:5a` |
|
|
| VT1200 IP | `100.103.100.103` |
|
|
| Host interface on that segment | `enp197s0f3u1` (host `100.103.255.254`) |
|
|
| latd node name | `SID` (this host's hostname) |
|
|
| LAT Ethertype | `0x6004` |
|
|
| LAT service-announcement multicast | `09:00:2b:00:00:0f` |
|
|
|
|
**Important:** the VT1200's *LAT Terminal Window* connects by default to the
|
|
service named after the **latd node name** (`SID`), i.e. the node-name service —
|
|
not to a service you type, unless you type one explicitly.
|
|
|
|
---
|
|
|
|
## 2. One-command workflow (`lat/run-local.sh`)
|
|
|
|
```sh
|
|
cd $LAT
|
|
sudo ./run-local.sh build # bootstrap + configure + make -> lat/local/build-debug/
|
|
sudo ./run-local.sh start # start latd + apply group/service config
|
|
sudo ./run-local.sh status # show config + advertised services
|
|
sudo ./run-local.sh capture # tcpdump LAT frames to lat/local/capture-*.pcap
|
|
sudo ./run-local.sh stop # stop latd
|
|
sudo ./run-local.sh restart
|
|
```
|
|
|
|
Env overrides:
|
|
|
|
```sh
|
|
IFACE=enp197s0f3u1 VARIANT=debug sudo -E ./run-local.sh start
|
|
# IFACE interface latd uses (default enp197s0f3u1)
|
|
# VARIANT release|debug (default debug; debug = verbose, -DNO_FORK)
|
|
```
|
|
|
|
`build` writes its output to `lat/local/build.log`.
|
|
`start` writes the verbose latd log to `lat/local/latd.log`.
|
|
|
|
### What `start` does
|
|
|
|
1. `setsid latcp -s -d -v -v -i $IFACE` (foreground/verbose latd, detached)
|
|
2. `latcp -m 10` — advertise every 10 s
|
|
3. `latcp -G 1` — advertise in group 1 (must match the VT1200's group code)
|
|
4. **removes** the default `SID` service, then re-adds `SID` and `VT1200` both
|
|
running `$LAT/tools/osc-filter.py`
|
|
|
|
> The default service that latd creates at startup is named after the node
|
|
> (`SID`) and runs `/bin/login` **unfiltered** — it must be replaced, or the
|
|
> VT1200 will freeze on it. `latcp -A` on an existing name fails
|
|
> ("Local service already exists"), so `start` deletes it first.
|
|
|
|
---
|
|
|
|
## 3. Manual equivalent (exactly how it was run by hand)
|
|
|
|
All commands run as root (`sudo`). `BUILD` below is the chosen build dir.
|
|
|
|
### Build
|
|
|
|
```sh
|
|
cd $LAT/code/latd && ./bootstrap.sh # only once; needs autoconf+automake
|
|
|
|
# release build
|
|
mkdir -p $LAT/local/build && cd $LAT/local/build
|
|
../../code/latd/configure \
|
|
--with-latcp-socket=$LAT/local/run/latcp \
|
|
--with-llogin-socket=$LAT/local/run/latlogin
|
|
make -j"$(nproc)"
|
|
|
|
# verbose/debug build (what was used for the investigation)
|
|
mkdir -p $LAT/local/build-debug && cd $LAT/local/build-debug
|
|
../../code/latd/configure --enable-debug \
|
|
--with-latcp-socket=$LAT/local/run/latcp \
|
|
--with-llogin-socket=$LAT/local/run/latlogin
|
|
make -j"$(nproc)"
|
|
```
|
|
|
|
`--enable-debug` adds `-g3 -O0 -DVERBOSE_DEBUG -DNO_FORK`; the running latd was
|
|
the debug one. Sockets are kept under `lat/local/run/` (configure options), so
|
|
nothing lands in `/var/run`. `/etc/latd.conf` does not exist, so no global
|
|
startup script is run.
|
|
|
|
### Run
|
|
|
|
```sh
|
|
cd $LAT/local/build-debug
|
|
sudo setsid ./latcp -s -d -v -v -i enp197s0f3u1 >$LAT/local/latd.log 2>&1 </dev/null &
|
|
sudo ./latcp -m 10
|
|
sudo ./latcp -G 1
|
|
sudo ./latcp -D -a SID # drop the unfiltered default
|
|
sudo ./latcp -A -a SID -i 'filtered login (node name)' -r 100 -C $LAT/tools/osc-filter.py
|
|
sudo ./latcp -A -a VT1200 -i 'filtered login' -r 100 -C $LAT/tools/osc-filter.py
|
|
sudo ./latcp -d # verify
|
|
sudo ./latcp -h # stop
|
|
```
|
|
|
|
### Capture / decode
|
|
|
|
```sh
|
|
sudo tcpdump -i enp197s0f3u1 -w $LAT/local/out.pcap 'ether proto 0x6004'
|
|
|
|
tshark -r $LAT/local/out.pcap -T fields -e frame.number -e eth.src \
|
|
-e lat.msg_typ -e lat.slot.type -e lat.slot.slot_data
|
|
tshark -r $LAT/local/out.pcap -Y 'lat.msg_typ==10' -V -O lat # announcements
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Root cause of the "login hangs" behaviour
|
|
|
|
Modern Linux writes **OSC strings** (`ESC ] … BEL` / `ESC ] … ESC \`) into the
|
|
terminal stream. The VT1200's 1990 terminal emulator is not a general VT
|
|
emulator for control strings and **locks up** on them. Sources observed here:
|
|
|
|
| Source | Sequence | Guard |
|
|
|---|---|---|
|
|
| `pam_systemd` (via `login`) | `OSC 3008 type=session` | only if `TERM` != `dumb` (`pam_systemd.c`: `if (streq_ptr(term,"dumb")) return PAM_SUCCESS;`) |
|
|
| `/etc/profile.d/80-systemd-osc-context.sh` | `OSC 3008 type=shell/command` | bash-only; skipped when `TERM=dumb` |
|
|
| `~/.config/zsh/functions/semantic-prompt.zsh` | `OSC 133`, `OSC 122` | **none** — unconditional |
|
|
| `~/.config/zsh/functions/osc7-pwd.zsh` | `OSC 7` | **none** — unconditional |
|
|
|
|
Evidence: `vt-session.pcap` — latd sends a 222-byte `OSC 3008` then a 160-byte
|
|
prompt containing `OSC 133`, after which the VT1200 never transmits again.
|
|
Confirmation: a service running a bare `/bin/sh` (no OSC) works.
|
|
|
|
Upstream context: systemd issues #38527 and #41973.
|
|
|
|
Because the zsh scripts emit OSC unconditionally, `TERM=dumb` alone is not
|
|
enough — the output must be filtered.
|
|
|
|
---
|
|
|
|
## 5. Fix: `lat/tools/osc-filter.py`
|
|
|
|
A pty filter used as the latd service command (default inner command
|
|
`/bin/login`):
|
|
|
|
- forks `login` on an **inner** pty and relays both directions;
|
|
- drops control strings (`ESC ]` OSC, `ESC P` DCS, `ESC X` SOS, `ESC ^` PM,
|
|
`ESC _` APC) from child→terminal; keeps all CSI (e.g. colours);
|
|
- handles sequences split across reads;
|
|
- puts the **outer** pty (latd's) into **raw mode**, so it is a transparent pipe
|
|
— this is what makes passwords hidden (no local echo) and makes `^C` reach
|
|
`login` (no `ISIG` on the outer tty).
|
|
|
|
Verified locally against the exact captured `OSC 3008` / `OSC 133` bytes.
|
|
|
|
**Status:** with the filter, login and authentication succeeded on the VT1200
|
|
and the password/`^C` problems were fixed by the raw-mode change. The **final
|
|
test produced a VT1200 coredump** — see §7.
|
|
|
|
---
|
|
|
|
## 6. Pitfalls / gotchas learned
|
|
|
|
- The VT1200 connects to the **node-name service (`SID`)** by default; that
|
|
service is created by latd **unfiltered**. Always replace/re-point it.
|
|
- `latcp -A` refuses an existing service name ("Local service already exists");
|
|
delete with `latcp -D -a NAME` first.
|
|
- Services need **group 1** on both ends (`latcp -G 1` + the terminal's
|
|
`Customize → LAT…` group code), or they won't appear in the VT's list.
|
|
- `latcp -s` runs `/etc/latd.conf` if it exists (hardcoded) — it does not here.
|
|
- `latd` must be started via `latcp -s` (it refuses `LATCP_STARTED` otherwise);
|
|
`-C <cmd>` and `-u <user>` on `latcp -A` set the service's command/user.
|
|
- Don't `pkill -f 'login -- …'` — the pattern matches your own shell; use
|
|
`pkill -x login`.
|
|
|
|
---
|
|
|
|
## 7. VT1200 CRASH (open item for the next session)
|
|
|
|
During the last test (login via `osc-filter.py`, outer pty in raw mode) the
|
|
**terminal crashed with a coredump**. This may be a usefully reachable firmware
|
|
bug — the whole `EXPLOIT_NOTES.md` effort is about exactly that.
|
|
|
|
- Capture of that session: **`lat/local/filter-test2.pcap`** (547 LAT frames).
|
|
- Related earlier capture (OSC leaking, using the unfiltered `SID`):
|
|
`lat/local/filter-test.pcap`.
|
|
|
|
Suggested next steps: replay/step the frames in `filter-test2.pcap`, isolate the
|
|
byte sequence that trips the firmware, and compare against `vt-session.pcap`
|
|
(which merely froze, no crash).
|
|
|
|
---
|
|
|
|
## 8. Evidence / artifacts (`lat/local/`)
|
|
|
|
| File | What |
|
|
|---|---|
|
|
| `filter-test2.pcap` | capture around the run that **crashed** the VT1200 |
|
|
| `filter-test.pcap` | earlier filtered attempt where OSC still leaked (unfiltered `SID`) |
|
|
| `vt-session.pcap` | the original freeze, with the `OSC 3008` + `OSC 133` payloads |
|
|
| `probe.pcap`, `lat-announce.pcap`, `session-*.pcap` | service announcements / early probes |
|
|
| `latd.log` | full verbose latd log (sessions, slots, credits) |
|
|
| `build.log` | last `run-local.sh build` output |
|
|
| `svc-plain.sh`, `svc-osc.sh` | A/B probe scripts used to confirm the OSC trigger |
|
|
| `HANDOFF.md` | this file |
|
|
|
|
Other relevant paths:
|
|
|
|
```
|
|
lat/code/latd/ # latd 1.36 source (GPL-2.0)
|
|
lat/manuals/ # DEC LAT specification + network manager's guide
|
|
lat/tools/osc-filter.py # the filter
|
|
lat/run-local.sh # build/run/capture helper
|
|
lat/local/build/ # release build
|
|
lat/local/build-debug/ # debug build (used for the investigation)
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Remaining open items
|
|
|
|
1. Reproduce/root-cause the **VT1200 coredump** from `filter-test2.pcap` (§7).
|
|
2. Set a proper `TERM` for the VT1200 session (currently unset) — e.g. `vt220`.
|
|
3. Decide the service naming (node-name `SID` vs an explicit `VT1200`).
|
|
4. Optional: build latd with `--disable-defservice` so the unfiltered default
|
|
service never exists (then `run-local.sh` needn't delete `SID`).
|