feat(docker): expose netem-extension packet filters on kernel links

Surface the netem extensions delivered in uBridge feature/tc-netem-ext
as GNS3 filter types: rate (tc-style bandwidth string), reorder (needs
delay), gemodel (Gilbert-Elliot bursty loss, exclusive with
packet_loss), duplicate, seed and limit, plus the delay jitter
distribution and the loss/dup correlation parameters. All are
kernel-datapath only — the relay's packet-filter registry has no
equivalent, so available_filters hides them on relay links, setting one
on a created relay link returns 409, and the project-load path drops
them with a warning (update_filters runs before the datapath is
decided, so it cannot reject there). Validation mirrors the tc grammar:
reorder requires delay, gemodel and packet_loss are mutually exclusive,
a distribution requires jitter > 0, rate is integer+unit capped at
100gbit.

The translation in DockerVM._netem_command_parts emits the frozen
grammar order and gates extension keywords on the 'tc capabilities'
netem token list (probed once per uBridge process; a plain
delay/loss/corrupt/dup filter never probes, keeping old uBridge builds
untouched; the correl suffix has no token and gates on 'rate' as the
extension-build marker).

Reconcile became reset-before-set: the kernel's netem change merges
optional attributes (rate, correlation, reorder, corrupt, gemodel,
distribution, seed keep their previous value when the new message omits
them), so removing a parameter needs an explicit 'tc reset' — the
bpf_drop filters are re-added right after in the same flow, so the
clsact churn is invisible. The capability check runs before the reset
so a failed update leaves the previous qdisc in place.

e2e (isolated instance, kernel link): 24/24 — rate 512kbit adds exactly
2x the per-packet serialization to the RTT (45.5 ms for 1400-byte
pings), paretonormal jitter + rate-cleared leak check, reorder gap 5,
gemodel 50% bursty loss, duplicate, loss 30% correl 50%, seed 42 +
limit 5000 visible in the qdisc dump, suspend/resume and node restart
keep the filters, relay link rejects rate with 409, teardown leaves no
netem. Full unit suite: 1916 passed.
This commit is contained in:
YueGuobin committed 2026-10-06 00:21:08 +08:00
1 parent b1eb0984bc
commit d4b57d49fb
13 files changed
+1012 -108

No files matched your search

+14
View File
@@ -356,6 +356,20 @@ class BaseManager:
except OSError as e:
raise ComputeError(f"Could not create an UDP connection to {rhost}:{rport}: {e}")
nio = NIOUDP(lport, rhost, rport)
# The netem-extension filters (rate, reorder, gemodel…) have no
# uBridge relay equivalent — reject them here (the controller
# already keeps them off relay links; this is the second guard
# for direct compute API use, mirroring the kernel-NIO guard).
from gns3server.utils.packet_filter_validation import kernel_only_features
kernel_only = kernel_only_features(nio_settings.get("filters") or {})
if kernel_only:
raise ComputeError(
"Packet filter(s) {} only run on a kernel-datapath link "
"(tc netem on the veth host end); the uBridge relay has no equivalent".format(
", ".join(sorted(kernel_only))
)
)
nio.filters = nio_settings.get("filters", {})
nio.markers = nio_settings.get("markers", {})
nio.suspend = nio_settings.get("suspend", False)