manag veth directly with sniffer
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 10s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 10s
This commit is contained in:
143
backend/src/utilities/capture_pipeline.py
Normal file
143
backend/src/utilities/capture_pipeline.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""Manage the optional tc-based bridge capture pipeline for mirrored packet storage."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
from src.config import settings
|
||||
|
||||
logger = logging.getLogger("capture_pipeline")
|
||||
|
||||
|
||||
class CapturePipelineManager:
|
||||
"""Reference-count mirrored capture pipelines per bridge."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._lock = threading.Lock()
|
||||
self._refs: Dict[str, int] = {}
|
||||
self._active: Dict[str, Dict[str, str]] = {}
|
||||
|
||||
def acquire(self, bridge: str) -> Optional[str]:
|
||||
"""Ensure a bridge capture pipeline exists and return the capture interface name."""
|
||||
if not settings.capture_automanage:
|
||||
return settings.capture_interface or None
|
||||
|
||||
bridge = bridge.strip()
|
||||
if not bridge:
|
||||
raise ValueError("Bridge name is required for capture pipeline setup")
|
||||
|
||||
with self._lock:
|
||||
refcount = self._refs.get(bridge, 0)
|
||||
if refcount > 0:
|
||||
self._refs[bridge] = refcount + 1
|
||||
active = self._active.get(bridge, {})
|
||||
return active.get("capture_if") or settings.capture_interface or settings.capture_automanaged_capture_if
|
||||
|
||||
self._setup_bridge_locked(bridge)
|
||||
self._refs[bridge] = 1
|
||||
active = self._active.get(bridge, {})
|
||||
return active.get("capture_if") or settings.capture_interface or settings.capture_automanaged_capture_if
|
||||
|
||||
def release(self, bridge: str) -> None:
|
||||
"""Tear down a bridge capture pipeline when the last session releases it."""
|
||||
if not settings.capture_automanage:
|
||||
return
|
||||
|
||||
bridge = bridge.strip()
|
||||
if not bridge:
|
||||
return
|
||||
|
||||
with self._lock:
|
||||
refcount = self._refs.get(bridge, 0)
|
||||
if refcount <= 1:
|
||||
self._refs.pop(bridge, None)
|
||||
try:
|
||||
self._teardown_bridge_locked(bridge)
|
||||
finally:
|
||||
self._active.pop(bridge, None)
|
||||
return
|
||||
|
||||
self._refs[bridge] = refcount - 1
|
||||
|
||||
def stop_all(self) -> None:
|
||||
"""Remove all managed capture pipelines."""
|
||||
if not settings.capture_automanage:
|
||||
return
|
||||
|
||||
with self._lock:
|
||||
bridges = list(self._active.keys())
|
||||
self._refs.clear()
|
||||
for bridge in bridges:
|
||||
try:
|
||||
self._teardown_bridge_locked(bridge)
|
||||
except Exception:
|
||||
logger.exception("Failed to tear down capture pipeline for bridge %s", bridge)
|
||||
self._active.clear()
|
||||
|
||||
def get_debug_snapshot(self) -> Dict[str, object]:
|
||||
with self._lock:
|
||||
return {
|
||||
"automanage_enabled": settings.capture_automanage,
|
||||
"refs": dict(self._refs),
|
||||
"active": dict(self._active),
|
||||
}
|
||||
|
||||
def _setup_bridge_locked(self, bridge: str) -> None:
|
||||
script = self._repo_root() / "tools" / "setup_bridge_capture.sh"
|
||||
mirror_if = settings.capture_automanaged_mirror_if
|
||||
capture_if = settings.capture_interface or settings.capture_automanaged_capture_if
|
||||
cmd = [
|
||||
str(script),
|
||||
"--bridge",
|
||||
bridge,
|
||||
"--mirror-if",
|
||||
mirror_if,
|
||||
"--capture-if",
|
||||
capture_if,
|
||||
"--build-dir",
|
||||
settings.capture_build_dir,
|
||||
]
|
||||
logger.info(
|
||||
"Setting up capture pipeline for bridge=%s mirror_if=%s capture_if=%s",
|
||||
bridge,
|
||||
mirror_if,
|
||||
capture_if,
|
||||
)
|
||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
self._active[bridge] = {
|
||||
"mirror_if": mirror_if,
|
||||
"capture_if": capture_if,
|
||||
"build_dir": settings.capture_build_dir,
|
||||
}
|
||||
|
||||
def _teardown_bridge_locked(self, bridge: str) -> None:
|
||||
script = self._repo_root() / "tools" / "teardown_bridge_capture.sh"
|
||||
active = self._active.get(bridge, {})
|
||||
mirror_if = active.get("mirror_if") or settings.capture_automanaged_mirror_if
|
||||
capture_if = active.get("capture_if") or settings.capture_interface or settings.capture_automanaged_capture_if
|
||||
cmd = [
|
||||
str(script),
|
||||
"--bridge",
|
||||
bridge,
|
||||
"--mirror-if",
|
||||
mirror_if,
|
||||
"--capture-if",
|
||||
capture_if,
|
||||
]
|
||||
logger.info(
|
||||
"Tearing down capture pipeline for bridge=%s mirror_if=%s capture_if=%s",
|
||||
bridge,
|
||||
mirror_if,
|
||||
capture_if,
|
||||
)
|
||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
|
||||
def _repo_root(self) -> Path:
|
||||
return Path(__file__).resolve().parents[3]
|
||||
|
||||
|
||||
capture_pipeline_manager = CapturePipelineManager()
|
||||
Reference in New Issue
Block a user