Skip to content

API: live extra port forwards via monitor hostfwd_add (no restart) #141

Description

@mobileskyfi

Context

Today hostfwd is baked at QEMU launch: buildHostfwdString(state.ports)-netdev user,id=net0,hostfwd=… in src/lib/qemu.ts:189, persisted in machine.json (portBase, extraPorts, ports). Adding a port to a running machine today requires a restart — stop()/start() — there is no live path.

Agent use case: chr.start()chr.rest("/something") that needs a new host→guest port (e.g. btest UDP range 2000–2010, a user-added /ip service, container port) — the agent wants chr.addHostFwd({host:28282, guest:80, proto:"udp"}) without bouncing the VM.

What QEMU already does

QEMU human monitor (HMP) on the user netdev supports hostfwd_add / hostfwd_remove at runtime (hostfwd_add [netdev:] [tcp|udp]:[host_ip]:host_port-[guest_ip]:guest_port). quickchr already drives HMP via src/lib/channels.ts:monitorCommand (used for quit, savevm, info snapshots, etc.), so the transport exists — there is just no hostfwd_add barrel.

grep -R hostfwd_add src/ is empty today. src/lib/forward-spec.ts:21 notes QEMU has no native range — btest:9200-9210:2000-2010/udp expands to one hostfwd per port, which the live path would mirror.

Scope / current behavior

  • --forward / extraPorts and the range expansion (expandForwardSpec) are start-time only.
  • MachineState.ports is treated as immutable after spawn.

Proposed enhancement

Expose a ChrInstance method (e.g. addHostFwd / `addForwar

  • validates like validateExplicitExtraPorts() + `isPortAvai checks, including concurrency against port allocator: concurrent auto starts select the same block and one QEMU fails #140's allocatorreservation,
  • sends hostfwd_add via monitorCommand() per PortMappingd by FORWARD_RANGE_MAX = 64`),
  • persists the new mapping(s) to machine.json so the next start() replays them, with typed errors (PORT_CONFLICT / MACHINE_STOPPED / monitor
    error),
  • is user-NIC only — vmnet-shared/bridged/tap/socket have no hostfwd to extend (typed error if no user netdev),
  • complement with removeHostFwd via hostfwd_remove.
const chr = await QuickCHR.start({ name: "lab" });
await chr.rest("/ip/service/enable", { ... });
await chr.addHostFwd({ host: 28282, guest: 80, proto: "udp" });
await chr.addHostFwd(expandForwardSpec("btest:9200-9210:2000-

Done-when

  • Live add/remove via monitor works without restart; new forward is reachable immediately.
  • New mappings survive stop()/start() (persisted) and are(concurrent callers reuse port allocator: concurrent auto starts select the same block and one QEMU fails #140 reservation).
  • Deterministic unit coverage + one live-QEMU integration (rest → addHostFwd → reachable).
  • MANUAL.md + routeros-quickchr skill document live vs re
  • Non-user netdev is a typed error, not silent no-op.

Relation to other port work

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:library-apiQuickCHR/ChrInstance public API for consumersarea:networkingport forwarding, sockets, L2/MNDP, vmnet/TAPenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions