Files
clearpilot/system/clearpilot/vpn-monitor.sh
Brian Hanson c33e155c56 UI overhaul, VPN management, controlsd fix, screen recorder removal
- Fix controlsd crash: self.state.name AttributeError when state is int
- Move telemetry tlog import to module level in carstate.py (was per-frame)
- Remove FrogPilot screen recorder from UI (was crashing OMX on init)
- Ready screen: boot logo background, 8-bit READY! sprite, error states
  (panda not connected, car not recognized) with 10s startup grace period
- ClearPilot menu: always opens to General, QButtonGroup for sidebar,
  System Status uses ButtonControl, VPN toggle with process control
- Sidebar hidden on construction (no flash before splash)
- Status window: threaded data collection (QtConcurrent), panda detection
  via scene.pandaType (SPI, not USB), only refreshes when visible
- VPN: moved to system/clearpilot/, SIGTERM graceful shutdown, keepalive
  ping through tunnel, killall openvpn on disable, launched from
  launch_openpilot.sh instead of continue.sh
- Disable gpsd and dashcamd temporarily for perf testing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 22:43:54 -05:00

154 lines
4.4 KiB
Bash
Executable File

#!/usr/bin/bash
# VPN monitor — connects OpenVPN when internet is up, disconnects when down.
# Drops and reconnects when WiFi SSID changes (stale tunnel prevention).
# On non-home networks, resolves VPN hostname via 8.8.8.8 and passes IP directly.
# Keepalive: pings gateway through tunnel, two failures 10s apart = reconnect.
# SIGTERM: gracefully stops tunnel and exits.
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CONF="$SCRIPT_DIR/vpn.ovpn"
VPN_HOST="vpn.hanson.xyz"
VPN_PORT="1194"
HOME_SSID="risa"
VPN_GW="192.168.69.1"
CHECK_HOST="1.1.1.1"
INTERVAL=30
CONNECT_TIMEOUT=30
MAX_FAILURES=3
PREV_SSID=""
FAIL_COUNT=0
ACTIVE_VPN_IP=""
kill_vpn() {
killall openvpn 2>/dev/null
# Clean up host route to VPN server
if [ -n "$ACTIVE_VPN_IP" ]; then
ip route del "$ACTIVE_VPN_IP/32" 2>/dev/null
ACTIVE_VPN_IP=""
fi
}
get_default_gw() {
ip route show default | awk '/via/ {print $3; exit}'
}
resolve_vpn() {
if [ "$CURR_SSID" != "$HOME_SSID" ]; then
dig +short @8.8.8.8 "$VPN_HOST" 2>/dev/null | tail -1
else
dig +short "$VPN_HOST" 2>/dev/null | tail -1
fi
}
# Graceful shutdown on SIGTERM
shutdown() {
echo "$(date): SIGTERM received, stopping vpn and exiting"
kill_vpn
exit 0
}
trap shutdown SIGTERM SIGINT
# Kill other instances of this script and wait for graceful shutdown
for pid in $(pgrep -f 'vpn-monitor.sh' | grep -v $$); do
kill "$pid" 2>/dev/null
done
sleep 5
# Force kill any that didn't exit
for pid in $(pgrep -f 'vpn-monitor.sh' | grep -v $$); do
kill -9 "$pid" 2>/dev/null
done
# Kill any existing VPN and clean up
kill_vpn
sleep 1
while true; do
CURR_SSID="$(iwgetid -r 2>/dev/null)"
# Detect SSID change (only when switching between two known networks)
if [ -n "$PREV_SSID" ] && [ -n "$CURR_SSID" ] && [ "$PREV_SSID" != "$CURR_SSID" ]; then
echo "$(date): wifi changed from '$PREV_SSID' to '$CURR_SSID', dropping vpn"
kill_vpn
FAIL_COUNT=0
sleep 5
fi
PREV_SSID="$CURR_SSID"
if ping -c 1 -W 3 "$CHECK_HOST" > /dev/null 2>&1; then
# Internet is up — check tunnel health if connected
if ip link show tun0 > /dev/null 2>&1; then
# Keepalive: ping gateway through tunnel, two failures 10s apart = dead
if ! ping -c 1 -W 3 -I tun0 "$VPN_GW" > /dev/null 2>&1; then
sleep 10
if ! ping -c 1 -W 3 -I tun0 "$VPN_GW" > /dev/null 2>&1; then
echo "$(date): keepalive failed twice, dropping vpn"
kill_vpn
sleep 5
continue
fi
fi
fi
# Start VPN if not running
if ! ip link show tun0 > /dev/null 2>&1; then
if [ "$FAIL_COUNT" -ge "$MAX_FAILURES" ]; then
# Back off after repeated failures — just wait for next interval
sleep "$INTERVAL"
continue
fi
# Resolve VPN server IP (via 8.8.8.8 on non-home networks)
RESOLVED_IP="$(resolve_vpn)"
if [ -z "$RESOLVED_IP" ]; then
echo "$(date): failed to resolve $VPN_HOST (ssid=$CURR_SSID)"
FAIL_COUNT=$((FAIL_COUNT + 1))
sleep "$INTERVAL"
continue
fi
# Add host route to VPN server via current default gateway
# so VPN traffic survives tun0 coming up
GW="$(get_default_gw)"
if [ -n "$GW" ]; then
ip route replace "$RESOLVED_IP/32" via "$GW"
echo "$(date): host route $RESOLVED_IP via $GW"
fi
ACTIVE_VPN_IP="$RESOLVED_IP"
echo "$(date): starting openvpn -> $RESOLVED_IP (attempt $((FAIL_COUNT + 1))/$MAX_FAILURES, ssid=$CURR_SSID)"
nice -n 19 openvpn --config "$CONF" --remote "$RESOLVED_IP" "$VPN_PORT" --daemon --log-append /tmp/openvpn.log
# Wait for tunnel to come up
CONNECTED=0
for i in $(seq 1 "$CONNECT_TIMEOUT"); do
if ip link show tun0 > /dev/null 2>&1; then
CONNECTED=1
break
fi
sleep 1
done
if [ "$CONNECTED" -eq 1 ]; then
echo "$(date): vpn connected (took ${i}s)"
FAIL_COUNT=0
else
echo "$(date): vpn failed to connect within ${CONNECT_TIMEOUT}s, killing"
kill_vpn
FAIL_COUNT=$((FAIL_COUNT + 1))
if [ "$FAIL_COUNT" -ge "$MAX_FAILURES" ]; then
echo "$(date): $MAX_FAILURES consecutive failures, backing off"
fi
fi
fi
else
# Internet is down — kill VPN if running
if ip link show tun0 > /dev/null 2>&1; then
echo "$(date): internet down, stopping openvpn"
kill_vpn
fi
FAIL_COUNT=0
fi
sleep "$INTERVAL"
done