mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 22:11:14 +02:00
feat: concurrent kernel captures, one per interface
uBridge's capture module now serves one AF_PACKET capture per interface, any number per process, and `capture stop_kernel` takes the interface it stops — this rides that (breaking, paired-binary) protocol change. The server-side single-slot tracker (_kernel_capture_ifc with its reserve/release/owned_by helpers) is gone: same-port duplicates were already refused by the NIO capturing flag, uBridge answers EALREADY on a same-interface duplicate, and per-interface stops removed the process- wide stop the ownership gating guarded against. The docker e2e drives ten concurrent captures on one node's uBridge and asserts a per-interface stop freezes exactly the stopped half while the live half keeps recording.
This commit is contained in:
15 files changed
+155
-171
No files matched your search
@@ -264,52 +264,6 @@ class KernelDatapathMixin:
|
||||
|
||||
return not (isinstance(nio, NIOBridge) and nio.bridge is None)
|
||||
|
||||
# The anchor whose kernel capture currently holds the uBridge process's
|
||||
# single AF_PACKET capture slot (None = free; see _reserve_kernel_capture).
|
||||
# Class-level default so every node type gets it without touching its
|
||||
# __init__; BaseNode._stop_ubridge resets it when uBridge goes away.
|
||||
_kernel_capture_ifc = None
|
||||
|
||||
def _reserve_kernel_capture(self, anchor):
|
||||
"""
|
||||
Claim uBridge's single kernel-capture slot for *anchor*.
|
||||
|
||||
uBridge serves one AF_PACKET capture per process (a static slot: a
|
||||
second ``capture start_kernel`` answers EALREADY) while the server
|
||||
models captures per port — a second kernel port of the same node is
|
||||
refused here with a clear error. Without the claim the loser's port
|
||||
would stay marked as capturing, and its stop would issue the
|
||||
process-wide, argument-less ``capture stop_kernel``, killing the
|
||||
winner's capture while its own flag still says capturing.
|
||||
"""
|
||||
|
||||
if self._kernel_capture_ifc is not None and self._kernel_capture_ifc != anchor:
|
||||
raise self._kernel_error(
|
||||
f"Cannot start the packet capture: this node's uBridge already captures "
|
||||
f"{self._kernel_capture_ifc} (one kernel capture per node); stop that capture first"
|
||||
)
|
||||
self._kernel_capture_ifc = anchor
|
||||
|
||||
def _release_kernel_capture(self, anchor):
|
||||
"""
|
||||
Release the slot if *anchor* holds it: after a stop, or after a
|
||||
failed start whose port must not stay marked as capturing.
|
||||
"""
|
||||
|
||||
if self._kernel_capture_ifc == anchor:
|
||||
self._kernel_capture_ifc = None
|
||||
|
||||
def _kernel_capture_owned_by(self, anchor):
|
||||
"""
|
||||
Whether *anchor* may issue the process-wide ``capture stop_kernel``:
|
||||
only the port holding the slot — or a node whose slot is empty (a
|
||||
capture predating the tracker), where stopping is the historical
|
||||
best-effort behaviour. A second port must never reach it: the
|
||||
command takes no interface and would stop the owner's capture.
|
||||
"""
|
||||
|
||||
return self._kernel_capture_ifc is None or self._kernel_capture_ifc == anchor
|
||||
|
||||
async def _kernel_attach(self, anchor, nio):
|
||||
"""
|
||||
Attach an anchor to the per-link kernel bridge carried by *nio*
|
||||
@@ -342,10 +296,7 @@ class KernelDatapathMixin:
|
||||
await self._ubridge_send(f'brctl addif "{nio.bridge}" "{anchor}"')
|
||||
if nio.capturing:
|
||||
# Restore a capture that was active before a node restart
|
||||
# (mirrors the relay path's start_capture). The slot is claimed
|
||||
# like a fresh start: a second kernel port's replay is refused
|
||||
# with a clear error instead of an EALREADY deep in the wiring.
|
||||
self._reserve_kernel_capture(anchor)
|
||||
# (mirrors the relay path's start_capture).
|
||||
await self._ubridge_send(f'capture start_kernel {anchor} "{nio.pcap_output_file}"')
|
||||
# Markers carried by the NIO attach to the anchor (AF_PACKET taps) —
|
||||
# the anchor is the interface, not a relay bridge.
|
||||
|
||||
Reference in new issue
Block a user