Run Cisco CML containerized IOL images (e.g. iol-xe/iol-xe:17-18-02, driven by virl.lab/cmd/iol-runner) as GNS3 Docker router nodes: - VendorDockerVM: generic GNS3_UNIX_SOCKET_NIO/GNS3_UNIX_SOCKET_DIR knobs wiring adapters through AF_UNIX datagram socket pairs (add_nio_unix cNN.sock sNN.sock) instead of TAP + move_to_ns; node creation fails if the socket dir is not a persisted volume. - IOLDockerVM (GNS3_IOL_RUNNER=1): forces skip-init + unix-socket NIO + /config,/tmp volumes, writes iol-config.json on every start (num-eth tracks adapters, runner drops to the server uid/gid so the sockets are reachable), pre-creates tmp/run, cleans stale sockets after unclean kills, and makes reload a graceful stop + full start (NVRAM flush + rewiring). Console stays telnet on PID 1 stdio. - Manager selects the node class from console_type or GNS3_* environment markers (create-time, like console_type). - Image-free tests (25) and feature documentation.
6.2 KiB
This documentation is organized by AI with reference to actual code. AI can make mistakes — please verify against the source code when in doubt.
IOL Images with iol-runner (Cisco CML containerized IOL) as Docker Nodes
Overview
IOL images packaged with Cisco CML's container runner — for example
iol-xe/iol-xe:17-18-02 (IOS-XE 17.18.02 IOL in a scratch image driven by
iol-runner, module virl.lab/cmd/iol-runner) — run as first-class GNS3
Docker router nodes with zero changes to the image. The integration adds
two generic server mechanisms:
- Unix-socket NIO (
GNS3_UNIX_SOCKET_NIO=1, onVendorDockerVM): link adapters through per-interface AF_UNIX datagram sockets instead of a TAP interface moved into the container's network namespace. IOLDockerVM(markerGNS3_IOL_RUNNER=1): generates the runner's config file per start, prepares its runtime directory and cleans up stale sockets — the iol-runner-specific glue on top of the vendor path.
How the image works
graph LR
subgraph Container["scratch container (PID 1)"]
RUNNER["/iol-runner -config /config/iol-config.json -stdio"]
IOL["IOL process<br/>(IOS-XE 17.18.02)"]
NETIOMUX["netiomux"]
SOCKETS["/tmp/s00.sock (recv)<br/>/tmp/c00.sock (send-to path)<br/>… one pair per interface"]
RUNNER -->|"spawn -e/-s/-m + app id"| IOL
IOL -->|"netio bus /tmp/netio<uid>/"| NETIOMUX --> SOCKETS
end
subgraph Host
UBRIDGE["uBridge bridgeN<br/>add_nio_unix c00.sock s00.sock<br/>+ add_nio_udp (topology)"]
VOL["project-files/docker/<node>/tmp"]
end
SOCKETS <-->|"raw Ethernet frames<br/>(bind volume dir)"| VOL <--> UBRIDGE
- Console: the runner muxes the IOS console onto PID 1 stdio (
-stdioentrypoint flag). The plainconsole_type: "telnet"attaches to it — nodocker_execneeded. The runner requires a TTY, which GNS3 always allocates; without one the runner exits (inappropriate ioctl for device). - Networking: the runner does not touch the container's network
namespace. Per interface N it creates, inside
/tmp: a receive sockets%02d.sock(frames sent there are injected into guest interface N) and a send-to pathc%02d.sock(whoever binds it receives the guest's frames). Frames are raw Ethernet, one datagram per frame. Because/tmpis a persisted volume (bind-mounted from the node directory), uBridge can bindcNN.sockand send tosNN.sockon the host. - Licensing: the image ships a self-consistent
/etc/hostid+.iourcpair, and the runner regenerates the license from the host ID at boot — nothing to configure. - Persistence:
/tmp/run/inside the volume holds the NETMAP, the startup-config (config, plain IOS format) and NVRAM (nvram_00001), so the router's configuration survives stop/start and container recreation. The generated config maps the runner to the server's uid/gid (user-id/group-id), which is also what makes the/tmpsockets reachable by uBridge and all volume files owned by the server user (no permission-fix pass needed).
Template
{
"name": "IOS-XE 17.18.02 IOL",
"template_type": "docker",
"image": "iol-xe/iol-xe:17-18-02",
"category": "router",
"symbol": ":/symbols/router.svg",
"adapters": 4,
"console_type": "telnet",
"environment": "GNS3_IOL_RUNNER=1",
"extra_volumes": ["/config", "/tmp"],
"memory": 2560
}
/config and /tmp are auto-added even if omitted; listing them keeps the
template self-documenting.
Server mechanisms
| Mechanism | Where | What it does |
|---|---|---|
GNS3_UNIX_SOCKET_NIO=1 |
VendorDockerVM |
_add_ubridge_connection override: bridge create + bridge add_nio_unix <dir>/c{N:02d}.sock <dir>/s{N:02d}.sock instead of TAP + docker move_to_ns. No TAP allocation, no set_mac_addr, namespace untouched. Fails node creation if the socket dir is not a persisted volume. |
GNS3_UNIX_SOCKET_DIR=<dir> |
VendorDockerVM |
Socket directory (default /tmp). Any image whose agent exposes the s%02d/c%02d datagram pairs can use this without the IOL specifics. |
GNS3_IOL_RUNNER=1 |
IOLDockerVM (selected in the manager) |
Forces skip-init + unix-socket NIO + the two volumes; on every start writes <node>/config/iol-config.json (num-eth = adapter count, num-serial = 0, memory from GNS3_IOL_MEMORY, default 2048), creates <node>/tmp/run/ (the IOL process dies without it) and removes stale s/c??.sock + netio* left by an unclean kill (tmp/run is never touched). |
restart() hardening |
IOLDockerVM |
The base docker restart would boot the runner on a stale config and leave uBridge wired to the previous run's sockets; reload becomes graceful stop (SIGTERM → NVRAM flush) + full start. |
GNS3_STOP_TIMEOUT (default 60) controls the SIGTERM grace period on stop.
Extra iol-runner flags can be passed via start_command, e.g. -keep
(L1 keepalives) or -debug 9 (verbose process.log — very useful when
diagnosing wiring issues).
Notes and caveats
- Memory sizing:
memorycaps the whole container; the IOL process getsGNS3_IOL_MEMORY(default 2048 MB). Keep container memory at IOL memory- ~512 MB headroom or the OOM-killer will shoot the router.
- MAC addresses: the
mac_addresstemplate field and per-adapter custom MACs are ignored — IOL derives its own scheme (aabb.cc00.0XY0). - Adapters: change the adapter count while the node is stopped; the config is regenerated on the next start and the runner creates the matching socket set (IOL granularity is 4 ports per unit).
- Stop before editing: NVRAM is only flushed on a graceful stop (SIGTERM,
"cleanup done" in
process.log); a kill loses the running-config changes since the lastwrite memory. - Class selection is create-time: toggling
GNS3_IOL_RUNNERvia PUT takes effect after a project reload (same asdocker_exec). - The startup-config lives at
project-files/docker/<node>/tmp/run/config;extra_configstargets under persisted volumes are warned against by the generic create path — edit the file directly or paste via the console.