feat(iol): anchor IOL runner containers on persistent TAPs

IOL runner containers (IOLDockerVM, GNS3_IOL_RUNNER) had no kernel
datapath: their guest leg is the runner's AF_UNIX socket pairs, so every
link rode the uBridge relay. Their Ethernet bays now own persistent TAP
anchors (gx names, the iol_docker key of the shared naming contract —
born at node start like IOU's, before any link, so the switch fast
path's deferred join can never miss them), and the per-port bridge's
topology leg swaps between UDP (relay) and the anchor (kernel link)
through the frozen stop -> delete -> add -> start contract — the unix
binding never re-binds across a swap.

- iol_docker_vm: KernelDatapathMixin hooks, the anchor lifecycle
  (_prepare_tap_datapath at _start_ubridge: probe ubridge_bridge_tap +
  ubridge_tap, sweep leftover, tap create born down), the kernel attach
  (ensure-then-add — tap create is strictly create-only, IFF_TUN_EXCL,
  so the ensure checks /sys/class/net first: an unconditional create
  answers EBUSY on the device the start loop just made), the release
  (port bridge stop + delete_nio_tap before the shared teardown) and the
  stop order the fd holders dictate (port bridges first, then tap
  delete, then the per-link bridges — all before the hypervisor stops).
- vendor_docker_vm: the port-bridge creation factored into
  _ensure_unix_port_bridge (idempotent on the registered name), keeping
  the NIOBridge rejection for the generic unix-socket containers.
- docker_vm: the three _kernel_veths lookups go through the
  _kernel_host_ifc hook — behavior-identical for DockerVM, and it is
  what lets the subclass route without overriding the binding methods.
- kernel_anchor: the iol_docker key (gx) and kernel_anchor_type, the
  controller-side mapping for naming an IOL runner's anchor on the
  switch fast path (the marker check stays inline: this module sits
  below everything and must not import the controller package).
This commit is contained in:
YueGuobin committed 2026-10-06 00:21:09 +08:00
1 parent c20125c180
commit 4b4a0fc35f
7 files changed
+773 -31

No files matched your search

+28 -5
View File
@@ -20,10 +20,12 @@ The kernel-datapath anchor naming contract, in one place.
Every node type whose adapters own a host-side interface names that anchor
deterministically: ``g<type>{node_id[:8]}e{adapter}p{port}`` — ``gv`` for a
Docker veth host end, ``gq`` for a QEMU TAP, ``gi`` for an IOU port TAP,
``gd`` for a Dynamips port TAP. The prefixes keep anchors out of the
``gns3`` bridge/TAP name space and name-collision-free on a host; the 8
hex chars of the node id plus adapter/port keep every name unique and
within IFNAMSIZ (15).
``gd`` for a Dynamips port TAP, ``gx`` for an IOL runner container's port
TAP (a Docker node whose guest leg is unix sockets, so its anchor is a TAP
like IOU's, not a veth). The prefixes keep anchors out of the ``gns3``
bridge/TAP name space and name-collision-free on a host; the 8 hex chars of
the node id plus adapter/port keep every name unique and within IFNAMSIZ
(15).
The compute classes create their anchors with these helpers (single source
of truth), and so does the controller when a switch link has to name the
@@ -40,12 +42,32 @@ log = logging.getLogger(__name__)
# Docker veth guest end) is container-local and deliberately absent.
ANCHOR_PREFIX = {
"docker": "gv",
"iol_docker": "gx",
"qemu": "gq",
"iou": "gi",
"dynamips": "gd",
}
def kernel_anchor_type(node_type, environment=None):
"""
The anchor-type key a node of this type and environment names its
anchors under. IOL runner containers are Docker nodes (node_type
"docker") whose anchors are persistent TAPs, not veth host ends — their
own key keeps the ``gv`` veth namespace free of TAPs (stale-interface
sweeps and human eyes both key on the prefix). Every other node type is
its own key unchanged.
"""
# The marker check mirrors utils.application_id.is_iol_runner_environment
# inline: this module sits below every node module (compute and
# controller alike import it during their own import), so it must not
# depend on anything that pulls the controller package in.
if node_type == "docker" and "GNS3_IOL_RUNNER=" in (environment or ""):
return "iol_docker"
return node_type
def anchor_suffix(node_id, adapter_number, port_number=0):
"""
The shared ``{node_id[:8]}e{adapter}p{port}`` name stem (8 hex chars of
@@ -59,7 +81,8 @@ def kernel_anchor_name(node_type, node_id, adapter_number, port_number=0):
"""
The host-side kernel-datapath anchor of one adapter port, or None for a
node type without one (the anchor-less types never anchor a kernel
link).
link). Accepts either a raw node type or an anchor-type key from
:func:`kernel_anchor_type`.
"""
prefix = ANCHOR_PREFIX.get(node_type)