mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 05:51:15 +02:00
fix(compute): refuse a duplicate port NIO and wire before bookkeeping
The compute-side backstop of the controller's duplicate-port guard (keep-port-links + live-port bind made the controller guard reliable; this is the layer under it). IOU and Dynamips bound the adapter NIO before the uBridge/hypervisor attach: a failed attach left the port's binding replaced — the link that still owned the port lost its handle and its host state leaked (the IOU EBUSY incident's 8 orphan TAPs). Dynamips was worse: its wiring idempotence check silently skipped the attach on a second binding and rebound only the bookkeeping. A port that already carries an NIO is now refused, and both types bind the NIO only after the wiring succeeded.
This commit is contained in:
4 files changed
+125
-17
No files matched your search
@@ -1312,12 +1312,26 @@ class Router(KernelDatapathMixin, BaseNode):
|
||||
if not adapter.port_exists(port_number):
|
||||
raise DynamipsError(f"Port {port_number} does not exist on adapter {adapter}")
|
||||
|
||||
# The compute-side backstop of the controller's duplicate-port guard:
|
||||
# a port carries at most one NIO, and overwriting the one a link
|
||||
# still owns orphans that link's teardown (its host state leaks).
|
||||
# Refusing also keeps the kernel wiring's own idempotence check from
|
||||
# silently skipping the attach while rebinding the bookkeeping.
|
||||
if adapter.get_nio(port_number) is not None:
|
||||
raise DynamipsError(
|
||||
"Port {port_number} on slot {slot_number} of router '{name}' already has a link".format(
|
||||
name=self._name, slot_number=slot_number, port_number=port_number
|
||||
)
|
||||
)
|
||||
|
||||
if isinstance(nio, NIOBridge):
|
||||
# Kernel datapath: the port's anchor TAP is opened in the
|
||||
# hypervisor and enslaved into the NIO's per-link bridge (the
|
||||
# wiring is deferred to the node's start when no anchors exist).
|
||||
adapter.add_nio(port_number, nio)
|
||||
# Wire before bookkeeping: an attach that fails mid-way leaves
|
||||
# the port unbound instead of half-wired.
|
||||
await self._attach_kernel_nio(slot_number, port_number, nio)
|
||||
adapter.add_nio(port_number, nio)
|
||||
log.debug(
|
||||
'Router "{name}" [{id}]: {nio} bound to port {slot_number}/{port_number}'.format(
|
||||
name=self._name, id=self._id, nio=nio, slot_number=slot_number, port_number=port_number
|
||||
|
||||
@@ -1345,27 +1345,39 @@ class IOUVM(KernelDatapathMixin, BaseNode):
|
||||
if not adapter.port_exists(port_number):
|
||||
raise IOUError(f"Port {port_number} does not exist on adapter {adapter}")
|
||||
|
||||
adapter.add_nio(port_number, nio)
|
||||
log.debug(f'IOU "{self._name}" [{self._id}]: {nio} added to {adapter_number}/{port_number}')
|
||||
# The compute-side backstop of the controller's duplicate-port guard:
|
||||
# a port carries at most one NIO, and overwriting the one a link
|
||||
# still owns orphans that link's teardown (its host state leaks).
|
||||
if adapter.get_nio(port_number) is not None:
|
||||
raise IOUError(
|
||||
"Port {port_number} on adapter {adapter_number} of IOU '{name}' already has a link".format(
|
||||
name=self._name, adapter_number=adapter_number, port_number=port_number
|
||||
)
|
||||
)
|
||||
|
||||
# Wire before bookkeeping: an attach that fails mid-way leaves the
|
||||
# port unbound instead of half-wired.
|
||||
if self.ubridge:
|
||||
if isinstance(nio, NIOBridge):
|
||||
await self._attach_kernel_nio(adapter_number, port_number, nio)
|
||||
return
|
||||
await self._ubridge_send(
|
||||
"iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(
|
||||
name=self._iol_bridge_name(),
|
||||
iol_id=self.application_id,
|
||||
bay=adapter_number,
|
||||
unit=port_number,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport,
|
||||
else:
|
||||
await self._ubridge_send(
|
||||
"iol_bridge add_nio_udp {name} {iol_id} {bay} {unit} {lport} {rhost} {rport}".format(
|
||||
name=self._iol_bridge_name(),
|
||||
iol_id=self.application_id,
|
||||
bay=adapter_number,
|
||||
unit=port_number,
|
||||
lport=nio.lport,
|
||||
rhost=nio.rhost,
|
||||
rport=nio.rport,
|
||||
)
|
||||
)
|
||||
)
|
||||
location = self._iol_location(adapter_number, port_number)
|
||||
await self._ubridge_apply_filters(location, nio.filters)
|
||||
await self._ubridge_apply_markers(location, nio)
|
||||
location = self._iol_location(adapter_number, port_number)
|
||||
await self._ubridge_apply_filters(location, nio.filters)
|
||||
await self._ubridge_apply_markers(location, nio)
|
||||
|
||||
adapter.add_nio(port_number, nio)
|
||||
log.debug(f'IOU "{self._name}" [{self._id}]: {nio} added to {adapter_number}/{port_number}')
|
||||
|
||||
async def adapter_update_nio_binding(self, adapter_number, port_number, nio):
|
||||
"""
|
||||
|
||||
Reference in new issue
Block a user