mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-06 15:21:13 +02:00
fix: resolve the code-review findings on the kernel datapath
Compute: - Track the anchor that owns uBridge's single AF_PACKET capture slot: a second kernel port's capture is refused with a clear error (its flag rolled back), a failed start no longer wedges the port, and only the owning port's stop issues the process-wide `capture stop_kernel` (docker/qemu/iou/dynamips/switch, via the mixin's slot helpers). - Unwind failed starts instead of leaking uBridge and the anchor TAPs: QEMU's stop body runs under try/finally, QEMU and IOU start the datapath inside the launch try, Dynamips undoes a half-attached port (the retry used to be short-circuited into a silent black hole), and Docker deletes the veth pair when the namespace move fails. - Restore the relay filter guard on IOU (kernel-only filters raise NodeError, an uncompilable BPF line degrades to a warning), validate the eBPF/quota/window/bpf filter shapes before touching the bridge (short or non-numeric values surfaced as 500s after partial state), and refuse a second NIO on an occupied Docker/QEMU port (the guard IOU and Dynamips already had). - Probe fixes: `_probe_bridge_tap` survives a failing uBridge start, and the probe caches key on ctime too, so a chmod/setcap remedy re-probes. Controller: - Parse GNS3_UNIX_SOCKET_NIO like the compute does (line-anchored, truthy value) and mirror the compute's class selection in kernel_anchor_type. - Read partial capability reports defensively; make BridgeNIO.bridge required-but-nullable so an omitted key is a 422 rather than a KeyError. - Strip the packet_loss correlation slot even when it is zero: the relay filter takes exactly one argument and rejected `packet_loss 10 0`. Ethernet switch: skip ports whose absorbed anchor does not exist yet (the join is deferred by design) and roll the ports_mapping back when the VLAN reconcile fails, so a retry still diffs against the old mapping. Agent: reject device config entries without a device_name instead of silently dropping them (a malformed batch used to execute partially while reporting success). Also split the netem builder to clear C901 and drop the two per-file ruff ignores the PR had added, keeping the remove-only policy.
This commit is contained in:
21 files changed
+654
-274
No files matched your search
@@ -723,6 +723,13 @@ class EthernetSwitch(KernelDatapathMixin, BaseNode):
|
||||
previous = {port["port_number"]: port for port in (previous_mapping or [])}
|
||||
wired = dict(self._tap_by_port)
|
||||
for port_number, anchor in self._kernel_ports.items():
|
||||
if not os.path.exists(f"/sys/class/net/{anchor}"):
|
||||
# A kernel port whose peer is stopped keeps its registration
|
||||
# but has no interface yet (the join is deferred by design):
|
||||
# vlan commands would fail with ENODEV and abort the whole
|
||||
# reconcile. Its new settings apply when the peer starts —
|
||||
# the deferred join runs _apply_port_vlan then.
|
||||
continue
|
||||
wired[port_number] = anchor
|
||||
for port_number, iface in wired.items():
|
||||
new_settings = self._port_settings(port_number)
|
||||
@@ -761,7 +768,16 @@ class EthernetSwitch(KernelDatapathMixin, BaseNode):
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
anchor = self._kernel_ports.get(port_number)
|
||||
if anchor is not None:
|
||||
await self._ubridge_send(f'capture start_kernel {anchor} "{output_file}"')
|
||||
# One capture per uBridge process — see
|
||||
# _reserve_kernel_capture; a failed start rolls the port's
|
||||
# flag back so it cannot later stop the winner's capture.
|
||||
try:
|
||||
self._reserve_kernel_capture(anchor)
|
||||
await self._ubridge_send(f'capture start_kernel {anchor} "{output_file}"')
|
||||
except Exception:
|
||||
self._release_kernel_capture(anchor)
|
||||
nio.stop_packet_capture()
|
||||
raise
|
||||
else:
|
||||
ubridge_bridge = self._ubridge_bridge_name(port_number)
|
||||
await self._ubridge_send(f'bridge start_capture {ubridge_bridge} "{output_file}"')
|
||||
@@ -779,8 +795,14 @@ class EthernetSwitch(KernelDatapathMixin, BaseNode):
|
||||
return
|
||||
nio.stop_packet_capture()
|
||||
if self._ubridge_hypervisor and self._ubridge_hypervisor.is_running():
|
||||
if port_number in self._kernel_ports:
|
||||
await self._ubridge_send("capture stop_kernel")
|
||||
anchor = self._kernel_ports.get(port_number)
|
||||
if anchor is not None:
|
||||
# Process-wide and argument-less: only the port owning the
|
||||
# slot may stop it (a second port's stop would kill this
|
||||
# capture while its own flag still says capturing).
|
||||
if self._kernel_capture_owned_by(anchor):
|
||||
await self._ubridge_send("capture stop_kernel")
|
||||
self._release_kernel_capture(anchor)
|
||||
else:
|
||||
ubridge_bridge = self._ubridge_bridge_name(port_number)
|
||||
await self._ubridge_send(f"bridge stop_capture {ubridge_bridge}")
|
||||
|
||||
Reference in new issue
Block a user