Files
opencode.energia.arti.ee 3a56de7757 Add all-in-one IRIX network install server
Provide BOOTP, TFTP (including SGI boot-tape TPD members) and the BSD
rsh/rcmd service that IRIX inst needs. Map exec /bin/sh to mksh or dash
and run the shell in a read-only bubblewrap sandbox with an
IRIX-compatible dd wrapper. Add protocol tests and a usage guide.
2026-11-02 14:39:54 +00:00

187 lines
5.6 KiB
Markdown

# IRIX network install server
`tools/irix_netinstall.py` is a single-file BOOTP + TFTP + rsh server for
booting the SGI PROM and running the IRIX `inst` network installer.
It is intended for a dedicated install network. rsh is unauthenticated by
design.
## Requirements
- Linux with Python 3.10 or newer.
- `mksh` or `dash` (the server prefers `mksh`).
- `bubblewrap` for the read-only shell sandbox (recommended; `--sandbox none`
runs without it, but then absolute paths can reach the host filesystem).
- Root, or the ability to bind UDP 67, UDP 69 and TCP 514.
- No other BOOTP/TFTP/rsh service on those ports.
On Debian/Ubuntu:
```sh
sudo apt-get install mksh bubblewrap
```
## Install tree
The server serves an already-extracted IRIX distribution tree. It does not
mount ISO images or unpack `tardist` files itself. The paths used by the PROM
and `inst` are relative to `--root`:
```
/srv/irix/
6.5.30/
stand/
fx.ARCS
fx.64
sashARCS
sash64
dist/
...
install.cmds
```
The `dist` directory is what `inst` opens as the remote distribution. The
`stand` directory is what the PROM boots with `bootp()`.
## Run it
```sh
sudo ./tools/irix_netinstall.py \
--root /srv/irix \
--server-ip 10.0.0.1 \
--client-ip 10.0.0.2 \
--client-mac 08:00:69:07:dc:e0 \
--bootfile /6.5.30/stand/fx.ARCS
```
Useful options:
| Option | Meaning |
| --- | --- |
| `--l2-iface tap-mess-1000-0` | BOOTP over AF_PACKET for MAME/QEMU taps |
| `--shell mksh` / `--shell dash` | force a shell |
| `--sandbox bwrap` / `--sandbox none` | force a shell sandbox mode |
| `--bootp-port`, `--tftp-port`, `--rsh-port` | non-default ports (testing) |
| `--allow-high-ports` | accept rsh clients from non-reserved ports (tests only) |
At the PROM:
```
setenv netaddr 10.0.0.2
boot -f bootp():/6.5.30/stand/fx.ARCS
```
For a 64-bit machine use `fx.64`; for the 32-bit IP22 Indy/Indigo2 use
`fx.ARCS`.
After partitioning, return to the maintenance menu, choose the software
installation option, enter the server address and the distribution path
without a trailing slash, for example:
```
/6.5.30/dist
```
Then load a command file at `Inst>`:
```
admin source 10.0.0.1:/install.cmds
```
## What the services do
### BOOTP
Answers RFC 951 requests on UDP 67. The reply carries:
- `yiaddr` = `--client-ip`
- `siaddr` = `--server-ip`
- `file` = the file the PROM asked for, or `--bootfile`
- the client's `chaddr`, transaction ID and flags echoed
It can answer by MAC (`--client-mac`) and accepts plain UDP or AF_PACKET
(`--l2-iface`). The L2 mode exists because some emulated clients send from
`0.0.0.0` in a way the host IP stack never receives.
### TFTP
Serves RFC 1350 RRQ in octet mode. It opens a new UDP socket per transfer and
keeps that socket's source port **at or below 32767**, because the 5.0 B6 and
5.1.2 B4 PROMs compare the server port as a signed 16-bit value.
It also supports the SGI boot-tape directory format:
```
bootp():/images/boottape(sash)
```
The first 512-byte block is checked for magic `0xACED1234` (or the
byte-swapped `0x3412EDAC`), the checksum, and the 20-entry directory; only the
requested member's bytes are transferred.
### rsh
Serves the BSD `rcmd` protocol on TCP 514:
- the client must connect from a reserved source port (512-1023);
- the server reads the stderr port, dials back **before** reading the user and
command fields, then answers with a zero byte;
- `exec /bin/sh` is accepted and mapped to `mksh`, falling back to `dash`;
- stdout is the primary connection, stderr is the callback connection;
- callback bytes `0x02` are forwarded to the shell's process group as SIGINT.
The host's `/bin/sh` is never changed.
## Why mksh/dash, not bash or csh
`inst` runs `exec /bin/sh` and streams real POSIX shell lines: `dd`, `cat`,
`ls`, `grep`, pipes, subshells, `trap`, `$?`, and the marker idiom:
```sh
echo 'TOKEN\c'
```
Bash's builtin `echo` does not interpret `\c` by default (not even with
`--posix`), so the marker gets a literal `\c` and a newline. csh is a different
syntax family entirely and cannot parse the stream.
`mksh` and `dash` handle `\c` correctly. The server prefers `mksh` because it
is the shell used by the proven IRIX install guides.
The shell is run in a read-only bubblewrap sandbox:
- the install tree is bound at `/irix` and each top-level entry is symlinked
to `/`, so absolute `inst` paths such as `/6.5.30/dist` work;
- host `/bin`, `/usr`, `/lib`, `/lib64` and `/etc` are bind-mounted read-only;
- a small wrapper directory provides an IRIX-compatible `dd` that accepts
`iseek`/`oseek` and refuses writes;
- `egrep`/`fgrep` wrappers map to GNU `grep -E`/`-F`.
With `--sandbox none`, the shell runs with the install tree as its working
directory but can still reach absolute host paths. Use that only on an
isolated install network.
## Tested
Protocol tests run without root on loopback:
```sh
cd tools
python3 -m unittest -v test_irix_netinstall
```
They cover BOOTP, TFTP file and TPD-member transfers, rsh shell sessions, the
`\c` marker, and `dd` under both `none` and `bwrap`. The server has not yet
been run against a physical Indy, so treat a real install as the acceptance
test.
## Prior art
- [instigator](https://github.com/jamesbraid/instigator) — Go server with
BOOTP/TFTP/rsh and a constrained POSIX interpreter; the best reference for
the `inst` shell vocabulary.
- [irixboot](https://github.com/halfmanhalftaco/irixboot) — Vagrant VM using
isc-dhcp, tftpd-hpa and rsh-server.
- [Remote IRIX 6.5 install from Linux](http://techpubs.spinlocksolutions.com/irix/remote-irix-6.5-installation-from-linux.html)
— the `mksh` + `in.rshd` + `bootp` + `tftpd` recipe.