Skip to content

Configuration Reference

Every unit can have its own config file, read once, the first time it's opened (open count transitions 0→1 — see below). An unconfigured unit works with sensible compiled-in defaults; you only need a config file to change something.

File location

ENV:sana2loop/unit<N>.config

falling back to

S:sana2loop/unit<N>.config

if ENV: doesn't have one — useful on a minimal system with no ENV: assign at all. <N> is the unit number with no leading zero (unit0.config, unit10.config, ...).

A missing file is not an error — the unit just uses its compiled-in defaults. A file that exists but has a problem (an unrecognized key, or a malformed line) fails the entire OpenDevice() call with S2ERR_BAD_ARGUMENT — this device treats a config typo as something that should be loud, not silently ignored or silently reinterpreted.

File format

Plain text, one key=value per line:

mode=crossover
peer=3
mtu=1400
# a comment
  • Lines starting with # are comments.
  • Blank lines are ignored.
  • No surrounding whitespace tolerance — key = value (with spaces) is malformed, not forgiving.
  • Every value below is validated (range-checked for numeric keys); an out-of-range or malformed value fails the whole file exactly like an unrecognized key does.

Keys

General

Key Values Default Meaning
mtu 1–1500 1500 (1006 for a non-Ethernet wire=) Maximum Transmit Unit reported by S2_DEVICEQUERY and enforced on CMD_WRITE/S2_BROADCAST/S2_MULTICAST. SANA2IOF_RAW writes get 14 extra bytes of headroom (Ethernet header size) — see the SANA-II spec's raw-frame convention. This same number (MTU+14) is also reported as RawMTU (SANA-II Rev 4) via S2_DEVICEQUERY — except on a mode=replay unit or a non-Ethernet wire= unit, which always report RawMTU=0 (no raw writes supported there).
mintu 0–1500 0 (no minimum) Minimum accepted write size. A CMD_WRITE/S2_BROADCAST/S2_MULTICAST shorter than this fails with S2ERR_BAD_ARGUMENTrejected, never silently padded (padding would change the bytes a reader sees, against this device's byte-exact delivery contract). Useful for catching a stack that forgets real Ethernet's 60-byte minimum-frame padding, which works fine on a loopback but fails on real hardware. A mintu above the unit's (resolved) mtu fails the whole config. 0 (the default) disables the check entirely.
bps any nonzero number 10000000 (see wire= for its per-wire defaults) The link speed S2_DEVICEQUERY reports in its BPS field. Reported only — nothing in this device throttles or delays traffic based on it.
wire ethernet, slip, cslip, ppp ethernet What kind of wire this unit pretends to be — see Wire types below.
addr colon-separated hex bytes (derived per unit) Pins the unit's factory station address (and, with it, the current address a fresh open starts from) — e.g. addr=02:11:22:33:44:55, or addr=C0:A8:4C:02 on a non-Ethernet wire=. The byte count must match the wire's address width exactly (6 for Ethernet, 4 otherwise), and an Ethernet address with the group (multicast) bit set is rejected. S2_CONFIGINTERFACE still works on top exactly as always: it changes the current address; the pinned factory value stays. Useful for reproducible recorded fixtures and for mimicking a specific topology a stack config expects.
rxqueue 0–64 0 Bounded convenience receive queue. 0 means strict drop: a packet with no queued CMD_READ/S2_READORPHAN waiting for it is dropped immediately (and counted in Sana2DeviceStats.UnknownTypesReceived) — real SANA-II drivers hold no internal buffers, and this default matches that. A nonzero value lets up to that many undelivered packets sit in a per-unit queue instead.
buffhooks 8, 16, 32 8 Which SANA-II Rev 2/3 buffer-management hook width this unit actually invokes, for whichever opener negotiated it (offered the matching S2_CopyToBuff16/32/S2_CopyFromBuff16/32 tag at OpenDevice()). An opener that didn't negotiate the configured width falls back to its own base hook transparently — this is never a hard requirement, only a preference. 8 (the default) always uses the base hook, matching every version of this device before this knob existed.

Wire types

wire= selects the hardware type S2_DEVICEQUERY reports and, with it, how addressing works on the unit:

ethernet (default) slip / cslip / ppp
HardwareType S2WireType_Ethernet S2WireType_SLIP / _CSLIP / _PPP
Address size 6 bytes (48 bits) 4 bytes (32 bits) — an IP address stands in for a hardware address, the established convention for serial-wire SANA-II drivers
Factory address 02:53:32:4C:00:<unit> 0A:53:32:<unit> (i.e. 10.83.50.<unit>)
Default mtu / bps 1500 / 10000000 1006 / 19200 (ppp: 1006 / 115200) — an explicit mtu=/bps= always wins, wherever it sits in the file
Broadcast / multicast supported S2_BROADCAST, S2_MULTICAST, and S2_ADD/DELMULTICASTADDRESS all fail S2ERR_NOT_SUPPORTED — a point-to-point wire has no such addressing
SANA2IOF_RAW writes allowed (MTU+14) fail S2ERR_NOT_SUPPORTED; RawMTU reports 0
mode=replay / record= supported fails the config — the pcap side of this device is built around LINKTYPE_ETHERNET framing

A crossover pair should configure the same wire= on both ends — a write while the two ends' wire types differ fails with S2ERR_BAD_STATE (loudly, rather than silently never matching the peer's differently-sized address). Since config is only read at a unit's own first open, that also means opening both ends of a non-Ethernet pair before writing.

Mode and pairing (see Echo and Crossover Modes, Replay and Record)

Key Values Default Meaning
mode echo, crossover, replay echo (crossover for units 2 and 3) What the unit does with traffic.
peer a unit number itself Required for mode=crossover — the other unit this one is paired with. A crossover unit with no distinct peer fails to open.
replay a dos.library-openable path Required for mode=replay — the .pcap capture to load.
replaycap 1–256 4 Maximum records a mode=replay unit will load — a capture with more fails the whole open (S2ERR_BAD_ARGUMENT). The default (4) is deliberately small (see Replay and Record for why); raise it per-unit if your capture is bigger and your target has the memory to spare — a value that doesn't actually fit fails with S2ERR_NO_RESOURCES instead, since the device can't know your target's free memory in advance.
replaymode consume, scheduled consume How a replay unit delivers its loaded records — see Replay and Record.
record a dos.library-openable path If set, every packet this unit actually receives (in any mode) is captured to this path as .pcap, flushed at last close.
recordmax 1–262144 65536 RAM buffer size (bytes) for record=. Once full, further packets are silently not appended (counted, not fatal) — see S2_GETSPECIALSTATS in Replay and Record.

replay=/record= buffers are held in RAM for as long as the device stays loaded, even after every opener of the unit has closed it — see Replay and Record: Memory usage, and freeing it manually if that matters for your setup.

Fault injection (see Fault Injection for the full pipeline and examples)

Key Values Default Meaning
seed any number PRNG seed for this unit's fault injection (xorshift32). 0 is accepted but coerced to 1 when actually used (an all-zero xorshift32 state never changes). Leave unset for a fixed default seed.
errors 0–65535 0 (disabled) 1-in-N chance a write fails visibly: S2ERR_TX_FAILURE with S2WERR_TOO_MANY_RETRIES, plus S2EVENT_TX\|S2EVENT_ERROR fired to any queued S2_ONEVENT. The loud counterpart to droppct/loss's silent vanishing — exercises error-handling/retry paths instead of timeouts. 1 = every write fails (deterministic, no PRNG).
droppct 0–100 0 Percent chance a written packet is silently lost (accepted by the writer, never delivered).
loss 0–65535 0 (disabled) 1-in-N chance a written packet is silently lost — same observable behavior as droppct, at rates a whole percent can't express (loss=1000 ≈ 0.1%). Composes with droppct; each draws independently. 1 = every write lost (deterministic, no PRNG).
duppct 0–100 0 Percent chance a written packet is delivered twice (a genuine second copy, independently subject to reordering).
truncate 0–1500 0 (disabled) Caps delivered packet length to this many bytes. 0 disables truncation.
reorder 0–8 0 (disabled) Reorder window size. 0 disables reordering; delivery order matches write order.
delay 0–3000 0 (disabled, synchronous delivery) Fixed delivery latency in VBlank ticks (50/sec). Runs last in the pipeline, after reorder.
deviation 0–3000 0 (no jitter) Symmetric jitter around delay, in ticks — actual latency per write is drawn from [delay-deviation, delay+deviation], clamped at 0. Legal with delay=0 (pure scatter, no floor). Can reorder delivery on its own, independent of reorder=.
oversilent 0 or 1 0 Crossover only. When a written packet exceeds the peer's MTU (not the writer's own, which is checked unconditionally), 1 accepts the write and silently vanishes the packet — simulating a PMTUD black hole — instead of failing the write with S2ERR_MTU_EXCEEDED.
offlineafter any count 0 (disabled) Auto-forces the unit offline after this many successful writes, as if S2_OFFLINE had been issued.
onlineafter any count 0 (disabled) Auto-forces the unit back online after this many failed write attempts while offline, as if S2_ONLINE had been issued — simulates a flaky link recovering on its own.

When configuration is read

A unit resets to a fully clean state (offline, factory station address, zeroed stats, rxqueue back to 0, mode/peer/fault-injection knobs back to their compiled-in defaults) every time its open count transitions from 0 to 1 — i.e. the first OpenDevice() after the last close. Its config file is read at that same moment. A busy unit's live configuration is never disturbed by a config file changing on disk; you have to let every opener close first (dropping the count to 0) before a config edit takes effect on the next open.

The config file is only read when the opener is a real Process (FindTask(NULL)->tc_Node.ln_Type == NT_PROCESS) — a bare Exec Task opening the device gets compiled-in defaults with no dos.library call at all, preserving the device's Kickstart 1.3 guarantee that its core open/read/write path never depends on DOS being available.

ENV:sana2loop/unit4.config:

mode=crossover
peer=5
droppct=5
seed=42

ENV:sana2loop/unit5.config:

mode=crossover
peer=4

Opening units 4 and 5 now gives you a virtual null-modem link where ~5% of what unit 4 writes never arrives at unit 5 — useful for testing retry logic without touching real hardware.