#!/bin/sh # aurora-vpn (B15): full-tunnel VPN for the Wi-Fi LAN. Two modes (/etc/aurora/vpn.conf VPN_MODE): # vless = sing-box VLESS-over-WS-over-TLS via CDN, TUN singtun0 (config /etc/sing-box/config.json) # wg = kernel WireGuard wg0 (config /etc/wireguard/wg0.conf) [+ B22: wg1 from /etc/wireguard/wg1.conf if present] # B22: with wg1 present both tunnels are up; the LAN default route (table 51820) starts on wg0 and aurora-vpnsel moves it between # wg1 (direct WireGuard over LTE, preferred) and wg0 (via the free-turn relay on 127.0.0.1). The firewall allows both. # LAN clients are policy-routed into the tunnel; the board itself, USB NCM management and the tunnel's own # outbound (to the VPN server/CDN) stay DIRECT on wwan0 (source-based rule -> no loop). # Fail-closed: while up, LAN may leave ONLY via the tunnel; if it is down, LAN has no internet (never LTE). # aurora-vpn up | down | status. Needs kernel 7.2.7-aurora-b15 (TUN; WireGuard for wg mode). TBL=51820; RULE_PRI=100; S=/run/aurora-vpn; mkdir -p $S; LOG=$S/vpn.log [ -f /etc/aurora/router.conf ] && . /etc/aurora/router.conf [ -f /etc/aurora/vpn.conf ] && . /etc/aurora/vpn.conf VPN_MODE=${VPN_MODE:-wg}; LAN_IF=${LAN_IF:-wlan0}; LAN_NET=${LAN_NET:-192.168.77.0/24}; LAN_ADDR=${LAN_ADDR:-192.168.77.1} WAN_IF=${WAN_IF:-wwan0}; MGMT_IF=${MGMT_IF:-usb0}; TUN_IF=${TUN_IF:-singtun0}; TUN_MTU=${TUN_MTU:-1400} SB=/etc/sing-box/config.json; WGC=/etc/wireguard/wg0.conf now() { cut -d' ' -f1 /proc/uptime; } log() { m="[$(now)] $*"; echo "$m"; echo "$m" >> $LOG; echo "<5>[aurora-vpn] $*" > /dev/kmsg 2>/dev/null; } fail() { log "FAIL: $*"; exit 1; } tif() { [ "$VPN_MODE" = wg ] && echo wg0 || echo "$TUN_IF"; } # all tunnel interfaces the LAN may leave through, as an nft list body tl() { if [ "$VPN_MODE" = wg ]; then [ -f /etc/wireguard/wg1.conf ] && echo '"wg0", "wg1"' || echo '"wg0"'; else echo "\"$TUN_IF\""; fi; } # bring up one WireGuard interface from /etc/wireguard/.conf; non-loopback endpoint gets a /32 direct route via WAN wg_up() { i=$1; c=/etc/wireguard/$i.conf ip link add dev $i type wireguard 2>/dev/null || ip link show $i >/dev/null 2>&1 || fail "no WireGuard" [ -f $c ] || fail "$c missing" A=$(sed -n 's/^Address[ ]*=[ ]*//p' $c | head -1); M=$(sed -n 's/^MTU[ ]*=[ ]*//p' $c | head -1); EP=$(sed -n 's/^Endpoint[ ]*=[ ]*//p' $c | head -1 | sed 's/:[0-9]*$//') wg-quick strip $i 2>/dev/null | wg setconf $i /dev/stdin || { ip link del $i; fail "wg setconf $i"; } ip addr flush dev $i; ip addr add "$A" dev $i; ip link set $i mtu "${M:-$TUN_MTU}" up # loopback endpoint = local relay (free-turn-client on 127.0.0.1): its own uplink is board traffic -> main -> wwan0 case "$EP" in 127.*) log "$i endpoint $EP is local relay: no direct route";; *) GW=$(ip route show default dev $WAN_IF | sed -n 's/.* via \([^ ]*\).*/\1/p' | head -1) # point-to-point wwan0: usually no gateway ip route replace "$EP/32" dev $WAN_IF ${GW:+via $GW}; echo "$EP" > $S/endpoint-$i log "$i endpoint $EP direct via $WAN_IF";; esac } firewall() { T=$(tif); TL=$(tl); MSS=$(( ${1:-$TUN_MTU} - 40 )) nft list table inet aurora_router >/dev/null 2>&1 && nft delete table inet aurora_router nft -f - < /proc/sys/net/ipv4/ip_forward } unroute() { while ip rule del from "$LAN_NET" lookup $TBL 2>/dev/null; do :; done while ip rule del from "$LAN_NET" to "$LAN_NET" lookup main 2>/dev/null; do :; done ip route flush table $TBL 2>/dev/null } case "$1" in up) log "=== VPN UP ($VPN_MODE)" if [ "$VPN_MODE" = vless ]; then [ -f $SB ] || fail "$SB missing" command -v sing-box >/dev/null || fail "sing-box not installed" pidof sing-box >/dev/null && { log "sing-box already running"; } || { sing-box check -c $SB 2>>$LOG || fail "sing-box config invalid" setsid sh -c "trap '' HUP; exec sing-box run -c $SB" >> $S/sing-box.log 2>&1 < /dev/null & echo $! > $S/sing-box.pid } i=0; while [ ! -e /sys/class/net/$TUN_IF ] && [ $i -lt 100 ]; do sleep 0.1; i=$((i+1)); done [ -e /sys/class/net/$TUN_IF ] || { tail -8 $S/sing-box.log; fail "$TUN_IF did not appear"; } ip link set $TUN_IF mtu $TUN_MTU 2>/dev/null else wg_up wg0; M0=$M [ -f /etc/wireguard/wg1.conf ] && wg_up wg1 M=$M0 # MSS clamp from wg0 (the smaller MTU, 1280) covers both tunnels fi route_lan; firewall $M log "=== VPN UP OK mode=$VPN_MODE tun=$(tif) LAN $LAN_NET -> $(tif) (table $TBL); fail-closed on" ;; down) log "=== VPN DOWN" [ "$VPN_MODE" = vless ] && { pidof sing-box >/dev/null && kill $(pidof sing-box); sleep 1; pidof sing-box >/dev/null && kill -9 $(pidof sing-box); rm -f $S/sing-box.pid; } [ "$VPN_MODE" = wg ] && { ip link del wg0 2>/dev/null; ip link del wg1 2>/dev/null; } unroute for e in $S/endpoint $S/endpoint-*; do [ -f $e ] && { ip route del "$(cat $e)/32" dev $WAN_IF 2>/dev/null; rm -f $e; }; done nft list table inet aurora_vpn >/dev/null 2>&1 && nft delete table inet aurora_vpn /usr/sbin/aurora-router up >/dev/null 2>&1 || log "note: run 'aurora-router up' to restore LAN->LTE" log "=== VPN DOWN OK (router restored: $(nft list tables 2>/dev/null | tr '\n' ' '))" ;; status) T=$(tif); echo "kernel $(uname -r) mode $VPN_MODE" if [ -e /sys/class/net/$T ]; then echo "$T UP mtu=$(cat /sys/class/net/$T/mtu)" if [ "$VPN_MODE" = vless ]; then echo "sing-box pid=$(pidof sing-box) rss_kB=$(awk '/VmRSS/{print $2}' /proc/$(pidof sing-box 2>/dev/null)/status 2>/dev/null)" tail -3 $S/sing-box.log 2>/dev/null | sed 's/^/ /' else for i in wg0 wg1; do [ -e /sys/class/net/$i ] || continue HS=$(wg show $i latest-handshakes 2>/dev/null | awk '{print $2}' | head -1) echo "$i handshake_age=$([ -n "$HS" ] && [ "$HS" != 0 ] && echo $(( $(date +%s)-HS ))s || echo never) transfer=$(wg show $i transfer 2>/dev/null | awk '{print $2"/"$3}')" done echo "active: $(ip route show table $TBL | awk '/^default/{print $3}') ($(cat /run/aurora-vpnsel/mode 2>/dev/null || echo no-selector))" fi echo "rules:"; ip rule show | grep -E "lookup $TBL|$LAN_NET" echo "table $TBL:"; ip route show table $TBL else echo "$T DOWN"; fi nft list table inet aurora_vpn 2>/dev/null | grep -E "oifname|masquerade|drop" | sed 's/^[ \t]*//' | head ;; *) echo "usage: $0 up|down|status"; exit 2;; esac