fix: hide kernel-datapath interfaces from the cloud node

The cloud node offered the kernel datapath's host-side interfaces —
node anchors (gv/gq/gi/gd/gx/gp), switch cascade veth ends (gs) and
cross-compute VXLAN devices (gvl) — in its interface list and even
pre-filled them as ports on a freshly created cloud: none of them is
flagged special and only "gns3*" names were filtered. These interfaces
belong to the link or node that created them for the project's whole
lifetime, so bridging a cloud to one would race its owner.

Add is_kernel_datapath_interface() next to the naming contract in
utils/kernel_anchor.py: exact-shape matching (prefix plus its
fixed-length id-derived tail), so a user interface that merely starts
with "gv" is never swallowed. The cloud keeps managed names out of its
host-interface list and default ports mapping, while saved mappings
keep working (the existence checks still see the full interface list).
This commit is contained in:
YueGuobin committed 2026-10-14 10:11:49 +08:00
1 parent b08441b9ad
commit c0568dc25f
4 files changed
+108 -4

No files matched your search

+34
View File
@@ -36,6 +36,7 @@ is what makes links to stopped nodes wireable.
"""
import logging
import re
log = logging.getLogger(__name__)
@@ -113,3 +114,36 @@ def kernel_cascade_names(link_id):
stem = str(link_id).replace("-", "")[:10]
return f"gs{stem}0", f"gs{stem}1"
# Host-side interfaces the kernel datapath owns outright, recognised by
# exact shape — the prefix plus its fixed-length id-derived tail — never by
# bare prefix, so a user interface that merely starts with "gv" or "gs" is
# not swallowed. Consumers keep these out of human-facing interface lists:
# each belongs to the link or node that created it for as long as the
# project exists (the names are deterministic, valid whether it runs or
# not), so nothing else may bridge to it.
_MANAGED_ANCHOR_PREFIXES = frozenset(ANCHOR_PREFIX.values())
_MANAGED_ANCHOR_SUFFIX_SHAPE = re.compile(r"[0-9a-f]{8}e\d{1,2}p\d{1,2}")
_MANAGED_CASCADE_SHAPE = re.compile(r"gs[0-9a-f]{10}[01]")
# a cross-compute VXLAN device: "gvl" + 8 hex of the link id + 4 hex of the
# endpoint node id (the cross-compute VXLAN naming keeps its devices out of
# the anchor and "gns3" namespaces the same deterministic way)
_MANAGED_VXLAN_SHAPE = re.compile(r"gvl[0-9a-f]{12}")
def is_kernel_datapath_interface(name):
"""
Whether *name* is a host interface the kernel datapath manages by name —
a node anchor, a switch cascade veth end, or a cross-compute VXLAN
device. True for exactly the names the naming contract above can
produce: the cloud node (and any other interface picker) uses this to
refuse offering them, since bridging to one would race whatever link or
node already owns it.
"""
# "gvl" begins with the docker anchor prefix "gv", so an anchor miss must
# still fall through to the VXLAN shape
if name[:2] in _MANAGED_ANCHOR_PREFIXES and _MANAGED_ANCHOR_SUFFIX_SHAPE.fullmatch(name[2:]):
return True
return _MANAGED_CASCADE_SHAPE.fullmatch(name) is not None or _MANAGED_VXLAN_SHAPE.fullmatch(name) is not None