feat(docker): wire Docker-to-Docker links through a kernel bridge

Eligible links (both endpoints Docker, same compute, no filters/markers,
stopped nodes, no unix-socket NIO containers) are wired as veth pairs
enslaved into a per-link Linux kernel bridge (gns3 + 11 hex of the link
id, filling IFNAMSIZ) instead of the uBridge UDP relay, so frames flow
entirely in the kernel.

- NIOBridge NIO type: the controller decides the datapath in
  UDPLink._prepare and both computes enslave their deterministic gv*/gc*
  veth pair via uBridge's docker/brctl modules
- carrier/suspend = host-end admin state; both endpoints race-safely
  create/delete the bridge (EEXIST/EBUSY/ENOENT tolerated)
- veth adapters can still attach relay (UDP) links on a running node:
  lazy relay bridge over AF_PACKET on the host end (add_nio_ethernet)
- filters/markers/capture are rejected with explicit errors on this
  datapath (they live in the uBridge relay); vendor unix-socket
  containers are excluded; Server.enable_kernel_datapath is the
  kill-switch (default on)
- host-end veths survive container death and are removed on stop plus
  swept by name on the next start (crash/stale recovery)
This commit is contained in:
YueGuobin committed 2026-10-06 00:21:07 +08:00
1 parent 65c858d455
commit 37d7b9121e
13 files changed
+1031 -21

No files matched your search

+15
View File
@@ -111,6 +111,15 @@ class Link:
"""
return self._filters
@property
def kernel_datapath(self):
"""
Whether this link is wired on the kernel datapath (veth pairs
enslaved into a per-link Linux bridge — no uBridge relay in the
forwarding path, so filters, markers and capture are unavailable).
"""
return any(d.get("type") == "nio_bridge" for d in (getattr(self, "_link_data", None) or []))
@property
def markers(self):
"""
@@ -224,6 +233,12 @@ class Link:
except FilterValidationError as e:
raise ControllerError(f"Invalid packet filter parameters: {e!s}")
if new_filters and self.kernel_datapath:
raise ControllerError(
"Packet filters are not supported on kernel-datapath links (no uBridge relay "
"in the forwarding path); delete and recreate the link to use filters"
)
if new_filters != self.filters:
self._filters = new_filters
if self._created: