marker: validate def BPF once, skip re-validation on inherited fan-out

A marker definition validated its BPF N times — once per link in the fan-out
(start_marker runs validate_bpf_syntax on every copy), spawning one tcpdump -d
subprocess per link for the same expression. Definitions did not validate BPF
at all; only direction was checked.

Move the single validation point to the definition layer (create/update), and
validate each definition's BPF on project load (dropping any that have gone
invalid, like private markers). The inherited fan-out (start_marker) and def
sync (update_marker) now skip validate_bpf_syntax for inherited copies, since
the BPF comes from an already-validated definition. Private per-link markers
still validate inline as before. uBridge still runs pcap_compile at install, so
an invalid expression can never slip through.

Creating a definition over N links now runs one tcpdump instead of N.
This commit is contained in:
YueGuobin committed 2026-08-04 23:20:58 +08:00
1 parent 60e2bbbbbb
commit 1a0ce51f38
4 files changed
+139 -12

No files matched your search

+16 -6
View File
@@ -379,9 +379,15 @@ class UDPLink(Link):
if name in self._markers:
raise ControllerError(f"Marker '{name}' already exists on link {self._id}")
result = validate_bpf_syntax(bpf)
if not result.get("valid"):
raise ControllerError(f"Invalid BPF expression: {result.get('error', 'unknown error')}")
# Validate the BPF only for private per-link markers. An inherited copy
# (``inherited_from`` set) fans out from a definition whose BPF was
# already validated once at create/update (and on project load), so
# re-validating per link would spawn one ``tcpdump -d`` per link for the
# same expression.
if not inherited_from:
result = validate_bpf_syntax(bpf)
if not result.get("valid"):
raise ControllerError(f"Invalid BPF expression: {result.get('error', 'unknown error')}")
if capture_node_id and not inherited_from:
marker_side = self._node_by_id(capture_node_id)
@@ -474,9 +480,13 @@ class UDPLink(Link):
# Merge every changed field into the marker state first.
if bpf is not None and bpf != marker_info["bpf"]:
result = validate_bpf_syntax(bpf)
if not result.get("valid"):
raise ControllerError(f"Invalid BPF expression: {result.get('error', 'unknown error')}")
# An inherited marker is synced from a definition whose BPF was
# already validated at create/update (or load); re-validating per
# link is redundant. Private markers validate here as before.
if not inherited:
result = validate_bpf_syntax(bpf)
if not result.get("valid"):
raise ControllerError(f"Invalid BPF expression: {result.get('error', 'unknown error')}")
marker_info["bpf"] = bpf
if tag is not None:
marker_info["tag"] = tag