mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
feat(controller): offer QEMU the kernel datapath, gated per compute
Kernel eligibility becomes "both endpoints on one compute, each able to anchor": Docker always (adapters are born as veth pairs), QEMU when that compute's uBridge can create the persistent TAPs its adapters anchor on. The question is asked per compute, because the link needs both ends anchored -- so a mixed or half-upgraded deployment degrades to the relay instead of failing an attach. probe_tap_support() creates and deletes one throwaway persistent TAP through a scratch uBridge, cached by binary identity like the tc report; /capabilities exposes `ubridge_tap` (absent = unknown, which keeps links on the relay). The compute route for a QEMU adapter NIO took UDPNIO only, so a kernel link was rejected with 422: its body and response now accept Union[UDPNIO, BridgeNIO] exactly like Docker's. Stale "Docker-to-Docker" wording in the config, link and MCP descriptions now talks about anchors, and docs/features/qemu-kernel-datapath.md documents the datapath -- verified end-to-end on a real instance: 32/32 kernel-datapath checks and 29/29 on the relay variant.
This commit is contained in:
15 files changed
+444
-40
No files matched your search
@@ -163,30 +163,47 @@ class UDPLink(Link):
|
||||
|
||||
def _kernel_datapath_eligible(self, node1, node2):
|
||||
"""
|
||||
Whether this link can be wired on the kernel datapath (veth pairs
|
||||
enslaved into a per-link Linux bridge) instead of the uBridge UDP
|
||||
relay. Every GNS3 filter type has a kernel equivalent — netem for
|
||||
delay/packet_loss/corrupt and the netem extensions, cls_bpf for bpf,
|
||||
the eBPF stateful classifier for frequency_drop — so filters no
|
||||
longer disqualify the kernel path. Capture and markers are served by
|
||||
uBridge's AF_PACKET modules on the veth host end.
|
||||
Whether this link can be wired on the kernel datapath (the adapters'
|
||||
host-side anchors enslaved into a per-link Linux bridge) instead of
|
||||
the uBridge UDP relay. Every GNS3 filter type has a kernel equivalent
|
||||
— netem for delay/packet_loss/corrupt and the netem extensions,
|
||||
cls_bpf for bpf, the eBPF stateful classifier for frequency_drop — so
|
||||
filters no longer disqualify the kernel path. Capture and markers are
|
||||
served by uBridge's AF_PACKET modules on the anchor.
|
||||
|
||||
Docker adapters are born as veth pairs (unified interface), so the
|
||||
datapath is a runtime decision — links attach to running containers
|
||||
too: the compute side enslaves the veth host end (brctl) or attaches
|
||||
the relay to it (add_nio_ethernet) without touching the container's
|
||||
interfaces.
|
||||
Every adapter owns an anchor, so the datapath is a runtime decision —
|
||||
links attach to running nodes too: the compute side enslaves the
|
||||
anchor (brctl) or attaches the relay to it (add_nio_ethernet) without
|
||||
touching the interfaces the node itself uses. Both endpoints must be
|
||||
on the same compute (there is no kernel link across hosts) and both
|
||||
must be able to anchor: Docker adapters are born as veth pairs,
|
||||
QEMU adapters as persistent TAPs, which need uBridge's tap module —
|
||||
asked of that compute, since an old uBridge leaves QEMU on the relay
|
||||
datapath and cannot carry a kernel link at all.
|
||||
"""
|
||||
|
||||
if not Config.instance().settings.Server.enable_kernel_datapath:
|
||||
return False
|
||||
if node1.node_type != "docker" or node2.node_type != "docker":
|
||||
return False
|
||||
if node1.compute.id != node2.compute.id:
|
||||
return False
|
||||
if _is_unix_socket_docker(node1) or _is_unix_socket_docker(node2):
|
||||
return False
|
||||
return True
|
||||
return self._kernel_endpoint_ready(node1) and self._kernel_endpoint_ready(node2)
|
||||
|
||||
@staticmethod
|
||||
def _kernel_endpoint_ready(node):
|
||||
"""
|
||||
Whether a node can anchor a kernel link on its compute.
|
||||
"""
|
||||
|
||||
if node.node_type == "docker":
|
||||
return True
|
||||
if node.node_type != "qemu":
|
||||
return False
|
||||
capabilities = node.compute.capabilities or {}
|
||||
# Strict True: an unreported capability (old uBridge, failed probe)
|
||||
# keeps the link on the relay, where it always works.
|
||||
return capabilities.get("ubridge_tap") is True
|
||||
|
||||
async def _prepare(self):
|
||||
"""
|
||||
|
||||
Reference in new issue
Block a user