#!/usr/bin/env bash set -euo pipefail usage() { cat <<'EOF' Usage: tools/setup_bridge_capture.sh --bridge [--mirror-if mitmcap0] [--capture-if mitmcap1] [--build-dir /tmp/mitm-bpf] Sets up: 1. A veth pair used as a capture mirror target 2. tc ingress packet-id marking on each bridge slave 3. tc mirroring from each bridge slave into the mirror interface 4. A capture-header injector on the capture-side interface Set BACKEND_CAPTURE_INTERFACE to the capture interface printed at the end. EOF } BRIDGE="" MIRROR_IF="mitmcap0" CAPTURE_IF="mitmcap1" BUILD_DIR="/tmp/mitm-bpf" while [[ $# -gt 0 ]]; do case "$1" in --bridge) BRIDGE="$2"; shift 2 ;; --mirror-if) MIRROR_IF="$2"; shift 2 ;; --capture-if) CAPTURE_IF="$2"; shift 2 ;; --build-dir) BUILD_DIR="$2"; shift 2 ;; -h|--help) usage; exit 0 ;; *) echo "Unknown argument: $1" >&2; usage; exit 1 ;; esac done if [[ -z "$BRIDGE" ]]; then usage exit 1 fi if ! command -v tc >/dev/null 2>&1 || ! command -v clang >/dev/null 2>&1 || ! command -v ip >/dev/null 2>&1 || ! command -v bridge >/dev/null 2>&1; then echo "Missing required tools: tc, clang, ip, and bridge must be installed." >&2 exit 1 fi if ! ip link show "$BRIDGE" >/dev/null 2>&1; then echo "Bridge interface not found: $BRIDGE" >&2 exit 1 fi mkdir -p "$BUILD_DIR" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SRC_DIR="$SCRIPT_DIR/ebpf" MARK_OBJ="$BUILD_DIR/mark_packet_id.o" CAPTURE_OBJ="$BUILD_DIR/prepend_capture_header.o" MULTIARCH_INCLUDE="" if command -v gcc >/dev/null 2>&1; then GCC_TRIPLE="$(gcc -dumpmachine 2>/dev/null || true)" if [[ -n "$GCC_TRIPLE" && -d "/usr/include/$GCC_TRIPLE" ]]; then MULTIARCH_INCLUDE="-I/usr/include/$GCC_TRIPLE" fi fi clang -O2 -g -target bpf ${MULTIARCH_INCLUDE:+$MULTIARCH_INCLUDE} -c "$SRC_DIR/mark_packet_id.c" -o "$MARK_OBJ" clang -O2 -g -target bpf ${MULTIARCH_INCLUDE:+$MULTIARCH_INCLUDE} -c "$SRC_DIR/prepend_capture_header.c" -o "$CAPTURE_OBJ" if ! ip link show "$MIRROR_IF" >/dev/null 2>&1; then ip link add "$MIRROR_IF" type veth peer name "$CAPTURE_IF" fi ip link set "$MIRROR_IF" up ip link set "$CAPTURE_IF" up sysctl -q -w "net.ipv6.conf.$MIRROR_IF.disable_ipv6=1" >/dev/null || true sysctl -q -w "net.ipv6.conf.$CAPTURE_IF.disable_ipv6=1" >/dev/null || true mapfile -t PORTS < <(bridge link show master "$BRIDGE" | awk -F': ' '{print $2}' | awk '{print $1}') if [[ ${#PORTS[@]} -eq 0 ]]; then echo "No bridge slave interfaces found for $BRIDGE" >&2 exit 1 fi for port in "${PORTS[@]}"; do tc qdisc replace dev "$port" clsact tc filter replace dev "$port" ingress pref 10 protocol all bpf direct-action obj "$MARK_OBJ" sec classifier tc filter replace dev "$port" ingress pref 20 protocol all matchall action mirred egress mirror dev "$MIRROR_IF" done tc qdisc replace dev "$MIRROR_IF" clsact tc filter replace dev "$MIRROR_IF" egress pref 10 protocol all bpf direct-action obj "$CAPTURE_OBJ" sec classifier cat <