#!/usr/bin/env bash # Generic before/after interference test for Wi-Fi/SSH/tmux lag. # Run once before introducing the suspected interferer, then again after. # Usage: WIFI_IFACE=wlp1s0 ROUTER_IP=192.168.1.1 REMOTE_IP=1.2.3.4 ./network-interference-capture.sh [duration-seconds] [output-dir] set -euo pipefail DURATION="${1:-90}" STAMP="$(date +%Y%m%d-%H%M%S)" BASE="${2:-./network-interference-$STAMP}" IFACE="${WIFI_IFACE:-}" ROUTER="${ROUTER_IP:-}" PUBLIC="${PUBLIC_IP:-1.1.1.1}" REMOTE="${REMOTE_IP:-}" mkdir -p "$BASE" if [[ -z "$IFACE" ]]; then IFACE=$(iw dev 2>/dev/null | awk '/Interface/ {print $2; exit}' || true) fi if [[ -z "$ROUTER" ]]; then ROUTER=$(ip route show default 2>/dev/null | awk '{print $3; exit}' || true) fi log(){ printf '\n## %s\n' "$*" | tee -a "$BASE/summary.txt"; } run(){ printf '\n$ %s\n' "$*" | tee -a "$BASE/summary.txt"; bash -lc "$*" 2>&1 | tee -a "$BASE/summary.txt"; } log "Network interference test $STAMP duration=${DURATION}s iface=${IFACE:-unknown}" run "date; uname -a; uptime" if [[ -n "$IFACE" ]]; then run "nmcli -f GENERAL,WIFI-PROPERTIES,IP4 device show '$IFACE' 2>/dev/null | sed -n '1,140p' || true" run "nmcli -f ACTIVE,SSID,BSSID,CHAN,RATE,SIGNAL,BARS,SECURITY dev wifi list --rescan yes 2>/dev/null | sed -n '1,100p' || true" run "iw dev '$IFACE' link 2>/dev/null || true" run "ip -s link show '$IFACE'" fi run "ss -nti '( sport = :22 or dport = :22 )' || true" run "tailscale status 2>/dev/null | sed -n '1,100p' || true" run "tailscale netcheck 2>/dev/null || true" log "Starting ping streams" for target in "$ROUTER" "$PUBLIC" "$REMOTE"; do [[ -n "$target" ]] || continue safe="${target//[^A-Za-z0-9_.-]/_}" ping -D -i 0.2 "$target" > "$BASE/ping-$safe.log" 2>&1 & echo $! >> "$BASE/pids" done FILTER_PARTS=() for target in "$ROUTER" "$PUBLIC" "$REMOTE"; do [[ -n "$target" ]] && FILTER_PARTS+=("host $target") done FILTER="port 22" if [[ ${#FILTER_PARTS[@]} -gt 0 ]]; then FILTER="$(IFS=' or '; echo "${FILTER_PARTS[*]}") or port 22" fi if [[ -n "$IFACE" ]] && command -v tshark >/dev/null 2>&1; then log "Starting tshark capture" sudo tshark -i "$IFACE" -a "duration:$DURATION" -w "$BASE/capture.pcapng" -f "$FILTER" > "$BASE/tshark.log" 2>&1 & echo $! >> "$BASE/pids" elif [[ -n "$IFACE" ]] && command -v tcpdump >/dev/null 2>&1; then log "Starting tcpdump capture" sudo timeout "$DURATION" tcpdump -i "$IFACE" -s 0 -w "$BASE/capture.pcap" "$FILTER" > "$BASE/tcpdump.log" 2>&1 & echo $! >> "$BASE/pids" else log "No tshark/tcpdump capture available; install one if packet evidence is needed" fi sleep "$DURATION" if [[ -f "$BASE/pids" ]]; then while read -r pid; do kill "$pid" 2>/dev/null || true; done < "$BASE/pids" fi sleep 1 log "Post-test state" run "date; uptime" [[ -n "$IFACE" ]] && run "ip -s link show '$IFACE'" run "ss -nti '( sport = :22 or dport = :22 )' || true" run "journalctl --since '$(date -d "${DURATION} seconds ago" '+%Y-%m-%d %H:%M:%S')' --no-pager 2>/dev/null | egrep -i 'wlan|wlp|iwlwifi|tailscale|dns|disconnect|deauth|timeout|error|warn|UFW BLOCK' | tail -220 || true" log "Ping summaries" for f in "$BASE"/ping-*.log; do [[ -e "$f" ]] || continue echo "--- $f" | tee -a "$BASE/summary.txt" tail -20 "$f" | tee -a "$BASE/summary.txt" done printf '\nSaved network interference test directory: %s\nMain summary: %s\n' "$BASE" "$BASE/summary.txt"