# Networking subsystem — recon & bring-up plan Goal: bring the pod network up under the emulator so a pod boots the production way (`netnub -f BTL4OPT`, mission egg delivered **over the wire**) instead of the `-egg test.egg` dev bypass. This is also the substrate the ops-console port will plug into. ## What the pod network actually is (recon 2026-07-04) The stack, bottom to top: 1. **Ethernet NIC + Novell ODI** (AUTOEXEC.BAT): `lsl` (Link Support Layer) → `lnepci` (Lance/PCnet PCI ODI driver) → **`odipkt`** (Dan Lanciani's ODI→Packet-Driver shim). The ODI/Netware login side (IPXODI/VLM/NET LOGIN in STARTNET.BAT) is for file-server access; the game itself only needs the **packet-driver interface** odipkt exposes. `NET.CFG`: LNEPCI, FRAME Ethernet_II + 802.2. 2. **WATTCP** — the TCP/IP stack. Confirmed by `WATTCP.CFG` in `REL410/BT`, `REL410/RP`, and per-pod `VGL_LABS/THISPOD`: ``` my_ip = 200.0.0.113 netmask = 255.255.255.0 gateway = 200.0.0.1 nameserver = 200.0.0.1 ``` So each pod is a static host on an isolated **200.0.0.0/24** LAN; the ops console is almost certainly **200.0.0.1**. 3. **NetNub** (`netnub.exe`, real-mode) — launches the game as a child (`netnub -f BTL4OPT`) and is the network server for the protected-mode game. Interface (`NetNub/NETNUB.HPP`): a shared `Netcom` struct (version 11, 64KB buffer) + a software interrupt. The game sets a Function code (TCP_OPEN=3, TCP_LISTEN=4, TCP_CLOSE=5, RESOLVE_ADDRESS=6, CHECK_SOCKET=7, UDP_*, plus remote file OPEN/READ/WRITE/SEEK/CLOSE 12-19), copies the marked fields to real mode, INTs, copies back. `tcp_Socket` is ~4300 bytes = classic WATTCP. 4. **L4NetworkManager** (`L4NET.HPP/.TCP`) — the game's net brick. The **console is master and connects to the pods**; the pod receives `ReceiveEggFileMessage` (the mission egg), replies `AcknowledgeEggFileMessage` ("connected, ready, send the next host"), and tracks `HostConnected/HostDisconnected`. If the console drops, the pod is built to auto-start anyway. Topology to replicate: ``` [ops console 200.0.0.1] --TCP--> [pod 200.0.0.113] (+ more pods .114..) (master, egg source) (listens, ACKs, runs mission) ``` ## Emulator enablers (already in the fork) - **NE2000** ISA NIC emulated (`hardware/ne2000.cpp`, Bochs-derived); config `[ne2000] ne2000=true, nicbase=, nicirq=, macaddr=, backend=`. - **Two Ethernet backends** built: `misc/ethernet_pcap.cpp` (bridge to a host NIC via npcap) and `misc/ethernet_slirp.cpp` (user-mode virtual net / NAT). Plus `ethernet_nothing`. Key simplification: the emulated card is an **NE2000, not a PCI Lance**, so `lnepci` won't bind. We don't need the Novell ODI chain at all — WATTCP finds a packet driver by scanning INT 0x60-0x80 for the `PKT DRVR` signature, so we load a generic **NE2000 packet driver** (Crynwr `NE2000.COM`) directly against the emulated card's base/IRQ. That drops lsl/lnepci/odipkt/VLM entirely and hands NetNub/WATTCP the packet interface they expect. ## Bring-up plan **Backend choice.** Two viable paths: - *pcap + host-only adapter (recommended, matches real topology):* bridge the NE2000 to a host virtual switch; run the pod at 200.0.0.113 and the stand-in console at 200.0.0.1 on that segment. WATTCP's static IP + LAN assumptions hold exactly; the console connects inbound to the pod with no NAT. Cost: npcap + a host-only/loopback adapter + admin. - *slirp (fallback, self-contained):* no host NIC/admin, but it's NAT and defaults to 10.0.2.0/24 — the pod LISTENS, so inbound needs slirp host-forwarding and a guest-network/IP reconciliation with WATTCP's hard-coded 200.0.0.113. Investigate whether DOSBox-X slirp allows the custom net + static guest IP + inbound forward cleanly. **Milestones** 1. **NIC up**: `[ne2000]` on, NE2000.COM packet driver loaded, WATTCP/ NetNub start clean; pod boots via `netnub -f BTL4OPT` (no `-egg`) and sits waiting for the console. Verify NetNub reports net address 200.0.0.113 and a TCP_LISTEN is queued. (New scratch conf, mirror the RIO/sound conf pattern.) 2. **L3/L4 reachability**: from the host segment, confirm the pod answers ARP/ping at 200.0.0.113 and a raw TCP connect to its listen port completes (proves NE2000↔backend↔host path end-to-end). 3. **Decode the console→pod egg protocol**: `NetworkPacketHeader` + message framing from `network.hpp`/`hostmgr.hpp` + the ReceiveEggFileMessage layout, cross-checked with a live capture of the pod's listen/ACK. (Pin the listen port here — not yet found in source; grep NETNUB.EXE strings / capture.) 4. **Eggs over the wire**: a minimal host-side **stand-in console** (Python) connects to the pod, pushes a mission egg, handles the ACK → pod runs the mission with no `-egg` bypass. **This is the headline goal.** 5. *(later, joins the console-port workstream):* replace the stand-in with the ported/emulated Mac ops console; multi-pod coordination (HostConnected/Disconnected, mission review, camera ship). ## Milestone 1 — DONE (2026-07-04): pod boots on the network path Verified end to end under DOSBox-X (slirp backend), no `-egg` bypass: - `[ne2000] ne2000=true, nicbase=300, nicirq=3, backend=slirp` → NE2000 emulated at Base=0x300 irq=3; slirp 4.9.1 initialized. - **The Novell ODI chain works against the emulated NE2000**, no external packet driver needed: `lsl` → `ne2000` (Novell/Eagle NE2000 MLID v1.53, from NWCLIENT) → `odipkt` (FTP Software ODI packet driver). ODIPKT installed at **SINT 0x60**, MLID NE2000, MAC CE:3D:72:67:38:69, frames Ethernet_II (board 1) + 802.2 (board 2). - GOTCHA: the ODI tools read `NET.CFG` from the directory the `.COM` loads *from*, and the stock `NWCLIENT\NET.CFG` says `Link Driver LNEPCI` — with no NE2000 section the MLID defaults to 802.2-only and odipkt fails ("An MLID could not be found"). Fix without touching ALPHA_1: keep an emulator `NET.CFG` (`Link Driver NE2000` + `FRAME Ethernet_II`) beside copies of lsl/ne2000/odipkt on a scratch drive and load from there. - `netnub -f btl4opt` (no egg) launches the game as `btl4opt -net 250224`, sets up the game↔netnub channel at **INT 0x61** (separate from odipkt's 0x60), initializes the network manager ("Changing blocking from 0 to 1"), and the game boots through the VPX handshake to an open (blank) render window — **waiting for a console to deliver a mission egg.** Working scratch files: `scratchpad/net_stageB.conf`, `scratchpad/net/{NET.CFG,LSL.COM,NE2000.COM,ODIPKT.COM}`. Launch env: VPXLOG + VPX_RESPOND=1 + VPX_RENDER=1 (VPX board must answer or the game exits before networking). ## Console side: a Mac emulator stands in for the ops console (user, 2026-07-04) The user is building a **Mac emulator running the real 410console** as the console peer (instead of a from-scratch Python stand-in). This merges the networking and console-port workstreams: the real console software will connect to the pod and push eggs. Implication for topology — two separate emulators (DOSBox pod + Mac console) must share an L2 segment, which slirp (NAT, per-process isolation) cannot bridge. **Plan: move the pod's NE2000 to `backend=pcap` on a host-only/loopback adapter; bridge the Mac emulator to the same adapter; pod=200.0.0.113, console=200.0.0.1 on 200.0.0.0/24.** Then milestone 3 (protocol) can be captured live from the real console traffic rather than reverse-engineered blind. ## Open questions / notes - Exact TCP listen port(s) — not in the source grep; get from NETNUB.EXE or a capture at milestone 3. - Does WATTCP need a real ARP peer for the gateway at boot, or does it proceed with a static IP and only ARP on connect? Affects whether the stand-in console must answer ARP for 200.0.0.1. - `NETCLIENT=PNW` (PARAMETR.bat) selects Personal NetWare — file-server side, not the game's TCP path; likely irrelevant to egg delivery and can stay unloaded under emulation. - RP uses the identical MUNGA net brick + its own WATTCP.CFG — everything here carries over to Red Planet.