feat(docker): bpf packet filter on kernel links via cls_bpf match-drop

The GNS3 bpf filter (one expression per line, any line matching drops the
packet) now runs on the kernel datapath: uBridge's tc bpf_drop compiles
each line with libpcap (same compiler as the relay filter) into classic
BPF, attaches it to the veth host end's clsact egress as a cls_bpf
classifier with a gact TC_ACT_SHOT action, and the kernel migrates the
cBPF to eBPF internally -- no CAP_BPF needed.

- reconcile is flush + one add per line at priorities 10, 11, ... (OR
  semantics, same as the relay's per-line filters); tc reset (link
  delete / clear) is the full restore in uBridge and clears them
- capability-gated: the compute probes "tc capabilities" once per uBridge
  process (cached, invalidated on stop); without cbpf=1 the filter apply
  fails with an actionable upgrade error, and old uBridge builds without
  the tc module are never touched when no bpf filter is present
- a line that compiled for the controller's tcpdump but not for the
  compute's uBridge libpcap is skipped with a warning, mirroring the
  relay path
- KERNEL_UNSUPPORTED_FILTERS shrinks to frequency_drop (its eBPF stateful
  classifier is still pending, spec part B)

e2e (22/22): bpf "icmp" = 100% loss; size-discriminating expression
("greater 150") passes 98-byte and drops 300-byte pings (real byte-level
match); two-line OR; coexistence with netem (delay 50 -> small pings
100.2 ms while big dropped); clear removes clsact; suspend/resume keeps
the drops; link delete + recreate leaves no residual filters.
This commit is contained in:
YueGuobin committed 2026-10-06 00:21:08 +08:00
1 parent bd1c205651
commit 2b45f29b2c
7 files changed
+303 -50

No files matched your search

+7 -6
View File
@@ -66,7 +66,6 @@ class TAPNIO(BaseModel):
class BridgeNIOType(str, Enum):
bridge = "nio_bridge"
@@ -74,17 +73,19 @@ class BridgeNIO(BaseModel):
"""
Kernel-datapath bridge Network Input/Output properties. The NIO tells the
node to enslave its veth host end into the named kernel bridge instead of
wiring a uBridge UDP relay. Impairment filters with a tc netem equivalent
(delay, packet_loss, corrupt) run on the veth host end; the relay-only
kinds (frequency_drop, bpf) are rejected. Markers and packet capture are
served by uBridge's AF_PACKET modules on the veth host end.
wiring a uBridge UDP relay. Impairment filters run on the veth host end:
delay/packet_loss/corrupt as one tc netem qdisc, bpf as cls_bpf match-drop
classifiers (needs a uBridge reporting cbpf). frequency_drop still has no
kernel equivalent and is rejected. Markers and packet capture are served
by uBridge's AF_PACKET modules on the veth host end.
"""
type: BridgeNIOType
bridge: str = Field(..., description="Kernel bridge name e.g. gns3a1b2c3d4e5")
suspend: Optional[bool] = Field(None, description="Suspend the NIO")
filters: Optional[dict] = Field(
None, description="Packet filters served by tc netem on the veth host end (delay/packet_loss/corrupt)"
None,
description="Packet filters served on the veth host end (delay/packet_loss/corrupt via netem, bpf via cls_bpf)",
)
markers: Optional[dict] = Field(None, description="Traffic-insight markers (attached to the veth host end)")